aboutsummaryrefslogtreecommitdiff
path: root/unit_tests/scripts
diff options
context:
space:
mode:
authorBenoit Germain <benoit.germain@ubisoft.com>2024-12-13 17:22:17 +0100
committerBenoit Germain <benoit.germain@ubisoft.com>2024-12-13 17:22:17 +0100
commitdc7a2bc3a9c8316e17902493b832ca117805d29f (patch)
tree3c1a03edf586869119ef0de03631f6f603650720 /unit_tests/scripts
parent7500a80fc06c5311c46df8f1761f25ae67277fc4 (diff)
downloadlanes-dc7a2bc3a9c8316e17902493b832ca117805d29f.tar.gz
lanes-dc7a2bc3a9c8316e17902493b832ca117805d29f.tar.bz2
lanes-dc7a2bc3a9c8316e17902493b832ca117805d29f.zip
Append all unit tests to depot
Diffstat (limited to 'unit_tests/scripts')
-rw-r--r--unit_tests/scripts/_assert.lua321
-rw-r--r--unit_tests/scripts/_utils.lua75
-rw-r--r--unit_tests/scripts/coro/basics.lua94
-rw-r--r--unit_tests/scripts/coro/error_handling.lua64
-rw-r--r--unit_tests/scripts/lane/cooperative_shutdown.lua45
-rw-r--r--unit_tests/scripts/lane/stdlib_naming.lua87
-rw-r--r--unit_tests/scripts/lane/tasking_basic.lua74
-rw-r--r--unit_tests/scripts/lane/tasking_cancelling.lua122
-rw-r--r--unit_tests/scripts/lane/tasking_comms_criss_cross.lua53
-rw-r--r--unit_tests/scripts/lane/tasking_communications.lua149
-rw-r--r--unit_tests/scripts/lane/tasking_error.lua49
-rw-r--r--unit_tests/scripts/lane/tasking_join_test.lua65
-rw-r--r--unit_tests/scripts/lane/tasking_send_receive_code.lua91
-rw-r--r--unit_tests/scripts/lane/uncooperative_shutdown.lua24
-rw-r--r--unit_tests/scripts/linda/multiple_keepers.lua26
-rw-r--r--unit_tests/scripts/linda/send_receive.lua46
-rw-r--r--unit_tests/scripts/linda/send_registered_userdata.lua5
17 files changed, 1390 insertions, 0 deletions
diff --git a/unit_tests/scripts/_assert.lua b/unit_tests/scripts/_assert.lua
new file mode 100644
index 0000000..9bc8d32
--- /dev/null
+++ b/unit_tests/scripts/_assert.lua
@@ -0,0 +1,321 @@
1--
2-- ASSERT.LUA Copyright (c) 2006-07, <akauppi@gmail.com>
3--
4-- Converting the Lua 'assert' function into a namespace table (without
5-- breaking compatibility with the basic 'assert()' calling).
6--
7-- This module allows shorthand use s.a. 'assert.table()' for asserting
8-- variable types, and is also being used by Lua-super constraints system
9-- for testing function argument & return types.
10--
11-- All in all, a worthy module and could be part of Lua future versions.
12--
13-- Note: the 'assert' table is available for your own assertions, too. Just add
14-- more functions s.a. 'assert.myobj()' to check for custom invariants.
15-- They will then be available for the constraints check, too.
16--
17-- Author: <akauppi@gmail.com>
18--
19--[[
20/******************************************************************************
21* Lua 5.1.1 support and extension functions (assert.lua)
22*
23* Copyright (C) 2006-07, Asko Kauppi.
24*
25* NOTE: This license concerns only the particular source file; not necessarily
26* the project with which it has been delivered (the project may have a more
27* restrictive license, s.a. [L]GPL).
28*
29* Permission is hereby granted, free of charge, to any person obtaining
30* a copy of this software and associated documentation files (the
31* "Software"), to deal in the Software without restriction, including
32* without limitation the rights to use, copy, modify, merge, publish,
33* distribute, sublicense, and/or sell copies of the Software, and to
34* permit persons to whom the Software is furnished to do so, subject to
35* the following conditions:
36*
37* The above copyright notice and this permission notice shall be
38* included in all copies or substantial portions of the Software.
39*
40* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
41* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
42* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
43* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
44* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
45* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
46* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
47******************************************************************************/
48]]--
49
50local m= { _info= { MODULE= "Assert.* functions for constraints, and unit testing",
51 AUTHOR= "akauppi@gmail.com",
52 VERSION= 20240702, -- last change (yyyymmdd)
53 LICENSE= "MIT/X11" } }
54
55-- Global changes:
56-- 'assert' redefined, in a backwards compatible way
57--
58-- Module functions:
59-- none
60
61assert( type(assert) == "function" ) -- system assert function
62assert( type(error) == "function")
63
64-----
65-- Integer range: INT_MIN..INT_MAX
66--
67local function try_maxint( n )
68 return (n > n-1) and n-1 -- false when outside the integer range
69end
70
71local INT_MAX=
72 try_maxint( 2^64 ) or
73 try_maxint( 2^32 ) or
74 try_maxint( 2^24 ) or -- float (24-bit mantissa)
75 assert( false )
76
77local INT_MIN= -(INT_MAX+1)
78
79
80---=== assert.*() ===---
81
82local at_msg= "type assertion error" -- TBD: better messages, about that exact situation
83local av_msg= "value assertion error"
84
85-- void= _assert( val [, msg_str [, lev_uint]] )
86--
87local function _assert( cond, msg, lev )
88 -- original 'assert' provides no level override, so we use 'error'.
89 --
90 if not cond then
91 error( msg or "assertion failed!", (lev or 1)+1 )
92 end
93end
94
95-- Note: following code uses the _new_ 'assert()' by purpose, since it provides
96-- a level override (original doesn't)
97--
98local function assert_v( v0 )
99 return function(v,msg)
100 _assert( v == v0, msg or av_msg, 2 )
101 return v
102 end
103end
104local function assert_t( str )
105 return function(v,msg)
106 _assert( type(v) == str, msg or at_msg, 2 )
107 return v
108 end
109end
110local function assert_t2( str )
111 return function(v,subtype,msg)
112 local t,st= type(v)
113 _assert( t==str and ((not subtype) or (st==subtype)),
114 msg or at_msg, 2 )
115 return v
116 end
117end
118
119-- global base function assert() is replaced by this table
120assert=
121 {
122 __metatable = "assert()",
123 __call= function(_,v,msg) -- plain 'assert()' (compatibility)
124 if v then return v end
125 _assert( v, msg, 2 )
126 end,
127
128 -- Hopefully, Lua will allow use of 'nil', 'function' and other reserved words as table
129 -- shortcuts in the future (5.1.1 does not).
130 --
131 ["nil"]= assert_v( nil ),
132 boolean= assert_t "boolean",
133 table= assert_t2 "table",
134 ["function"]= assert_t "function",
135 userdata= assert_t2 "userdata",
136
137 string= function( v, msg )
138 local s= tostring(v)
139 _assert( s, msg or at_msg, 2 )
140 return v
141 end,
142
143 char= function( v, msg )
144 -- 'char' is _not_ doing int->string conversion
145 _assert( type(v)=="string" and v:len()==1, msg or at_msg, 2 )
146 return v
147 end,
148
149 number= function( v, msg )
150 _assert( tonumber(v), msg or at_msg, 2 )
151 return v
152 end,
153
154 int= function( v, msg )
155 local n= tonumber(v)
156 _assert( n and (n >= INT_MIN) and (n <= INT_MAX) and math.floor(n) == n,
157 msg or at_msg, 2 )
158 return v
159 end,
160
161 uint= function( v, msg )
162 local n= tonumber(v)
163 -- unsigned integer upper range is the same as integers' (there's no
164 -- real unsigned within the Lua)
165 _assert( n and (n >= 0) and (n <= INT_MAX) and math.floor(n) == n,
166 msg or at_msg, 2 )
167 return v
168 end,
169
170 ['true']= assert_v( true ),
171 ['false']= assert_v( false ),
172
173 string_or_table= function( v, msg )
174 assert( tostring(v) or type(v)=="table", msg or at_msg, 2 )
175 return v
176 end,
177
178 number_or_string= function( v, msg )
179 assert( tonumber(v) or type(v)=="table", msg or at_msg, 2 )
180 return v
181 end,
182
183 any= function( v, msg )
184 assert( v ~= nil, msg or av_msg, 2 )
185 return v
186 end,
187
188 -- Range assertion, with extra arguments per instance
189 --
190 -- Note: values may be of _any_ type that can do >=, <= comparisons.
191 --
192 range= function( lo, hi )
193 _assert( lo and hi and lo <= hi, "Bad limits", 2 )
194 -- make sure the limits make sense (just once)
195
196 return function(v,msg,lev)
197 if ( (lo and v<lo) or (hi and v>hi) ) then
198 msg= msg or "not in range: ("..(lo or "")..","..(hi or "")..")"
199 _assert( false, msg, 2 )
200 end
201 return v
202 end
203 end,
204
205 -- Table contents assertion
206 -- - no unknown (non-numeric) keys are allowed
207 -- - numeric keys are ignored
208 --
209 -- Constraints patch should point to this, when using the ":{ ... }" constraint.
210 --
211 ["{}"]= function( tbl )
212
213 -- check all keys in 't' (including numeric, if any) that they do exist,
214 -- and carry the right type
215 --
216 local function subf1(v,t,msg,lev)
217 _assert(lev)
218 for k,f in pairs(t) do
219 -- 'f' is an assert function, or subtable
220 local ft= type(f)
221 if ft=="function" then
222 f( v[k], msg, lev+1 )
223 elseif ft=="table" then
224 _assert( type(v[k])=="table", msg or "no subtable "..tostring(k), lev+1 )
225 subf1( v[k], f, msg, lev+1 )
226 else
227 error( "Bad constraints table for '"..tostring(k).."'! (not a function)", lev+1 )
228 end
229 end
230 end
231
232 -- check there are no other (non-numeric) keys in 'v'
233 local function subf2(v,t,msg,lev)
234 _assert(lev)
235 for k,vv in pairs(v) do
236 if type(k)=="number" then
237 -- skip them
238 elseif not t[k] then
239 _assert( false, msg or "extra field: '"..tostring(k).."'", lev+1 )
240 elseif type(vv)=="table" then
241 subf2( vv, t[k], msg, lev+1 )
242 end
243 end
244 end
245
246 _assert( type(tbl)=="table", "Wrong argument to assert['{}']" )
247
248 return function( v, msg, lev )
249 lev= (lev or 1)+1
250 _assert( type(v)=="table" ,msg, lev )
251 subf1( v, tbl, msg, lev )
252 subf2( v, tbl, msg, lev )
253 return v
254 end
255 end,
256
257 -- ...
258}
259setmetatable( assert, assert )
260
261assert.void= assert["nil"]
262
263
264-----
265-- void= assert.fails( function [,err_msg_str] )
266--
267-- Special assert function, to make sure the call within it fails, and gives a
268-- specific error message (to be used in unit testing).
269--
270function assert.fails( func_block, err_msg )
271 --
272 local st,err= pcall( func_block )
273 if st then
274 _assert( false, "Block expected to fail, but didn't.", 2 )
275 elseif err_msg and err ~= err_msg then
276 _assert( false, "Failed with wrong error message: \n"..
277 "'"..err.."'\nexpected: '"..err_msg.."'", 2 )
278 end
279end
280
281
282-----
283-- void= assert.failsnot( function [,err_msg_str] )
284--
285-- Similar to 'assert.fails' but expects the code to survive.
286--
287function assert.failsnot( func_block, err_msg )
288 --
289 local st,err= pcall( func_block )
290 if not st then
291 _assert( false, "Block expected NOT to fail, but did."..
292 (err and "\n\tError: '"..err.."'" or ""), 2 )
293 end
294end
295
296
297-----
298-- void= assert.nilerr( function [,err_msg_str] )
299--
300-- Expects the function to return with 'nil,err' failure code, with
301-- optionally error string matching. Similar to --> 'assert.fails()'
302--
303function assert.nilerr( func_block, err_msg )
304 --
305 local v,err= func_block()
306 _assert( v==nil, "Expected to return nil, but didn't: "..tostring(v), 2 )
307 if err_msg and err ~= err_msg then
308 _assert( false, "Failed with wrong error message: \n"..
309 "'"..err.."'\nexpected: '"..err_msg.."'", 2 )
310 end
311end
312
313
314-- Sanity check
315--
316assert( true )
317assert.fails( function() assert( false ) end )
318assert.fails( function() assert( nil ) end )
319
320
321return m -- just info
diff --git a/unit_tests/scripts/_utils.lua b/unit_tests/scripts/_utils.lua
new file mode 100644
index 0000000..d710702
--- /dev/null
+++ b/unit_tests/scripts/_utils.lua
@@ -0,0 +1,75 @@
1local io = assert(io)
2local pairs = assert(pairs)
3local select = assert(select)
4local string_format = assert(string.format)
5local tostring = assert(tostring)
6local type = assert(type)
7
8local print_id = 0
9local P = function(whence_, ...)
10 if not io then return end
11 print_id = print_id + 1
12 local _str = string_format("%s: %02d.\t", whence_, print_id)
13 for _i = 1, select('#', ...) do
14 _str = _str .. tostring(select(_i, ...)) .. "\t"
15 end
16 if io then
17 io.stderr:write(_str .. "\n")
18 end
19end
20
21local MAKE_PRINT = function()
22 local _whence = lane_threadname and lane_threadname() or "main"
23 return function(...)
24 P(_whence, ...)
25 end
26end
27
28local tables_match
29
30-- true if 'a' is a subtable of 'b'
31--
32local function subtable(a, b)
33 --
34 assert(type(a)=="table" and type(b)=="table")
35
36 for k,v in pairs(b) do
37 if type(v)~=type(a[k]) then
38 return false -- not subtable (different types, or missing key)
39 elseif type(v)=="table" then
40 if not tables_match(v,a[k]) then return false end
41 else
42 if a[k] ~= v then return false end
43 end
44 end
45 return true -- is a subtable
46end
47
48-- true when contents of 'a' and 'b' are identical
49--
50tables_match = function(a, b)
51 return subtable(a, b) and subtable(b, a)
52end
53
54local function dump_error_stack(error_reporting_mode_, stack)
55 local PRINT = MAKE_PRINT()
56 if error_reporting_mode_ == "minimal" then
57 assert(stack == nil, table.concat{"stack is a ", type(stack)})
58 elseif error_reporting_mode_ == "basic" then -- each stack line is a string in basic mode
59 PRINT("STACK:\n", table.concat(stack,"\n\t"));
60 else -- each stack line is a table in extended mode
61 PRINT "STACK:"
62 for line, details in pairs(stack) do
63 PRINT("\t", line);
64 for detail, value in pairs(details) do
65 PRINT("\t\t", detail, ": ", value)
66 end
67 end
68 end
69end
70
71return {
72 MAKE_PRINT = MAKE_PRINT,
73 tables_match = tables_match,
74 dump_error_stack = dump_error_stack
75}
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 @@
1local lanes = require "lanes"
2
3local fixture = require "fixture"
4lanes.finally(fixture.throwing_finalizer)
5
6local utils = lanes.require "_utils"
7local PRINT = utils.MAKE_PRINT()
8
9if 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")
27end
28
29-- a lane coroutine that yields back what got in, one element at a time
30local 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!"
41end
42
43if 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)
58end
59
60-- the generator
61local coro_g = lanes.coro("*", {name = "auto"}, yielder)
62
63if 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!")
73end
74
75if 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")
94end
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 @@
1local lanes = require "lanes".configure{strip_functions = true}
2
3local fixture = require "fixture"
4lanes.finally(fixture.throwing_finalizer)
5
6local utils = lanes.require "_utils"
7local PRINT = utils.MAKE_PRINT()
8
9-- a lane coroutine that yields back what got in, one element at a time
10local 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!"
21end
22
23local 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)
39end
40
41if false then
42 force_error_test("minimal")
43end
44
45if false then
46 force_error_test("basic")
47end
48
49if false then
50 force_error_test("extended")
51end
52
53if 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)
64end
diff --git a/unit_tests/scripts/lane/cooperative_shutdown.lua b/unit_tests/scripts/lane/cooperative_shutdown.lua
new file mode 100644
index 0000000..2649079
--- /dev/null
+++ b/unit_tests/scripts/lane/cooperative_shutdown.lua
@@ -0,0 +1,45 @@
1local lanes = require "lanes"
2
3-- launch lanes that cooperate properly with cancellation request
4
5local lane1 = function()
6 -- loop breaks on cancellation request
7 repeat
8 lanes.sleep(0)
9 until cancel_test()
10 print "lane1 cancelled"
11end
12
13local lane2 = function(linda_)
14 -- loop breaks on lane/linda cancellation
15 repeat
16 local k, v = linda_:receive('k')
17 until v == lanes.cancel_error
18 print "lane2 cancelled"
19end
20
21local lane3 = function()
22 -- this one cooperates too, because of the hook cancellation modes that Lanes will be using
23 -- but not with LuaJIT, because the function is compiled, and we don't call anyone, so no hook triggers
24 repeat until false
25end
26
27
28
29-- the generators
30local g1 = lanes.gen("*", lane1)
31local g2 = lanes.gen("*", lane2)
32local g3 = lanes.gen("*", lane3)
33
34-- launch lanes
35local h1 = g1()
36
37local linda = lanes.linda()
38local h2 = g2(linda)
39
40local h3 = g3()
41
42-- wait until they are both started
43repeat until h1.status == "running" and h2.status == "waiting" and h3.status == "running"
44
45-- let the script terminate, Lanes should not crash at shutdown
diff --git a/unit_tests/scripts/lane/stdlib_naming.lua b/unit_tests/scripts/lane/stdlib_naming.lua
new file mode 100644
index 0000000..2e045c3
--- /dev/null
+++ b/unit_tests/scripts/lane/stdlib_naming.lua
@@ -0,0 +1,87 @@
1local require_lanes_result_1, require_lanes_result_2 = require "lanes".configure(config).configure()
2print("require_lanes_result:", require_lanes_result_1, require_lanes_result_2)
3local lanes = require_lanes_result_1
4
5local require_assert_result_1, require_assert_result_2 = require "_assert"
6print("require_assert_result:", require_assert_result_1, require_assert_result_2)
7
8local utils = lanes.require "_utils"
9local PRINT = utils.MAKE_PRINT()
10
11local lanes_gen = assert(lanes.gen)
12local lanes_linda = assert(lanes.linda)
13
14-- ##################################################################################################
15-- ##################################################################################################
16-- ##################################################################################################
17
18local function task(a, b, c)
19 lane_threadname("task("..a..","..b..","..c..")")
20 --error "111" -- testing error messages
21 assert(hey)
22 local v=0
23 for i=a,b,c do
24 v= v+i
25 end
26 return v, hey
27end
28
29local gc_cb = function(name_, status_)
30 PRINT(" ---> lane '" .. name_ .. "' collected with status '" .. status_ .. "'")
31end
32
33-- ##################################################################################################
34-- ##################################################################################################
35-- ##################################################################################################
36
37PRINT("\n\n", "---=== Stdlib naming ===---", "\n\n")
38
39local function dump_g(_x)
40 lane_threadname "dump_g"
41 assert(print)
42 print("### dumping _G for '" .. _x .. "'")
43 for k, v in pairs(_G) do
44 print("\t" .. k .. ": " .. type(v))
45 end
46 return true
47end
48
49local function io_os_f(_x)
50 lane_threadname "io_os_f"
51 assert(print)
52 print("### checking io and os libs existence for '" .. _x .. "'")
53 assert(io)
54 assert(os)
55 return true
56end
57
58local function coro_f(_x)
59 lane_threadname "coro_f"
60 assert(print)
61 print("### checking coroutine lib existence for '" .. _x .. "'")
62 assert(coroutine)
63 return true
64end
65
66assert.fails(function() lanes_gen("xxx", {gc_cb = gc_cb}, io_os_f) end)
67
68local stdlib_naming_tests =
69{
70 -- { "", dump_g},
71 -- { "coroutine", dump_g},
72 -- { "io", dump_g},
73 -- { "bit32", dump_g},
74 { "coroutine?", coro_f}, -- in Lua 5.1, the coroutine base library doesn't exist (coroutine table is created when 'base' is opened)
75 { "*", io_os_f},
76 { "io,os", io_os_f},
77 { "io+os", io_os_f},
78 { "/io;os[base{", io_os_f}, -- use unconventional name separators to check that everything works fine
79}
80
81for _, t in ipairs(stdlib_naming_tests) do
82 local f= lanes_gen(t[1], {gc_cb = gc_cb}, t[2]) -- any delimiter will do
83 assert(f(t[1])[1])
84end
85
86PRINT("collectgarbage")
87collectgarbage()
diff --git a/unit_tests/scripts/lane/tasking_basic.lua b/unit_tests/scripts/lane/tasking_basic.lua
new file mode 100644
index 0000000..99be321
--- /dev/null
+++ b/unit_tests/scripts/lane/tasking_basic.lua
@@ -0,0 +1,74 @@
1local require_lanes_result_1, require_lanes_result_2 = require "lanes".configure(config).configure()
2print("require_lanes_result:", require_lanes_result_1, require_lanes_result_2)
3local lanes = require_lanes_result_1
4
5local require_assert_result_1, require_assert_result_2 = require "_assert"
6print("require_assert_result:", require_assert_result_1, require_assert_result_2)
7
8local utils = lanes.require "_utils"
9local PRINT = utils.MAKE_PRINT()
10
11local lanes_gen = assert(lanes.gen)
12local lanes_linda = assert(lanes.linda)
13
14-- ##################################################################################################
15-- ##################################################################################################
16-- ##################################################################################################
17
18local function task(a, b, c)
19 local new_name = "task("..a..","..b..","..c..")"
20 -- test lane naming change
21 lane_threadname(new_name)
22 assert(lane_threadname() == new_name)
23 --error "111" -- testing error messages
24 assert(hey)
25 local v=0
26 for i=a,b,c do
27 v= v+i
28 end
29 return v, hey
30end
31
32local gc_cb = function(name_, status_)
33 PRINT(" ---> lane '" .. name_ .. "' collected with status '" .. status_ .. "'")
34end
35
36-- ##################################################################################################
37-- ##################################################################################################
38-- ##################################################################################################
39
40PRINT("\n\n", "---=== Tasking (basic) ===---", "\n\n")
41
42local task_launch = lanes_gen("", { globals={hey=true}, gc_cb = gc_cb}, task)
43 -- base stdlibs, normal priority
44
45-- 'task_launch' is a factory of multithreaded tasks, we can launch several:
46
47local lane1 = task_launch(100,200,3)
48assert.fails(function() print(lane1[lane1]) end) -- indexing the lane with anything other than a string or a number should fail
49lanes.sleep(0.1) -- give some time so that the lane can set its name
50assert(lane1:get_threadname() == "task(100,200,3)", "Lane name is " .. lane1:get_threadname())
51
52local lane2= task_launch(200,300,4)
53
54-- At this stage, states may be "pending", "running" or "done"
55
56local st1,st2= lane1.status, lane2.status
57PRINT(st1,st2)
58assert(st1=="pending" or st1=="running" or st1=="done")
59assert(st2=="pending" or st2=="running" or st2=="done")
60
61-- Accessing results ([1..N]) pends until they are available
62--
63PRINT("waiting...")
64local v1, v1_hey= lane1[1], lane1[2]
65local v2, v2_hey= lane2[1], lane2[2]
66
67PRINT(v1, v1_hey)
68assert(v1_hey == true)
69
70PRINT(v2, v2_hey)
71assert(v2_hey == true)
72
73assert(lane1.status == "done")
74assert(lane1.status == "done")
diff --git a/unit_tests/scripts/lane/tasking_cancelling.lua b/unit_tests/scripts/lane/tasking_cancelling.lua
new file mode 100644
index 0000000..a4e0fde
--- /dev/null
+++ b/unit_tests/scripts/lane/tasking_cancelling.lua
@@ -0,0 +1,122 @@
1local require_lanes_result_1, require_lanes_result_2 = require "lanes".configure(config).configure()
2print("require_lanes_result:", require_lanes_result_1, require_lanes_result_2)
3local lanes = require_lanes_result_1
4
5local require_assert_result_1, require_assert_result_2 = require "_assert"
6print("require_assert_result:", require_assert_result_1, require_assert_result_2)
7
8local utils = lanes.require "_utils"
9local PRINT = utils.MAKE_PRINT()
10
11local lanes_gen = assert(lanes.gen)
12local lanes_linda = assert(lanes.linda)
13
14-- ##################################################################################################
15-- ##################################################################################################
16-- ##################################################################################################
17
18local function task(a, b, c)
19 lane_threadname("task("..a..","..b..","..c..")")
20 --error "111" -- testing error messages
21 assert(hey)
22 local v=0
23 for i=a,b,c do
24 v= v+i
25 end
26 return v, hey
27end
28
29local gc_cb = function(name_, status_)
30 PRINT(" ---> lane '" .. name_ .. "' collected with status '" .. status_ .. "'")
31end
32
33-- ##################################################################################################
34-- ##################################################################################################
35-- ##################################################################################################
36
37PRINT("\n\n", "---=== Tasking (cancelling) ===---", "\n\n")
38
39local task_launch2 = lanes_gen("", { globals={hey=true}, gc_cb = gc_cb}, task)
40
41local N=999999999
42local lane9= task_launch2(1,N,1) -- huuuuuuge...
43
44-- Wait until state changes "pending"->"running"
45--
46local st
47local t0= os.time()
48while os.time()-t0 < 5 do
49 st= lane9.status
50 io.stderr:write((i==1) and st.." " or '.')
51 if st~="pending" then break end
52end
53PRINT(" "..st)
54
55if st=="error" then
56 local _= lane9[0] -- propagate the error here
57end
58if st=="done" then
59 error("Looping to "..N.." was not long enough (cannot test cancellation)")
60end
61assert(st=="running", "st == " .. st)
62
63-- when running under luajit, the function is JIT-ed, and the instruction count isn't hit, so we need a different hook
64lane9:cancel(jit and "line" or "count", 100) -- 0 timeout, hook triggers cancelslation when reaching the specified count
65
66local t0= os.time()
67while os.time()-t0 < 5 do
68 st= lane9.status
69 io.stderr:write((i==1) and st.." " or '.')
70 if st~="running" then break end
71end
72PRINT(" "..st)
73assert(st == "cancelled", "st is '" .. st .. "' instead of 'cancelled'")
74
75-- cancellation of lanes waiting on a linda
76local limited = lanes_linda("limited")
77assert.fails(function() limited:limit("key", -1) end)
78assert.failsnot(function() limited:limit("key", 1) end)
79-- [[################################################
80limited:send("key", "hello") -- saturate linda
81for k, v in pairs(limited:dump()) do
82 PRINT("limited[" .. tostring(k) .. "] = " .. tostring(v))
83end
84local wait_send = function()
85 local a,b
86 set_finalizer(function() print("wait_send", a, b) end)
87 a,b = limited:send("key", "bybye") -- infinite timeout, returns only when lane is cancelled
88end
89
90local wait_send_lane = lanes.gen("*", wait_send)()
91repeat until wait_send_lane.status == "waiting"
92print "wait_send_lane is waiting"
93wait_send_lane:cancel() -- hard cancel, 0 timeout
94repeat until wait_send_lane.status == "cancelled"
95print "wait_send_lane is cancelled"
96--################################################]]
97local wait_receive = function()
98 local k, v
99 set_finalizer(function() print("wait_receive", k, v) end)
100 k, v = limited:receive("dummy") -- infinite timeout, returns only when lane is cancelled
101end
102
103local wait_receive_lane = lanes.gen("*", wait_receive)()
104repeat until wait_receive_lane.status == "waiting"
105print "wait_receive_lane is waiting"
106wait_receive_lane:cancel() -- hard cancel, 0 timeout
107repeat until wait_receive_lane.status == "cancelled"
108print "wait_receive_lane is cancelled"
109--################################################]]
110local wait_receive_batched = function()
111 local k, v1, v2
112 set_finalizer(function() print("wait_receive_batched", k, v1, v2) end)
113 k, v1, v2 = limited:receive(limited.batched, "dummy", 2) -- infinite timeout, returns only when lane is cancelled
114end
115
116local wait_receive_batched_lane = lanes.gen("*", wait_receive_batched)()
117repeat until wait_receive_batched_lane.status == "waiting"
118print "wait_receive_batched_lane is waiting"
119wait_receive_batched_lane:cancel() -- hard cancel, 0 timeout
120repeat until wait_receive_batched_lane.status == "cancelled"
121print "wait_receive_batched_lane is cancelled"
122--################################################]]
diff --git a/unit_tests/scripts/lane/tasking_comms_criss_cross.lua b/unit_tests/scripts/lane/tasking_comms_criss_cross.lua
new file mode 100644
index 0000000..db63b8e
--- /dev/null
+++ b/unit_tests/scripts/lane/tasking_comms_criss_cross.lua
@@ -0,0 +1,53 @@
1local require_lanes_result_1, require_lanes_result_2 = require "lanes".configure(config).configure()
2print("require_lanes_result:", require_lanes_result_1, require_lanes_result_2)
3local lanes = require_lanes_result_1
4
5local require_assert_result_1, require_assert_result_2 = require "_assert"
6print("require_assert_result:", require_assert_result_1, require_assert_result_2)
7
8local utils = lanes.require "_utils"
9local PRINT = utils.MAKE_PRINT()
10
11local lanes_gen = assert(lanes.gen)
12local lanes_linda = assert(lanes.linda)
13
14-- ##################################################################################################
15-- ##################################################################################################
16-- ##################################################################################################
17
18local gc_cb = function(name_, status_)
19 PRINT(" ---> lane '" .. name_ .. "' collected with status '" .. status_ .. "'")
20end
21
22-- ##################################################################################################
23-- ##################################################################################################
24-- ##################################################################################################
25
26PRINT("\n\n", "---=== Comms criss cross ===---", "\n\n")
27
28-- We make two identical lanes, which are using the same Linda channel.
29--
30local tc = lanes_gen("io", {gc_cb = gc_cb},
31 function(linda, ch_in, ch_out)
32 lane_threadname("criss cross " .. ch_in .. " -> " .. ch_out)
33 local function STAGE(str)
34 io.stderr:write(ch_in..": "..str.."\n")
35 linda:send(nil, ch_out, str)
36 local k,v= linda:receive(nil, ch_in)
37 assert(v==str)
38 end
39 STAGE("Hello")
40 STAGE("I was here first!")
41 STAGE("So what?")
42 end
43)
44
45local linda= lanes_linda("criss cross")
46
47local a,b= tc(linda, "A","B"), tc(linda, "B","A") -- launching two lanes, twisted comms
48
49local _= a[0],b[0] -- waits until they are both ready
50
51PRINT("collectgarbage")
52a, b = nil
53collectgarbage()
diff --git a/unit_tests/scripts/lane/tasking_communications.lua b/unit_tests/scripts/lane/tasking_communications.lua
new file mode 100644
index 0000000..b922973
--- /dev/null
+++ b/unit_tests/scripts/lane/tasking_communications.lua
@@ -0,0 +1,149 @@
1local require_lanes_result_1, require_lanes_result_2 = require "lanes".configure(config).configure()
2print("require_lanes_result:", require_lanes_result_1, require_lanes_result_2)
3local lanes = require_lanes_result_1
4
5local require_assert_result_1, require_assert_result_2 = require "_assert"
6print("require_assert_result:", require_assert_result_1, require_assert_result_2)
7
8local utils = lanes.require "_utils"
9local PRINT = utils.MAKE_PRINT()
10
11local lanes_gen = assert(lanes.gen)
12local lanes_linda = assert(lanes.linda)
13
14-- ##################################################################################################
15-- ##################################################################################################
16-- ##################################################################################################
17
18local function task(a, b, c)
19 lane_threadname("task("..a..","..b..","..c..")")
20 --error "111" -- testing error messages
21 assert(hey)
22 local v=0
23 for i=a,b,c do
24 v= v+i
25 end
26 return v, hey
27end
28
29local gc_cb = function(name_, status_)
30 PRINT(" ---> lane '" .. name_ .. "' collected with status '" .. status_ .. "'")
31end
32
33-- ##################################################################################################
34-- ##################################################################################################
35-- ##################################################################################################
36
37local tables_match = utils.tables_match
38
39local SLEEP = function(...)
40 local k, v = lanes.sleep(...)
41 assert(k == nil and v == "timeout")
42end
43
44PRINT("\n\n", "---=== Communications ===---", "\n\n")
45
46local function WR(...) io.stderr:write(...) end
47
48local chunk= function(linda)
49 local function receive() return linda:receive("->") end
50 local function send(...) local _res, _err = linda:send("<-", ...) assert(_res == true and _err == nil) end
51
52 WR("chunk ", "Lane starts!\n")
53
54 local k,v
55 k,v=receive(); WR("chunk ", v.." received (expecting 1)\n"); assert(k and v==1)
56 k,v=receive(); WR("chunk ", v.." received (expecting 2)\n"); assert(k and v==2)
57 k,v=receive(); WR("chunk ", v.." received (expecting 3)\n"); assert(k and v==3)
58 k,v=receive(); WR("chunk ", tostring(v).." received (expecting nil from __lanesconvert)\n"); assert(k and v==nil, "table with __lanesconvert==lanes.null should be received as nil, got " .. tostring(v)) -- a table with __lanesconvert was sent
59 k,v=receive(); WR("chunk ", tostring(v).." received (expecting nil)\n"); assert(k and v==nil)
60
61 send(4,5,6); WR("chunk ", "4,5,6 sent\n")
62 send 'aaa'; WR("chunk ", "'aaa' sent\n")
63 send(nil); WR("chunk ", "nil sent\n")
64 send { 'a', 'b', 'c', d=10 }; WR("chunk ","{'a','b','c',d=10} sent\n")
65
66 k,v=receive(); WR("chunk ", v.." received\n"); assert(v==4)
67
68 local subT1 = { "subT1"}
69 local subT2 = { "subT2"}
70 send { subT1, subT2, subT1, subT2}; WR("chunk ", "{ subT1, subT2, subT1, subT2} sent\n")
71
72 WR("chunk ", "Lane ends!\n")
73end
74
75local linda = lanes_linda("communications")
76assert(type(linda) == "userdata" and tostring(linda) == "Linda: communications")
77 --
78 -- ["->"] master -> slave
79 -- ["<-"] slave <- master
80
81WR "test linda:get/set..."
82linda:set("<->", "x", "y", "z")
83local b,x,y,z = linda:get("<->", 1)
84assert(b == 1 and x == "x" and y == nil and z == nil)
85local b,x,y,z = linda:get("<->", 2)
86assert(b == 2 and x == "x" and y == "y" and z == nil)
87local b,x,y,z = linda:get("<->", 3)
88assert(b == 3 and x == "x" and y == "y" and z == "z")
89local b,x,y,z,w = linda:get("<->", 4)
90assert(b == 3 and x == "x" and y == "y" and z == "z" and w == nil)
91local k, x = linda:receive("<->")
92assert(k == "<->" and x == "x")
93local k,y,z = linda:receive(linda.batched, "<->", 2)
94assert(k == "<->" and y == "y" and z == "z")
95linda:set("<->")
96local b,x,y,z,w = linda:get("<->", 4)
97assert(b == 0 and x == nil and y == nil and z == nil and w == nil)
98WR "ok\n"
99
100local function PEEK(...) return linda:get("<-", ...) end
101local function SEND(...) local _res, _err = linda:send("->", ...) assert(_res == true and _err == nil) end
102local function RECEIVE() local k,v = linda:receive(1, "<-") return v end
103
104local comms_lane = lanes_gen("io", {gc_cb = gc_cb, name = "auto"}, chunk)(linda) -- prepare & launch
105
106SEND(1); WR("main ", "1 sent\n")
107SEND(2); WR("main ", "2 sent\n")
108SEND(3); WR("main ", "3 sent\n")
109SEND(setmetatable({"should be ignored"},{__lanesconvert=lanes.null})); WR("main ", "__lanesconvert table sent\n")
110for i=1,40 do
111 WR "."
112 SLEEP(0.0001)
113 assert(PEEK() == 0) -- nothing coming in, yet
114end
115SEND(nil); WR("\nmain ", "nil sent\n")
116
117local a,b,c = RECEIVE(), RECEIVE(), RECEIVE()
118
119print("lane status: " .. comms_lane.status)
120if comms_lane.status == "error" then
121 print(comms_lane:join())
122else
123 WR("main ", tostring(a)..", "..tostring(b)..", "..tostring(c).." received\n")
124end
125
126assert(a==4 and b==5 and c==6)
127
128local aaa = RECEIVE(); WR("main ", aaa.." received\n")
129assert(aaa=='aaa')
130
131local null = RECEIVE(); WR(tostring(null).." received\n")
132assert(null==nil)
133
134local out_t = RECEIVE(); WR(type(out_t).." received\n")
135assert(tables_match(out_t, {'a','b','c',d=10}))
136
137assert(PEEK() == 0)
138SEND(4)
139
140local complex_table = RECEIVE(); WR(type(complex_table).." received\n")
141assert(complex_table[1] == complex_table[3] and complex_table[2] == complex_table[4])
142WR(table.concat({complex_table[1][1],complex_table[2][1],complex_table[3][1],complex_table[4][1]},", "))
143
144WR("collectgarbage")
145comms_lane = nil
146collectgarbage()
147-- wait
148WR("waiting 1s")
149SLEEP(1)
diff --git a/unit_tests/scripts/lane/tasking_error.lua b/unit_tests/scripts/lane/tasking_error.lua
new file mode 100644
index 0000000..1e2347f
--- /dev/null
+++ b/unit_tests/scripts/lane/tasking_error.lua
@@ -0,0 +1,49 @@
1local require_lanes_result_1, require_lanes_result_2 = require "lanes".configure(config).configure()
2print("require_lanes_result:", require_lanes_result_1, require_lanes_result_2)
3local lanes = require_lanes_result_1
4
5local require_assert_result_1, require_assert_result_2 = require "_assert"
6print("require_assert_result:", require_assert_result_1, require_assert_result_2)
7
8local utils = lanes.require "_utils"
9local PRINT = utils.MAKE_PRINT()
10
11local lanes_gen = assert(lanes.gen)
12local lanes_linda = assert(lanes.linda)
13
14-- ##################################################################################################
15-- ##################################################################################################
16-- ##################################################################################################
17
18local gc_cb = function(name_, status_)
19 PRINT(" ---> lane '" .. name_ .. "' collected with status '" .. status_ .. "'")
20end
21
22PRINT("---=== Tasking (error) ===---", "\n\n")
23
24-- a lane that throws immediately the error value it received
25local g = lanes_gen("", {gc_cb = gc_cb}, error)
26local errmsg = "ERROR!"
27
28if true then
29 -- if you index an errored lane, it should throw the error again
30 local lane = g(errmsg)
31 assert.fails(function() return lane[1] end)
32 assert(lane.status == "error")
33 -- even after indexing, joining a lane in error should give nil,<error>
34 local a,b = lane:join()
35 assert(a == nil and string.find(b, errmsg))
36end
37
38if true then
39 local lane = g(errmsg)
40 -- after indexing, joining a lane in error should give nil,<error>
41 local a, b = lane:join()
42 assert(lane.status == "error")
43 assert(a == nil and string.find(b, errmsg))
44 -- even after joining, indexing should raise an error
45 assert.fails(function() return lane[1] end)
46 -- unless we index with a negative value to get the error message
47 local c = lane[-1]
48 assert(c == b)
49end
diff --git a/unit_tests/scripts/lane/tasking_join_test.lua b/unit_tests/scripts/lane/tasking_join_test.lua
new file mode 100644
index 0000000..8f2d4db
--- /dev/null
+++ b/unit_tests/scripts/lane/tasking_join_test.lua
@@ -0,0 +1,65 @@
1local require_lanes_result_1, require_lanes_result_2 = require "lanes".configure(config).configure()
2print("require_lanes_result:", require_lanes_result_1, require_lanes_result_2)
3local lanes = require_lanes_result_1
4
5local require_assert_result_1, require_assert_result_2 = require "_assert"
6print("require_assert_result:", require_assert_result_1, require_assert_result_2)
7
8local utils = lanes.require "_utils"
9local PRINT = utils.MAKE_PRINT()
10
11local lanes_gen = assert(lanes.gen)
12local lanes_linda = assert(lanes.linda)
13
14-- ##################################################################################################
15-- ##################################################################################################
16-- ##################################################################################################
17
18local gc_cb = function(name_, status_)
19 PRINT(" ---> lane '" .. name_ .. "' collected with status '" .. status_ .. "'")
20end
21
22-- ##################################################################################################
23-- ##################################################################################################
24-- ##################################################################################################
25
26local SLEEP = function(...)
27 local k, v = lanes.sleep(...)
28 assert(k == nil and v == "timeout")
29end
30
31PRINT("---=== :join test ===---", "\n\n")
32
33-- NOTE: 'unpack()' cannot be used on the lane handle; it will always return nil
34-- (unless [1..n] has been read earlier, in which case it would seemingly
35-- work).
36
37local S= lanes_gen("table", {gc_cb = gc_cb},
38 function(arg)
39 lane_threadname "join test lane"
40 set_finalizer(function() end)
41 local aux= {}
42 for i, v in ipairs(arg) do
43 table.insert(aux, 1, v)
44 end
45 -- unpack was renamed table.unpack in Lua 5.2: cater for both!
46 return (unpack or table.unpack)(aux)
47end)
48
49h= S { 12, 13, 14 } -- execution starts, h[1..3] will get the return values
50-- wait a bit so that the lane has a chance to set its debug name
51SLEEP(0.5)
52print("joining with '" .. h:get_threadname() .. "'")
53local a,b,c,d= h:join()
54if h.status == "error" then
55 print(h:get_threadname(), "error: " , a, b, c, d)
56else
57 print(h:get_threadname(), a,b,c,d)
58 assert(a==14, "a == " .. tostring(a))
59 assert(b==13, "b == " .. tostring(b))
60 assert(c==12, "c == " .. tostring(c))
61 assert(d==nil, "d == " .. tostring(d))
62end
63
64local nameof_type, nameof_name = lanes.nameof(print)
65PRINT("name of " .. nameof_type .. " print = '" .. nameof_name .. "'")
diff --git a/unit_tests/scripts/lane/tasking_send_receive_code.lua b/unit_tests/scripts/lane/tasking_send_receive_code.lua
new file mode 100644
index 0000000..77a4b12
--- /dev/null
+++ b/unit_tests/scripts/lane/tasking_send_receive_code.lua
@@ -0,0 +1,91 @@
1local config = { with_timers = false, strip_functions = false, internal_allocator = "libc"}
2local require_lanes_result_1, require_lanes_result_2 = require "lanes".configure(config).configure()
3print("require_lanes_result:", require_lanes_result_1, require_lanes_result_2)
4local lanes = require_lanes_result_1
5
6local require_assert_result_1, require_assert_result_2 = require "_assert"
7print("require_assert_result:", require_assert_result_1, require_assert_result_2)
8
9local utils = lanes.require "_utils"
10local PRINT = utils.MAKE_PRINT()
11
12local lanes_gen = assert(lanes.gen)
13local lanes_linda = assert(lanes.linda)
14
15-- ##################################################################################################
16-- ##################################################################################################
17-- ##################################################################################################
18
19local gc_cb = function(name_, status_)
20 PRINT(" ---> lane '" .. name_ .. "' collected with status '" .. status_ .. "'")
21end
22
23-- ##################################################################################################
24-- ##################################################################################################
25-- ##################################################################################################
26
27PRINT("---=== Receive & send of code ===---", "\n")
28
29local upvalue = "123"
30local tostring = tostring
31
32local function chunk2(linda)
33 local utils = require "_utils"
34 local PRINT = utils.MAKE_PRINT()
35 PRINT("here")
36 assert(upvalue == "123") -- even when running as separate thread
37 -- function name & line number should be there even as separate thread
38 --
39 local info= debug.getinfo(1) -- 1 = us
40 --
41 PRINT("linda named-> '" ..tostring(linda).."'")
42 PRINT "debug.getinfo->"
43 for k,v in pairs(info) do PRINT(k,v) end
44
45 -- some assertions are adjusted depending on config.strip_functions, because it changes what we get out of debug.getinfo
46 assert(info.nups == (_VERSION == "Lua 5.1" and 3 or 4), "bad nups " .. info.nups) -- upvalue + config + tostring + _ENV (Lua > 5.2 only)
47 assert(info.what == "Lua", "bad what")
48 --assert(info.name == "chunk2") -- name does not seem to come through
49 assert(config.strip_functions and info.source=="=?" or string.match(info.source, "^@.*tasking_send_receive_code.lua$"), "bad info.source " .. info.source)
50 assert(config.strip_functions and info.short_src=="?" or string.match(info.short_src, "^.*tasking_send_receive_code.lua$"), "bad info.short_src " .. info.short_src)
51 -- These vary so let's not be picky (they're there..)
52 --
53 assert(info.linedefined == 32, "bad linedefined") -- start of 'chunk2'
54 assert(config.strip_functions and info.currentline==-1 or info.currentline > info.linedefined, "bad currentline") -- line of 'debug.getinfo'
55 assert(info.lastlinedefined > info.currentline, "bad lastlinedefined") -- end of 'chunk2'
56 local k,func= linda:receive("down")
57 assert(type(func)=="function", "not a function")
58 assert(k=="down")
59
60 func(linda)
61
62 local k,str= linda:receive("down")
63 assert(str=="ok", "bad receive result")
64
65 linda:send("up", function() return ":)" end, "ok2")
66end
67
68local linda = lanes_linda("auto")
69local t2= lanes_gen("debug,package,string,io", {gc_cb = gc_cb}, chunk2)(linda) -- prepare & launch
70linda:send("down", function(linda) linda:send("up", "ready!") end,
71 "ok")
72-- wait to see if the tiny function gets executed
73--
74local k,s= linda:receive(1, "up")
75if t2.status == "error" then
76 PRINT("t2 error: " , t2:join())
77 assert(false)
78end
79PRINT(s)
80assert(s=="ready!", s .. " is not 'ready!'")
81
82-- returns of the 'chunk2' itself
83--
84local k,f= linda:receive("up")
85assert(type(f)=="function")
86
87local s2= f()
88assert(s2==":)")
89
90local k,ok2= linda:receive("up")
91assert(ok2 == "ok2")
diff --git a/unit_tests/scripts/lane/uncooperative_shutdown.lua b/unit_tests/scripts/lane/uncooperative_shutdown.lua
new file mode 100644
index 0000000..ce7df57
--- /dev/null
+++ b/unit_tests/scripts/lane/uncooperative_shutdown.lua
@@ -0,0 +1,24 @@
1if not package.preload.fixture then
2 error "can't be run outside of UnitTest framework"
3end
4local fixture = require "fixture"
5
6local lanes = require "lanes".configure{shutdown_timeout = 0.001, on_state_create = fixture.on_state_create}
7
8-- launch lanes that blocks forever
9local lane = function()
10 local fixture = require "fixture"
11 fixture.forever()
12end
13
14-- the generator
15local g1 = lanes.gen("*", lane)
16
17-- launch lane
18local h1 = g1()
19
20-- wait until the lane is running
21repeat until h1.status == "running"
22
23-- let the script end, Lanes should throw std::logic_error because the lane did not gracefully terminate
24lanes.finally(fixture.throwing_finalizer)
diff --git a/unit_tests/scripts/linda/multiple_keepers.lua b/unit_tests/scripts/linda/multiple_keepers.lua
new file mode 100644
index 0000000..8733087
--- /dev/null
+++ b/unit_tests/scripts/linda/multiple_keepers.lua
@@ -0,0 +1,26 @@
1-- 3 keepers in addition to the one reserved for the timer linda
2local require_lanes_result_1, require_lanes_result_2 = require "lanes".configure{nb_user_keepers = 3, keepers_gc_threshold = 500}
3local lanes = require_lanes_result_1
4
5local a = lanes.linda("A", 1)
6local b = lanes.linda("B", 2)
7local c = lanes.linda("C", 3)
8
9-- store each linda in the other 2
10do
11 a:set("kA", a, b, c)
12 local nA, rA, rB, rC = a:get("kA", 3)
13 assert(nA == 3 and rA == a and rB == b and rC == c)
14end
15
16do
17 b:set("kB", a, b, c)
18 local nB, rA, rB, rC = b:get("kB", 3)
19 assert(nB == 3 and rA == a and rB == b and rC == c)
20end
21
22do
23 c:set("kC", a, b, c)
24 local nC, rA, rB, rC = c:get("kC", 3)
25 assert(nC == 3 and rA == a and rB == b and rC == c)
26end
diff --git a/unit_tests/scripts/linda/send_receive.lua b/unit_tests/scripts/linda/send_receive.lua
new file mode 100644
index 0000000..9a6a7be
--- /dev/null
+++ b/unit_tests/scripts/linda/send_receive.lua
@@ -0,0 +1,46 @@
1local lanes = require "lanes"
2
3-- a newly created linda doesn't contain anything
4local l = lanes.linda()
5assert(l.null == lanes.null)
6assert(l:dump() == nil)
7
8-- read something with 0 timeout, should fail
9local k,v = l:receive(0, "k")
10assert(k == nil and v == 'timeout')
11
12-- send some value
13assert(l:send("k1", 99) == true)
14-- make sure the contents look as expected
15local t = l:dump()
16assert(type(t) == 'table')
17local tk1 = t.k1
18assert(type(tk1) == 'table')
19assert(tk1.first == 1 and tk1.count == 1 and tk1.limit == 'unlimited' and tk1.restrict == 'none')
20assert(#tk1.fifo == tk1.count, #tk1.fifo .. " ~= " .. tk1.count)
21assert(tk1.fifo[1] == 99, tk1.fifo[1] .. " ~= " .. 99)
22
23-- read the value, should succeed
24local k,v = l:receive("k1")
25assert(k == "k1" and v == 99)
26-- after reading, the data is no longer available (even if the key itself still exists within the linda)
27local t = l:dump()
28assert(type(t) == 'table')
29local tk1 = t.k1
30assert(type(tk1) == 'table')
31assert(tk1.first == 1 and tk1.count == 0 and tk1.limit == 'unlimited' and tk1.restrict == 'none')
32assert(#tk1.fifo == tk1.count and tk1.fifo[1] == nil)
33-- read again, should fail
34local k,v = l:receive(0, "k1")
35assert(k == nil and v == 'timeout')
36
37-- send null, read nil
38l:send("k", l.null)
39local k,v = l:receive(0, "k")
40assert(k == "k" and v == nil)
41
42-- using a deep userdata (such as another linda) as key, should work
43local l2 = lanes.linda()
44l:send(nil, l2, 99)
45local k,v = l:receive(0, l2)
46assert(k == l2 and v == 99)
diff --git a/unit_tests/scripts/linda/send_registered_userdata.lua b/unit_tests/scripts/linda/send_registered_userdata.lua
new file mode 100644
index 0000000..2c0195a
--- /dev/null
+++ b/unit_tests/scripts/linda/send_registered_userdata.lua
@@ -0,0 +1,5 @@
1local lanes = require 'lanes'.configure{with_timers = false}
2local l = lanes.linda'gleh'
3l:set('yo', io.stdin)
4local n, stdin_out = l:get('yo')
5assert(n == 1 and stdin_out == io.stdin, tostring(stdin_out) .. " ~= " .. tostring(io.stdin))