diff options
Diffstat (limited to 'unit_tests/scripts/coro')
-rw-r--r-- | unit_tests/scripts/coro/basics.lua | 97 | ||||
-rw-r--r-- | unit_tests/scripts/coro/cancelling_suspended.lua | 31 | ||||
-rw-r--r-- | unit_tests/scripts/coro/collect_yielded_lane.lua | 64 | ||||
-rw-r--r-- | unit_tests/scripts/coro/error_handling.lua | 6 | ||||
-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/regular_function.lua | 38 | ||||
-rw-r--r-- | unit_tests/scripts/coro/resume_basics.lua | 40 | ||||
-rw-r--r-- | unit_tests/scripts/coro/yielding_in_non_coro_errors.lua | 28 |
10 files changed, 299 insertions, 100 deletions
diff --git a/unit_tests/scripts/coro/basics.lua b/unit_tests/scripts/coro/basics.lua deleted file mode 100644 index cd2f410..0000000 --- a/unit_tests/scripts/coro/basics.lua +++ /dev/null | |||
@@ -1,97 +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 | assert(h3:join() == "world" and h3.status == "suspended") | ||
89 | -- since we consumed the returned values, they should not be here when we resume | ||
90 | assert(h3:resume(2) == nil) | ||
91 | |||
92 | -- the rest should work as usual | ||
93 | assert(h3:resume(3) == "!") | ||
94 | |||
95 | -- the final return value of the lane body remains to be read | ||
96 | assert(h3:join() == "done!" and h3.status == "done") | ||
97 | end | ||
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 new file mode 100644 index 0000000..2ee58f8 --- /dev/null +++ b/unit_tests/scripts/coro/collect_yielded_lane.lua | |||
@@ -0,0 +1,64 @@ | |||
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 | ------------------------------------------------------------------------------ | ||
14 | -- TEST: to-be-closed variables are properly closed when the lane is collected | ||
15 | ------------------------------------------------------------------------------ | ||
16 | if true then | ||
17 | -- the generator | ||
18 | local coro_g = lanes.coro("*", utils.yielder_with_to_be_closed) | ||
19 | |||
20 | -- start the lane | ||
21 | local h = coro_g(out_linda) | ||
22 | |||
23 | -- join the lane. it should be done and give back the values resulting of the first yield point | ||
24 | local r, v1, v2 = h:join() | ||
25 | assert(r == true and v1 == "I yield!" and v2 == 1, "got " .. tostring(r) .. " " .. tostring(v1) .. " " .. tostring(v2)) | ||
26 | assert(h.status == "done", "got " .. h.status) | ||
27 | |||
28 | -- force collection of the lane | ||
29 | h = nil | ||
30 | collectgarbage() | ||
31 | |||
32 | -- I want the to-be-closed variable of the coroutine linda to be properly closed | ||
33 | local s, r = out_linda:receive(0, "out") | ||
34 | assert(s == "out" and r == "Closed!", "coro got " .. tostring(s) .. " " .. tostring(r)) -- THIS TEST FAILS | ||
35 | end | ||
36 | |||
37 | --------------------------------------------------------------------------------------------------- | ||
38 | -- TEST: if a to-be-closed handler takes longer than the join timeout, everything works as expected | ||
39 | --------------------------------------------------------------------------------------------------- | ||
40 | if true then | ||
41 | -- the generator | ||
42 | local coro_g = lanes.coro("*", utils.yielder_with_to_be_closed) | ||
43 | |||
44 | -- start the lane. The to-be-closed handler will sleep for 1 second | ||
45 | local h = coro_g(out_linda, 1) | ||
46 | |||
47 | -- first join attempt should timeout | ||
48 | local r, v = h:join(0.6) | ||
49 | assert(r == nil and v == "timeout", "got " .. tostring(r) .. " " .. tostring(v)) | ||
50 | assert(h.status == "running", "got " .. h.status) | ||
51 | |||
52 | -- join the lane again. it should be done and give back the values resulting of the first yield point | ||
53 | local r, v1, v2 = h:join(0.6) | ||
54 | assert(r == true and v1 == "I yield!" and v2 == 1, "got " .. tostring(r) .. " " .. tostring(v1) .. " " .. tostring(v2)) | ||
55 | assert(h.status == "done", "got " .. h.status) | ||
56 | |||
57 | -- force collection of the lane | ||
58 | h = nil | ||
59 | collectgarbage() | ||
60 | |||
61 | -- I want the to-be-closed variable of the coroutine linda to be properly closed | ||
62 | local s, r = out_linda:receive(0, "out") | ||
63 | assert(s == "out" and r == "Closed!", "coro got " .. tostring(s) .. " " .. tostring(r)) -- THIS TEST FAILS | ||
64 | 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/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/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/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_in_non_coro_errors.lua b/unit_tests/scripts/coro/yielding_in_non_coro_errors.lua new file mode 100644 index 0000000..fc0c072 --- /dev/null +++ b/unit_tests/scripts/coro/yielding_in_non_coro_errors.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 | -------------------------------------------------------------------------------------------------- | ||
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 | ["Lua 5.5"] = "attempt to yield from outside a coroutine" | ||
25 | } | ||
26 | local expected_msg = msgs[_VERSION] | ||
27 | PRINT("expected_msg = " .. expected_msg) | ||
28 | assert(err == nil and string.find(status, expected_msg, 1, true) and stack == nil, "status = " .. status) | ||