diff options
author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-12-13 17:22:17 +0100 |
---|---|---|
committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-12-13 17:22:17 +0100 |
commit | dc7a2bc3a9c8316e17902493b832ca117805d29f (patch) | |
tree | 3c1a03edf586869119ef0de03631f6f603650720 /unit_tests/scripts/coro | |
parent | 7500a80fc06c5311c46df8f1761f25ae67277fc4 (diff) | |
download | lanes-dc7a2bc3a9c8316e17902493b832ca117805d29f.tar.gz lanes-dc7a2bc3a9c8316e17902493b832ca117805d29f.tar.bz2 lanes-dc7a2bc3a9c8316e17902493b832ca117805d29f.zip |
Append all unit tests to depot
Diffstat (limited to 'unit_tests/scripts/coro')
-rw-r--r-- | unit_tests/scripts/coro/basics.lua | 94 | ||||
-rw-r--r-- | unit_tests/scripts/coro/error_handling.lua | 64 |
2 files changed, 158 insertions, 0 deletions
diff --git a/unit_tests/scripts/coro/basics.lua b/unit_tests/scripts/coro/basics.lua new file mode 100644 index 0000000..4444e87 --- /dev/null +++ b/unit_tests/scripts/coro/basics.lua | |||
@@ -0,0 +1,94 @@ | |||
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 | local msgs = { | ||
51 | ["Lua 5.1"] = "attempt to yield across metamethod/C-call boundary", | ||
52 | ["Lua 5.2"] = "attempt to yield from outside a coroutine", | ||
53 | ["Lua 5.3"] = "attempt to yield from outside a coroutine", | ||
54 | ["Lua 5.4"] = "attempt to yield from outside a coroutine" | ||
55 | } | ||
56 | local expected_msg = msgs[_VERSION] | ||
57 | assert(err == nil and status == expected_msg and stack == nil, "status = " .. status) | ||
58 | end | ||
59 | |||
60 | -- the generator | ||
61 | local coro_g = lanes.coro("*", {name = "auto"}, yielder) | ||
62 | |||
63 | if true then | ||
64 | -- launch coroutine lane | ||
65 | local h2 = coro_g("hello", "world", "!") | ||
66 | -- read the yielded values, sending back the expected index | ||
67 | assert(h2:resume(1) == "hello") | ||
68 | assert(h2:resume(2) == "world") | ||
69 | assert(h2:resume(3) == "!") | ||
70 | -- the lane return value is available as usual | ||
71 | local r = h2[1] | ||
72 | assert(r == "done!") | ||
73 | end | ||
74 | |||
75 | if true then | ||
76 | -- another coroutine lane | ||
77 | local h3 = coro_g("hello", "world", "!") | ||
78 | |||
79 | -- yielded values are available as regular return values | ||
80 | assert(h3[1] == "hello" and h3.status == "suspended") | ||
81 | -- since we consumed the returned values, they should not be here when we resume | ||
82 | assert(h3:resume(1) == nil) | ||
83 | |||
84 | -- similarly, we can get them with join() | ||
85 | assert(h3:join() == "world" and h3.status == "suspended") | ||
86 | -- since we consumed the returned values, they should not be here when we resume | ||
87 | assert(h3:resume(2) == nil) | ||
88 | |||
89 | -- the rest should work as usual | ||
90 | assert(h3:resume(3) == "!") | ||
91 | |||
92 | -- the final return value of the lane body remains to be read | ||
93 | assert(h3:join() == "done!" and h3.status == "done") | ||
94 | end | ||
diff --git a/unit_tests/scripts/coro/error_handling.lua b/unit_tests/scripts/coro/error_handling.lua new file mode 100644 index 0000000..3b50c7f --- /dev/null +++ b/unit_tests/scripts/coro/error_handling.lua | |||
@@ -0,0 +1,64 @@ | |||
1 | local lanes = require "lanes".configure{strip_functions = true} | ||
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, "unexpected reply ".._ack) | ||
19 | end | ||
20 | return "done!" | ||
21 | end | ||
22 | |||
23 | local force_error_test = function(error_trace_level_) | ||
24 | -- the generator, minimal error handling | ||
25 | local coro_g = lanes.coro("*", {name = "auto", error_trace_level = error_trace_level_}, yielder) | ||
26 | |||
27 | -- launch coroutine lane | ||
28 | local h = coro_g("hello", "world", "!") | ||
29 | -- read the yielded values, sending back the expected index | ||
30 | assert(h:resume(1) == "hello") | ||
31 | assert(h:resume(2) == "world") | ||
32 | -- mistake: we resume with 0 when the lane expects 3 -> assert() in the lane body! | ||
33 | assert(h:resume(0) == "!") | ||
34 | local a, b, c = h:join() | ||
35 | PRINT(error_trace_level_ .. ":", a, b, c) | ||
36 | local expected_c_type = error_trace_level_ == "minimal" and "nil" or "table" | ||
37 | assert(h.status == "error" and string.find(b, "unexpected reply 0", 1, true) and type(c) == expected_c_type, "error message is " .. b) | ||
38 | utils.dump_error_stack(error_trace_level_, c) | ||
39 | end | ||
40 | |||
41 | if false then | ||
42 | force_error_test("minimal") | ||
43 | end | ||
44 | |||
45 | if false then | ||
46 | force_error_test("basic") | ||
47 | end | ||
48 | |||
49 | if false then | ||
50 | force_error_test("extended") | ||
51 | end | ||
52 | |||
53 | if true then | ||
54 | -- start a coroutine lane that ends with a non-string error | ||
55 | local non_string_thrower = function() | ||
56 | error({"string in table"}) | ||
57 | end | ||
58 | local coro_g = lanes.coro("*", {name = "auto"}, non_string_thrower) | ||
59 | local h = coro_g() | ||
60 | local a, b, c = h:join() | ||
61 | -- we get the expected error back | ||
62 | PRINT("non_string_thrower:", a, b, c) | ||
63 | assert(a == nil and type(b) == "table" and b[1] == "string in table" and c == nil) | ||
64 | end | ||