diff options
Diffstat (limited to 'unit_tests/scripts/coro')
| -rw-r--r-- | unit_tests/scripts/coro/basics.lua | 99 | ||||
| -rw-r--r-- | unit_tests/scripts/coro/collect_yielded_lane.lua | 72 | ||||
| -rw-r--r-- | unit_tests/scripts/coro/error_handling.lua | 6 | ||||
| -rw-r--r-- | unit_tests/scripts/coro/regular_function.lua | 38 | ||||
| -rw-r--r-- | unit_tests/scripts/coro/yielding_function.lua | 107 |
5 files changed, 220 insertions, 102 deletions
diff --git a/unit_tests/scripts/coro/basics.lua b/unit_tests/scripts/coro/basics.lua deleted file mode 100644 index c0b7a36..0000000 --- a/unit_tests/scripts/coro/basics.lua +++ /dev/null | |||
| @@ -1,99 +0,0 @@ | |||
| 1 | local lanes = require "lanes" | ||
| 2 | |||
| 3 | local fixture = require "fixture" | ||
| 4 | lanes.finally(fixture.throwing_finalizer) | ||
| 5 | |||
| 6 | local utils = lanes.require "_utils" | ||
| 7 | local PRINT = utils.MAKE_PRINT() | ||
| 8 | |||
| 9 | if true then | ||
| 10 | -- a lane body that just returns some value | ||
| 11 | local lane = function(msg_) | ||
| 12 | local utils = lanes.require "_utils" | ||
| 13 | local PRINT = utils.MAKE_PRINT() | ||
| 14 | PRINT "In lane" | ||
| 15 | assert(msg_ == "hi") | ||
| 16 | return "bye" | ||
| 17 | end | ||
| 18 | |||
| 19 | -- the generator | ||
| 20 | local g1 = lanes.coro("*", {name = "auto"}, lane) | ||
| 21 | |||
| 22 | -- launch lane | ||
| 23 | local h1 = g1("hi") | ||
| 24 | |||
| 25 | local r = h1[1] | ||
| 26 | assert(r == "bye") | ||
| 27 | end | ||
| 28 | |||
| 29 | -- a lane coroutine that yields back what got in, one element at a time | ||
| 30 | local yielder = function(...) | ||
| 31 | local utils = lanes.require "_utils" | ||
| 32 | local PRINT = utils.MAKE_PRINT() | ||
| 33 | PRINT "In lane" | ||
| 34 | for _i = 1, select('#', ...) do | ||
| 35 | local _val = select(_i, ...) | ||
| 36 | PRINT("yielding #", _i, _val) | ||
| 37 | local _ack = coroutine.yield(_val) | ||
| 38 | assert(_ack == _i) | ||
| 39 | end | ||
| 40 | return "done!" | ||
| 41 | end | ||
| 42 | |||
| 43 | if true then | ||
| 44 | -- if we start a non-coroutine lane with a yielding function, we should get an error, right? | ||
| 45 | local fun_g = lanes.gen("*", { name = 'auto' }, yielder) | ||
| 46 | local h = fun_g("hello", "world", "!") | ||
| 47 | local err, status, stack = h:join() | ||
| 48 | PRINT(err, status, stack) | ||
| 49 | -- the actual error message is not the same for Lua 5.1 | ||
| 50 | -- of course, it also has to be different for LuaJIT as well | ||
| 51 | -- also, LuaJIT prepends a file:line to the actual error message, which Lua5.1 does not. | ||
| 52 | local msgs = { | ||
| 53 | ["Lua 5.1"] = jit and "attempt to yield across C-call boundary" or "attempt to yield across metamethod/C-call boundary", | ||
| 54 | ["Lua 5.2"] = "attempt to yield from outside a coroutine", | ||
| 55 | ["Lua 5.3"] = "attempt to yield from outside a coroutine", | ||
| 56 | ["Lua 5.4"] = "attempt to yield from outside a coroutine" | ||
| 57 | } | ||
| 58 | local expected_msg = msgs[_VERSION] | ||
| 59 | PRINT("expected_msg = " .. expected_msg) | ||
| 60 | assert(err == nil and string.find(status, expected_msg, 1, true) and stack == nil, "status = " .. status) | ||
| 61 | end | ||
| 62 | |||
| 63 | -- the generator | ||
| 64 | local coro_g = lanes.coro("*", {name = "auto"}, yielder) | ||
| 65 | |||
| 66 | if true then | ||
| 67 | -- launch coroutine lane | ||
| 68 | local h2 = coro_g("hello", "world", "!") | ||
| 69 | -- read the yielded values, sending back the expected index | ||
| 70 | assert(h2:resume(1) == "hello") | ||
| 71 | assert(h2:resume(2) == "world") | ||
| 72 | assert(h2:resume(3) == "!") | ||
| 73 | -- the lane return value is available as usual | ||
| 74 | local r = h2[1] | ||
| 75 | assert(r == "done!") | ||
| 76 | end | ||
| 77 | |||
| 78 | if true then | ||
| 79 | -- another coroutine lane | ||
| 80 | local h3 = coro_g("hello", "world", "!") | ||
| 81 | |||
| 82 | -- yielded values are available as regular return values | ||
| 83 | assert(h3[1] == "hello" and h3.status == "suspended") | ||
| 84 | -- since we consumed the returned values, they should not be here when we resume | ||
| 85 | assert(h3:resume(1) == nil) | ||
| 86 | |||
| 87 | -- similarly, we can get them with join() | ||
| 88 | local r3, ret3 = h3:join() | ||
| 89 | assert(r3 == true and ret3 == "world" and h3.status == "suspended") | ||
| 90 | -- since we consumed the returned values, they should not be here when we resume | ||
| 91 | assert(h3:resume(2) == nil) | ||
| 92 | |||
| 93 | -- the rest should work as usual | ||
| 94 | assert(h3:resume(3) == "!") | ||
| 95 | |||
| 96 | -- the final return value of the lane body remains to be read | ||
| 97 | local r3, ret3 = h3:join() | ||
| 98 | assert(r3 == true and ret3 == "done!" and h3.status == "done") | ||
| 99 | end | ||
diff --git a/unit_tests/scripts/coro/collect_yielded_lane.lua b/unit_tests/scripts/coro/collect_yielded_lane.lua new file mode 100644 index 0000000..0459698 --- /dev/null +++ b/unit_tests/scripts/coro/collect_yielded_lane.lua | |||
| @@ -0,0 +1,72 @@ | |||
| 1 | local lanes = require "lanes" | ||
| 2 | |||
| 3 | local fixture = require "fixture" | ||
| 4 | lanes.finally(fixture.throwing_finalizer) | ||
| 5 | |||
| 6 | local utils = lanes.require "_utils" | ||
| 7 | local PRINT = utils.MAKE_PRINT() | ||
| 8 | |||
| 9 | -- a lane body that yields stuff | ||
| 10 | local yielder = function(out_linda_) | ||
| 11 | -- 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( | ||
| 13 | { text = "Closed!" }, { | ||
| 14 | __close = function(self, err) | ||
| 15 | out_linda_:send("out", self.text) | ||
| 16 | end | ||
| 17 | } | ||
| 18 | ) | ||
| 19 | -- yield forever | ||
| 20 | while true do | ||
| 21 | coroutine.yield("I yield!") | ||
| 22 | end | ||
| 23 | end | ||
| 24 | |||
| 25 | local out_linda = lanes.linda() | ||
| 26 | |||
| 27 | local test_close = function(what_, f_) | ||
| 28 | local c = coroutine.create(f_) | ||
| 29 | for i = 1, 10 do | ||
| 30 | local t, r = coroutine.resume(c, out_linda) -- returns true + <yielded values> | ||
| 31 | assert(t == true and r == "I yield!", "got " .. tostring(t) .. " " .. tostring(r)) | ||
| 32 | local s = coroutine.status(c) | ||
| 33 | assert(s == "suspended") | ||
| 34 | end | ||
| 35 | local r, s = coroutine.close(c) | ||
| 36 | assert(r == true and s == nil) | ||
| 37 | -- the local variable inside the yielder body should be closed | ||
| 38 | local s, r = out_linda:receive(0, "out") | ||
| 39 | assert(s == "out" and r == "Closed!", what_ .. " got " .. tostring(s) .. " " .. tostring(r)) | ||
| 40 | end | ||
| 41 | |||
| 42 | -- first, try the close mechanism outside of a lane | ||
| 43 | test_close("base", yielder) | ||
| 44 | |||
| 45 | -- try again with a function obtained through dump/undump | ||
| 46 | -- note this means our yielder implementation can't have upvalues, as they are lost in the process | ||
| 47 | test_close("dumped", load(string.dump(yielder))) | ||
| 48 | |||
| 49 | ------------------------------------------------------------------------------ | ||
| 50 | -- TEST: to-be-closed variables are properly closed when the lane is collected | ||
| 51 | ------------------------------------------------------------------------------ | ||
| 52 | if false then -- NOT IMPLEMENTED YET! | ||
| 53 | |||
| 54 | -- the generator | ||
| 55 | local coro_g = lanes.coro("*", yielder) | ||
| 56 | |||
| 57 | -- start the lane | ||
| 58 | local h = coro_g(out_linda) | ||
| 59 | |||
| 60 | -- join it so that it reaches suspended state | ||
| 61 | local r, v = h:join(0.5) | ||
| 62 | assert(r == nil and v == "timeout", "got " .. tostring(r) .. " " .. tostring(v)) | ||
| 63 | assert(h.status == "suspended") | ||
| 64 | |||
| 65 | -- force collection of the lane | ||
| 66 | h = nil | ||
| 67 | collectgarbage() | ||
| 68 | |||
| 69 | -- I want the to-be-closed variable of the coroutine linda to be properly closed | ||
| 70 | local s, r = out_linda:receive(0, "out") | ||
| 71 | assert(s == "out" and r == "Closed!", "coro got " .. tostring(s) .. " " .. tostring(r)) | ||
| 72 | end | ||
diff --git a/unit_tests/scripts/coro/error_handling.lua b/unit_tests/scripts/coro/error_handling.lua index ba6cff6..1cfb8c8 100644 --- a/unit_tests/scripts/coro/error_handling.lua +++ b/unit_tests/scripts/coro/error_handling.lua | |||
| @@ -38,15 +38,15 @@ local force_error_test = function(error_trace_level_) | |||
| 38 | utils.dump_error_stack(error_trace_level_, c) | 38 | utils.dump_error_stack(error_trace_level_, c) |
| 39 | end | 39 | end |
| 40 | 40 | ||
| 41 | if false then | 41 | if true then |
| 42 | force_error_test("minimal") | 42 | force_error_test("minimal") |
| 43 | end | 43 | end |
| 44 | 44 | ||
| 45 | if false then | 45 | if true then |
| 46 | force_error_test("basic") | 46 | force_error_test("basic") |
| 47 | end | 47 | end |
| 48 | 48 | ||
| 49 | if false then | 49 | if true then |
| 50 | force_error_test("extended") | 50 | force_error_test("extended") |
| 51 | end | 51 | end |
| 52 | 52 | ||
diff --git a/unit_tests/scripts/coro/regular_function.lua b/unit_tests/scripts/coro/regular_function.lua new file mode 100644 index 0000000..09aa3b7 --- /dev/null +++ b/unit_tests/scripts/coro/regular_function.lua | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | local lanes = require "lanes".configure() | ||
| 2 | |||
| 3 | local utils = lanes.require "_utils" | ||
| 4 | local PRINT = utils.MAKE_PRINT() | ||
| 5 | |||
| 6 | -- a lane body that just returns some value | ||
| 7 | local returner = function(msg_) | ||
| 8 | local utils = lanes.require "_utils" | ||
| 9 | local PRINT = utils.MAKE_PRINT() | ||
| 10 | PRINT "In lane" | ||
| 11 | assert(msg_ == "hi") | ||
| 12 | return "bye" | ||
| 13 | end | ||
| 14 | |||
| 15 | -- a function that returns some value can run in a coroutine | ||
| 16 | if true then | ||
| 17 | -- the generator | ||
| 18 | local g = lanes.coro("*", {name = "auto"}, returner) | ||
| 19 | |||
| 20 | -- launch lane | ||
| 21 | local h = g("hi") | ||
| 22 | |||
| 23 | local r = h[1] | ||
| 24 | assert(r == "bye") | ||
| 25 | end | ||
| 26 | |||
| 27 | -- can't resume a coro after the lane body has returned | ||
| 28 | if true then | ||
| 29 | -- the generator | ||
| 30 | local g = lanes.coro("*", {name = "auto"}, returner) | ||
| 31 | |||
| 32 | -- launch lane | ||
| 33 | local h = g("hi") | ||
| 34 | |||
| 35 | -- resuming a lane that terminated execution should raise an error | ||
| 36 | local b, e = pcall(h.resume, h) | ||
| 37 | assert(b == false and type(e) == "string") | ||
| 38 | end | ||
diff --git a/unit_tests/scripts/coro/yielding_function.lua b/unit_tests/scripts/coro/yielding_function.lua new file mode 100644 index 0000000..e7367ea --- /dev/null +++ b/unit_tests/scripts/coro/yielding_function.lua | |||
| @@ -0,0 +1,107 @@ | |||
| 1 | local lanes = require "lanes" | ||
| 2 | |||
| 3 | local fixture = require "fixture" | ||
| 4 | lanes.finally(fixture.throwing_finalizer) | ||
| 5 | |||
| 6 | local utils = lanes.require "_utils" | ||
| 7 | local PRINT = utils.MAKE_PRINT() | ||
| 8 | |||
| 9 | -- a lane coroutine that yields back what got in, one element at a time | ||
| 10 | local yielder = function(...) | ||
| 11 | local utils = lanes.require "_utils" | ||
| 12 | local PRINT = utils.MAKE_PRINT() | ||
| 13 | PRINT "In lane" | ||
| 14 | for _i = 1, select('#', ...) do | ||
| 15 | local _val = select(_i, ...) | ||
| 16 | PRINT("yielding #", _i, _val) | ||
| 17 | local _ack = coroutine.yield(_val) | ||
| 18 | assert(_ack == _i) | ||
| 19 | end | ||
| 20 | return "done!" | ||
| 21 | end | ||
| 22 | |||
| 23 | -------------------------------------------------------------------------------------------------- | ||
| 24 | -- TEST: if we start a non-coroutine lane with a yielding function, we should get an error, right? | ||
| 25 | -------------------------------------------------------------------------------------------------- | ||
| 26 | if true then | ||
| 27 | local fun_g = lanes.gen("*", { name = 'auto' }, yielder) | ||
| 28 | local h = fun_g("hello", "world", "!") | ||
| 29 | local err, status, stack = h:join() | ||
| 30 | PRINT(err, status, stack) | ||
| 31 | -- the actual error message is not the same for Lua 5.1 | ||
| 32 | -- of course, it also has to be different for LuaJIT as well | ||
| 33 | -- also, LuaJIT prepends a file:line to the actual error message, which Lua5.1 does not. | ||
| 34 | local msgs = { | ||
| 35 | ["Lua 5.1"] = jit and "attempt to yield across C-call boundary" or "attempt to yield across metamethod/C-call boundary", | ||
| 36 | ["Lua 5.2"] = "attempt to yield from outside a coroutine", | ||
| 37 | ["Lua 5.3"] = "attempt to yield from outside a coroutine", | ||
| 38 | ["Lua 5.4"] = "attempt to yield from outside a coroutine" | ||
| 39 | } | ||
| 40 | local expected_msg = msgs[_VERSION] | ||
| 41 | PRINT("expected_msg = " .. expected_msg) | ||
| 42 | assert(err == nil and string.find(status, expected_msg, 1, true) and stack == nil, "status = " .. status) | ||
| 43 | end | ||
| 44 | |||
| 45 | -- the coroutine generator | ||
| 46 | local coro_g = lanes.coro("*", {name = "auto"}, yielder) | ||
| 47 | |||
| 48 | ------------------------------------------------------------------------------------------------- | ||
| 49 | -- TEST: we can resume as many times as the lane yields, then read the returned value on indexing | ||
| 50 | ------------------------------------------------------------------------------------------------- | ||
| 51 | if true then | ||
| 52 | -- launch coroutine lane | ||
| 53 | local h = coro_g("hello", "world", "!") | ||
| 54 | -- read the yielded values, sending back the expected index | ||
| 55 | assert(h:resume(1) == "hello") | ||
| 56 | assert(h:resume(2) == "world") | ||
| 57 | assert(h:resume(3) == "!") | ||
| 58 | -- the lane return value is available as usual | ||
| 59 | local r = h[1] | ||
| 60 | assert(r == "done!") | ||
| 61 | end | ||
| 62 | |||
| 63 | --------------------------------------------------------------------------------------------- | ||
| 64 | -- TEST: we can resume as many times as the lane yields, then read the returned value on join | ||
| 65 | --------------------------------------------------------------------------------------------- | ||
| 66 | if true then | ||
| 67 | -- launch coroutine lane | ||
| 68 | local h = coro_g("hello", "world", "!") | ||
| 69 | -- read the yielded values, sending back the expected index | ||
| 70 | assert(h:resume(1) == "hello") | ||
| 71 | assert(h:resume(2) == "world") | ||
| 72 | assert(h:resume(3) == "!") | ||
| 73 | -- the lane return value is available as usual | ||
| 74 | local s, r = h:join() | ||
| 75 | assert(h.status == "done" and s == true and r == "done!") | ||
| 76 | end | ||
| 77 | |||
| 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 | ||
| 80 | --------------------------------------------------------------------------------------------------- | ||
| 81 | if true then | ||
| 82 | -- launch coroutine lane | ||
| 83 | local h = coro_g("hello", "world", "!") | ||
| 84 | -- read the first yielded value, sending back the expected index | ||
| 85 | assert(h:resume(1) == "hello") | ||
| 86 | -- join the lane. since it will reach a yield point, it remains suspended, and we should get a timeout | ||
| 87 | local b, r = h:join(0.5) | ||
| 88 | local s = h.status | ||
| 89 | assert(s == "suspended" and b == nil and r == "timeout", "got " .. s .. " " .. tostring(b) .. " " .. r) | ||
| 90 | -- trying to resume again should proceed normally, since nothing changed | ||
| 91 | assert(h:resume(2) == "world") | ||
| 92 | assert(h:resume(3) == "!") | ||
| 93 | -- the lane return value is available as usual | ||
| 94 | local s, r = h:join() | ||
| 95 | assert(h.status == "done" and s == true and r == "done!") | ||
| 96 | end | ||
| 97 | |||
| 98 | --------------------------------------------------------- | ||
| 99 | -- TEST: if we index yielded lane, we should get an error | ||
| 100 | --------------------------------------------------------- | ||
| 101 | -- TODO: implement this test! | ||
| 102 | |||
| 103 | |||
| 104 | ------------------------------------------------------------------------------------- | ||
| 105 | -- TEST: if we close yielded lane, we can join it and get the last yielded values out | ||
| 106 | ------------------------------------------------------------------------------------- | ||
| 107 | -- TODO: we need to implement lane:close() for that! | ||
