diff options
Diffstat (limited to 'unit_tests')
| -rw-r--r-- | unit_tests/scripts/coro/collect_yielded_lane.lua | 105 | ||||
| -rw-r--r-- | unit_tests/scripts/coro/yielding_function.lua | 34 |
2 files changed, 100 insertions, 39 deletions
diff --git a/unit_tests/scripts/coro/collect_yielded_lane.lua b/unit_tests/scripts/coro/collect_yielded_lane.lua index 0459698..2bc4ae8 100644 --- a/unit_tests/scripts/coro/collect_yielded_lane.lua +++ b/unit_tests/scripts/coro/collect_yielded_lane.lua | |||
| @@ -1,4 +1,5 @@ | |||
| 1 | local lanes = require "lanes" | 1 | local fixture = require "fixture" |
| 2 | local lanes = require "lanes".configure{on_state_create = fixture.on_state_create} | ||
| 2 | 3 | ||
| 3 | local fixture = require "fixture" | 4 | local fixture = require "fixture" |
| 4 | lanes.finally(fixture.throwing_finalizer) | 5 | lanes.finally(fixture.throwing_finalizer) |
| @@ -7,18 +8,27 @@ local utils = lanes.require "_utils" | |||
| 7 | local PRINT = utils.MAKE_PRINT() | 8 | local PRINT = utils.MAKE_PRINT() |
| 8 | 9 | ||
| 9 | -- a lane body that yields stuff | 10 | -- a lane body that yields stuff |
| 10 | local yielder = function(out_linda_) | 11 | local yielder = function(out_linda_, wait_) |
| 12 | local fixture = assert(require "fixture") | ||
| 11 | -- here is a to-be-closed variable that, when closed, sends "Closed!" in the "out" slot of the provided linda | 13 | -- here is a to-be-closed variable that, when closed, sends "Closed!" in the "out" slot of the provided linda |
| 12 | local t <close> = setmetatable( | 14 | local t <close> = setmetatable( |
| 13 | { text = "Closed!" }, { | 15 | { text = "Closed!" }, { |
| 14 | __close = function(self, err) | 16 | __close = function(self, err) |
| 17 | if wait_ then | ||
| 18 | fixture.block_for(wait_) | ||
| 19 | end | ||
| 15 | out_linda_:send("out", self.text) | 20 | out_linda_:send("out", self.text) |
| 16 | end | 21 | end |
| 17 | } | 22 | } |
| 18 | ) | 23 | ) |
| 19 | -- yield forever | 24 | -- yield forever, but be cancel-friendly |
| 25 | local n = 1 | ||
| 20 | while true do | 26 | while true do |
| 21 | coroutine.yield("I yield!") | 27 | coroutine.yield("I yield!", n) |
| 28 | if cancel_test and cancel_test() then -- cancel_test does not exist when run immediately (not in a Lane) | ||
| 29 | return "I am cancelled" | ||
| 30 | end | ||
| 31 | n = n + 1 | ||
| 22 | end | 32 | end |
| 23 | end | 33 | end |
| 24 | 34 | ||
| @@ -27,8 +37,8 @@ local out_linda = lanes.linda() | |||
| 27 | local test_close = function(what_, f_) | 37 | local test_close = function(what_, f_) |
| 28 | local c = coroutine.create(f_) | 38 | local c = coroutine.create(f_) |
| 29 | for i = 1, 10 do | 39 | for i = 1, 10 do |
| 30 | local t, r = coroutine.resume(c, out_linda) -- returns true + <yielded values> | 40 | local t, r1, r2 = coroutine.resume(c, out_linda) -- returns true + <yielded values> |
| 31 | assert(t == true and r == "I yield!", "got " .. tostring(t) .. " " .. tostring(r)) | 41 | assert(t == true and r1 == "I yield!" and r2 == i, "got " .. tostring(t) .. " " .. tostring(r1) .. " " .. tostring(r2)) |
| 32 | local s = coroutine.status(c) | 42 | local s = coroutine.status(c) |
| 33 | assert(s == "suspended") | 43 | assert(s == "suspended") |
| 34 | end | 44 | end |
| @@ -39,28 +49,64 @@ local test_close = function(what_, f_) | |||
| 39 | assert(s == "out" and r == "Closed!", what_ .. " got " .. tostring(s) .. " " .. tostring(r)) | 49 | assert(s == "out" and r == "Closed!", what_ .. " got " .. tostring(s) .. " " .. tostring(r)) |
| 40 | end | 50 | end |
| 41 | 51 | ||
| 42 | -- first, try the close mechanism outside of a lane | 52 | --------------------------------------------------------- |
| 43 | test_close("base", yielder) | 53 | -- TEST: first, try the close mechanism outside of a lane |
| 54 | --------------------------------------------------------- | ||
| 55 | if true then | ||
| 56 | test_close("base", yielder) | ||
| 57 | end | ||
| 44 | 58 | ||
| 45 | -- try again with a function obtained through dump/undump | 59 | --------------------------------------------------------------- |
| 46 | -- note this means our yielder implementation can't have upvalues, as they are lost in the process | 60 | -- TEST: try again with a function obtained through dump/undump |
| 47 | test_close("dumped", load(string.dump(yielder))) | 61 | --------------------------------------------------------------- |
| 62 | if true then | ||
| 63 | -- note this means our yielder implementation can't have upvalues, as they are lost in the process | ||
| 64 | test_close("dumped", load(string.dump(yielder))) | ||
| 65 | end | ||
| 48 | 66 | ||
| 49 | ------------------------------------------------------------------------------ | 67 | ------------------------------------------------------------------------------ |
| 50 | -- TEST: to-be-closed variables are properly closed when the lane is collected | 68 | -- TEST: to-be-closed variables are properly closed whzen the lane is collected |
| 51 | ------------------------------------------------------------------------------ | 69 | ------------------------------------------------------------------------------ |
| 52 | if false then -- NOT IMPLEMENTED YET! | 70 | if true then |
| 53 | |||
| 54 | -- the generator | 71 | -- the generator |
| 55 | local coro_g = lanes.coro("*", yielder) | 72 | local coro_g = lanes.coro("*", yielder) |
| 56 | 73 | ||
| 57 | -- start the lane | 74 | -- start the lane |
| 58 | local h = coro_g(out_linda) | 75 | local h = coro_g(out_linda) |
| 59 | 76 | ||
| 60 | -- join it so that it reaches suspended state | 77 | -- join the lane. it should be done and give back the values resulting of the first yield point |
| 61 | local r, v = h:join(0.5) | 78 | local r, v1, v2 = h:join() |
| 79 | assert(r == true and v1 == "I yield!" and v2 == 1, "got " .. tostring(r) .. " " .. tostring(v1) .. " " .. tostring(v2)) | ||
| 80 | assert(h.status == "done", "got " .. h.status) | ||
| 81 | |||
| 82 | -- force collection of the lane | ||
| 83 | h = nil | ||
| 84 | collectgarbage() | ||
| 85 | |||
| 86 | -- I want the to-be-closed variable of the coroutine linda to be properly closed | ||
| 87 | local s, r = out_linda:receive(0, "out") | ||
| 88 | assert(s == "out" and r == "Closed!", "coro got " .. tostring(s) .. " " .. tostring(r)) -- THIS TEST FAILS | ||
| 89 | end | ||
| 90 | |||
| 91 | --------------------------------------------------------------------------------------------------- | ||
| 92 | -- TEST: if a to-be-closed handler takes longer than the join timeout, everything works as expected | ||
| 93 | --------------------------------------------------------------------------------------------------- | ||
| 94 | if true then | ||
| 95 | -- the generator | ||
| 96 | local coro_g = lanes.coro("*", yielder) | ||
| 97 | |||
| 98 | -- start the lane. The to-be-closed handler will sleep for 1 second | ||
| 99 | local h = coro_g(out_linda, 1) | ||
| 100 | |||
| 101 | -- first join attempt should timeout | ||
| 102 | local r, v = h:join(0.6) | ||
| 62 | assert(r == nil and v == "timeout", "got " .. tostring(r) .. " " .. tostring(v)) | 103 | assert(r == nil and v == "timeout", "got " .. tostring(r) .. " " .. tostring(v)) |
| 63 | assert(h.status == "suspended") | 104 | assert(h.status == "running", "got " .. h.status) |
| 105 | |||
| 106 | -- join the lane again. it should be done and give back the values resulting of the first yield point | ||
| 107 | local r, v1, v2 = h:join(0.6) | ||
| 108 | assert(r == true and v1 == "I yield!" and v2 == 1, "got " .. tostring(r) .. " " .. tostring(v1) .. " " .. tostring(v2)) | ||
| 109 | assert(h.status == "done", "got " .. h.status) | ||
| 64 | 110 | ||
| 65 | -- force collection of the lane | 111 | -- force collection of the lane |
| 66 | h = nil | 112 | h = nil |
| @@ -68,5 +114,28 @@ if false then -- NOT IMPLEMENTED YET! | |||
| 68 | 114 | ||
| 69 | -- I want the to-be-closed variable of the coroutine linda to be properly closed | 115 | -- I want the to-be-closed variable of the coroutine linda to be properly closed |
| 70 | local s, r = out_linda:receive(0, "out") | 116 | local s, r = out_linda:receive(0, "out") |
| 71 | assert(s == "out" and r == "Closed!", "coro got " .. tostring(s) .. " " .. tostring(r)) | 117 | assert(s == "out" and r == "Closed!", "coro got " .. tostring(s) .. " " .. tostring(r)) -- THIS TEST FAILS |
| 118 | end | ||
| 119 | |||
| 120 | -------------------------------------------------- | ||
| 121 | -- TEST: cancelling a suspended Lane should end it | ||
| 122 | -------------------------------------------------- | ||
| 123 | if true then | ||
| 124 | -- the generator | ||
| 125 | local coro_g = lanes.coro("*", yielder) | ||
| 126 | |||
| 127 | -- start the lane | ||
| 128 | local h = coro_g(out_linda) | ||
| 129 | repeat until h.status == "suspended" | ||
| 130 | |||
| 131 | -- first cancellation attempt: don't wake the lane | ||
| 132 | local b, r = h:cancel("soft", 0.5) | ||
| 133 | -- the lane is still blocked in its suspended state | ||
| 134 | assert(b == false and r == "timeout" and h.status == "suspended", "got " .. tostring(b) .. " " .. tostring(r) .. " " .. h.status) | ||
| 135 | |||
| 136 | -- cancel the Lane again, this time waking it. it will resume, and yielder()'s will break out of its infinite loop | ||
| 137 | h:cancel("soft", nil, true) | ||
| 138 | |||
| 139 | -- lane should be done, because it returned cooperatively when detecting a soft cancel | ||
| 140 | assert(h.status == "done", "got " .. h.status) | ||
| 72 | end | 141 | end |
diff --git a/unit_tests/scripts/coro/yielding_function.lua b/unit_tests/scripts/coro/yielding_function.lua index 636f094..6518d1f 100644 --- a/unit_tests/scripts/coro/yielding_function.lua +++ b/unit_tests/scripts/coro/yielding_function.lua | |||
| @@ -23,7 +23,7 @@ end | |||
| 23 | -------------------------------------------------------------------------------------------------- | 23 | -------------------------------------------------------------------------------------------------- |
| 24 | -- TEST: if we start a non-coroutine lane with a yielding function, we should get an error, right? | 24 | -- TEST: if we start a non-coroutine lane with a yielding function, we should get an error, right? |
| 25 | -------------------------------------------------------------------------------------------------- | 25 | -------------------------------------------------------------------------------------------------- |
| 26 | if false then | 26 | if true then |
| 27 | local fun_g = lanes.gen("*", { name = 'auto' }, yielder) | 27 | local fun_g = lanes.gen("*", { name = 'auto' }, yielder) |
| 28 | local h = fun_g("hello", "world", "!") | 28 | local h = fun_g("hello", "world", "!") |
| 29 | local err, status, stack = h:join() | 29 | local err, status, stack = h:join() |
| @@ -48,7 +48,7 @@ local coro_g = lanes.coro("*", {name = "auto"}, yielder) | |||
| 48 | ------------------------------------------------------------------------------------------------- | 48 | ------------------------------------------------------------------------------------------------- |
| 49 | -- TEST: we can resume as many times as the lane yields, then read the returned value on indexing | 49 | -- TEST: we can resume as many times as the lane yields, then read the returned value on indexing |
| 50 | ------------------------------------------------------------------------------------------------- | 50 | ------------------------------------------------------------------------------------------------- |
| 51 | if false then | 51 | if true then |
| 52 | -- launch coroutine lane | 52 | -- launch coroutine lane |
| 53 | local h = coro_g("hello", "world", "!") | 53 | local h = coro_g("hello", "world", "!") |
| 54 | -- read the yielded values, sending back the expected index | 54 | -- read the yielded values, sending back the expected index |
| @@ -63,7 +63,7 @@ end | |||
| 63 | --------------------------------------------------------------------------------------------- | 63 | --------------------------------------------------------------------------------------------- |
| 64 | -- TEST: we can resume as many times as the lane yields, then read the returned value on join | 64 | -- TEST: we can resume as many times as the lane yields, then read the returned value on join |
| 65 | --------------------------------------------------------------------------------------------- | 65 | --------------------------------------------------------------------------------------------- |
| 66 | if false then | 66 | if true then |
| 67 | -- launch coroutine lane | 67 | -- launch coroutine lane |
| 68 | local h = coro_g("hello", "world", "!") | 68 | local h = coro_g("hello", "world", "!") |
| 69 | -- read the yielded values, sending back the expected index | 69 | -- read the yielded values, sending back the expected index |
| @@ -75,9 +75,9 @@ if false then | |||
| 75 | assert(h.status == "done" and s == true and r == "bye!") | 75 | assert(h.status == "done" and s == true and r == "bye!") |
| 76 | end | 76 | end |
| 77 | 77 | ||
| 78 | --------------------------------------------------------------------------------------------------- | 78 | --------------------------------------------------- |
| 79 | -- TEST: if we join a yielded lane, we get a timeout, and we can resume as if we didn't try to join | 79 | -- TEST: if we join a yielded lane, the lane aborts |
| 80 | --------------------------------------------------------------------------------------------------- | 80 | --------------------------------------------------- |
| 81 | if true then | 81 | if true then |
| 82 | -- launch coroutine lane | 82 | -- launch coroutine lane |
| 83 | local h = coro_g("hello", "world", "!") | 83 | local h = coro_g("hello", "world", "!") |
| @@ -89,10 +89,10 @@ if true then | |||
| 89 | assert(s == "done" and b == true and r == "world", "got " .. s .. " " .. tostring(b) .. " " .. tostring(r)) | 89 | assert(s == "done" and b == true and r == "world", "got " .. s .. " " .. tostring(b) .. " " .. tostring(r)) |
| 90 | end | 90 | end |
| 91 | 91 | ||
| 92 | ----------------------------------------------------------------------- | 92 | ------------------------------------------------------------------------- |
| 93 | -- TEST: if we index yielded lane, we should get the last yielded value | 93 | -- TEST: if we index a yielded lane, we should get the last yielded value |
| 94 | ----------------------------------------------------------------------- | 94 | ------------------------------------------------------------------------- |
| 95 | if false then | 95 | if true then |
| 96 | -- launch coroutine lane | 96 | -- launch coroutine lane |
| 97 | local h = coro_g("hello", "world", "!") | 97 | local h = coro_g("hello", "world", "!") |
| 98 | -- read the first yielded value, sending back the expected index | 98 | -- read the first yielded value, sending back the expected index |
| @@ -102,15 +102,7 @@ if false then | |||
| 102 | local r2 = h[1] | 102 | local r2 = h[1] |
| 103 | local r3 = h[1] | 103 | local r3 = h[1] |
| 104 | assert(r1 == "world" and r2 == "world" and r3 == "world", "got " .. r1 .. " " .. r2 .. " " .. r3) | 104 | assert(r1 == "world" and r2 == "world" and r3 == "world", "got " .. r1 .. " " .. r2 .. " " .. r3) |
| 105 | assert(h:resume(2) == "world") | 105 | -- once the lane was indexed, it is no longer resumable (just like after join) |
| 106 | 106 | local b, e = pcall(h.resume, h, 2) | |
| 107 | -- THERE IS AN INCONSISTENCY: h:resume pulls the yielded values directly out of the lane's stack | 107 | assert(b == false and e == "cannot resume non-suspended coroutine Lane") |
| 108 | -- but h[n] removes them and stores them in the internal values storage table | ||
| 109 | -- TODO: so we need to decide: should indexing a yielded lane work like resume()? | ||
| 110 | assert(h:resume(3) == "!") | ||
| 111 | end | 108 | end |
| 112 | |||
| 113 | ------------------------------------------------------------------------------------- | ||
| 114 | -- TEST: if we close yielded lane, we can join it and get the last yielded values out | ||
| 115 | ------------------------------------------------------------------------------------- | ||
| 116 | -- TODO: we need to implement lane:close() for that! | ||
