-- SPDX-FileCopyrightText: © 2022—2026 Vladimir Zorin -- SPDX-License-Identifier: LicenseRef-OWL-1.0-or-later -- Licensed under OWL v1.0+. See LICENSE. local testimony = require("testimony") local lev = require("lev") local testify = testimony.new("== lev observability ==") -- ========================================================================= -- task_name() -- ========================================================================= testify:that("task_name: returns nil outside run", function() local result = lev.task_name() testimony.assert_nil(result) end) testify:that("task_name: set via opts.name in spawn", function() local seen_name lev.run(function() local task = lev.spawn(function() seen_name = lev.task_name() end, { name = "worker" }) lev.await(task) end) testimony.assert_equal("worker", seen_name) end) testify:that("task_name: set and get via task_name()", function() local before, after lev.run(function() local task = lev.spawn(function() before = lev.task_name() lev.task_name("renamed") after = lev.task_name() end) lev.await(task) end) testimony.assert_nil(before) testimony.assert_equal("renamed", after) end) testify:that("task_name: main task is auto-named 'main'", function() local name lev.run(function() name = lev.task_name() end) testimony.assert_equal("main", name) end) -- ========================================================================= -- stats() -- ========================================================================= testify:that("stats: returns nil outside run", function() local result = lev.stats() testimony.assert_nil(result) end) testify:that("stats: returns expected fields inside run", function() local s lev.run(function() s = lev.stats() end) testimony.assert_true(s ~= nil, "stats should not be nil") testimony.assert_true(s.active_coros ~= nil, "should have active_coros") testimony.assert_true(s.registered_fds ~= nil, "should have registered_fds") testimony.assert_true(s.timer_count ~= nil, "should have timer_count") testimony.assert_true(s.ready_count ~= nil, "should have ready_count") testimony.assert_true(s.task_count ~= nil, "should have task_count") end) testify:that("stats: active_coros reflects spawned tasks", function() local s_before, s_during lev.run(function() s_before = lev.stats() local t1 = lev.spawn(function() lev.sleep(0.05) end) local t2 = lev.spawn(function() lev.sleep(0.05) end) -- Give tasks a chance to start lev.sleep(0.01) s_during = lev.stats() lev.await(t1) lev.await(t2) end) -- main task is 1 testimony.assert_equal(1, s_before.active_coros) -- main + 2 spawned testimony.assert_equal(3, s_during.active_coros) end) -- ========================================================================= -- task_tree() -- ========================================================================= testify:that("task_tree: returns nil outside run", function() local result = lev.task_tree() testimony.assert_nil(result) end) testify:that("task_tree: entries have expected fields", function() local tree lev.run(function() local task = lev.spawn(function() lev.sleep(0.01) end, { name = "child" }) lev.sleep(0.005) tree = lev.task_tree() lev.await(task) end) testimony.assert_true(tree ~= nil, "tree should not be nil") testimony.assert_true(#tree >= 2, "should have at least 2 tasks") local first = tree[1] testimony.assert_true(first.id ~= nil, "should have id") testimony.assert_true(first.status ~= nil, "should have status") testimony.assert_true(first.co_status ~= nil, "should have co_status") end) testify:that("task_tree: parent_id linkage", function() local tree lev.run(function() local main_id = nil for _, t in ipairs(lev.task_tree()) do if t.name == "main" then main_id = t.id break end end local task = lev.spawn(function() lev.sleep(0.01) end, { name = "linked_child" }) lev.sleep(0.005) tree = lev.task_tree() lev.await(task) -- Find the child and verify its parent_id local child_entry for _, t in ipairs(tree) do if t.name == "linked_child" then child_entry = t break end end testimony.assert_true(child_entry ~= nil, "should find child entry") testimony.assert_equal(main_id, child_entry.parent_id) end) end) testify:that("task_tree: sorted by id", function() local tree lev.run(function() local t1 = lev.spawn(function() lev.sleep(0.01) end) local t2 = lev.spawn(function() lev.sleep(0.01) end) local t3 = lev.spawn(function() lev.sleep(0.01) end) lev.sleep(0.005) tree = lev.task_tree() lev.await(t1) lev.await(t2) lev.await(t3) end) for i = 2, #tree do testimony.assert_true(tree[i].id > tree[i - 1].id, "tasks should be sorted by id") end end) -- ========================================================================= -- task_dump() -- ========================================================================= testify:that("task_dump: returns nil outside run", function() local result = lev.task_dump() testimony.assert_nil(result) end) testify:that("task_dump: includes traceback for suspended tasks", function() local dump lev.run(function() local task = lev.spawn(function() lev.sleep(0.05) end, { name = "sleeper" }) lev.sleep(0.01) dump = lev.task_dump() lev.await(task) end) testimony.assert_true(dump ~= nil, "dump should not be nil") local sleeper for _, t in ipairs(dump) do if t.name == "sleeper" then sleeper = t break end end testimony.assert_true(sleeper ~= nil, "should find sleeper task") testimony.assert_true(sleeper.traceback ~= nil, "suspended task should have traceback") testimony.assert_true(#sleeper.traceback > 0, "traceback should not be empty") end) -- ========================================================================= -- set_logger() -- ========================================================================= testify:that("set_logger: cleanup errors routed through injected logger", function() local logged_msgs = {} local mock_logger = { log = function(_, entry, level) logged_msgs[#logged_msgs + 1] = { msg = entry.msg, level = level } end, } lev.set_logger(mock_logger) lev.run(function() local task = lev.spawn(function() lev.defer(function() error("cleanup boom") end) end) lev.await(task) end) lev.set_logger(nil) testimony.assert_true(#logged_msgs > 0, "logger should have received messages") testimony.assert_match("cleanup", logged_msgs[1].msg) testimony.assert_equal("error", logged_msgs[1].level) end) testify:conclude()