-- make sure we are pointing to the local copas first package.path = string.format("../src/?.lua;%s", package.path) local copas = require "copas" local now = copas.gettime local Queue = copas.queue local test_complete = false copas.loop(function() -- basic push/pop local q = Queue:new() q:push "hello" assert(q:pop() == "hello", "expected the input to be returned") -- yielding on pop when queue is empty local s = now() copas.addthread(function() copas.pause(0.5) q:push("delayed") end) assert(q:pop() == "delayed", "expected a delayed result") assert(now() - s >= 0.5, "result was not delayed!") -- pop times out local ok, err = q:pop(0.5) assert(err == "timeout", "expected a timeout") assert(ok == nil) -- get_size returns queue size assert(q:get_size() == 0) q:push(1) assert(q:get_size() == 1) q:push(2) assert(q:get_size() == 2) q:push(3) assert(q:get_size() == 3) -- queue behaves as fifo assert(q:pop() == 1) assert(q:pop() == 2) assert(q:pop() == 3) -- handles nil values q:push(1) q:push(nil) q:push(3) assert(q:pop() == 1) local val, err = q:pop() assert(val == nil) assert(err == nil) assert(q:pop() == 3) -- stopping q:push(1) q:push(2) q:push(3) assert(q:stop()) local count = 0 local coro = q:add_worker(function(item) count = count + 1 end) copas.pause(0.1) assert(count == 3, "expected all 3 items handled") assert(coroutine.status(coro) == "dead", "expected thread to be gone") -- coro should be GC'able local weak = setmetatable({}, {__mode="v"}) weak[{}] = coro coro = nil -- luacheck: ignore collectgarbage() collectgarbage() assert(not next(weak)) -- worker exited, so queue is destroyed now? ok, err = q:push() assert(err == "destroyed", "expected queue to be destroyed") assert(ok == nil) ok, err = q:pop() assert(err == "destroyed", "expected queue to be destroyed") assert(ok == nil) test_complete = true end) -- copas loop exited when here assert(test_complete, "test did not complete!") print("test 1 success!") -- a worker handling nil values local count = 0 copas.loop(function() local q = Queue:new() q:push(1) q:push(nil) q:push(3) q:add_worker(function() count = count + 1 end) copas.pause(0.5) -- to activate the worker, which will now be blocked on the q semaphore assert(q:finish(5)) end) assert(count == 3, "expected count to be 3, got "..tostring(count)) print("test 2 success!") -- finish blocks for a timeout local passed = false copas.loop(function() local q = Queue:new() q:push(1) -- no workers, so this one will not be handled local s = now() local ok, err = q:finish(1) local duration = now() - s assert(not ok, "expected a falsy value, got: "..tostring(ok)) assert(err == "timeout", "expected error 'timeout', got: "..tostring(err)) assert(duration > 1 and duration < 1.2, string.format("expected timeout of 1 second, but took: %f",duration)) passed = true end) assert(passed, "test failed!") print("test 3 success!") -- destroying a queue while workers are idle copas.loop(function() local q = Queue:new() q:add_worker(function() end) copas.pause(0.5) -- to activate the worker, which will now be blocked on the q semaphore q:stop() -- this should exit the idle workers and exit the copas loop end) print("test 4 success!") -- finish a queue while workers are idle copas.loop(function() local q = Queue:new() q:add_worker(function() end) copas.pause(0.5) -- to activate the worker, which will now be blocked on the q semaphore q:finish() -- this should exit the idle workers and exit the copas loop end) print("test 5 success!") -- finish doesn't return until all workers are done (finished handling the last queue item) local result = {} local passed = true copas.loop(function() local q = Queue:new() q:push(1) q:push(2) q:push(3) for i = 1,2 do -- add 2 workers q:add_worker(function(n) table.insert(result, "start item " .. n) copas.pause(0.5) table.insert(result, "end item " .. n) end) end -- local s = now() table.insert(result, "start queue") copas.pause(0.75) table.insert(result, "start finish") local ok, err = q:finish() table.insert(result, "finished "..tostring(ok).." "..tostring(err)) copas.pause(1) local expected = { "start queue", "start item 1", "start item 2", "end item 1", "start item 3", "end item 2", "start finish", "end item 3", "finished true nil", } for i = 1, math.max(#result, #expected) do if result[i] ~= expected[i] then for n = 1, math.max(#result, #expected) do print(n, result[n], expected[n], result[n] == expected[n] and "" or " <--- failed") end passed = false break end end end) assert(passed, "test 6 failed!") print("test 6 success!")