Annotate

spec/util_fsm_spec.lua @ 13652:a08065207ef0

net.server_epoll: Call :shutdown() on TLS sockets when supported Comment from Matthew: This fixes a potential issue where the Prosody process gets blocked on sockets waiting for them to close. Unlike non-TLS sockets, closing a TLS socket sends layer 7 data, and this can cause problems for sockets which are in the process of being cleaned up. This depends on LuaSec changes which are not yet upstream. From Martijn's original email: So first my analysis of luasec. in ssl.c the socket is put into blocking mode right before calling SSL_shutdown() inside meth_destroy(). My best guess to why this is is because meth_destroy is linked to the __close and __gc methods, which can't exactly be called multiple times and luasec does want to make sure that a tls session is shutdown as clean as possible. I can't say I disagree with this reasoning and don't want to change this behaviour. My solution to this without changing the current behaviour is to introduce a shutdown() method. I am aware that this overlaps in a conflicting way with tcp's shutdown method, but it stays close to the OpenSSL name. This method calls SSL_shutdown() in the current (non)blocking mode of the underlying socket and returns a boolean whether or not the shutdown is completed (matching SSL_shutdown()'s 0 or 1 return values), and returns the familiar ssl_ioerror() strings on error with a false for completion. This error can then be used to determine if we have wantread/wantwrite to finalize things. Once meth_shutdown() has been called once a shutdown flag will be set, which indicates to meth_destroy() that the SSL_shutdown() has been handled by the application and it shouldn't be needed to set the socket to blocking mode. I've left the SSL_shutdown() call in the LSEC_STATE_CONNECTED to prevent TOCTOU if the application reaches a timeout for the shutdown code, which might allow SSL_shutdown() to clean up anyway at the last possible moment. Another thing I've changed to luasec is the call to socket_setblocking() right before calling close(2) in socket_destroy() in usocket.c. According to the latest POSIX[0]: Note that the requirement for close() on a socket to block for up to the current linger interval is not conditional on the O_NONBLOCK setting. Which I read to mean that removing O_NONBLOCK on the socket before close doesn't impact the behaviour and only causes noise in system call tracers. I didn't touch the windows bits of this, since I don't do windows. For the prosody side of things I've made the TLS shutdown bits resemble interface:onwritable(), and put it under a combined guard of self._tls and self.conn.shutdown. The self._tls bit is there to prevent getting stuck on this condition, and self.conn.shutdown is there to prevent the code being called by instances where the patched luasec isn't deployed. The destroy() method can be called from various places and is read by me as the "we give up" error path. To accommodate for these unexpected entrypoints I've added a single call to self.conn:shutdown() to prevent the socket being put into blocking mode. I have no expectations that there is any other use here. Same as previous, the self.conn.shutdown check is there to make sure it's not called on unpatched luasec deployments and self._tls is there to make sure we don't call shutdown() on tcp sockets. I wouldn't recommend logging of the conn:shutdown() error inside close(), since a lot of clients simply close the connection before SSL_shutdown() is done.
author Martijn van Duren <martijn@openbsd.org>
date Thu, 06 Feb 2025 15:04:38 +0000
parent 13019:8a2f75e38eb2
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
13019
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
1 describe("util.fsm", function ()
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
2 local new_fsm = require "util.fsm".new;
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
3
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
4 do
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
5 local fsm = new_fsm({
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
6 transitions = {
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
7 { name = "melt", from = "solid", to = "liquid" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
8 { name = "freeze", from = "liquid", to = "solid" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
9 };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
10 });
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
11
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
12 it("works", function ()
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
13 local water = fsm:init("liquid");
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
14 water:freeze();
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
15 assert.equal("solid", water.state);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
16 water:melt();
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
17 assert.equal("liquid", water.state);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
18 end);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
19
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
20 it("does not allow invalid transitions", function ()
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
21 local water = fsm:init("liquid");
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
22 assert.has_errors(function ()
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
23 water:melt();
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
24 end, "Invalid state transition: liquid cannot melt");
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
25
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
26 water:freeze();
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
27 assert.equal("solid", water.state);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
28
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
29 water:melt();
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
30 assert.equal("liquid", water.state);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
31
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
32 assert.has_errors(function ()
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
33 water:melt();
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
34 end, "Invalid state transition: liquid cannot melt");
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
35 end);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
36 end
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
37
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
38 it("notifies observers", function ()
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
39 local n = 0;
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
40 local has_become_solid = spy.new(function (transition)
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
41 assert.is_table(transition);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
42 assert.equal("solid", transition.to);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
43 assert.is_not_nil(transition.instance);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
44 n = n + 1;
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
45 if n == 1 then
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
46 assert.is_nil(transition.from);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
47 assert.is_nil(transition.from_attr);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
48 elseif n == 2 then
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
49 assert.equal("liquid", transition.from);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
50 assert.is_nil(transition.from_attr);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
51 assert.equal("freeze", transition.name);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
52 end
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
53 end);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
54 local is_melting = spy.new(function (transition)
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
55 assert.is_table(transition);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
56 assert.equal("melt", transition.name);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
57 assert.is_not_nil(transition.instance);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
58 end);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
59 local fsm = new_fsm({
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
60 transitions = {
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
61 { name = "melt", from = "solid", to = "liquid" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
62 { name = "freeze", from = "liquid", to = "solid" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
63 };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
64 state_handlers = {
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
65 solid = has_become_solid;
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
66 };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
67
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
68 transition_handlers = {
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
69 melt = is_melting;
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
70 };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
71 });
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
72
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
73 local water = fsm:init("liquid");
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
74 assert.spy(has_become_solid).was_not_called();
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
75
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
76 local ice = fsm:init("solid"); --luacheck: ignore 211/ice
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
77 assert.spy(has_become_solid).was_called(1);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
78
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
79 water:freeze();
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
80
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
81 assert.spy(is_melting).was_not_called();
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
82 water:melt();
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
83 assert.spy(is_melting).was_called(1);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
84 end);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
85
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
86 local function test_machine(fsm_spec, expected_transitions, test_func)
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
87 fsm_spec.handlers = fsm_spec.handlers or {};
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
88 fsm_spec.handlers.transitioned = function (transition)
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
89 local expected_transition = table.remove(expected_transitions, 1);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
90 assert.same(expected_transition, {
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
91 name = transition.name;
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
92 to = transition.to;
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
93 to_attr = transition.to_attr;
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
94 from = transition.from;
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
95 from_attr = transition.from_attr;
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
96 });
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
97 end;
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
98 local fsm = new_fsm(fsm_spec);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
99 test_func(fsm);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
100 assert.equal(0, #expected_transitions);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
101 end
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
102
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
103
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
104 it("handles transitions with the same name", function ()
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
105 local expected_transitions = {
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
106 { name = nil , from = "none", to = "A" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
107 { name = "step", from = "A", to = "B" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
108 { name = "step", from = "B", to = "C" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
109 { name = "step", from = "C", to = "D" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
110 };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
111
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
112 test_machine({
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
113 default_state = "none";
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
114 transitions = {
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
115 { name = "step", from = "A", to = "B" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
116 { name = "step", from = "B", to = "C" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
117 { name = "step", from = "C", to = "D" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
118 };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
119 }, expected_transitions, function (fsm)
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
120 local i = fsm:init("A");
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
121 i:step(); -- B
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
122 i:step(); -- C
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
123 i:step(); -- D
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
124 assert.has_errors(function ()
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
125 i:step();
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
126 end, "Invalid state transition: D cannot step");
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
127 end);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
128 end);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
129
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
130 it("handles supports wildcard transitions", function ()
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
131 local expected_transitions = {
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
132 { name = nil , from = "none", to = "A" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
133 { name = "step", from = "A", to = "B" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
134 { name = "step", from = "B", to = "C" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
135 { name = "reset", from = "C", to = "A" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
136 { name = "step", from = "A", to = "B" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
137 { name = "step", from = "B", to = "C" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
138 { name = "step", from = "C", to = "D" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
139 };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
140
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
141 test_machine({
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
142 default_state = "none";
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
143 transitions = {
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
144 { name = "step", from = "A", to = "B" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
145 { name = "step", from = "B", to = "C" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
146 { name = "step", from = "C", to = "D" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
147 { name = "reset", from = "*", to = "A" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
148 };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
149 }, expected_transitions, function (fsm)
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
150 local i = fsm:init("A");
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
151 i:step(); -- B
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
152 i:step(); -- C
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
153 i:reset(); -- A
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
154 i:step(); -- B
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
155 i:step(); -- C
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
156 i:step(); -- D
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
157 assert.has_errors(function ()
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
158 i:step();
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
159 end, "Invalid state transition: D cannot step");
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
160 end);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
161 end);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
162
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
163 it("supports specifying multiple from states", function ()
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
164 local expected_transitions = {
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
165 { name = nil , from = "none", to = "A" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
166 { name = "step", from = "A", to = "B" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
167 { name = "step", from = "B", to = "C" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
168 { name = "reset", from = "C", to = "A" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
169 { name = "step", from = "A", to = "B" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
170 { name = "step", from = "B", to = "C" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
171 { name = "step", from = "C", to = "D" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
172 };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
173
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
174 test_machine({
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
175 default_state = "none";
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
176 transitions = {
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
177 { name = "step", from = "A", to = "B" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
178 { name = "step", from = "B", to = "C" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
179 { name = "step", from = "C", to = "D" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
180 { name = "reset", from = {"B", "C", "D"}, to = "A" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
181 };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
182 }, expected_transitions, function (fsm)
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
183 local i = fsm:init("A");
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
184 i:step(); -- B
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
185 i:step(); -- C
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
186 i:reset(); -- A
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
187 assert.has_errors(function ()
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
188 i:reset();
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
189 end, "Invalid state transition: A cannot reset");
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
190 i:step(); -- B
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
191 i:step(); -- C
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
192 i:step(); -- D
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
193 assert.has_errors(function ()
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
194 i:step();
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
195 end, "Invalid state transition: D cannot step");
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
196 end);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
197 end);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
198
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
199 it("handles transitions with the same start/end state", function ()
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
200 local expected_transitions = {
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
201 { name = nil , from = "none", to = "A" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
202 { name = "step", from = "A", to = "B" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
203 { name = "step", from = "B", to = "B" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
204 { name = "step", from = "B", to = "B" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
205 };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
206
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
207 test_machine({
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
208 default_state = "none";
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
209 transitions = {
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
210 { name = "step", from = "A", to = "B" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
211 { name = "step", from = "B", to = "B" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
212 };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
213 }, expected_transitions, function (fsm)
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
214 local i = fsm:init("A");
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
215 i:step(); -- B
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
216 i:step(); -- B
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
217 i:step(); -- B
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
218 end);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
219 end);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
220
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
221 it("can identify instances of a specific fsm", function ()
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
222 local fsm1 = new_fsm({ default_state = "a" });
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
223 local fsm2 = new_fsm({ default_state = "a" });
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
224
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
225 local i1 = fsm1:init();
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
226 local i2 = fsm2:init();
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
227
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
228 assert.truthy(fsm1:is_instance(i1));
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
229 assert.truthy(fsm2:is_instance(i2));
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
230
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
231 assert.falsy(fsm1:is_instance(i2));
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
232 assert.falsy(fsm2:is_instance(i1));
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
233 end);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
234
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
235 it("errors when an invalid initial state is passed", function ()
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
236 local fsm1 = new_fsm({
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
237 transitions = {
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
238 { name = "", from = "A", to = "B" };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
239 };
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
240 });
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
241
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
242 assert.has_no_errors(function ()
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
243 fsm1:init("A");
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
244 end);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
245
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
246 assert.has_errors(function ()
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
247 fsm1:init("C");
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
248 end);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
249 end);
8a2f75e38eb2 util.fsm: New utility lib for finite state machines
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
250 end);