Skip to content

Commit

Permalink
Improved gr config for tests. Added runtime ns for working with chart…
Browse files Browse the repository at this point in the history
…s externally.
  • Loading branch information
awkay committed Feb 25, 2025
1 parent 17da0f8 commit 8f8f87e
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 10 deletions.
16 changes: 10 additions & 6 deletions guardrails-test.edn
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
{:defn-macro nil
:throw? true
:emit-spec? true
:disable-exclusions? true
:expound {:show-valid-values? false
:print-specs? false}}
{:defn-macro nil
:throw? true
:emit-spec? false
:disable-exclusions? true
:guardrails/use-stderr? true
:guardrails/compact? true
:guardrails/trace? true
:guardrails/stack-trace :none ; or :prune, :full, or :none
:expound {:show-valid-values? false
:print-specs? false}}
64 changes: 64 additions & 0 deletions src/main/com/fulcrologic/statecharts/runtime.cljc
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
(ns com.fulcrologic.statecharts.runtime
"This ns provides utility functions for working with a statecharts runtime environment, where you have some
number of statecharts running using the setup defined as a statechart env. Note that a statechart env is
a map that defines a statechart system's environment: That is to say the processing algorithm,
event queue, the working memory store, the invocation processor, the execution model, and the data model. All of
these components are pluggable.
This namespace also assumes that you are doing the *recommended* (but not required) step of using a working memory
store and event queue via those protocols (the algorithm can also be used as a pure function).
So, the functions in this namespace that ask for an ::sc/env (see the function definition) require that such
both of those are provided.
The `env` INSIDE of statechart lambdas is known as a *processing environment*. A *processing environment* IS
a system env that ALSO contains keys relating to the current execution context. Such a map can be used with
these functions as well (in other words these functions work both outside AND inside of a statechart),
though they assume you're passing a processing environment (and ignore the processing keys)."
(:require
[com.fulcrologic.guardrails.malli.core :refer [>defn => ?]]
[com.fulcrologic.statecharts.algorithms.v20150901-impl :as impl]
[com.fulcrologic.statecharts :as sc]
[com.fulcrologic.statecharts.protocols :as sp]
[com.fulcrologic.statecharts.malli-specs]
[taoensso.timbre :as log]))

(>defn current-configuration
"Returns the current configuration of the statechart with the given session-id. You MUST be using an implementation
that uses a WorkingMemoryStore for this to work.
Returns a set of keywords for the states that are active, or nil if there is no such session."
[{::sc/keys [working-memory-store] :as env} session-id]
[[:map ::sc/working-memory-store] ::sc/session-id => (? [:set :keyword])]
(let [{::sc/keys [configuration]} (sp/get-working-memory working-memory-store env session-id)]
configuration))

(>defn processing-env
"Returns the processing env (as would be seen from within a statechart expression) for the given system environment
and session-id.
Assumes you are using the v20150901 implementation of statecharts (the default), and that your system has a working
memory store.
"
[{::sc/keys [working-memory-store] :as system-env} session-id]
[::sc/env ::sc/session-id => (? ::sc/processing-env)]
(let [{::sc/keys [statechart-src] :as wmem} (sp/get-working-memory working-memory-store system-env session-id)]
(when statechart-src
(impl/processing-env system-env statechart-src wmem))))

(>defn session-data
"Return the statechart-local data for the given system environment, statechart session id, and abstract data path
(defined by the data model itself). Note that data models that use runtime context (such as active state) will not
work properly with this method. If no `data-path` is provided, then the entire data model value is returned.
Assumes you are using the v20150901 implementation of statecharts (the default).
"
([{::sc/keys [data-model working-memory-store] :as env} session-id]
[[:map ::sc/data-model ::sc/working-memory-store] ::sc/session-id => :any]
(let [penv (processing-env env session-id)]
(when penv (sp/current-data data-model penv))))
([{::sc/keys [data-model working-memory-store] :as env} session-id data-path]
[[:map ::sc/data-model ::sc/working-memory-store] ::sc/session-id vector? => :any]
(let [penv (processing-env env session-id)]
(when penv
(sp/get-at data-model penv data-path)))))
11 changes: 7 additions & 4 deletions src/main/com/fulcrologic/statecharts/testing.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -165,11 +165,14 @@
(ran-in-order? [_ frefs] (ran-in-order? (::sc/execution-model env) frefs)))

(defn new-testing-env
"Returns a new testing `env` that can be used to run events against a state chart or otherwise
manipulate it for tests.
"Returns a new testing object (test environment) that can be used to run events against a state chart or otherwise
manipulate it for tests. This testing object INCLUDES the key `:env` which is a standard statechart *system env*.
`mocks` is a map from expression *value* (e.g. fn ref) to either a literal value or a `(fn [env])`, where
`env` will be the testing env with :ncalls set to the ordinal (from 1) number of times that expression
The configuration options can be used to provide overrides to how the processor and data model elements of the system
env are created. The `event-queue` defaults to a mock queue.
The `mocks` parameter is a map from expression *value* (e.g. fn ref) to either a literal value or a `(fn [env])`, where
the `env` in the mocks will be the *testing env* with :ncalls set to the ordinal (from 1) number of times that expression
has been run.
`validator` is a `(fn [machine working-memory] vector?)` that returns a list of problems with the
Expand Down
42 changes: 42 additions & 0 deletions src/test/com/fulcrologic/statecharts/runtime_test.cljc
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
(ns com.fulcrologic.statecharts.runtime-test
(:require
[com.fulcrologic.statecharts.simple :as simple]
[com.fulcrologic.statecharts.runtime :as rt]
[com.fulcrologic.statecharts.chart :as chart]
[com.fulcrologic.statecharts.elements :as ele :refer [on-entry state transition]]
[com.fulcrologic.statecharts.testing :as testing]
[fulcro-spec.core :refer [=> assertions specification]]))

(specification
"current-configuration"
(let [chart (chart/statechart
{:initial :s2}
(state {:id :uber}
(state {:id :s1}
(on-entry {})
(transition {:event :ev1, :target :s2}))
(state {:id :s2})))
{:keys [env] :as test-env} (testing/new-testing-env {:statechart chart
:session-id :session} {})]
(testing/start! test-env)
(assertions
"finds the set of live states for a statechart session"
(rt/current-configuration env :session) => #{:uber :s2})))

(specification
"session-data"
(let [chart (chart/statechart {:initial :s2}
(ele/data-model {:expr {:x {:y 1}}})
(state {:id :uber}
(state {:id :s1}
(on-entry {})
(transition {:event :ev1, :target :s2}))
(state {:id :s2})))
{:keys [env] :as test-env} (testing/new-testing-env {:statechart chart
:session-id :session} {})]
(testing/start! test-env)
(assertions
"can return the full data model value"
(rt/session-data env :session) => {:x {:y 1}}
"can return the data at a path"
(rt/session-data env :session [:x :y]) => 1)))

0 comments on commit 8f8f87e

Please sign in to comment.