From 403ba5a03f428dbc6a8f217712a184b7f0732de8 Mon Sep 17 00:00:00 2001 From: PeaAshMeter Date: Sat, 31 Jan 2026 02:11:55 +0300 Subject: [PATCH] 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 --- lib/utils/counter.lua | 2 ++ lib/utils/task.lua | 30 +++++++++++++++++++++ main.lua | 4 +++ test/runner.lua | 46 ++++++++++++++++++++++++++++++++ test/task.lua | 61 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 143 insertions(+) create mode 100644 test/runner.lua create mode 100644 test/task.lua diff --git a/lib/utils/counter.lua b/lib/utils/counter.lua index 9e8273f..5f75337 100644 --- a/lib/utils/counter.lua +++ b/lib/utils/counter.lua @@ -4,6 +4,7 @@ --- @field private isAlive boolean --- @field push fun():nil добавить 1 к счетчику --- @field pop fun():nil убавить 1 у счетчика +--- @field set fun(count: integer): nil установить значение на счетчике local counter = {} counter.__index = counter @@ -31,6 +32,7 @@ local function new(onFinish) } t.push = function() counter._push(t) end t.pop = function() counter._pop(t) end + t.set = function(count) t.count = count end return setmetatable(t, counter) end diff --git a/lib/utils/task.lua b/lib/utils/task.lua index a64ba49..009f0e7 100644 --- a/lib/utils/task.lua +++ b/lib/utils/task.lua @@ -34,3 +34,33 @@ --- ``` --- @generic T --- @alias Task 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 +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, +} diff --git a/main.lua b/main.lua index b2f120c..b5064d8 100644 --- a/main.lua +++ b/main.lua @@ -2,6 +2,8 @@ local character = require "lib/character/character" local testLayout +local TestRunner = require "test.runner" +TestRunner:register(require "test.task") function love.conf(t) t.console = true @@ -67,6 +69,8 @@ end local lt = "0" function love.update(dt) + TestRunner:update(dt) -- закомментировать для отключения тестов + local t1 = love.timer.getTime() Tree.controls:poll() Tree.level.camera:update(dt) -- сначала логика камеры, потому что на нее завязан UI diff --git a/test/runner.lua b/test/runner.lua new file mode 100644 index 0000000..e865e1e --- /dev/null +++ b/test/runner.lua @@ -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 diff --git a/test/task.lua b/test/task.lua new file mode 100644 index 0000000..93908bf --- /dev/null +++ b/test/task.lua @@ -0,0 +1,61 @@ +local task = require "lib.utils.task" + +local test = {} + +local t0 +local task1Start, task2Start +local task1Callback, task2Callback + +--- @return Task +local function task1() + return function(callback) + task1Start = love.timer.getTime() + task1Callback = callback + end +end + +--- @return Task +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