diff options
Diffstat (limited to 'unit_tests/scripts')
| -rw-r--r-- | unit_tests/scripts/_utils.lua | 20 | ||||
| -rw-r--r-- | unit_tests/scripts/_utils54.lua | 30 | ||||
| -rw-r--r-- | unit_tests/scripts/coro/cancelling_suspended.lua | 31 | ||||
| -rw-r--r-- | unit_tests/scripts/coro/collect_yielded_lane.lua | 87 | ||||
| -rw-r--r-- | unit_tests/scripts/coro/index_suspended.lua | 28 | ||||
| -rw-r--r-- | unit_tests/scripts/coro/join_suspended.lua | 24 | ||||
| -rw-r--r-- | unit_tests/scripts/coro/linda_in_close_handler.lua | 43 | ||||
| -rw-r--r-- | unit_tests/scripts/coro/resume_basics.lua | 40 | ||||
| -rw-r--r-- | unit_tests/scripts/coro/yielding_function.lua | 108 | ||||
| -rw-r--r-- | unit_tests/scripts/coro/yielding_in_non_coro_errors.lua | 27 |
10 files changed, 247 insertions, 191 deletions
diff --git a/unit_tests/scripts/_utils.lua b/unit_tests/scripts/_utils.lua index d710702..9f46237 100644 --- a/unit_tests/scripts/_utils.lua +++ b/unit_tests/scripts/_utils.lua | |||
| @@ -68,8 +68,26 @@ local function dump_error_stack(error_reporting_mode_, stack) | |||
| 68 | end | 68 | end |
| 69 | end | 69 | end |
| 70 | 70 | ||
| 71 | -- a function that yields back what got in, one element at a time | ||
| 72 | local yield_one_by_one = function(...) | ||
| 73 | local PRINT = MAKE_PRINT() | ||
| 74 | PRINT "In lane" | ||
| 75 | for _i = 1, select('#', ...) do | ||
| 76 | local _val = select(_i, ...) | ||
| 77 | PRINT("yielding #", _i, _val) | ||
| 78 | local _ack = coroutine.yield(_val) | ||
| 79 | if cancel_test and cancel_test() then -- cancel_test does not exist when run immediately (not in a Lane) | ||
| 80 | return "cancelled!" | ||
| 81 | end | ||
| 82 | -- of course, if we are cancelled, we were not resumed, and yield() didn't return what we expect | ||
| 83 | assert(_ack == _i) | ||
| 84 | end | ||
| 85 | return "bye!" | ||
| 86 | end | ||
| 87 | |||
| 71 | return { | 88 | return { |
| 72 | MAKE_PRINT = MAKE_PRINT, | 89 | MAKE_PRINT = MAKE_PRINT, |
| 73 | tables_match = tables_match, | 90 | tables_match = tables_match, |
| 74 | dump_error_stack = dump_error_stack | 91 | dump_error_stack = dump_error_stack, |
| 92 | yield_one_by_one = yield_one_by_one | ||
| 75 | } | 93 | } |
diff --git a/unit_tests/scripts/_utils54.lua b/unit_tests/scripts/_utils54.lua new file mode 100644 index 0000000..a511563 --- /dev/null +++ b/unit_tests/scripts/_utils54.lua | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | local utils = require "_utils" | ||
| 2 | |||
| 3 | -- expand _utils module with Lua5.4 specific stuff | ||
| 4 | |||
| 5 | -- a lane body that yields stuff | ||
| 6 | utils.yielder_with_to_be_closed = function(out_linda_, wait_) | ||
| 7 | local fixture = assert(require "fixture") | ||
| 8 | -- here is a to-be-closed variable that, when closed, sends "Closed!" in the "out" slot of the provided linda | ||
| 9 | local t <close> = setmetatable( | ||
| 10 | { text = "Closed!" }, { | ||
| 11 | __close = function(self, err) | ||
| 12 | if wait_ then | ||
| 13 | fixture.block_for(wait_) | ||
| 14 | end | ||
| 15 | out_linda_:send("out", self.text) | ||
| 16 | end | ||
| 17 | } | ||
| 18 | ) | ||
| 19 | -- yield forever, but be cancel-friendly | ||
| 20 | local n = 1 | ||
| 21 | while true do | ||
| 22 | coroutine.yield("I yield!", n) | ||
| 23 | if cancel_test and cancel_test() then -- cancel_test does not exist when run immediately (not in a Lane) | ||
| 24 | return "I am cancelled" | ||
| 25 | end | ||
| 26 | n = n + 1 | ||
| 27 | end | ||
| 28 | end | ||
| 29 | |||
| 30 | return utils | ||
diff --git a/unit_tests/scripts/coro/cancelling_suspended.lua b/unit_tests/scripts/coro/cancelling_suspended.lua new file mode 100644 index 0000000..3a29e55 --- /dev/null +++ b/unit_tests/scripts/coro/cancelling_suspended.lua | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | local fixture = require "fixture" | ||
| 2 | local lanes = require "lanes".configure{on_state_create = fixture.on_state_create} | ||
| 3 | |||
| 4 | local fixture = require "fixture" | ||
| 5 | lanes.finally(fixture.throwing_finalizer) | ||
| 6 | |||
| 7 | local utils = lanes.require "_utils" | ||
| 8 | local PRINT = utils.MAKE_PRINT() | ||
| 9 | |||
| 10 | -------------------------------------------------- | ||
| 11 | -- TEST: cancelling a suspended Lane should end it | ||
| 12 | -------------------------------------------------- | ||
| 13 | if true then | ||
| 14 | -- the generator | ||
| 15 | local coro_g = lanes.coro("*", utils.yield_one_by_one) | ||
| 16 | |||
| 17 | -- start the lane | ||
| 18 | local h = coro_g("hello", "world", "!") | ||
| 19 | repeat until h.status == "suspended" | ||
| 20 | |||
| 21 | -- first cancellation attempt: don't wake the lane | ||
| 22 | local b, r = h:cancel("soft", 0.5) | ||
| 23 | -- the lane is still blocked in its suspended state | ||
| 24 | assert(b == false and r == "timeout" and h.status == "suspended", "got " .. tostring(b) .. " " .. tostring(r) .. " " .. h.status) | ||
| 25 | |||
| 26 | -- cancel the Lane again, this time waking it. it will resume, and yielder()'s will break out of its infinite loop | ||
| 27 | h:cancel("soft", nil, true) | ||
| 28 | |||
| 29 | -- lane should be done, because it returned cooperatively when detecting a soft cancel | ||
| 30 | assert(h.status == "done", "got " .. h.status) | ||
| 31 | end | ||
diff --git a/unit_tests/scripts/coro/collect_yielded_lane.lua b/unit_tests/scripts/coro/collect_yielded_lane.lua index 2bc4ae8..2ee58f8 100644 --- a/unit_tests/scripts/coro/collect_yielded_lane.lua +++ b/unit_tests/scripts/coro/collect_yielded_lane.lua | |||
| @@ -4,72 +4,18 @@ local lanes = require "lanes".configure{on_state_create = fixture.on_state_creat | |||
| 4 | local fixture = require "fixture" | 4 | local fixture = require "fixture" |
| 5 | lanes.finally(fixture.throwing_finalizer) | 5 | lanes.finally(fixture.throwing_finalizer) |
| 6 | 6 | ||
| 7 | local utils = lanes.require "_utils" | 7 | -- this test is only for Lua 5.4+ |
| 8 | local utils = lanes.require "_utils54" | ||
| 8 | local PRINT = utils.MAKE_PRINT() | 9 | local PRINT = utils.MAKE_PRINT() |
| 9 | 10 | ||
| 10 | -- a lane body that yields stuff | ||
| 11 | local yielder = function(out_linda_, wait_) | ||
| 12 | local fixture = assert(require "fixture") | ||
| 13 | -- here is a to-be-closed variable that, when closed, sends "Closed!" in the "out" slot of the provided linda | ||
| 14 | local t <close> = setmetatable( | ||
| 15 | { text = "Closed!" }, { | ||
| 16 | __close = function(self, err) | ||
| 17 | if wait_ then | ||
| 18 | fixture.block_for(wait_) | ||
| 19 | end | ||
| 20 | out_linda_:send("out", self.text) | ||
| 21 | end | ||
| 22 | } | ||
| 23 | ) | ||
| 24 | -- yield forever, but be cancel-friendly | ||
| 25 | local n = 1 | ||
| 26 | while true do | ||
| 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 | ||
| 32 | end | ||
| 33 | end | ||
| 34 | |||
| 35 | local out_linda = lanes.linda() | 11 | local out_linda = lanes.linda() |
| 36 | 12 | ||
| 37 | local test_close = function(what_, f_) | ||
| 38 | local c = coroutine.create(f_) | ||
| 39 | for i = 1, 10 do | ||
| 40 | local t, r1, r2 = coroutine.resume(c, out_linda) -- returns true + <yielded values> | ||
| 41 | assert(t == true and r1 == "I yield!" and r2 == i, "got " .. tostring(t) .. " " .. tostring(r1) .. " " .. tostring(r2)) | ||
| 42 | local s = coroutine.status(c) | ||
| 43 | assert(s == "suspended") | ||
| 44 | end | ||
| 45 | local r, s = coroutine.close(c) | ||
| 46 | assert(r == true and s == nil) | ||
| 47 | -- the local variable inside the yielder body should be closed | ||
| 48 | local s, r = out_linda:receive(0, "out") | ||
| 49 | assert(s == "out" and r == "Closed!", what_ .. " got " .. tostring(s) .. " " .. tostring(r)) | ||
| 50 | end | ||
| 51 | |||
| 52 | --------------------------------------------------------- | ||
| 53 | -- TEST: first, try the close mechanism outside of a lane | ||
| 54 | --------------------------------------------------------- | ||
| 55 | if true then | ||
| 56 | test_close("base", yielder) | ||
| 57 | end | ||
| 58 | |||
| 59 | --------------------------------------------------------------- | ||
| 60 | -- TEST: try again with a function obtained through dump/undump | ||
| 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 | ||
| 66 | |||
| 67 | ------------------------------------------------------------------------------ | 13 | ------------------------------------------------------------------------------ |
| 68 | -- TEST: to-be-closed variables are properly closed whzen the lane is collected | 14 | -- TEST: to-be-closed variables are properly closed when the lane is collected |
| 69 | ------------------------------------------------------------------------------ | 15 | ------------------------------------------------------------------------------ |
| 70 | if true then | 16 | if true then |
| 71 | -- the generator | 17 | -- the generator |
| 72 | local coro_g = lanes.coro("*", yielder) | 18 | local coro_g = lanes.coro("*", utils.yielder_with_to_be_closed) |
| 73 | 19 | ||
| 74 | -- start the lane | 20 | -- start the lane |
| 75 | local h = coro_g(out_linda) | 21 | local h = coro_g(out_linda) |
| @@ -93,7 +39,7 @@ end | |||
| 93 | --------------------------------------------------------------------------------------------------- | 39 | --------------------------------------------------------------------------------------------------- |
| 94 | if true then | 40 | if true then |
| 95 | -- the generator | 41 | -- the generator |
| 96 | local coro_g = lanes.coro("*", yielder) | 42 | local coro_g = lanes.coro("*", utils.yielder_with_to_be_closed) |
| 97 | 43 | ||
| 98 | -- start the lane. The to-be-closed handler will sleep for 1 second | 44 | -- start the lane. The to-be-closed handler will sleep for 1 second |
| 99 | local h = coro_g(out_linda, 1) | 45 | local h = coro_g(out_linda, 1) |
| @@ -116,26 +62,3 @@ if true then | |||
| 116 | local s, r = out_linda:receive(0, "out") | 62 | local s, r = out_linda:receive(0, "out") |
| 117 | assert(s == "out" and r == "Closed!", "coro got " .. tostring(s) .. " " .. tostring(r)) -- THIS TEST FAILS | 63 | assert(s == "out" and r == "Closed!", "coro got " .. tostring(s) .. " " .. tostring(r)) -- THIS TEST FAILS |
| 118 | end | 64 | 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) | ||
| 141 | end | ||
diff --git a/unit_tests/scripts/coro/index_suspended.lua b/unit_tests/scripts/coro/index_suspended.lua new file mode 100644 index 0000000..2cd8c28 --- /dev/null +++ b/unit_tests/scripts/coro/index_suspended.lua | |||
| @@ -0,0 +1,28 @@ | |||
| 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 | -- the coroutine generator | ||
| 10 | local coro_g = lanes.coro("*", {name = "auto"}, utils.yield_one_by_one) | ||
| 11 | |||
| 12 | ------------------------------------------------------------------------- | ||
| 13 | -- TEST: if we index a yielded lane, we should get the last yielded value | ||
| 14 | ------------------------------------------------------------------------- | ||
| 15 | if true then | ||
| 16 | -- launch coroutine lane | ||
| 17 | local h = coro_g("hello", "world", "!") | ||
| 18 | -- read the first yielded value, sending back the expected index | ||
| 19 | assert(h:resume(1) == "hello") | ||
| 20 | -- indexing multiple times gives back the same us the same yielded value | ||
| 21 | local r1 = h[1] | ||
| 22 | local r2 = h[1] | ||
| 23 | local r3 = h[1] | ||
| 24 | assert(r1 == "world" and r2 == "world" and r3 == "world", "got " .. r1 .. " " .. r2 .. " " .. r3) | ||
| 25 | -- once the lane was indexed, it is no longer resumable (just like after join) | ||
| 26 | local b, e = pcall(h.resume, h, 2) | ||
| 27 | assert(b == false and e == "cannot resume non-suspended coroutine Lane") | ||
| 28 | end | ||
diff --git a/unit_tests/scripts/coro/join_suspended.lua b/unit_tests/scripts/coro/join_suspended.lua new file mode 100644 index 0000000..33be406 --- /dev/null +++ b/unit_tests/scripts/coro/join_suspended.lua | |||
| @@ -0,0 +1,24 @@ | |||
| 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 | -- the coroutine generator | ||
| 10 | local coro_g = lanes.coro("*", {name = "auto"}, utils.yield_one_by_one) | ||
| 11 | |||
| 12 | --------------------------------------------------- | ||
| 13 | -- TEST: if we join a yielded lane, the lane aborts | ||
| 14 | --------------------------------------------------- | ||
| 15 | if true then | ||
| 16 | -- launch coroutine lane | ||
| 17 | local h = coro_g("hello", "world", "!") | ||
| 18 | -- read the first yielded value, sending back the expected index | ||
| 19 | assert(h:resume(1) == "hello") | ||
| 20 | -- join the lane. since it will reach a yield point, it unblocks and ends. last yielded values are returned normally | ||
| 21 | local b, r = h:join(0.5) | ||
| 22 | local s = h.status | ||
| 23 | assert(s == "done" and b == true and r == "world", "got " .. s .. " " .. tostring(b) .. " " .. tostring(r)) | ||
| 24 | end | ||
diff --git a/unit_tests/scripts/coro/linda_in_close_handler.lua b/unit_tests/scripts/coro/linda_in_close_handler.lua new file mode 100644 index 0000000..8636f01 --- /dev/null +++ b/unit_tests/scripts/coro/linda_in_close_handler.lua | |||
| @@ -0,0 +1,43 @@ | |||
| 1 | local fixture = require "fixture" | ||
| 2 | local lanes = require "lanes".configure{on_state_create = fixture.on_state_create} | ||
| 3 | |||
| 4 | local fixture = require "fixture" | ||
| 5 | lanes.finally(fixture.throwing_finalizer) | ||
| 6 | |||
| 7 | -- this test is only for Lua 5.4+ | ||
| 8 | local utils = lanes.require "_utils54" | ||
| 9 | local PRINT = utils.MAKE_PRINT() | ||
| 10 | |||
| 11 | local out_linda = lanes.linda() | ||
| 12 | |||
| 13 | local test_close = function(what_, f_) | ||
| 14 | local c = coroutine.create(f_) | ||
| 15 | for i = 1, 10 do | ||
| 16 | local t, r1, r2 = coroutine.resume(c, out_linda) -- returns true + <yielded values> | ||
| 17 | assert(t == true and r1 == "I yield!" and r2 == i, "got " .. tostring(t) .. " " .. tostring(r1) .. " " .. tostring(r2)) | ||
| 18 | local s = coroutine.status(c) | ||
| 19 | assert(s == "suspended") | ||
| 20 | end | ||
| 21 | local r, s = coroutine.close(c) | ||
| 22 | assert(r == true and s == nil) | ||
| 23 | -- the local variable inside the yielder body should be closed | ||
| 24 | local s, r = out_linda:receive(0, "out") | ||
| 25 | assert(s == "out" and r == "Closed!", what_ .. " got " .. tostring(s) .. " " .. tostring(r)) | ||
| 26 | end | ||
| 27 | |||
| 28 | --------------------------------------------------------- | ||
| 29 | -- TEST: first, try the close mechanism outside of a lane | ||
| 30 | --------------------------------------------------------- | ||
| 31 | if true then | ||
| 32 | assert(type(utils.yielder_with_to_be_closed) == "function") | ||
| 33 | test_close("base", utils.yielder_with_to_be_closed) | ||
| 34 | end | ||
| 35 | |||
| 36 | --------------------------------------------------------------- | ||
| 37 | -- TEST: try again with a function obtained through dump/undump | ||
| 38 | --------------------------------------------------------------- | ||
| 39 | if true then | ||
| 40 | -- note this means our yielder implementation can't have upvalues, as they are lost in the process | ||
| 41 | test_close("dumped", load(string.dump(utils.yielder_with_to_be_closed))) | ||
| 42 | end | ||
| 43 | |||
diff --git a/unit_tests/scripts/coro/resume_basics.lua b/unit_tests/scripts/coro/resume_basics.lua new file mode 100644 index 0000000..5b124f5 --- /dev/null +++ b/unit_tests/scripts/coro/resume_basics.lua | |||
| @@ -0,0 +1,40 @@ | |||
| 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 | -- the coroutine generator | ||
| 10 | local coro_g = lanes.coro("*", {name = "auto"}, utils.yield_one_by_one) | ||
| 11 | |||
| 12 | ------------------------------------------------------------------------------------------------- | ||
| 13 | -- TEST: we can resume as many times as the lane yields, then read the returned value on indexing | ||
| 14 | ------------------------------------------------------------------------------------------------- | ||
| 15 | if true then | ||
| 16 | -- launch coroutine lane | ||
| 17 | local h = coro_g("hello", "world", "!") | ||
| 18 | -- read the yielded values, sending back the expected index | ||
| 19 | assert(h:resume(1) == "hello") | ||
| 20 | assert(h:resume(2) == "world") | ||
| 21 | assert(h:resume(3) == "!") | ||
| 22 | -- the lane return value is available as usual | ||
| 23 | local r = h[1] | ||
| 24 | assert(r == "bye!") | ||
| 25 | end | ||
| 26 | |||
| 27 | --------------------------------------------------------------------------------------------- | ||
| 28 | -- TEST: we can resume as many times as the lane yields, then read the returned value on join | ||
| 29 | --------------------------------------------------------------------------------------------- | ||
| 30 | if true then | ||
| 31 | -- launch coroutine lane | ||
| 32 | local h = coro_g("hello", "world", "!") | ||
| 33 | -- read the yielded values, sending back the expected index | ||
| 34 | assert(h:resume(1) == "hello") | ||
| 35 | assert(h:resume(2) == "world") | ||
| 36 | assert(h:resume(3) == "!") | ||
| 37 | -- the lane return value is available as usual | ||
| 38 | local s, r = h:join() | ||
| 39 | assert(h.status == "done" and s == true and r == "bye!") | ||
| 40 | end | ||
diff --git a/unit_tests/scripts/coro/yielding_function.lua b/unit_tests/scripts/coro/yielding_function.lua deleted file mode 100644 index 6518d1f..0000000 --- a/unit_tests/scripts/coro/yielding_function.lua +++ /dev/null | |||
| @@ -1,108 +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 | -- 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 "bye!" | ||
| 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 == "bye!") | ||
| 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 == "bye!") | ||
| 76 | end | ||
| 77 | |||
| 78 | --------------------------------------------------- | ||
| 79 | -- TEST: if we join a yielded lane, the lane aborts | ||
| 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 unblocks and ends. last yielded values are returned normally | ||
| 87 | local b, r = h:join(0.5) | ||
| 88 | local s = h.status | ||
| 89 | assert(s == "done" and b == true and r == "world", "got " .. s .. " " .. tostring(b) .. " " .. tostring(r)) | ||
| 90 | end | ||
| 91 | |||
| 92 | ------------------------------------------------------------------------- | ||
| 93 | -- TEST: if we index a yielded lane, we should get the last yielded value | ||
| 94 | ------------------------------------------------------------------------- | ||
| 95 | if true then | ||
| 96 | -- launch coroutine lane | ||
| 97 | local h = coro_g("hello", "world", "!") | ||
| 98 | -- read the first yielded value, sending back the expected index | ||
| 99 | assert(h:resume(1) == "hello") | ||
| 100 | -- indexing multiple times gives back the same us the same yielded value | ||
| 101 | local r1 = h[1] | ||
| 102 | local r2 = h[1] | ||
| 103 | local r3 = h[1] | ||
| 104 | assert(r1 == "world" and r2 == "world" and r3 == "world", "got " .. r1 .. " " .. r2 .. " " .. r3) | ||
| 105 | -- once the lane was indexed, it is no longer resumable (just like after join) | ||
| 106 | local b, e = pcall(h.resume, h, 2) | ||
| 107 | assert(b == false and e == "cannot resume non-suspended coroutine Lane") | ||
| 108 | end | ||
diff --git a/unit_tests/scripts/coro/yielding_in_non_coro_errors.lua b/unit_tests/scripts/coro/yielding_in_non_coro_errors.lua new file mode 100644 index 0000000..88d5fe0 --- /dev/null +++ b/unit_tests/scripts/coro/yielding_in_non_coro_errors.lua | |||
| @@ -0,0 +1,27 @@ | |||
| 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 | -------------------------------------------------------------------------------------------------- | ||
| 10 | -- TEST: if we start a non-coroutine lane with a yielding function, we should get an error, right? | ||
| 11 | -------------------------------------------------------------------------------------------------- | ||
| 12 | local fun_g = lanes.gen("*", { name = 'auto' }, utils.yield_one_by_one) | ||
| 13 | local h = fun_g("hello", "world", "!") | ||
| 14 | local err, status, stack = h:join() | ||
| 15 | PRINT(err, status, stack) | ||
| 16 | -- the actual error message is not the same for Lua 5.1 | ||
| 17 | -- of course, it also has to be different for LuaJIT as well | ||
| 18 | -- also, LuaJIT prepends a file:line to the actual error message, which Lua5.1 does not. | ||
| 19 | local msgs = { | ||
| 20 | ["Lua 5.1"] = jit and "attempt to yield across C-call boundary" or "attempt to yield across metamethod/C-call boundary", | ||
| 21 | ["Lua 5.2"] = "attempt to yield from outside a coroutine", | ||
| 22 | ["Lua 5.3"] = "attempt to yield from outside a coroutine", | ||
| 23 | ["Lua 5.4"] = "attempt to yield from outside a coroutine" | ||
| 24 | } | ||
| 25 | local expected_msg = msgs[_VERSION] | ||
| 26 | PRINT("expected_msg = " .. expected_msg) | ||
| 27 | assert(err == nil and string.find(status, expected_msg, 1, true) and stack == nil, "status = " .. status) | ||
