From 94b4b11180a4c1ef0a94fc0ab2a321ca04988c3f Mon Sep 17 00:00:00 2001 From: Grazfather Date: Sun, 26 Sep 2021 09:12:12 -0400 Subject: [PATCH] Add tests --- lib/testing/assert.fnl | 4 ++ test/new-statemachine-test.fnl | 97 ++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 test/new-statemachine-test.fnl diff --git a/lib/testing/assert.fnl b/lib/testing/assert.fnl index becbf5f..3bd7478 100644 --- a/lib/testing/assert.fnl +++ b/lib/testing/assert.fnl @@ -4,6 +4,10 @@ [actual expected message] (assert (= actual expected) (.. message " instead got " (hs.inspect actual)))) +(fn exports.not-eq? + [first second message] + (assert (not= first second) (.. message " instead both were " (hs.inspect first)))) + (fn exports.ok? [actual message] (assert (= (not (not actual)) true) (.. message " instead got " (hs.inspect actual)))) diff --git a/test/new-statemachine-test.fnl b/test/new-statemachine-test.fnl new file mode 100644 index 0000000..725ccc6 --- /dev/null +++ b/test/new-statemachine-test.fnl @@ -0,0 +1,97 @@ +(local is (require :lib.testing.assert)) +(local statemachine (require :lib.new-statemachine)) +(local atom (require :lib.atom)) + +(fn make-fsm + [] + (statemachine.new + ;; States that the machine can be in mapped to their actions and transitions + {:state {:current-state :closed + :context {:i 0 + :event nil}} + + :states {:closed {:toggle (fn closed->opened + [state action extra] + {:state {:current-state :opened + :context {:i (+ state.context.i 1)}} + :effect :opening})} + :opened {:toggle (fn opened->closed + [state action extra] + {:state {:current-state :closed + :context {:i (+ state.context.i 1)}} + :effect :closing})}}})) + +(describe + "State Machine" + (fn [] + + (it "Should create a new fsm in the closed state" + (fn [] + (let [fsm (make-fsm)] + (is.eq? (. (atom.deref fsm.state) :current-state) :closed "Initial state was not closed")))) + + (it "Should include some methods" + (fn [] + (let [fsm (make-fsm)] + (is.eq? (type fsm.get-state) :function "No get-state method") + (is.eq? (type fsm.signal) :function "No get-state method") + (is.eq? (type fsm.subscribe) :function "No get-state method")))) + + (it "Should transition to opened on toggle action" + (fn [] + (let [fsm (make-fsm)] + (is.eq? (fsm.signal :toggle) true "Dispatch did not return true for handled event") + (is.eq? (. (atom.deref fsm.state) :current-state) :opened "State did not transition to opened")))) + + (it "Should transition from closed -> opened -> closed" + (fn [] + (let [fsm (make-fsm)] + (fsm.signal :toggle) + (fsm.signal :toggle) + (is.eq? (. (atom.deref fsm.state) :current-state) :closed "State did not transition back to closed") + (is.eq? (. (atom.deref fsm.state) :context :i) 2 "context.i should be 2 from 2 transitions")))) + + (it "Should not explode when dispatching an unhandled event" + (fn [] + (let [fsm (make-fsm)] + (is.eq? (fsm.signal :fail nil) false "The FSM exploded from dispatching a :fail event")))) + + (it "Subscribers should be called on events" + (fn [] + (let [fsm (make-fsm)] + (let [i (atom.new 0)] + (fsm.subscribe (fn [] (atom.swap! i (fn [v] (+ v 1))))) + (fsm.signal :toggle) + (is.eq? (atom.deref i) 1 "The subscriber was not called"))))) + + (it "Subscribers should be provided old and new context, action, effect, and extra" + (fn [] + (let [fsm (make-fsm)] + (let [_old (atom.new nil) + _new (atom.new nil) + _action (atom.new nil) + _effect (atom.new nil) + _extra (atom.new nil)] + (fsm.subscribe (fn [{: prev-state : next-state : action : effect : extra}] + (atom.swap! _old (fn [_ nv] nv) prev-state) + (atom.swap! _new (fn [_ nv] nv) next-state) + (atom.swap! _action (fn [_ nv] nv) action) + (atom.swap! _effect (fn [_ nv] nv) effect) + (atom.swap! _extra (fn [_ nv] nv) extra))) + (fsm.signal :toggle :extra) + (is.not-eq? (. (atom.deref _old) :context :i) + (. (atom.deref _new) :context :i) "Subscriber did not get old and new state") + (is.eq? (atom.deref _action) :toggle "Subscriber did not get correct action") + (is.eq? (atom.deref _effect) :opening "Subscriber did not get correct effect") + (is.eq? (atom.deref _extra) :extra "Subscriber did not get correct extra"))))) + + ;; (print (hs.inspect fsm)) ;; DELETEME + (it "Subscribers should be able to unsubscribe" + (fn [] + (let [fsm (make-fsm)] + (let [i (atom.new 0) + unsub (fsm.subscribe (fn [] (atom.swap! i (fn [v] (+ v 1)))))] + (fsm.signal :toggle) + (unsub) + (fsm.signal :toggle) + (is.eq? (atom.deref i) 1 "The subscriber was called after unsubscribing")))))))