local ok, task = pcall(require, "lb:task") if not ok then return end describe("spawn", function() test("callback receives args", function() spawn(function(...) assert(select("#", ...) == 0) end):await() spawn(function(...) assert(select("#", ...) == 1) assert((...) == nil) end, nil):await() spawn(function(...) assert(select("#", ...) == 4) local args = table.pack(...) assert(args[1] == 1 and args[2] == 2 and args[3] == nil and args[4] == 3) end, 1, 2, nil, 3):await() end) test("await returns callback results", function() local res = table.pack(spawn(function() -- no returns end):await()) assert(res.n == 0) local res = table.pack(spawn(function() return nil end):await()) assert(res.n == 1 and res[1] == nil) local res = table.pack(spawn(function() return 1, 2, nil, 3 end):await()) assert(res.n == 4 and res[1] == 1 and res[2] == 2 and res[3] == nil and res[4] == 3) end) test("handles invalid args", function() assert(not pcall(spawn)) assert(not pcall(spawn, 123)) assert(not pcall(spawn, 1, 2, 3)) assert(not pcall(spawn, {}, 2, 3)) end) test("callback args and results", function() local res = table.pack(spawn(function(...) assert(select("#", ...) == 5) local args = table.pack(...) assert(args[1] == 5 and args[2] == 4 and args[3] == nil and args[4] == 3, args[5] == nil) return 1, 3, nil end, 5, 4, nil, 3, nil):await()) assert(res.n == 3 and res[1] == 1 and res[2] == 3 and res[3] == nil) end) test("large number of args", function() local args = {} for i = 1, 1000 do args[i] = i end local res = table.pack(spawn(function(...) return ... end, table.unpack(args)):await()) assert(res.n == 1000 and res[1] == 1 and res[1000] == 1000) end) test("callback closes over upvalues", function() local x = 42 local function f() return x end assert(spawn(f):await() == 42) end) test("order is consistent", function() -- all tasks spawned in one batch should be resumed in the spawn order local tasks, nums = {}, {} for i = 1, 10 do table.insert( tasks, spawn(function() table.insert(nums, i) end) ) end for i = 10, 1, -1 do tasks[i]:await() end assert(#nums == 10) for i = 1, 10 do assert(nums[i] == i) end end) test("nested spawns", function() local result = {} local function inner() table.insert(result, "inner") return "done" end local function outer() table.insert(result, "outer") local v = spawn(inner):await() table.insert(result, v) return v end local v = spawn(outer):await() assert(v == "done") assert(result[1] == "outer" and result[2] == "inner" and result[3] == "done") end) end) describe("sleep", function() test("invalid arg", function() assert(not pcall(task.sleep, "invalid")) task.sleep(-1) -- negative sleep should just become 0ms end) test("sleep is asynchronous", function() local value spawn(function() value = "value" end) assert(value == nil) task.sleep(100) -- implicit await: if it's synchronous, value wouldn't change assert(value == "value") end) test("sleep in nested spawns", function() local value1, value2, value3 = nil, nil, nil local results = {} local function inner() task.sleep(30) value1 = "set by inner" table.insert(results, "inner") return value1 end local function middle() task.sleep(20) value2 = "set by middle" local v = spawn(inner):await() table.insert(results, v) table.insert(results, "middle") return v, value2 end local function outer() task.sleep(10) value3 = "set by outer" local v1, v2 = spawn(middle):await() table.insert(results, v1) table.insert(results, v2) table.insert(results, "outer") return v1, v2, value3 end assert(value1 == nil and value2 == nil and value3 == nil) local r1, r2, r3 = spawn(outer):await() assert(r1 == "set by inner" and r2 == "set by middle" and r3 == "set by outer") assert(value1 == "set by inner" and value2 == "set by middle" and value3 == "set by outer") assert(results[1] == "inner") assert(results[2] == "set by inner") assert(results[3] == "middle") assert(results[4] == "set by inner") assert(results[5] == "set by middle") assert(results[6] == "outer") end) end) describe("task", function() test("properly unrefs arg and ret table", function() local registry = debug.getregistry() local ref, t local cb = function() return "my ret" end do local task = spawn(cb, "my arg") ref = task.__ref t = registry[ref] assert(type(t) == "table") assert(t[1] == cb) assert(t[2] == "my arg") task:await() assert(registry[task.__ref] == t) assert(t[1] == "my ret") end collectgarbage() assert(registry[ref] ~= t) -- if task unref'ed it, it should be either nil or the next freelist number end) end)