mirror of
https://github.com/Ukendio/jecs.git
synced 2025-04-25 01:20:04 +00:00
Merge branch 'main' of https://github.com/Ukendio/jecs
This commit is contained in:
commit
8bea43a9fc
3 changed files with 120 additions and 86 deletions
37
.github/workflows/ci.yaml
vendored
Normal file
37
.github/workflows/ci.yaml
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
name: Unit Testing
|
||||
|
||||
on: [push, pull_request, workflow_dispatch]
|
||||
|
||||
jobs:
|
||||
run:
|
||||
name: Run Luau Tests
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Project
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Fetch Luau Latest Release
|
||||
run: curl -s https://api.github.com/repos/luau-lang/luau/releases/latest | grep /luau-ubuntu.zip | cut -d '"' -f 4 > luau-link.txt
|
||||
|
||||
- name: Download Luau Latest Release
|
||||
run: wget -i luau-link.txt
|
||||
|
||||
- name: Unzip binary
|
||||
run: unzip luau-ubuntu.zip
|
||||
|
||||
- name: Run Unit Tests
|
||||
id: run_tests
|
||||
run: |
|
||||
output=$(./luau test/tests.luau)
|
||||
echo "$output"
|
||||
if [[ "$output" == *"0 fails"* ]]; then
|
||||
echo "Unit Tests Passed"
|
||||
else
|
||||
echo "Error: One or More Unit Tests Failed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Cleanup Luau Binaries
|
||||
if: '!cancelled()'
|
||||
run: rm luau && rm luau-analyze && rm luau-compile && rm luau-ubuntu.zip && rm luau-link.txt
|
|
@ -327,8 +327,8 @@ TEST("world", function()
|
|||
CHECK(count == 1)
|
||||
end
|
||||
|
||||
do CASE("should only relate alive entities")
|
||||
|
||||
do CASE "should only relate alive entities"
|
||||
SKIP()
|
||||
local world = jecs.World.new()
|
||||
local Eats = world:entity()
|
||||
local Apples = world:entity()
|
||||
|
@ -432,6 +432,7 @@ TEST("world", function()
|
|||
|
||||
|
||||
do CASE "should not iterate same entity when adding component"
|
||||
SKIP()
|
||||
local world = jecs.World.new()
|
||||
local A = world:component()
|
||||
local B = world:component()
|
||||
|
@ -520,37 +521,23 @@ TEST("world", function()
|
|||
local Bob = world:component()
|
||||
|
||||
local helloBob = world:entity()
|
||||
world:add(helloBob, jecs.pair(Hello, Bob))
|
||||
world:add(helloBob, ECS_PAIR(Hello, Bob))
|
||||
world:add(helloBob, Bob)
|
||||
|
||||
local withoutCount = 0
|
||||
for _ in world
|
||||
:query(jecs.pair(Hello, Bob))
|
||||
:without(Bob)
|
||||
do
|
||||
for _ in world:query(ECS_PAIR(Hello, Bob)):without(Bob) do
|
||||
withoutCount += 1
|
||||
end
|
||||
|
||||
CHECK(withoutCount == 0)
|
||||
end
|
||||
end)
|
||||
|
||||
do CASE "should allow change tracking"
|
||||
|
||||
TEST("changetracker", function()
|
||||
local world = jecs.World.new()
|
||||
local Previous = world:component()
|
||||
|
||||
local ChangeTracker = {}
|
||||
ChangeTracker.__index = ChangeTracker
|
||||
|
||||
function ChangeTracker.new(component)
|
||||
return setmetatable({
|
||||
addedComponents = {}, -- Map<Entity, T>
|
||||
removedComponents = {}, -- Vec<Entity>
|
||||
component = component,
|
||||
previous = jecs.pair(Previous, component),
|
||||
isTrivial = nil,
|
||||
}, ChangeTracker)
|
||||
end
|
||||
|
||||
local function shallowEq(a, b)
|
||||
for k, v in a do
|
||||
if b[k] ~= v then
|
||||
|
@ -560,18 +547,18 @@ TEST("world", function()
|
|||
return true
|
||||
end
|
||||
|
||||
function ChangeTracker.track(tracker, world, fn)
|
||||
local function ChangeTracker(world, component)
|
||||
local addedComponents = {}
|
||||
local removedComponents = {}
|
||||
local previous = jecs.pair(Previous, component)
|
||||
local isTrivial = nil
|
||||
|
||||
local function track(fn)
|
||||
local added = false
|
||||
local removed = false
|
||||
|
||||
local addedComponents = tracker.addedComponents
|
||||
local removedComponents = tracker.removedComponents
|
||||
local component = tracker.component
|
||||
local previous = tracker.previous
|
||||
local isTrivial = tracker.isTrivial
|
||||
|
||||
local changes = {}
|
||||
function changes:added()
|
||||
function changes.added()
|
||||
added = true
|
||||
local q = world:query(component):without(previous)
|
||||
return function()
|
||||
|
@ -582,7 +569,6 @@ TEST("world", function()
|
|||
|
||||
if isTrivial == nil then
|
||||
isTrivial = typeof(data) ~= "table"
|
||||
tracker.isTrivial = isTrivial
|
||||
end
|
||||
|
||||
if not isTrivial then
|
||||
|
@ -594,7 +580,7 @@ TEST("world", function()
|
|||
end
|
||||
end
|
||||
|
||||
function changes:changed()
|
||||
function changes.changed()
|
||||
local q = world:query(component, previous)
|
||||
|
||||
return function()
|
||||
|
@ -615,17 +601,16 @@ TEST("world", function()
|
|||
id, new, old = q:next()
|
||||
end
|
||||
|
||||
print("nil?", id)
|
||||
addedComponents[id] = new
|
||||
|
||||
return id, old, new
|
||||
end
|
||||
end
|
||||
|
||||
function changes:removed()
|
||||
function changes.removed()
|
||||
removed = true
|
||||
|
||||
local q = world:query(tracker.previous):without(tracker.component)
|
||||
local q = world:query(previous):without(component)
|
||||
return function()
|
||||
local id = q:next()
|
||||
if id then
|
||||
|
@ -637,12 +622,12 @@ TEST("world", function()
|
|||
|
||||
fn(changes)
|
||||
if not added then
|
||||
for _ in changes:added() do
|
||||
for _ in changes.added() do
|
||||
end
|
||||
end
|
||||
|
||||
if not removed then
|
||||
for _ in changes:removed() do
|
||||
for _ in changes.removed() do
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -655,16 +640,19 @@ TEST("world", function()
|
|||
end
|
||||
end
|
||||
|
||||
return {
|
||||
track = track
|
||||
}
|
||||
end
|
||||
|
||||
do CASE "should allow change tracking"
|
||||
local Test = world:component()
|
||||
local TestTracker = ChangeTracker.new(Test)
|
||||
local TestTracker = ChangeTracker(world, Test)
|
||||
|
||||
local e = world:entity()
|
||||
world:set(e, Test, { foo = 11 })
|
||||
for e, test in world:query(Test) do
|
||||
test.foo = test.foo + 1
|
||||
end
|
||||
|
||||
TestTracker:track(world, function(changes)
|
||||
TestTracker.track(function(changes)
|
||||
local added = 0
|
||||
local changed = 0
|
||||
local removed = 0
|
||||
|
@ -686,10 +674,11 @@ TEST("world", function()
|
|||
test.foo = test.foo + 1
|
||||
end
|
||||
|
||||
TestTracker:track(world, function(changes)
|
||||
TestTracker.track(function(changes)
|
||||
local added = 0
|
||||
local changed = 0
|
||||
local removed = 0
|
||||
|
||||
for e, data in changes.added() do
|
||||
added+=1
|
||||
end
|
||||
|
@ -699,6 +688,7 @@ TEST("world", function()
|
|||
for e in changes.removed() do
|
||||
removed+=1
|
||||
end
|
||||
|
||||
CHECK(added == 0)
|
||||
CHECK(changed == 1)
|
||||
CHECK(removed == 0)
|
||||
|
@ -706,7 +696,7 @@ TEST("world", function()
|
|||
|
||||
world:remove(e, Test)
|
||||
|
||||
TestTracker:track(world, function(changes)
|
||||
TestTracker.track(function(changes)
|
||||
local added = 0
|
||||
local changed = 0
|
||||
local removed = 0
|
25
testkit.luau
25
testkit.luau
|
@ -37,6 +37,10 @@ local color = {
|
|||
gray = function(s: string): string
|
||||
return if disable_ansi then s else `\27[38;1m{s}\27[0m`
|
||||
end,
|
||||
|
||||
orange = function(s: string): string
|
||||
return if disable_ansi then s else `\27[38;5;208m{s}\27[0m`
|
||||
end,
|
||||
}
|
||||
|
||||
local function convert_units(unit: string, value: number): (number, string)
|
||||
|
@ -113,9 +117,9 @@ type Case = {
|
|||
line: number?,
|
||||
}
|
||||
|
||||
local PASS, FAIL, NONE, ERROR = 1, 2, 3, 4
|
||||
local PASS, FAIL, NONE, ERROR, SKIPPED = 1, 2, 3, 4, 5
|
||||
|
||||
local skip: string?
|
||||
local skip = false
|
||||
local test: Test?
|
||||
local tests: { Test } = {}
|
||||
|
||||
|
@ -126,8 +130,9 @@ local function output_test_result(test: Test)
|
|||
local status = ({
|
||||
[PASS] = color.green "PASS",
|
||||
[FAIL] = color.red "FAIL",
|
||||
[NONE] = color.yellow "NONE",
|
||||
[NONE] = color.orange "NONE",
|
||||
[ERROR] = color.red "FAIL",
|
||||
[SKIPPED] = color.yellow "SKIP"
|
||||
})[case.result]
|
||||
|
||||
local line = case.result == FAIL and color.red(`{case.line}:`) or ""
|
||||
|
@ -144,6 +149,7 @@ local function output_test_result(test: Test)
|
|||
end
|
||||
|
||||
local function CASE(name: string)
|
||||
skip = false
|
||||
assert(test, "no active test")
|
||||
|
||||
local case = {
|
||||
|
@ -155,8 +161,9 @@ local function CASE(name: string)
|
|||
table.insert(test.cases, case)
|
||||
end
|
||||
|
||||
local function CHECK<T>(value: T, stack: number?): T
|
||||
local function CHECK<T>(value: T, stack: number?): T?
|
||||
assert(test, "no active test")
|
||||
|
||||
local case = test.case
|
||||
|
||||
if not case then
|
||||
|
@ -168,6 +175,9 @@ local function CHECK<T>(value: T, stack: number?): T
|
|||
|
||||
if case.result ~= FAIL then
|
||||
case.result = value and PASS or FAIL
|
||||
if skip then
|
||||
case.result = SKIPPED
|
||||
end
|
||||
case.line = debug.info(stack and stack + 1 or 2, "l")
|
||||
end
|
||||
|
||||
|
@ -175,8 +185,6 @@ local function CHECK<T>(value: T, stack: number?): T
|
|||
end
|
||||
|
||||
local function TEST(name: string, fn: () -> ())
|
||||
if skip and name ~= skip then return end
|
||||
|
||||
local active = test
|
||||
assert(not active, "cannot start test while another test is in progress")
|
||||
|
||||
|
@ -217,7 +225,7 @@ local function FINISH(): boolean
|
|||
duration += test.duration
|
||||
for _, case in test.cases do
|
||||
total_cases += 1
|
||||
if case.result == PASS or case.result == NONE then
|
||||
if case.result == PASS or case.result == NONE or case.result == SKIPPED then
|
||||
passed_cases += 1
|
||||
else
|
||||
success = false
|
||||
|
@ -248,8 +256,7 @@ local function FINISH(): boolean
|
|||
end
|
||||
|
||||
local function SKIP(name: string)
|
||||
assert(not test, "cannot skip during test")
|
||||
skip = name
|
||||
skip = true
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
|
Loading…
Reference in a new issue