Add Task.wait to combine multiple tasks into one

Add TestRunner for running asynchronous tests with update support

Add test for Task.wait to verify concurrent task completion

Add set method to Counter for explicit value assignment
This commit is contained in:
PeaAshMeter 2026-01-31 02:11:55 +03:00
parent e02c221e31
commit 403ba5a03f
5 changed files with 143 additions and 0 deletions

View File

@ -4,6 +4,7 @@
--- @field private isAlive boolean --- @field private isAlive boolean
--- @field push fun():nil добавить 1 к счетчику --- @field push fun():nil добавить 1 к счетчику
--- @field pop fun():nil убавить 1 у счетчика --- @field pop fun():nil убавить 1 у счетчика
--- @field set fun(count: integer): nil установить значение на счетчике
local counter = {} local counter = {}
counter.__index = counter counter.__index = counter
@ -31,6 +32,7 @@ local function new(onFinish)
} }
t.push = function() counter._push(t) end t.push = function() counter._push(t) end
t.pop = function() counter._pop(t) end t.pop = function() counter._pop(t) end
t.set = function(count) t.count = count end
return setmetatable(t, counter) return setmetatable(t, counter)
end end

View File

@ -34,3 +34,33 @@
--- ``` --- ```
--- @generic T --- @generic T
--- @alias Task<T> fun(callback: fun(value: T): nil): nil --- @alias Task<T> fun(callback: fun(value: T): nil): nil
--- Возвращает новый Task, который завершится после завершения всех переданных `tasks`.
---
--- Значение созданного Task будет содержать список значений `tasks` в том же порядке.
---
--- См. также https://api.dart.dev/dart-async/Future/wait.html
--- @generic T
--- @param tasks Task[]
--- @return Task<T[]>
local function wait(tasks)
local count = #tasks
local results = {}
return function(callback)
for i, task in ipairs(tasks) do
task(
function(result)
results[i] = result
count = count - 1
if count == 0 then callback(results) end
end
)
end
end
end
return {
wait = wait,
}

View File

@ -2,6 +2,8 @@
local character = require "lib/character/character" local character = require "lib/character/character"
local testLayout local testLayout
local TestRunner = require "test.runner"
TestRunner:register(require "test.task")
function love.conf(t) function love.conf(t)
t.console = true t.console = true
@ -67,6 +69,8 @@ end
local lt = "0" local lt = "0"
function love.update(dt) function love.update(dt)
TestRunner:update(dt) -- закомментировать для отключения тестов
local t1 = love.timer.getTime() local t1 = love.timer.getTime()
Tree.controls:poll() Tree.controls:poll()
Tree.level.camera:update(dt) -- сначала логика камеры, потому что на нее завязан UI Tree.level.camera:update(dt) -- сначала логика камеры, потому что на нее завязан UI

46
test/runner.lua Normal file
View File

@ -0,0 +1,46 @@
--- @class Test
local test = {}
function test:run(complete) end
function test:update(dt) end
--- @class TestRunner
--- @field private tests Test[]
--- @field private state "loading" | "running" | "completed"
--- @field private completedCount integer
local runner = {}
runner.tests = {}
runner.state = "loading"
runner.completedCount = 0
--- глобальный update для тестов, нужен для тестирования фич, зависимых от времени
function runner:update(dt)
if self.state == "loading" then
print("[TestRunner]: running " .. #self.tests .. " tests")
for _, t in ipairs(self.tests) do
t:run(
function()
self.completedCount = self.completedCount + 1
if self.completedCount == #self.tests then
self.state = "completed"
print("[TestRunner]: tests completed")
end
end
)
end
self.state = "running"
end
for _, t in ipairs(self.tests) do
if t.update then t:update(dt) end
end
end
--- добавляет тест для прохождения
--- @param t Test
function runner:register(t)
table.insert(self.tests, t)
end
return runner

61
test/task.lua Normal file
View File

@ -0,0 +1,61 @@
local task = require "lib.utils.task"
local test = {}
local t0
local task1Start, task2Start
local task1Callback, task2Callback
--- @return Task<number>
local function task1()
return function(callback)
task1Start = love.timer.getTime()
task1Callback = callback
end
end
--- @return Task<number>
local function task2()
return function(callback)
task2Start = love.timer.getTime()
task2Callback = callback
end
end
function test:run(complete)
t0 = love.timer.getTime()
task.wait {
task1(),
task2()
} (function(values)
local tWait = love.timer.getTime()
local dt = tWait - t0
local t1 = values[1]
local t2 = values[2]
assert(type(t1) == "number" and type(t2) == "number")
assert(t2 > t1)
assert(dt >= 2, "dt = " .. dt)
print("task.wait completed in " .. dt .. " sec", "t1 = " .. t1 - t0, "t2 = " .. t2 - t0)
complete()
end)
end
function test:update(dt)
local t = love.timer.getTime()
if task1Start and t - task1Start >= 1 then
task1Callback(t)
task1Start = nil
end
if task2Start and t - task2Start >= 2 then
task2Callback(t)
task2Start = nil
end
end
return test