230 lines
5.1 KiB
Lua
230 lines
5.1 KiB
Lua
-- Eventloop behavior tests for the CraftOS-PC harness.
|
|
-- Invoked via `craftos --headless --script tests/eventloop.lua` from `just test`.
|
|
local createEventLoop = require('/apis/eventloop');
|
|
|
|
local args = { ... };
|
|
local verbose = false;
|
|
local tests = {};
|
|
|
|
for _, arg in ipairs(args) do
|
|
if arg == '--verbose' then
|
|
verbose = true;
|
|
end
|
|
end
|
|
|
|
local function test(name, fn)
|
|
tests[#tests + 1] = { name = name, fn = fn };
|
|
end
|
|
|
|
local function fail(message)
|
|
error(message, 2);
|
|
end
|
|
|
|
local function assertEquals(actual, expected, message)
|
|
if actual ~= expected then
|
|
fail((message or 'assertEquals failed') .. ': expected ' .. tostring(expected) .. ', got ' .. tostring(actual));
|
|
end
|
|
end
|
|
|
|
local function assertTrue(value, message)
|
|
if not value then
|
|
fail(message or 'assertTrue failed');
|
|
end
|
|
end
|
|
|
|
local function assertErrors(fn, expected)
|
|
local ok, err = pcall(fn);
|
|
if ok then
|
|
fail('expected error');
|
|
end
|
|
if expected and not string.find(tostring(err), expected, 1, true) then
|
|
fail('expected error containing ' .. expected .. ', got ' .. tostring(err));
|
|
end
|
|
end
|
|
|
|
test('register dispatches queued event args', function()
|
|
local events = createEventLoop();
|
|
local called = 0;
|
|
|
|
events.register('eventloop_test_basic', function(a, b)
|
|
called = called + 1;
|
|
assertEquals(a, 'first');
|
|
assertEquals(b, 42);
|
|
events.stopLoop();
|
|
end);
|
|
|
|
os.queueEvent('eventloop_test_basic', 'first', 42);
|
|
events.runLoop();
|
|
|
|
assertEquals(called, 1);
|
|
assertTrue(not events.isRunningLoop());
|
|
end);
|
|
|
|
test('STOP unregisters handler after first call', function()
|
|
local events = createEventLoop();
|
|
local stoppedHandlerCalls = 0;
|
|
local observerCalls = 0;
|
|
|
|
events.register('eventloop_test_stop', function()
|
|
stoppedHandlerCalls = stoppedHandlerCalls + 1;
|
|
return events.STOP;
|
|
end);
|
|
|
|
events.register('eventloop_test_stop', function()
|
|
observerCalls = observerCalls + 1;
|
|
if observerCalls == 1 then
|
|
os.queueEvent('eventloop_test_stop');
|
|
else
|
|
events.stopLoop();
|
|
end
|
|
end);
|
|
|
|
os.queueEvent('eventloop_test_stop');
|
|
events.runLoop();
|
|
|
|
assertEquals(stoppedHandlerCalls, 1);
|
|
assertEquals(observerCalls, 2);
|
|
end);
|
|
|
|
test('manual unregister prevents dispatch', function()
|
|
local events = createEventLoop();
|
|
local called = 0;
|
|
local dispose = events.register('eventloop_test_unregister', function()
|
|
called = called + 1;
|
|
end);
|
|
|
|
events.setTimeout(function()
|
|
events.stopLoop();
|
|
end, 0);
|
|
dispose();
|
|
|
|
os.queueEvent('eventloop_test_unregister');
|
|
events.runLoop();
|
|
|
|
assertEquals(called, 0);
|
|
end);
|
|
|
|
test('setTimeout before runLoop fires once', function()
|
|
local events = createEventLoop();
|
|
local called = 0;
|
|
|
|
events.setTimeout(function()
|
|
called = called + 1;
|
|
events.stopLoop();
|
|
end, 0);
|
|
|
|
events.runLoop();
|
|
|
|
assertEquals(called, 1);
|
|
end);
|
|
|
|
test('setTimeout during runLoop fires after event handler', function()
|
|
local events = createEventLoop();
|
|
local order = '';
|
|
|
|
events.register('eventloop_test_runtime_timeout', function()
|
|
order = order .. 'event>';
|
|
events.setTimeout(function()
|
|
order = order .. 'timeout';
|
|
events.stopLoop();
|
|
end, 0);
|
|
return events.STOP;
|
|
end);
|
|
|
|
os.queueEvent('eventloop_test_runtime_timeout');
|
|
events.runLoop();
|
|
|
|
assertEquals(order, 'event>timeout');
|
|
end);
|
|
|
|
test('cleared timeout before runLoop does not fire', function()
|
|
local events = createEventLoop();
|
|
local called = 0;
|
|
|
|
local clear = events.setTimeout(function()
|
|
called = called + 1;
|
|
end, 0);
|
|
clear();
|
|
|
|
events.setTimeout(function()
|
|
events.stopLoop();
|
|
end, 0);
|
|
events.runLoop();
|
|
|
|
assertEquals(called, 0);
|
|
end);
|
|
|
|
test('onStart and onStop run around loop', function()
|
|
local events = createEventLoop();
|
|
local order = '';
|
|
|
|
events.onStart(function()
|
|
order = order .. 'start>';
|
|
end);
|
|
|
|
events.onStop(function()
|
|
order = order .. 'stop';
|
|
end);
|
|
|
|
events.setTimeout(function()
|
|
order = order .. 'timeout>';
|
|
events.stopLoop();
|
|
end, 0);
|
|
|
|
events.runLoop();
|
|
|
|
assertEquals(order, 'start>timeout>stop');
|
|
end);
|
|
|
|
test('empty loop returns and runs onStop', function()
|
|
local events = createEventLoop();
|
|
local stopped = false;
|
|
|
|
events.onStop(function()
|
|
stopped = true;
|
|
end);
|
|
|
|
events.runLoop();
|
|
|
|
assertTrue(stopped);
|
|
assertTrue(not events.isRunningLoop());
|
|
end);
|
|
|
|
test('error contracts are enforced', function()
|
|
local events = createEventLoop();
|
|
local function handler()
|
|
end
|
|
|
|
events.register('eventloop_test_errors', handler);
|
|
|
|
assertErrors(function()
|
|
events.register('eventloop_test_errors', handler);
|
|
end, 'handler already registered');
|
|
|
|
assertErrors(function()
|
|
events.stopLoop();
|
|
end, 'loop is already stopped');
|
|
|
|
assertErrors(function()
|
|
events.register(1, handler);
|
|
end, 'string expected');
|
|
|
|
assertErrors(function()
|
|
events.register('eventloop_test_errors_2', 'not a function');
|
|
end, 'function expected');
|
|
end);
|
|
|
|
for _, t in ipairs(tests) do
|
|
if verbose then
|
|
print('RUN ' .. t.name);
|
|
end
|
|
local ok, err = pcall(t.fn);
|
|
if not ok then
|
|
print('FAIL ' .. t.name .. ': ' .. tostring(err));
|
|
os.shutdown();
|
|
end
|
|
end
|
|
|
|
print('__READY__');
|
|
os.shutdown();
|