require("lunit") local network = require("network") local serialization = require("serialization") local BaseControl = require("bc") local Event = BaseControl.Event module("bc-tests", package.seeall, lunit.testcase) -- Basics {{{ function test_init() local bc = BaseControl({test_noun=true}, {test_verb=function() end}) assert_true(bc:has_noun("test_noun"), "Local nouns were not initialized") assert_true(bc:has_verb("test_verb"), "Local verbs were not initialized") end function test_local_noun() local bc = BaseControl({test_noun2=1234}) assert_equal(1234, bc:get_noun("test_noun2"), "Invalid initial value") bc:set_noun("test_noun2", 4321) assert_equal(4321, bc:get_noun("test_noun2"), "Invalid new value") end function test_local_verb() local bc = BaseControl(nil, { test_verb2 = function(_, _, a, b) assert_equal(12, a, "Invalid parameter A") assert_equal(34, b, "Invalid parameter B") end, }) bc:call_verb("test_verb2", 12, 34) end function test_unknown() local bc = BaseControl() assert_false(bc:has_noun("unknown"), "has_noun failed") assert_equal(nil, bc:get_noun("unknown"), "get_noun failed") assert_error("Unknown noun writable", function() bc:set_noun("unknown", 1234) end) end -- }}} -- Networking {{{ function test_hello() local bc1 = BaseControl({hello1=true}, {hello1v=function() end}) local bc2 = BaseControl({hello2=true}, {hello2v=function() end}) assert_true(bc1:has_noun("hello1"), "Announcement did not come through") assert_true(bc2:has_noun("hello1"), "Announcement did not come through") assert_true(bc1:has_noun("hello2"), "Announcement did not come through") assert_true(bc2:has_noun("hello2"), "Announcement did not come through") assert_true(bc1:has_verb("hello1v"), "Anverbcement did not come through") assert_true(bc2:has_verb("hello1v"), "Anverbcement did not come through") assert_true(bc1:has_verb("hello2v"), "Anverbcement did not come through") assert_true(bc2:has_verb("hello2v"), "Anverbcement did not come through") end function test_remote_noun() local a = {n = "A", bc = BaseControl({noun1=1234}), addr = network.get_scene()} local b = {n = "B", bc = BaseControl({noun2=4321}), addr = network.get_scene()} local c = {n = "C", bc = BaseControl(), addr = network.get_scene()} local abc = {a, b, c, a, c} for _, bc in ipairs(abc) do network.set_scene(bc.addr) assert_equal(1234, bc.bc:get_noun("noun1"), bc.n..": noun1 failed!") assert_equal(4321, bc.bc:get_noun("noun2"), bc.n..": noun2 failed!") end end function test_noun_nv() local bc1 = BaseControl({noun_nv=1234}) local addr1 = network.get_scene() local bc2 = BaseControl() local addr2 = network.get_scene() assert_equal(1234, bc2:get_noun("noun_nv"), "Wrong answer!") network.set_scene(addr1) bc1:set_noun("noun_nv", 4321) network.set_scene(addr2) assert_equal(4321, bc2:get_noun("noun_nv"), "Wrong answer!") end function test_remote_verb() local tmp_a = 1 local tmp_b = 64 local a = {n = "A", bc = BaseControl(), addr = network.get_scene()} local b = {n = "B", bc = BaseControl(nil, {verb1 = function(_, _, a, b) assert_equal(tmp_a, a, "A is wrong!") assert_equal(tmp_b, b, "B is wrong!") end}), addr = network.get_scene()} local c = {n = "C", bc = BaseControl(), addr = network.get_scene()} local abc = {b, a, b, c, a, c} for _, bc in ipairs(abc) do network.set_scene(bc.addr) bc.bc:call_verb("verb1", tmp_a, tmp_b) tmp_a = tmp_a + 1 tmp_b = tmp_b + 1 end end function test_two_way_verb() local bc1 = BaseControl(nil, {twoway1 = function(bc, _, verb, par) bc:call_verb(verb, par * 2) end}) local addr1 = network.get_scene() local result local bc2 = BaseControl(nil, {twoway2 = function(bc, _, res) result = res end}) local addr2 = network.get_scene() bc2:call_verb("twoway1", "twoway2", 1234) assert_equal(2468, result, "Double call failed") end -- }}} -- Listening {{{ function test_listen_attach() local bc1 = BaseControl({listen1=4321}) local addr1 = network.get_scene() local bc2 = BaseControl() local tmp local tmp2 local id = bc2:listen("listen1", Event.Change, function(newval) if tmp == nil then fail("Should not have been called") end assert_equal(tmp, newval, "Change not correct") tmp2 = newval end) network.set_scene(addr1) for _, t in ipairs{1, 2, 3, 4} do tmp = t bc1:set_noun("listen1", t) assert_equal(t, tmp2, "Listener not called") end bc2:cancel(id) bc1:set_noun("listen1", 1234) end function test_listen_modes() local bc1 = BaseControl({listen2=4321}) local addr1 = network.get_scene() local bc2 = BaseControl() local addr2 = network.get_scene() local values = { {e=Event.Change, p=nil, s=1234, snot=1234, s2=1235}, {e=Event.Rising, p=nil, s=1236, snot=1233, s2=1237}, {e=Event.Falling, p=nil, s=1236, snot=1237, s2=1235}, {e=Event.Equals, p=1337, s=1337, snot=1234, s2=1337}, {e=Event.Above, p=4321, s=4333, snot=1234, s2=4334}, {e=Event.Below, p=4321, s=1234, snot=4444, s2=1212}, } for _, test in ipairs(values) do local active = false local fired = false network.set_scene(addr2) local id = bc2:listen("listen2", test.e, test.p, function(new) if not active then fail("Should not have been called") end assert_equal(test.s, new, "Wrong value!") fired = true end) network.set_scene(addr1) active = true bc1:set_noun("listen2", test.s) assert_true(fired, "Listener was not called") fired = false bc1:set_noun("listen2", test.snot) assert_false(fired, "Listener was accidentally called") network.set_scene(addr2) active = false bc2:cancel(id) network.set_scene(addr1) bc1:set_noun("listen2", test.s2) end end -- }}} -- Dirty Business {{{ function test_connectivity_loss_listening() local bc1 = BaseControl({connectivity2=12345}) local addr1 = network.get_scene() local bc2 = BaseControl() local addr2 = network.get_scene() local called = 0 local id = bc2:listen("connectivity2", Event.Change, nil, function() called = called + 1 end) network.set_scene(addr1) bc1:set_noun("connectivity2", 321) assert_equal(1, called, "Listener was not called!") network.deregister(addr2) network.allow_blackhole = true do bc1:set_noun("connectivity2", 123) assert_equal(1, called, "Listener was called?!") local bc2 = BaseControl() local addr2 = network.get_scene() local called2 = false local id2 = bc2:listen("connectivity2", Event.Change, nil, function() called2 = true end) network.set_scene(addr1) bc1:set_noun("connectivity2", 321) assert_true(called2, "Listener was not called!") assert_equal(1, called, "Original listener was called?!") end network.allow_blackhole = false end function test_cleanup() local bc1 = BaseControl({cleanup1=1234}) local addr1 = network.get_scene() local bc2 = BaseControl() local addr2 = network.get_scene() assert_true(bc2:has_noun("cleanup1"), "Setup failed") assert_equal(1234, bc2:get_noun("cleanup1"), "Setup failed") network.set_scene(addr1) bc1:cleanup() network.set_scene(addr2) assert_equal(nil, bc2:get_noun("cleanup1"), "Cleanup failed") assert_false(bc2:has_noun("cleanup1"), "Cleanup failed") end function test_connectivity_loss() local bc1 = BaseControl({connectivity=4433}) local addr1 = network.get_scene() local bc2 = BaseControl() local addr2 = network.get_scene() assert_true(bc2:has_noun("connectivity"), "Setup failed") assert_equal(4433, bc2:get_noun("connectivity"), "Setup failed") network.deregister(addr1) network.allow_blackhole = true assert_true(bc2:has_noun("connectivity"), "Test error") assert_equal(nil, bc2:get_noun("connectivity"), "Invalid response from offline node") network.allow_blackhole = false -- Come back online local bc1 = BaseControl({connectivity=4432}) network.set_scene(addr2) assert_true(bc2:has_noun("connectivity"), "Reconnect failed") assert_equal(4432, bc2:get_noun("connectivity"), "Reconnect failed") end function test_isolation() local bc_outside = BaseControl({isolation1=4321}) local bc1 = BaseControl({isolation2=1234}, nil, { port=1234, }) local bc2 = BaseControl(nil, nil, { port=1234, }) assert_equal(nil, bc2:get_noun("isolation1"), "Leak!") assert_equal(1234, bc2:get_noun("isolation2"), "Port failure") end function test_malicious_messages() local bc1 = BaseControl({malicious=1234}) local addr1 = network.get_scene() local bc2 = BaseControl({malicious2=4321}) local addr2 = network.get_scene() network.send(addr1, bc1.port, serialization.serialize{ ty=7, -- LISTEN_REQUEST noun="unknown", event=Event.Change, id="dead-beef", }) network.send(addr1, bc1.port, serialization.serialize{ ty=8, -- LISTEN_NOTIFY id="dead-beef", value=1234567, }) network.send(addr1, bc1.port, serialization.serialize{ ty=9, -- LISTEN_CANCEL noun="unknown", id="dead-beef", }) assert_equal(1234, bc2:get_noun("malicious")) end function test_malicious_broadcast() local bc1 = BaseControl({malicious3=1234}) local addr1 = network.get_scene() local bc2 = BaseControl() local addr2 = network.get_scene() local bc3 = BaseControl() local addr3 = network.get_scene() network.broadcast(bc1.port, serialization.serialize{ ty=2, -- ANNOUNCE nouns={"malicious3"}, verbs={}, }) -- bc1 can't be fooled, as it owns this noun network.set_scene(addr1) assert_equal(1234, bc1:get_noun("malicious3"), "Injection successful") -- bc2 unfortunately can be fooled and must be network.set_scene(addr2) assert_equal(nil, bc2:get_noun("malicious3"), "Injection failed") end -- }}}