luby/crates/lb/tests/task.lua

190 lines
5.1 KiB
Lua

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)