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 | |
| 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')
| -rw-r--r-- | unit_tests/scripts/_assert.lua | 321 | ||||
| -rw-r--r-- | unit_tests/scripts/_utils.lua | 75 | ||||
| -rw-r--r-- | unit_tests/scripts/coro/basics.lua | 94 | ||||
| -rw-r--r-- | unit_tests/scripts/coro/error_handling.lua | 64 | ||||
| -rw-r--r-- | unit_tests/scripts/lane/cooperative_shutdown.lua | 45 | ||||
| -rw-r--r-- | unit_tests/scripts/lane/stdlib_naming.lua | 87 | ||||
| -rw-r--r-- | unit_tests/scripts/lane/tasking_basic.lua | 74 | ||||
| -rw-r--r-- | unit_tests/scripts/lane/tasking_cancelling.lua | 122 | ||||
| -rw-r--r-- | unit_tests/scripts/lane/tasking_comms_criss_cross.lua | 53 | ||||
| -rw-r--r-- | unit_tests/scripts/lane/tasking_communications.lua | 149 | ||||
| -rw-r--r-- | unit_tests/scripts/lane/tasking_error.lua | 49 | ||||
| -rw-r--r-- | unit_tests/scripts/lane/tasking_join_test.lua | 65 | ||||
| -rw-r--r-- | unit_tests/scripts/lane/tasking_send_receive_code.lua | 91 | ||||
| -rw-r--r-- | unit_tests/scripts/lane/uncooperative_shutdown.lua | 24 | ||||
| -rw-r--r-- | unit_tests/scripts/linda/multiple_keepers.lua | 26 | ||||
| -rw-r--r-- | unit_tests/scripts/linda/send_receive.lua | 46 | ||||
| -rw-r--r-- | unit_tests/scripts/linda/send_registered_userdata.lua | 5 |
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 | |||
| 50 | local 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 | |||
| 61 | assert( type(assert) == "function" ) -- system assert function | ||
| 62 | assert( type(error) == "function") | ||
| 63 | |||
| 64 | ----- | ||
| 65 | -- Integer range: INT_MIN..INT_MAX | ||
| 66 | -- | ||
| 67 | local function try_maxint( n ) | ||
| 68 | return (n > n-1) and n-1 -- false when outside the integer range | ||
| 69 | end | ||
| 70 | |||
| 71 | local 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 | |||
| 77 | local INT_MIN= -(INT_MAX+1) | ||
| 78 | |||
| 79 | |||
| 80 | ---=== assert.*() ===--- | ||
| 81 | |||
| 82 | local at_msg= "type assertion error" -- TBD: better messages, about that exact situation | ||
| 83 | local av_msg= "value assertion error" | ||
| 84 | |||
| 85 | -- void= _assert( val [, msg_str [, lev_uint]] ) | ||
| 86 | -- | ||
| 87 | local 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 | ||
| 93 | end | ||
| 94 | |||
| 95 | -- Note: following code uses the _new_ 'assert()' by purpose, since it provides | ||
| 96 | -- a level override (original doesn't) | ||
| 97 | -- | ||
| 98 | local function assert_v( v0 ) | ||
| 99 | return function(v,msg) | ||
| 100 | _assert( v == v0, msg or av_msg, 2 ) | ||
| 101 | return v | ||
| 102 | end | ||
| 103 | end | ||
| 104 | local function assert_t( str ) | ||
| 105 | return function(v,msg) | ||
| 106 | _assert( type(v) == str, msg or at_msg, 2 ) | ||
| 107 | return v | ||
| 108 | end | ||
| 109 | end | ||
| 110 | local 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 | ||
| 117 | end | ||
| 118 | |||
| 119 | -- global base function assert() is replaced by this table | ||
| 120 | assert= | ||
| 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 | } | ||
| 259 | setmetatable( assert, assert ) | ||
| 260 | |||
| 261 | assert.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 | -- | ||
| 270 | function 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 | ||
| 279 | end | ||
| 280 | |||
| 281 | |||
| 282 | ----- | ||
| 283 | -- void= assert.failsnot( function [,err_msg_str] ) | ||
| 284 | -- | ||
| 285 | -- Similar to 'assert.fails' but expects the code to survive. | ||
| 286 | -- | ||
| 287 | function 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 | ||
| 294 | end | ||
| 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 | -- | ||
| 303 | function 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 | ||
| 311 | end | ||
| 312 | |||
| 313 | |||
| 314 | -- Sanity check | ||
| 315 | -- | ||
| 316 | assert( true ) | ||
| 317 | assert.fails( function() assert( false ) end ) | ||
| 318 | assert.fails( function() assert( nil ) end ) | ||
| 319 | |||
| 320 | |||
| 321 | return 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 @@ | |||
| 1 | local io = assert(io) | ||
| 2 | local pairs = assert(pairs) | ||
| 3 | local select = assert(select) | ||
| 4 | local string_format = assert(string.format) | ||
| 5 | local tostring = assert(tostring) | ||
| 6 | local type = assert(type) | ||
| 7 | |||
| 8 | local print_id = 0 | ||
| 9 | local 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 | ||
| 19 | end | ||
| 20 | |||
| 21 | local MAKE_PRINT = function() | ||
| 22 | local _whence = lane_threadname and lane_threadname() or "main" | ||
| 23 | return function(...) | ||
| 24 | P(_whence, ...) | ||
| 25 | end | ||
| 26 | end | ||
| 27 | |||
| 28 | local tables_match | ||
| 29 | |||
| 30 | -- true if 'a' is a subtable of 'b' | ||
| 31 | -- | ||
| 32 | local 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 | ||
| 46 | end | ||
| 47 | |||
| 48 | -- true when contents of 'a' and 'b' are identical | ||
| 49 | -- | ||
| 50 | tables_match = function(a, b) | ||
| 51 | return subtable(a, b) and subtable(b, a) | ||
| 52 | end | ||
| 53 | |||
| 54 | local 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 | ||
| 69 | end | ||
| 70 | |||
| 71 | return { | ||
| 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 @@ | |||
| 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 | ||
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 @@ | |||
| 1 | local lanes = require "lanes" | ||
| 2 | |||
| 3 | -- launch lanes that cooperate properly with cancellation request | ||
| 4 | |||
| 5 | local lane1 = function() | ||
| 6 | -- loop breaks on cancellation request | ||
| 7 | repeat | ||
| 8 | lanes.sleep(0) | ||
| 9 | until cancel_test() | ||
| 10 | print "lane1 cancelled" | ||
| 11 | end | ||
| 12 | |||
| 13 | local 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" | ||
| 19 | end | ||
| 20 | |||
| 21 | local 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 | ||
| 25 | end | ||
| 26 | |||
| 27 | |||
| 28 | |||
| 29 | -- the generators | ||
| 30 | local g1 = lanes.gen("*", lane1) | ||
| 31 | local g2 = lanes.gen("*", lane2) | ||
| 32 | local g3 = lanes.gen("*", lane3) | ||
| 33 | |||
| 34 | -- launch lanes | ||
| 35 | local h1 = g1() | ||
| 36 | |||
| 37 | local linda = lanes.linda() | ||
| 38 | local h2 = g2(linda) | ||
| 39 | |||
| 40 | local h3 = g3() | ||
| 41 | |||
| 42 | -- wait until they are both started | ||
| 43 | repeat 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 @@ | |||
| 1 | local require_lanes_result_1, require_lanes_result_2 = require "lanes".configure(config).configure() | ||
| 2 | print("require_lanes_result:", require_lanes_result_1, require_lanes_result_2) | ||
| 3 | local lanes = require_lanes_result_1 | ||
| 4 | |||
| 5 | local require_assert_result_1, require_assert_result_2 = require "_assert" | ||
| 6 | print("require_assert_result:", require_assert_result_1, require_assert_result_2) | ||
| 7 | |||
| 8 | local utils = lanes.require "_utils" | ||
| 9 | local PRINT = utils.MAKE_PRINT() | ||
| 10 | |||
| 11 | local lanes_gen = assert(lanes.gen) | ||
| 12 | local lanes_linda = assert(lanes.linda) | ||
| 13 | |||
| 14 | -- ################################################################################################## | ||
| 15 | -- ################################################################################################## | ||
| 16 | -- ################################################################################################## | ||
| 17 | |||
| 18 | local 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 | ||
| 27 | end | ||
| 28 | |||
| 29 | local gc_cb = function(name_, status_) | ||
| 30 | PRINT(" ---> lane '" .. name_ .. "' collected with status '" .. status_ .. "'") | ||
| 31 | end | ||
| 32 | |||
| 33 | -- ################################################################################################## | ||
| 34 | -- ################################################################################################## | ||
| 35 | -- ################################################################################################## | ||
| 36 | |||
| 37 | PRINT("\n\n", "---=== Stdlib naming ===---", "\n\n") | ||
| 38 | |||
| 39 | local 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 | ||
| 47 | end | ||
| 48 | |||
| 49 | local 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 | ||
| 56 | end | ||
| 57 | |||
| 58 | local 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 | ||
| 64 | end | ||
| 65 | |||
| 66 | assert.fails(function() lanes_gen("xxx", {gc_cb = gc_cb}, io_os_f) end) | ||
| 67 | |||
| 68 | local 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 | |||
| 81 | for _, 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]) | ||
| 84 | end | ||
| 85 | |||
| 86 | PRINT("collectgarbage") | ||
| 87 | collectgarbage() | ||
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 @@ | |||
| 1 | local require_lanes_result_1, require_lanes_result_2 = require "lanes".configure(config).configure() | ||
| 2 | print("require_lanes_result:", require_lanes_result_1, require_lanes_result_2) | ||
| 3 | local lanes = require_lanes_result_1 | ||
| 4 | |||
| 5 | local require_assert_result_1, require_assert_result_2 = require "_assert" | ||
| 6 | print("require_assert_result:", require_assert_result_1, require_assert_result_2) | ||
| 7 | |||
| 8 | local utils = lanes.require "_utils" | ||
| 9 | local PRINT = utils.MAKE_PRINT() | ||
| 10 | |||
| 11 | local lanes_gen = assert(lanes.gen) | ||
| 12 | local lanes_linda = assert(lanes.linda) | ||
| 13 | |||
| 14 | -- ################################################################################################## | ||
| 15 | -- ################################################################################################## | ||
| 16 | -- ################################################################################################## | ||
| 17 | |||
| 18 | local 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 | ||
| 30 | end | ||
| 31 | |||
| 32 | local gc_cb = function(name_, status_) | ||
| 33 | PRINT(" ---> lane '" .. name_ .. "' collected with status '" .. status_ .. "'") | ||
| 34 | end | ||
| 35 | |||
| 36 | -- ################################################################################################## | ||
| 37 | -- ################################################################################################## | ||
| 38 | -- ################################################################################################## | ||
| 39 | |||
| 40 | PRINT("\n\n", "---=== Tasking (basic) ===---", "\n\n") | ||
| 41 | |||
| 42 | local 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 | |||
| 47 | local lane1 = task_launch(100,200,3) | ||
| 48 | assert.fails(function() print(lane1[lane1]) end) -- indexing the lane with anything other than a string or a number should fail | ||
| 49 | lanes.sleep(0.1) -- give some time so that the lane can set its name | ||
| 50 | assert(lane1:get_threadname() == "task(100,200,3)", "Lane name is " .. lane1:get_threadname()) | ||
| 51 | |||
| 52 | local lane2= task_launch(200,300,4) | ||
| 53 | |||
| 54 | -- At this stage, states may be "pending", "running" or "done" | ||
| 55 | |||
| 56 | local st1,st2= lane1.status, lane2.status | ||
| 57 | PRINT(st1,st2) | ||
| 58 | assert(st1=="pending" or st1=="running" or st1=="done") | ||
| 59 | assert(st2=="pending" or st2=="running" or st2=="done") | ||
| 60 | |||
| 61 | -- Accessing results ([1..N]) pends until they are available | ||
| 62 | -- | ||
| 63 | PRINT("waiting...") | ||
| 64 | local v1, v1_hey= lane1[1], lane1[2] | ||
| 65 | local v2, v2_hey= lane2[1], lane2[2] | ||
| 66 | |||
| 67 | PRINT(v1, v1_hey) | ||
| 68 | assert(v1_hey == true) | ||
| 69 | |||
| 70 | PRINT(v2, v2_hey) | ||
| 71 | assert(v2_hey == true) | ||
| 72 | |||
| 73 | assert(lane1.status == "done") | ||
| 74 | assert(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 @@ | |||
| 1 | local require_lanes_result_1, require_lanes_result_2 = require "lanes".configure(config).configure() | ||
| 2 | print("require_lanes_result:", require_lanes_result_1, require_lanes_result_2) | ||
| 3 | local lanes = require_lanes_result_1 | ||
| 4 | |||
| 5 | local require_assert_result_1, require_assert_result_2 = require "_assert" | ||
| 6 | print("require_assert_result:", require_assert_result_1, require_assert_result_2) | ||
| 7 | |||
| 8 | local utils = lanes.require "_utils" | ||
| 9 | local PRINT = utils.MAKE_PRINT() | ||
| 10 | |||
| 11 | local lanes_gen = assert(lanes.gen) | ||
| 12 | local lanes_linda = assert(lanes.linda) | ||
| 13 | |||
| 14 | -- ################################################################################################## | ||
| 15 | -- ################################################################################################## | ||
| 16 | -- ################################################################################################## | ||
| 17 | |||
| 18 | local 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 | ||
| 27 | end | ||
| 28 | |||
| 29 | local gc_cb = function(name_, status_) | ||
| 30 | PRINT(" ---> lane '" .. name_ .. "' collected with status '" .. status_ .. "'") | ||
| 31 | end | ||
| 32 | |||
| 33 | -- ################################################################################################## | ||
| 34 | -- ################################################################################################## | ||
| 35 | -- ################################################################################################## | ||
| 36 | |||
| 37 | PRINT("\n\n", "---=== Tasking (cancelling) ===---", "\n\n") | ||
| 38 | |||
| 39 | local task_launch2 = lanes_gen("", { globals={hey=true}, gc_cb = gc_cb}, task) | ||
| 40 | |||
| 41 | local N=999999999 | ||
| 42 | local lane9= task_launch2(1,N,1) -- huuuuuuge... | ||
| 43 | |||
| 44 | -- Wait until state changes "pending"->"running" | ||
| 45 | -- | ||
| 46 | local st | ||
| 47 | local t0= os.time() | ||
| 48 | while 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 | ||
| 52 | end | ||
| 53 | PRINT(" "..st) | ||
| 54 | |||
| 55 | if st=="error" then | ||
| 56 | local _= lane9[0] -- propagate the error here | ||
| 57 | end | ||
| 58 | if st=="done" then | ||
| 59 | error("Looping to "..N.." was not long enough (cannot test cancellation)") | ||
| 60 | end | ||
| 61 | assert(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 | ||
| 64 | lane9:cancel(jit and "line" or "count", 100) -- 0 timeout, hook triggers cancelslation when reaching the specified count | ||
| 65 | |||
| 66 | local t0= os.time() | ||
| 67 | while 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 | ||
| 71 | end | ||
| 72 | PRINT(" "..st) | ||
| 73 | assert(st == "cancelled", "st is '" .. st .. "' instead of 'cancelled'") | ||
| 74 | |||
| 75 | -- cancellation of lanes waiting on a linda | ||
| 76 | local limited = lanes_linda("limited") | ||
| 77 | assert.fails(function() limited:limit("key", -1) end) | ||
| 78 | assert.failsnot(function() limited:limit("key", 1) end) | ||
| 79 | -- [[################################################ | ||
| 80 | limited:send("key", "hello") -- saturate linda | ||
| 81 | for k, v in pairs(limited:dump()) do | ||
| 82 | PRINT("limited[" .. tostring(k) .. "] = " .. tostring(v)) | ||
| 83 | end | ||
| 84 | local 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 | ||
| 88 | end | ||
| 89 | |||
| 90 | local wait_send_lane = lanes.gen("*", wait_send)() | ||
| 91 | repeat until wait_send_lane.status == "waiting" | ||
| 92 | print "wait_send_lane is waiting" | ||
| 93 | wait_send_lane:cancel() -- hard cancel, 0 timeout | ||
| 94 | repeat until wait_send_lane.status == "cancelled" | ||
| 95 | print "wait_send_lane is cancelled" | ||
| 96 | --################################################]] | ||
| 97 | local 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 | ||
| 101 | end | ||
| 102 | |||
| 103 | local wait_receive_lane = lanes.gen("*", wait_receive)() | ||
| 104 | repeat until wait_receive_lane.status == "waiting" | ||
| 105 | print "wait_receive_lane is waiting" | ||
| 106 | wait_receive_lane:cancel() -- hard cancel, 0 timeout | ||
| 107 | repeat until wait_receive_lane.status == "cancelled" | ||
| 108 | print "wait_receive_lane is cancelled" | ||
| 109 | --################################################]] | ||
| 110 | local 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 | ||
| 114 | end | ||
| 115 | |||
| 116 | local wait_receive_batched_lane = lanes.gen("*", wait_receive_batched)() | ||
| 117 | repeat until wait_receive_batched_lane.status == "waiting" | ||
| 118 | print "wait_receive_batched_lane is waiting" | ||
| 119 | wait_receive_batched_lane:cancel() -- hard cancel, 0 timeout | ||
| 120 | repeat until wait_receive_batched_lane.status == "cancelled" | ||
| 121 | print "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 @@ | |||
| 1 | local require_lanes_result_1, require_lanes_result_2 = require "lanes".configure(config).configure() | ||
| 2 | print("require_lanes_result:", require_lanes_result_1, require_lanes_result_2) | ||
| 3 | local lanes = require_lanes_result_1 | ||
| 4 | |||
| 5 | local require_assert_result_1, require_assert_result_2 = require "_assert" | ||
| 6 | print("require_assert_result:", require_assert_result_1, require_assert_result_2) | ||
| 7 | |||
| 8 | local utils = lanes.require "_utils" | ||
| 9 | local PRINT = utils.MAKE_PRINT() | ||
| 10 | |||
| 11 | local lanes_gen = assert(lanes.gen) | ||
| 12 | local lanes_linda = assert(lanes.linda) | ||
| 13 | |||
| 14 | -- ################################################################################################## | ||
| 15 | -- ################################################################################################## | ||
| 16 | -- ################################################################################################## | ||
| 17 | |||
| 18 | local gc_cb = function(name_, status_) | ||
| 19 | PRINT(" ---> lane '" .. name_ .. "' collected with status '" .. status_ .. "'") | ||
| 20 | end | ||
| 21 | |||
| 22 | -- ################################################################################################## | ||
| 23 | -- ################################################################################################## | ||
| 24 | -- ################################################################################################## | ||
| 25 | |||
| 26 | PRINT("\n\n", "---=== Comms criss cross ===---", "\n\n") | ||
| 27 | |||
| 28 | -- We make two identical lanes, which are using the same Linda channel. | ||
| 29 | -- | ||
| 30 | local 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 | |||
| 45 | local linda= lanes_linda("criss cross") | ||
| 46 | |||
| 47 | local a,b= tc(linda, "A","B"), tc(linda, "B","A") -- launching two lanes, twisted comms | ||
| 48 | |||
| 49 | local _= a[0],b[0] -- waits until they are both ready | ||
| 50 | |||
| 51 | PRINT("collectgarbage") | ||
| 52 | a, b = nil | ||
| 53 | collectgarbage() | ||
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 @@ | |||
| 1 | local require_lanes_result_1, require_lanes_result_2 = require "lanes".configure(config).configure() | ||
| 2 | print("require_lanes_result:", require_lanes_result_1, require_lanes_result_2) | ||
| 3 | local lanes = require_lanes_result_1 | ||
| 4 | |||
| 5 | local require_assert_result_1, require_assert_result_2 = require "_assert" | ||
| 6 | print("require_assert_result:", require_assert_result_1, require_assert_result_2) | ||
| 7 | |||
| 8 | local utils = lanes.require "_utils" | ||
| 9 | local PRINT = utils.MAKE_PRINT() | ||
| 10 | |||
| 11 | local lanes_gen = assert(lanes.gen) | ||
| 12 | local lanes_linda = assert(lanes.linda) | ||
| 13 | |||
| 14 | -- ################################################################################################## | ||
| 15 | -- ################################################################################################## | ||
| 16 | -- ################################################################################################## | ||
| 17 | |||
| 18 | local 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 | ||
| 27 | end | ||
| 28 | |||
| 29 | local gc_cb = function(name_, status_) | ||
| 30 | PRINT(" ---> lane '" .. name_ .. "' collected with status '" .. status_ .. "'") | ||
| 31 | end | ||
| 32 | |||
| 33 | -- ################################################################################################## | ||
| 34 | -- ################################################################################################## | ||
| 35 | -- ################################################################################################## | ||
| 36 | |||
| 37 | local tables_match = utils.tables_match | ||
| 38 | |||
| 39 | local SLEEP = function(...) | ||
| 40 | local k, v = lanes.sleep(...) | ||
| 41 | assert(k == nil and v == "timeout") | ||
| 42 | end | ||
| 43 | |||
| 44 | PRINT("\n\n", "---=== Communications ===---", "\n\n") | ||
| 45 | |||
| 46 | local function WR(...) io.stderr:write(...) end | ||
| 47 | |||
| 48 | local 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") | ||
| 73 | end | ||
| 74 | |||
| 75 | local linda = lanes_linda("communications") | ||
| 76 | assert(type(linda) == "userdata" and tostring(linda) == "Linda: communications") | ||
| 77 | -- | ||
| 78 | -- ["->"] master -> slave | ||
| 79 | -- ["<-"] slave <- master | ||
| 80 | |||
| 81 | WR "test linda:get/set..." | ||
| 82 | linda:set("<->", "x", "y", "z") | ||
| 83 | local b,x,y,z = linda:get("<->", 1) | ||
| 84 | assert(b == 1 and x == "x" and y == nil and z == nil) | ||
| 85 | local b,x,y,z = linda:get("<->", 2) | ||
| 86 | assert(b == 2 and x == "x" and y == "y" and z == nil) | ||
| 87 | local b,x,y,z = linda:get("<->", 3) | ||
| 88 | assert(b == 3 and x == "x" and y == "y" and z == "z") | ||
| 89 | local b,x,y,z,w = linda:get("<->", 4) | ||
| 90 | assert(b == 3 and x == "x" and y == "y" and z == "z" and w == nil) | ||
| 91 | local k, x = linda:receive("<->") | ||
| 92 | assert(k == "<->" and x == "x") | ||
| 93 | local k,y,z = linda:receive(linda.batched, "<->", 2) | ||
| 94 | assert(k == "<->" and y == "y" and z == "z") | ||
| 95 | linda:set("<->") | ||
| 96 | local b,x,y,z,w = linda:get("<->", 4) | ||
| 97 | assert(b == 0 and x == nil and y == nil and z == nil and w == nil) | ||
| 98 | WR "ok\n" | ||
| 99 | |||
| 100 | local function PEEK(...) return linda:get("<-", ...) end | ||
| 101 | local function SEND(...) local _res, _err = linda:send("->", ...) assert(_res == true and _err == nil) end | ||
| 102 | local function RECEIVE() local k,v = linda:receive(1, "<-") return v end | ||
| 103 | |||
| 104 | local comms_lane = lanes_gen("io", {gc_cb = gc_cb, name = "auto"}, chunk)(linda) -- prepare & launch | ||
| 105 | |||
| 106 | SEND(1); WR("main ", "1 sent\n") | ||
| 107 | SEND(2); WR("main ", "2 sent\n") | ||
| 108 | SEND(3); WR("main ", "3 sent\n") | ||
| 109 | SEND(setmetatable({"should be ignored"},{__lanesconvert=lanes.null})); WR("main ", "__lanesconvert table sent\n") | ||
| 110 | for i=1,40 do | ||
| 111 | WR "." | ||
| 112 | SLEEP(0.0001) | ||
| 113 | assert(PEEK() == 0) -- nothing coming in, yet | ||
| 114 | end | ||
| 115 | SEND(nil); WR("\nmain ", "nil sent\n") | ||
| 116 | |||
| 117 | local a,b,c = RECEIVE(), RECEIVE(), RECEIVE() | ||
| 118 | |||
| 119 | print("lane status: " .. comms_lane.status) | ||
| 120 | if comms_lane.status == "error" then | ||
| 121 | print(comms_lane:join()) | ||
| 122 | else | ||
| 123 | WR("main ", tostring(a)..", "..tostring(b)..", "..tostring(c).." received\n") | ||
| 124 | end | ||
| 125 | |||
| 126 | assert(a==4 and b==5 and c==6) | ||
| 127 | |||
| 128 | local aaa = RECEIVE(); WR("main ", aaa.." received\n") | ||
| 129 | assert(aaa=='aaa') | ||
| 130 | |||
| 131 | local null = RECEIVE(); WR(tostring(null).." received\n") | ||
| 132 | assert(null==nil) | ||
| 133 | |||
| 134 | local out_t = RECEIVE(); WR(type(out_t).." received\n") | ||
| 135 | assert(tables_match(out_t, {'a','b','c',d=10})) | ||
| 136 | |||
| 137 | assert(PEEK() == 0) | ||
| 138 | SEND(4) | ||
| 139 | |||
| 140 | local complex_table = RECEIVE(); WR(type(complex_table).." received\n") | ||
| 141 | assert(complex_table[1] == complex_table[3] and complex_table[2] == complex_table[4]) | ||
| 142 | WR(table.concat({complex_table[1][1],complex_table[2][1],complex_table[3][1],complex_table[4][1]},", ")) | ||
| 143 | |||
| 144 | WR("collectgarbage") | ||
| 145 | comms_lane = nil | ||
| 146 | collectgarbage() | ||
| 147 | -- wait | ||
| 148 | WR("waiting 1s") | ||
| 149 | SLEEP(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 @@ | |||
| 1 | local require_lanes_result_1, require_lanes_result_2 = require "lanes".configure(config).configure() | ||
| 2 | print("require_lanes_result:", require_lanes_result_1, require_lanes_result_2) | ||
| 3 | local lanes = require_lanes_result_1 | ||
| 4 | |||
| 5 | local require_assert_result_1, require_assert_result_2 = require "_assert" | ||
| 6 | print("require_assert_result:", require_assert_result_1, require_assert_result_2) | ||
| 7 | |||
| 8 | local utils = lanes.require "_utils" | ||
| 9 | local PRINT = utils.MAKE_PRINT() | ||
| 10 | |||
| 11 | local lanes_gen = assert(lanes.gen) | ||
| 12 | local lanes_linda = assert(lanes.linda) | ||
| 13 | |||
| 14 | -- ################################################################################################## | ||
| 15 | -- ################################################################################################## | ||
| 16 | -- ################################################################################################## | ||
| 17 | |||
| 18 | local gc_cb = function(name_, status_) | ||
| 19 | PRINT(" ---> lane '" .. name_ .. "' collected with status '" .. status_ .. "'") | ||
| 20 | end | ||
| 21 | |||
| 22 | PRINT("---=== Tasking (error) ===---", "\n\n") | ||
| 23 | |||
| 24 | -- a lane that throws immediately the error value it received | ||
| 25 | local g = lanes_gen("", {gc_cb = gc_cb}, error) | ||
| 26 | local errmsg = "ERROR!" | ||
| 27 | |||
| 28 | if 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)) | ||
| 36 | end | ||
| 37 | |||
| 38 | if 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) | ||
| 49 | end | ||
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 @@ | |||
| 1 | local require_lanes_result_1, require_lanes_result_2 = require "lanes".configure(config).configure() | ||
| 2 | print("require_lanes_result:", require_lanes_result_1, require_lanes_result_2) | ||
| 3 | local lanes = require_lanes_result_1 | ||
| 4 | |||
| 5 | local require_assert_result_1, require_assert_result_2 = require "_assert" | ||
| 6 | print("require_assert_result:", require_assert_result_1, require_assert_result_2) | ||
| 7 | |||
| 8 | local utils = lanes.require "_utils" | ||
| 9 | local PRINT = utils.MAKE_PRINT() | ||
| 10 | |||
| 11 | local lanes_gen = assert(lanes.gen) | ||
| 12 | local lanes_linda = assert(lanes.linda) | ||
| 13 | |||
| 14 | -- ################################################################################################## | ||
| 15 | -- ################################################################################################## | ||
| 16 | -- ################################################################################################## | ||
| 17 | |||
| 18 | local gc_cb = function(name_, status_) | ||
| 19 | PRINT(" ---> lane '" .. name_ .. "' collected with status '" .. status_ .. "'") | ||
| 20 | end | ||
| 21 | |||
| 22 | -- ################################################################################################## | ||
| 23 | -- ################################################################################################## | ||
| 24 | -- ################################################################################################## | ||
| 25 | |||
| 26 | local SLEEP = function(...) | ||
| 27 | local k, v = lanes.sleep(...) | ||
| 28 | assert(k == nil and v == "timeout") | ||
| 29 | end | ||
| 30 | |||
| 31 | PRINT("---=== :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 | |||
| 37 | local 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) | ||
| 47 | end) | ||
| 48 | |||
| 49 | h= 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 | ||
| 51 | SLEEP(0.5) | ||
| 52 | print("joining with '" .. h:get_threadname() .. "'") | ||
| 53 | local a,b,c,d= h:join() | ||
| 54 | if h.status == "error" then | ||
| 55 | print(h:get_threadname(), "error: " , a, b, c, d) | ||
| 56 | else | ||
| 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)) | ||
| 62 | end | ||
| 63 | |||
| 64 | local nameof_type, nameof_name = lanes.nameof(print) | ||
| 65 | PRINT("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 @@ | |||
| 1 | local config = { with_timers = false, strip_functions = false, internal_allocator = "libc"} | ||
| 2 | local require_lanes_result_1, require_lanes_result_2 = require "lanes".configure(config).configure() | ||
| 3 | print("require_lanes_result:", require_lanes_result_1, require_lanes_result_2) | ||
| 4 | local lanes = require_lanes_result_1 | ||
| 5 | |||
| 6 | local require_assert_result_1, require_assert_result_2 = require "_assert" | ||
| 7 | print("require_assert_result:", require_assert_result_1, require_assert_result_2) | ||
| 8 | |||
| 9 | local utils = lanes.require "_utils" | ||
| 10 | local PRINT = utils.MAKE_PRINT() | ||
| 11 | |||
| 12 | local lanes_gen = assert(lanes.gen) | ||
| 13 | local lanes_linda = assert(lanes.linda) | ||
| 14 | |||
| 15 | -- ################################################################################################## | ||
| 16 | -- ################################################################################################## | ||
| 17 | -- ################################################################################################## | ||
| 18 | |||
| 19 | local gc_cb = function(name_, status_) | ||
| 20 | PRINT(" ---> lane '" .. name_ .. "' collected with status '" .. status_ .. "'") | ||
| 21 | end | ||
| 22 | |||
| 23 | -- ################################################################################################## | ||
| 24 | -- ################################################################################################## | ||
| 25 | -- ################################################################################################## | ||
| 26 | |||
| 27 | PRINT("---=== Receive & send of code ===---", "\n") | ||
| 28 | |||
| 29 | local upvalue = "123" | ||
| 30 | local tostring = tostring | ||
| 31 | |||
| 32 | local 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") | ||
| 66 | end | ||
| 67 | |||
| 68 | local linda = lanes_linda("auto") | ||
| 69 | local t2= lanes_gen("debug,package,string,io", {gc_cb = gc_cb}, chunk2)(linda) -- prepare & launch | ||
| 70 | linda:send("down", function(linda) linda:send("up", "ready!") end, | ||
| 71 | "ok") | ||
| 72 | -- wait to see if the tiny function gets executed | ||
| 73 | -- | ||
| 74 | local k,s= linda:receive(1, "up") | ||
| 75 | if t2.status == "error" then | ||
| 76 | PRINT("t2 error: " , t2:join()) | ||
| 77 | assert(false) | ||
| 78 | end | ||
| 79 | PRINT(s) | ||
| 80 | assert(s=="ready!", s .. " is not 'ready!'") | ||
| 81 | |||
| 82 | -- returns of the 'chunk2' itself | ||
| 83 | -- | ||
| 84 | local k,f= linda:receive("up") | ||
| 85 | assert(type(f)=="function") | ||
| 86 | |||
| 87 | local s2= f() | ||
| 88 | assert(s2==":)") | ||
| 89 | |||
| 90 | local k,ok2= linda:receive("up") | ||
| 91 | assert(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 @@ | |||
| 1 | if not package.preload.fixture then | ||
| 2 | error "can't be run outside of UnitTest framework" | ||
| 3 | end | ||
| 4 | local fixture = require "fixture" | ||
| 5 | |||
| 6 | local lanes = require "lanes".configure{shutdown_timeout = 0.001, on_state_create = fixture.on_state_create} | ||
| 7 | |||
| 8 | -- launch lanes that blocks forever | ||
| 9 | local lane = function() | ||
| 10 | local fixture = require "fixture" | ||
| 11 | fixture.forever() | ||
| 12 | end | ||
| 13 | |||
| 14 | -- the generator | ||
| 15 | local g1 = lanes.gen("*", lane) | ||
| 16 | |||
| 17 | -- launch lane | ||
| 18 | local h1 = g1() | ||
| 19 | |||
| 20 | -- wait until the lane is running | ||
| 21 | repeat until h1.status == "running" | ||
| 22 | |||
| 23 | -- let the script end, Lanes should throw std::logic_error because the lane did not gracefully terminate | ||
| 24 | lanes.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 | ||
| 2 | local require_lanes_result_1, require_lanes_result_2 = require "lanes".configure{nb_user_keepers = 3, keepers_gc_threshold = 500} | ||
| 3 | local lanes = require_lanes_result_1 | ||
| 4 | |||
| 5 | local a = lanes.linda("A", 1) | ||
| 6 | local b = lanes.linda("B", 2) | ||
| 7 | local c = lanes.linda("C", 3) | ||
| 8 | |||
| 9 | -- store each linda in the other 2 | ||
| 10 | do | ||
| 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) | ||
| 14 | end | ||
| 15 | |||
| 16 | do | ||
| 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) | ||
| 20 | end | ||
| 21 | |||
| 22 | do | ||
| 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) | ||
| 26 | end | ||
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 @@ | |||
| 1 | local lanes = require "lanes" | ||
| 2 | |||
| 3 | -- a newly created linda doesn't contain anything | ||
| 4 | local l = lanes.linda() | ||
| 5 | assert(l.null == lanes.null) | ||
| 6 | assert(l:dump() == nil) | ||
| 7 | |||
| 8 | -- read something with 0 timeout, should fail | ||
| 9 | local k,v = l:receive(0, "k") | ||
| 10 | assert(k == nil and v == 'timeout') | ||
| 11 | |||
| 12 | -- send some value | ||
| 13 | assert(l:send("k1", 99) == true) | ||
| 14 | -- make sure the contents look as expected | ||
| 15 | local t = l:dump() | ||
| 16 | assert(type(t) == 'table') | ||
| 17 | local tk1 = t.k1 | ||
| 18 | assert(type(tk1) == 'table') | ||
| 19 | assert(tk1.first == 1 and tk1.count == 1 and tk1.limit == 'unlimited' and tk1.restrict == 'none') | ||
| 20 | assert(#tk1.fifo == tk1.count, #tk1.fifo .. " ~= " .. tk1.count) | ||
| 21 | assert(tk1.fifo[1] == 99, tk1.fifo[1] .. " ~= " .. 99) | ||
| 22 | |||
| 23 | -- read the value, should succeed | ||
| 24 | local k,v = l:receive("k1") | ||
| 25 | assert(k == "k1" and v == 99) | ||
| 26 | -- after reading, the data is no longer available (even if the key itself still exists within the linda) | ||
| 27 | local t = l:dump() | ||
| 28 | assert(type(t) == 'table') | ||
| 29 | local tk1 = t.k1 | ||
| 30 | assert(type(tk1) == 'table') | ||
| 31 | assert(tk1.first == 1 and tk1.count == 0 and tk1.limit == 'unlimited' and tk1.restrict == 'none') | ||
| 32 | assert(#tk1.fifo == tk1.count and tk1.fifo[1] == nil) | ||
| 33 | -- read again, should fail | ||
| 34 | local k,v = l:receive(0, "k1") | ||
| 35 | assert(k == nil and v == 'timeout') | ||
| 36 | |||
| 37 | -- send null, read nil | ||
| 38 | l:send("k", l.null) | ||
| 39 | local k,v = l:receive(0, "k") | ||
| 40 | assert(k == "k" and v == nil) | ||
| 41 | |||
| 42 | -- using a deep userdata (such as another linda) as key, should work | ||
| 43 | local l2 = lanes.linda() | ||
| 44 | l:send(nil, l2, 99) | ||
| 45 | local k,v = l:receive(0, l2) | ||
| 46 | assert(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 @@ | |||
| 1 | local lanes = require 'lanes'.configure{with_timers = false} | ||
| 2 | local l = lanes.linda'gleh' | ||
| 3 | l:set('yo', io.stdin) | ||
| 4 | local n, stdin_out = l:get('yo') | ||
| 5 | assert(n == 1 and stdin_out == io.stdin, tostring(stdin_out) .. " ~= " .. tostring(io.stdin)) | ||
