aboutsummaryrefslogtreecommitdiff
path: root/unit_tests
diff options
context:
space:
mode:
Diffstat (limited to 'unit_tests')
-rw-r--r--unit_tests/scripts/coro/collect_yielded_lane.lua105
-rw-r--r--unit_tests/scripts/coro/yielding_function.lua34
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 @@
1local lanes = require "lanes" 1local fixture = require "fixture"
2local lanes = require "lanes".configure{on_state_create = fixture.on_state_create}
2 3
3local fixture = require "fixture" 4local fixture = require "fixture"
4lanes.finally(fixture.throwing_finalizer) 5lanes.finally(fixture.throwing_finalizer)
@@ -7,18 +8,27 @@ local utils = lanes.require "_utils"
7local PRINT = utils.MAKE_PRINT() 8local PRINT = utils.MAKE_PRINT()
8 9
9-- a lane body that yields stuff 10-- a lane body that yields stuff
10local yielder = function(out_linda_) 11local 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
23end 33end
24 34
@@ -27,8 +37,8 @@ local out_linda = lanes.linda()
27local test_close = function(what_, f_) 37local 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))
40end 50end
41 51
42-- first, try the close mechanism outside of a lane 52---------------------------------------------------------
43test_close("base", yielder) 53-- TEST: first, try the close mechanism outside of a lane
54---------------------------------------------------------
55if true then
56 test_close("base", yielder)
57end
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
47test_close("dumped", load(string.dump(yielder))) 61---------------------------------------------------------------
62if 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)))
65end
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------------------------------------------------------------------------------
52if false then -- NOT IMPLEMENTED YET! 70if 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
89end
90
91---------------------------------------------------------------------------------------------------
92-- TEST: if a to-be-closed handler takes longer than the join timeout, everything works as expected
93---------------------------------------------------------------------------------------------------
94if 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
118end
119
120--------------------------------------------------
121-- TEST: cancelling a suspended Lane should end it
122--------------------------------------------------
123if 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)
72end 141end
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--------------------------------------------------------------------------------------------------
26if false then 26if 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-------------------------------------------------------------------------------------------------
51if false then 51if 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---------------------------------------------------------------------------------------------
66if false then 66if 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!")
76end 76end
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---------------------------------------------------
81if true then 81if 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))
90end 90end
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-------------------------------------------------------------------------
95if false then 95if 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) == "!")
111end 108end
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!