tests.luau Coverage

Total Execution Hits: 72

Function Coverage Overview: 79.10%

Function Coverage:

FunctionHits
1
white_underline:300
white:340
green:380
red:420
yellow:460
red_highlight:500
green_highlight:540
gray:580
pe:630
pp:680
debug_world_inspect:737
record:740
tbl:776
archetype:804
records:831
columns:860
row:893
tuple:942
name:1160
:1201
:1361
:1741
:1841
:1891
getTargets:1922
setAttacksAndEats:2120
:2401
:2561
:3121
:3861
:4351
:4461
:4531
:4751
:5141
:5721
:5961
:8961
:10201
:10481
:10851
:11841
:12131
:12311
:12421
:12501
:13251
:13301
:13701
:13911
:15121
:15411
:16391
:16511
:16561
:16671
:16811
:16971
:17111
:17701
updateCooldowns:17772
:18201
:18891
:18971
:19191
:19391

Source Code:

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
LineHitsCode
11local jecs = require("@jecs")
2N/A
31local testkit = require("@testkit")
41local BENCH, START = testkit.benchmark()
51local __ = jecs.Wildcard
61local ECS_ID, ECS_GENERATION = jecs.ECS_ID, jecs.ECS_GENERATION
71local ECS_GENERATION_INC = jecs.ECS_GENERATION_INC
81local IS_PAIR = jecs.IS_PAIR
91local pair = jecs.pair
101local ecs_pair_first = jecs.pair_first
111local ecs_pair_second = jecs.pair_second
121local entity_index_try_get_any = jecs.entity_index_try_get_any
131local entity_index_get_alive = jecs.entity_index_get_alive
141local entity_index_is_alive = jecs.entity_index_is_alive
151local ChildOf = jecs.ChildOf
161local world_new = jecs.World.new
17N/A
181local it = testkit.test()
191local TEST, CASE = it.TEST, it.CASE
201local CHECK, FINISH = it.CHECK, it.FINISH
211local SKIP, FOCUS = it.SKIP, it.FOCUS
221local CHECK_EXPECT_ERR = it.CHECK_EXPECT_ERR
23N/A
241local N = 2 ^ 8
25N/A
260type World = jecs.World
270type Entity = jecs.Entity
28N/A
291local c = {
301white_underline = function(s: any)
310return `\27[1;4m{s}\27[0m`
32N/Aend,
33N/A
341white = function(s: any)
350return `\27[37;1m{s}\27[0m`
36N/Aend,
37N/A
381green = function(s: any)
390return `\27[32;1m{s}\27[0m`
40N/Aend,
41N/A
421red = function(s: any)
430return `\27[31;1m{s}\27[0m`
44N/Aend,
45N/A
461yellow = function(s: any)
470return `\27[33;1m{s}\27[0m`
48N/Aend,
49N/A
501red_highlight = function(s: any)
510return `\27[41;1;30m{s}\27[0m`
52N/Aend,
53N/A
541green_highlight = function(s: any)
550return `\27[42;1;30m{s}\27[0m`
56N/Aend,
57N/A
581gray = function(s: any)
590return `\27[30;1m{s}\27[0m`
60N/Aend,
610}
62N/A
631local function pe(e)
640local gen = ECS_GENERATION(e)
650return c.green(`e{ECS_ID(e)}`)..c.yellow(`v{gen}`)
66N/Aend
67N/A
681local function pp(e)
690local gen = ECS_GENERATION(e)
700return c.green(`e{ECS_ID(e)}`)..c.yellow(`v{jecs.ECS_ENTITY_T_HI(e)}`)
71N/Aend
72N/A
731local function debug_world_inspect(world: World)
747local function record(e): jecs.Record
752return entity_index_try_get_any(world.entity_index, e) :: any
76N/Aend
777local function tbl(e)
781return record(e).archetype
79N/Aend
807local function archetype(e)
814return tbl(e).type
82N/Aend
837local function records(e)
841return tbl(e).records
85N/Aend
867local function columns(e)
871return tbl(e).columns
88N/Aend
897local function row(e)
902return record(e).row
91N/Aend
92N/A
93N/A-- Important to order them in the order of their columns
947local function tuple(e, ...)
951for i, column in columns(e) do
962if select(i, ...) ~= column[row(e)] then
970return false
98N/Aend
99N/Aend
1001return true
101N/Aend
102N/A
1037return {
1047record = record,
1057tbl = tbl,
1067archetype = archetype,
1077records = records,
1087row = row,
1097tuple = tuple,
1107columns = columns
1110}
112N/Aend
113N/A
1141local dwi = debug_world_inspect
115N/A
1161local function name(world, e)
1170return world:get(e, jecs.Name)
118N/Aend
119N/A
1201TEST("#adding a recycled target", function()
1211local world = world_new()
1221local R = world:component()
123N/A
1241local e = world:entity()
1251local T = world:entity()
1261world:add(e, pair(R, T))
1271world:delete(T)
1281CHECK(not world:has(e, pair(R, T)))
1291local T2 = world:entity()
1301world:add(e, pair(R, T2))
1311CHECK(world:target(e, R) ~= T)
1321CHECK(world:target(e, R) ~= 0)
133N/A
134N/Aend)
135N/A
1361TEST("#repro2", function()
1371local world = world_new()
1381local Lifetime = world:component() :: jecs.Id
1391local Particle = world:entity()
1401local Beam = world:entity()
141N/A
1421local entity = world:entity()
1431world:set(entity, pair(Lifetime, Particle), 1)
1441world:set(entity, pair(Lifetime, Beam), 2)
1451world:set(entity, pair(4, 5), 6) -- noise
146N/A
1471local entity_visualizer = require("@tools/entity_visualiser")
148N/A-- entity_visualizer.components(world, entity)
149N/A
1501for e in world:each(pair(Lifetime, __)) do
1512local i = 0
1522local nth = world:target(e, Lifetime, i)
1532while nth do
154N/A-- entity_visualizer.components(world, e)
155N/A
1562local data = world:get(e, pair(Lifetime, nth))
1572data -= 1
1582if data <= 0 then
1591world:remove(e, pair(Lifetime, nth))
1600else
1611world:set(e, pair(Lifetime, nth), data)
162N/Aend
1632i += 1
1642nth = world:target(e, Lifetime, i)
165N/Aend
166N/Aend
167N/A
1681CHECK(not world:has(entity, pair(Lifetime, Particle)))
1691CHECK(world:get(entity, pair(Lifetime, Beam)) == 1)
170N/Aend)
171N/A
1721local lifetime_tracker_add = require("@tools/lifetime_tracker")
173N/A
1741TEST("another", function()
1751local world = world_new()
176N/A-- world = lifetime_tracker_add(world, {padding_enabled=false})
1771local e1 = world:entity()
1781local e2 = world:entity()
1791local e3 = world:entity()
1801world:delete(e2)
1811local e2_e3 = pair(e2, e3)
1821CHECK(jecs.pair_first(world, e2_e3) == 0)
1831CHECK(jecs.pair_second(world, e2_e3) == e3)
1841CHECK_EXPECT_ERR(function()
1851world:add(e1, pair(e2, e3))
186N/Aend)
187N/Aend)
188N/A
1891TEST("#repro", function()
1901local world = world_new()
191N/A
1921local function getTargets(relation)
1932local tgts = {}
1942local pairwildcard = pair(relation, jecs.Wildcard)
1952for _, archetype in world:query(pairwildcard):archetypes() do
1962local tr = archetype.records[pairwildcard]
1972local count = archetype.counts[pairwildcard]
1982local types = archetype.types
1992for _, entity in archetype.entities do
2002for i = 0, count - 1 do
2012local tgt = jecs.pair_second(world, types[i + tr])
2022table.insert(tgts, tgt)
203N/Aend
204N/Aend
205N/Aend
2062return tgts
207N/Aend
208N/A
2091local Attacks = world:component()
2101local Eats = world:component()
211N/A
2121local function setAttacksAndEats(entity1, entity2)
2130world:add(entity1, pair(Attacks, entity2))
2140world:add(entity1, pair(Eats, entity2))
215N/Aend
216N/A
2171local e1 = world:entity()
2181local e2 = world:entity()
2191local e3 = world:entity()
2201setAttacksAndEats(e3, e1)
2211setAttacksAndEats(e3, e2)
2221setAttacksAndEats(e1, e2)
2231local d = dwi(world)
2241world:delete(e2)
2251local types1 = { pair(Attacks, e1), pair(Eats, e1) }
2261table.sort(types1)
227N/A
228N/A
2291CHECK(d.tbl(e1).type == "")
2301CHECK(d.tbl(e3).type == table.concat(types1, "_"))
231N/A
2321for _, entity in getTargets(Attacks) do
2331CHECK(entity == e1)
234N/Aend
2351for _, entity in getTargets(Eats) do
2361CHECK(entity == e1)
237N/Aend
238N/Aend)
239N/A
2401TEST("archetype", function()
2411local archetype_traverse_add = jecs.archetype_traverse_add
2421local archetype_traverse_remove = jecs.archetype_traverse_remove
243N/A
2441local world = world_new()
2451local root = world.ROOT_ARCHETYPE
2461local c1 = world:component()
2471local c2 = world:component()
2481local c3 = world:component()
249N/A
2501local a1 = archetype_traverse_add(world, c1, nil :: any)
2511local a2 = archetype_traverse_remove(world, c1, a1)
2521CHECK(root.add[c1].to == a1)
2531CHECK(root == a2)
254N/Aend)
255N/A
2561TEST("world:cleanup()", function()
2571local world = world_new()
2581local A = world:component() :: jecs.Id
2591local B = world:component() :: jecs.Id
2601local C = world:component() :: jecs.Id
261N/A
2621local e1 = world:entity()
2631local e2 = world:entity()
2641local e3 = world:entity()
265N/A
2661world:set(e1, A, true)
267N/A
2681world:set(e2, A, true)
2691world:set(e2, B, true)
270N/A
271N/A
2721world:set(e3, A, true)
2731world:set(e3, B, true)
2741world:set(e3, C, true)
275N/A
2761local archetype_index = world.archetype_index
277N/A
2781CHECK(#archetype_index["1"].entities == 1)
2791CHECK(#archetype_index["1_2"].entities == 1)
2801CHECK(#archetype_index["1_2_3"].entities == 1)
281N/A
2821world:delete(e1)
2831world:delete(e2)
2841world:delete(e3)
285N/A
2861world:cleanup()
287N/A
2881archetype_index = world.archetype_index
289N/A
2901CHECK((archetype_index["1"] :: jecs.Archetype?) == nil)
2911CHECK((archetype_index["1_2"] :: jecs.Archetype?) == nil)
2921CHECK((archetype_index["1_2_3"] :: jecs.Archetype?) == nil)
293N/A
2941local e4 = world:entity()
2951world:set(e4, A, true)
2961CHECK(#archetype_index["1"].entities == 1)
2971CHECK((archetype_index["1_2"] :: jecs.Archetype?) == nil)
2981CHECK((archetype_index["1_2_3"] :: jecs.Archetype?) == nil)
2991world:set(e4, B, true)
3001CHECK(#archetype_index["1"].entities == 0)
3011CHECK(#archetype_index["1_2"].entities == 1)
3021CHECK((archetype_index["1_2_3"] :: jecs.Archetype?) == nil)
3031world:set(e4, C, true)
3041CHECK(#archetype_index["1"].entities == 0)
3051CHECK(#archetype_index["1_2"].entities == 0)
3061CHECK(#archetype_index["1_2_3"].entities == 1)
307N/Aend)
308N/A
3091local pe = require("@tools/entity_visualiser").prettify
3101local lifetime_tracker_add = require("@tools/lifetime_tracker")
311N/A
3121TEST("world:entity()", function()
3130do
3141CASE("unique IDs")
3151local world = jecs.World.new()
3161local set = {}
3171for i = 1, N do
318256local e = world:entity()
319256CHECK(not set[e])
320256set[e] = true
321N/Aend
322N/Aend
3230do
3241CASE("generations")
3251local world = jecs.World.new()
3261local e = world:entity() :: number
3271CHECK(ECS_ID(e) == 1 + jecs.Rest :: number)
3281CHECK(ECS_GENERATION(e) == 0) -- 0
3291e = ECS_GENERATION_INC(e)
3301CHECK(ECS_GENERATION(e) == 1) -- 1
331N/Aend
332N/A
3331do CASE "pairs"
3341local world = jecs.World.new()
3351local _e = world:entity()
3361local e2 = world:entity()
3371local e3 = world:entity()
338N/A
339N/A-- Incomplete pair, must have a bit flag that notes it is a pair
3401CHECK(IS_PAIR(world:entity()) == false)
341N/A
3421local p = pair(e2, e3)
3431CHECK(IS_PAIR(p) == true)
344N/A
3451CHECK(ecs_pair_first(world, p) == e2 :: number)
3461CHECK(ecs_pair_second(world, p) == e3 :: number)
347N/A
3481world:delete(e2)
3491local e2v2 = world:entity()
3501CHECK(IS_PAIR(e2v2) == false)
351N/A
3521CHECK(IS_PAIR(pair(e2v2, e3)) == true)
353N/Aend
354N/A
3551do CASE "Recycling"
3561local world = world_new()
3571local e = world:entity()
3581world:delete(e)
3591local e1 = world:entity()
3601world:delete(e1)
3611local e2 = world:entity()
3621CHECK(ECS_ID(e2) == e :: number)
3631CHECK(ECS_GENERATION(e2) == 2)
3641CHECK(world:contains(e2))
3651CHECK(not world:contains(e1))
3661CHECK(not world:contains(e))
367N/Aend
368N/A
3691do CASE "Recycling max generation"
3701local world = world_new()
3711local pin = (jecs.Rest :: any) :: number + 1
3721for i = 1, 2^16-1 do
37365535local e = world:entity()
37465535world:delete(e)
375N/Aend
3761local e = world:entity()
3771CHECK(ECS_ID(e) == pin)
3781CHECK(ECS_GENERATION(e) == 2^16-1)
3791world:delete(e)
3801e = world:entity()
3811CHECK(ECS_ID(e) == pin)
3821CHECK(ECS_GENERATION(e) == 0)
383N/Aend
384N/Aend)
385N/A
3861TEST("world:set()", function()
3871do CASE "archetype move"
3880do
3891local world = jecs.World.new()
390N/A
3911local d = debug_world_inspect(world)
392N/A
3931local _1 = world:component()
3941local _2 = world:component()
3951local e = world:entity()
396N/A-- An entity starts without an archetype or row
397N/A-- should therefore not need to copy over data
3981CHECK(d.tbl(e) == nil)
3991CHECK(d.row(e) == nil)
400N/A
4011local archetypes = #world.archetypes
402N/A-- This should create a new archetype since it is the first
403N/A-- entity to have moved there
4041world:set(e, _1, 1)
4051local oldRow = d.row(e)
4061local oldArchetype = d.archetype(e)
4071CHECK(#world.archetypes == archetypes + 1)
4081CHECK(oldArchetype == "1")
4091CHECK(d.tbl(e))
4101CHECK(oldRow == 1)
411N/A
4121world:set(e, _2, 2)
4131CHECK(d.archetype(e) == "1_2")
414N/A-- Should have tuple of fields to the next archetype and set the component data
4151CHECK(d.tuple(e, 1, 2))
416N/A-- Should have moved the data from the old archetype
4171CHECK(world.archetype_index[oldArchetype].columns[_1][oldRow] == nil)
418N/Aend
419N/Aend
420N/A
4211do CASE "pairs"
4221local world = jecs.World.new()
423N/A
4241local C1 = world:component()
4251local C2 = world:component()
4261local T1 = world:entity()
4271local T2 = world:entity()
428N/A
4291local e = world:entity()
430N/A
4311world:set(e, pair(C1, C2), true)
4321world:set(e, pair(C1, T1), true)
4331world:set(e, pair(T1, C1), true)
434N/A
4351CHECK_EXPECT_ERR(function()
4361world:set(e, pair(T1, T2), true :: any)
437N/Aend)
438N/A
4391CHECK(world:get(e, pair(C1, C2)))
4401CHECK(world:get(e, pair(C1, T1)))
4411CHECK(world:get(e, pair(T1, C1)))
4421CHECK(not world:get(e, pair(T1, T2)))
443N/A
4441local e2 = world:entity()
445N/A
4461CHECK_EXPECT_ERR(function()
4471world:set(e2, pair(jecs.ChildOf, e), true :: any)
448N/Aend)
4491CHECK(not world:get(e2, pair(jecs.ChildOf, e)))
450N/Aend
451N/Aend)
452N/A
4531TEST("world:remove()", function()
4540do
4551CASE("should allow remove a component that doesn't exist on entity")
4561local world = jecs.World.new()
457N/A
4581local Health = world:component()
4591local Poison = world:component()
460N/A
4611local id = world:entity()
4620do
4631world:remove(id, Poison)
4641CHECK(true) -- Didn't error
465N/Aend
466N/A
4671world:set(id, Health, 50)
4681world:remove(id, Poison)
469N/A
4701CHECK(world:get(id, Poison) == nil)
4711CHECK(world:get(id, Health) == 50)
472N/Aend
473N/Aend)
474N/A
4751TEST("world:add()", function()
4760do
4771CASE("idempotent")
4781local world = jecs.World.new()
4791local d = debug_world_inspect(world)
4801local _1, _2 = world:component(), world:component()
481N/A
4821local e = world:entity()
4831world:add(e, _1)
4841world:add(e, _2)
4851world:add(e, _2) -- should have 0 effects
4861CHECK(d.archetype(e) == "1_2")
487N/Aend
488N/A
4890do
4901CASE("archetype move")
4910do
4921local world = jecs.World.new()
493N/A
4941local d = debug_world_inspect(world)
495N/A
4961local _1 = world:component()
4971local e = world:entity()
498N/A-- An entity starts without an archetype or row
499N/A-- should therefore not need to copy over data
5001CHECK(d.tbl(e) == nil)
5011CHECK(d.row(e) == nil)
502N/A
5031local archetypes = #world.archetypes
504N/A-- This should create a new archetype
5051world:add(e, _1)
5061CHECK(#world.archetypes == archetypes + 1)
507N/A
5081CHECK(d.archetype(e) == "1")
5091CHECK(d.tbl(e))
510N/Aend
511N/Aend
512N/Aend)
513N/A
5141TEST("world:query()", function()
5151do CASE "cached"
5161local world = world_new()
5171local Foo = world:component()
5181local Bar = world:component()
5191local Baz = world:component()
5201local e = world:entity()
5211local q = world:query(Foo, Bar):without(Baz):cached()
5221world:set(e, Foo, true)
5231world:set(e, Bar, false)
5241local i = 0
525N/A
5261local iter = 0
5271for _, e in q:iter() do
5281iter += 1
5291i=1
530N/Aend
5311CHECK (iter == 1)
5321CHECK(i == 1)
5331for _, e in q:iter() do
5341i=2
535N/Aend
5361CHECK(i == 2)
5371for _, e in q :: any do
5381i=3
539N/Aend
5401CHECK(i == 3)
5411for _, e in q :: any do
5421i=4
543N/Aend
5441CHECK(i == 4)
545N/A
5461CHECK(#q:archetypes() == 1)
5471CHECK(not table.find(q:archetypes(), world.archetype_index[table.concat({Foo, Bar, Baz}, "_")]))
5481world:delete(Foo)
5491CHECK(#q:archetypes() == 0)
550N/Aend
5511do CASE "multiple iter"
5521local world = jecs.World.new()
5531local A = world:component()
5541local B = world:component()
5551local e = world:entity()
5561world:add(e, A)
5571world:add(e, B)
5581local q = world:query(A, B)
5591local counter = 0
5601for x in q:iter() do
5611counter += 1
562N/Aend
5631for x in q:iter() do
5641counter += 1
565N/Aend
5661CHECK(counter == 2)
567N/Aend
5681do CASE "tag"
5691local world = jecs.World.new()
5701local A = world:entity()
5711local e = world:entity()
5721CHECK_EXPECT_ERR(function()
5731world:set(e, A, "test" :: any)
574N/Aend)
5751local count = 0
5761for id, a in world:query(A) :: any do
5771count += 1
5781CHECK(a == nil)
579N/Aend
5801CHECK(count == 1)
581N/Aend
5821do CASE "pairs"
5831local world = jecs.World.new()
584N/A
5851local C1 = world:component() :: jecs.Id
5861local C2 = world:component() :: jecs.Id
5871local T1 = world:entity()
5881local T2 = world:entity()
589N/A
5901local e = world:entity()
591N/A
5921local C1_C2 = pair(C1, C2)
5931world:set(e, C1_C2, true)
5941world:set(e, pair(C1, T1), true)
5951world:set(e, pair(T1, C1), true)
5961CHECK_EXPECT_ERR(function()
5971world:set(e, pair(T1, T2), true :: any)
598N/Aend)
599N/A
6001for id, a, b, c, d in world:query(pair(C1, C2), pair(C1, T1), pair(T1, C1), pair(T1, T2)):iter() do
6011CHECK(a == true)
6021CHECK(b == true)
6031CHECK(c == true)
6041CHECK(d == nil)
605N/Aend
606N/Aend
6070do
6081CASE("query single component")
6090do
6101local world = jecs.World.new()
6111local A = world:component()
6121local B = world:component()
613N/A
6141local entities = {}
6151for i = 1, N do
616256local id = world:entity()
617N/A
618256world:set(id, A, true)
619256if i > 5 then
620251world:set(id, B, true)
621N/Aend
622256entities[i] = id
623N/Aend
624N/A
6251for id in world:query(A) :: any do
626256table.remove(entities, CHECK(table.find(entities, id)))
627N/Aend
628N/A
6291CHECK(#entities == 0)
630N/Aend
631N/A
6320do
6331local world = jecs.World.new() :: World
6341local A = world:component()
6351local B = world:component()
6361local eA = world:entity()
6371world:set(eA, A, true)
6381local eB = world:entity()
6391world:set(eB, B, true)
6401local eAB = world:entity()
6411world:set(eAB, A, true)
6421world:set(eAB, B, true)
643N/A
644N/A-- Should drain the iterator
6451local q = world:query(A)
646N/A
6471local i = 0
6481local j = 0
6491for _ in q :: any do
6502i += 1
651N/Aend
6521for _ in q :: any do
6530j += 1
654N/Aend
6551CHECK(i == 2)
6561CHECK(j == 0)
657N/Aend
658N/Aend
659N/A
6600do
6611CASE("query missing component")
6621local world = jecs.World.new()
6631local A = world:component()
6641local B = world:component()
6651local C = world:component()
666N/A
6671local e1 = world:entity()
6681local e2 = world:entity()
669N/A
6701world:set(e1, A, "abc")
6711world:set(e2, A, "def")
6721world:set(e1, B, 123)
6731world:set(e2, B, 457)
674N/A
6751local counter = 0
6761for _ in world:query(B, C) :: any do
6770counter += 1
678N/Aend
6791CHECK(counter == 0)
680N/Aend
681N/A
6820do
6831CASE("query more than 8 components")
6841local world = jecs.World.new()
6851local components = {}
686N/A
6871for i = 1, 9 do
6889local id = world:component()
6899components[i] = id
690N/Aend
6911local e = world:entity()
6921for i, id in components do
6939world:set(e, id, 13 ^ i)
694N/Aend
695N/A
6961for entity, a, b, c, d, e, f, g, h, i in world:query(unpack(components)) :: any do
6971CHECK(a == 13 ^ 1)
6981CHECK(b == 13 ^ 2)
6991CHECK(c == 13 ^ 3)
7001CHECK(d == 13 ^ 4)
7011CHECK(e == 13 ^ 5)
7021CHECK(f == 13 ^ 6)
7031CHECK(g == 13 ^ 7)
7041CHECK(h == 13 ^ 8)
7051CHECK(i == 13 ^ 9)
706N/Aend
707N/Aend
708N/A
7090do
7101CASE("should be able to get next results")
7111local world = jecs.World.new() :: World
7121world:component()
7131local A = world:component()
7141local B = world:component()
7151local eA = world:entity()
7161world:set(eA, A, true)
7171local eB = world:entity()
7181world:set(eB, B, true)
7191local eAB = world:entity()
7201world:set(eAB, A, true)
7211world:set(eAB, B, true)
722N/A
7231local it = world:query(A):iter()
724N/A
7251local e: number, data = it()
7261while e do
7272if e == eA :: number then
7281CHECK(data)
7291elseif e == eAB :: number then
7301CHECK(data)
7310else
7320CHECK(false)
733N/Aend
734N/A
7352e, data = it()
736N/Aend
7371CHECK(true)
738N/Aend
739N/A
7401do CASE "should query all matching entities when irrelevant component is removed"
7411local world = jecs.World.new()
7421local A = world:component()
7431local B = world:component()
7441local C = world:component()
745N/A
7461local entities = {}
7471for i = 1, N do
748256local id = world:entity()
749N/A
750N/A-- specifically put them in disorder to track regression
751N/A-- https://github.com/Ukendio/jecs/pull/15
752256world:set(id, B, true)
753256world:set(id, A, true)
754256if i > 5 then
755251world:remove(id, B)
756N/Aend
757256entities[i] = id
758N/Aend
759N/A
7601local added = 0
7611for id in world:query(A) :: any do
762256added += 1
763256table.remove(entities, CHECK(table.find(entities, id)))
764N/Aend
765N/A
7661CHECK(added == N)
767N/Aend
768N/A
7690do
7701CASE("should query all entities without B")
7711local world = jecs.World.new()
7721local A = world:component()
7731local B = world:component()
774N/A
7751local entities = {}
7761for i = 1, N do
777256local id = world:entity()
778N/A
779256world:set(id, A, true)
780256if i < 5 then
7814entities[i] = id
7820else
783252world:set(id, B, true)
784N/Aend
785N/Aend
786N/A
7871for id in world:query(A):without(B) :: any do
7884table.remove(entities, CHECK(table.find(entities, id)))
789N/Aend
790N/A
7911CHECK(#entities == 0)
792N/Aend
793N/A
7940do
7951CASE("should allow querying for relations")
7961local world = jecs.World.new()
7971local Eats = world:component()
7981local Apples = world:component()
7991local bob = world:entity()
800N/A
8011world:set(bob, pair(Eats, Apples), true)
8021for e, bool in world:query(pair(Eats, Apples)) :: any do
8031CHECK(e == bob)
8041CHECK(bool)
805N/Aend
806N/Aend
807N/A
8080do
8091CASE("should allow wildcards in queries")
8101local world = jecs.World.new()
8111local Eats = world:component()
8121local Apples = world:entity()
8131local bob = world:entity()
814N/A
8151world:set(bob, pair(Eats, Apples), "bob eats apples")
816N/A
8171local w = jecs.Wildcard
8181for e, data in world:query(pair(Eats, w)) :: any do
8191CHECK(e == bob)
8201CHECK(data == "bob eats apples")
821N/Aend
8221for e, data in world:query(pair(w, Apples)) :: any do
8231CHECK(e == bob)
8241CHECK(data == "bob eats apples")
825N/Aend
826N/Aend
827N/A
8280do
8291CASE("should match against multiple pairs")
8301local world = jecs.World.new()
8311local Eats = world:component()
8321local Apples = world:entity()
8331local Oranges = world:entity()
8341local bob = world:entity()
8351local alice = world:entity()
836N/A
8371world:set(bob, pair(Eats, Apples), "bob eats apples")
8381world:set(alice, pair(Eats, Oranges), "alice eats oranges")
839N/A
8401local w = jecs.Wildcard
8411local count = 0
8421for e, data in world:query(pair(Eats, w)) :: any do
8432count += 1
8442if e == bob then
8451CHECK(data == "bob eats apples")
8460else
8471CHECK(data == "alice eats oranges")
848N/Aend
849N/Aend
850N/A
8511CHECK(count == 2)
8521count = 0
853N/A
8541for e, data in world:query(pair(w, Apples)) :: any do
8551count += 1
8561CHECK(data == "bob eats apples")
857N/Aend
8581CHECK(count == 1)
859N/Aend
860N/A
8611do CASE "should only relate alive entities"
8621local world = jecs.World.new()
8631local Eats = world:entity()
8641local Apples = world:component()
8651local Oranges = world:component()
8661local bob = world:entity()
8671local alice = world:entity()
868N/A
8691world:set(bob, Apples, "apples")
8701world:set(bob, pair(Eats, Apples), "bob eats apples")
8711world:set(alice, pair(Eats, Oranges) :: Entity, "alice eats oranges")
872N/A
8731world:delete(Apples)
8741local Wildcard = jecs.Wildcard
875N/A
8761local count = 0
8771for _, data in world:query(pair(Wildcard, Apples)) :: any do
8780count += 1
879N/Aend
880N/A
8811world:delete(pair(Eats, Apples))
882N/A
8831CHECK(count == 0)
8841CHECK(world:get(bob, pair(Eats, Apples)) == nil)
885N/A
886N/Aend
887N/A
8880do
8891CASE("should error when setting invalid pair")
8901local world = jecs.World.new()
8911local Eats = world:component()
8921local Apples = world:component()
8931local bob = world:entity()
894N/A
8951world:delete(Apples)
8961CHECK_EXPECT_ERR(function()
8971world:set(bob, pair(Eats, Apples), "bob eats apples")
898N/Aend)
899N/Aend
900N/A
9010do
9021CASE("should find target for ChildOf")
9031local world = jecs.World.new()
9041local ChildOf = jecs.ChildOf
905N/A
9061local Name = world:component()
907N/A
9081local bob = world:entity()
9091local alice = world:entity()
9101local sara = world:entity()
911N/A
9121world:add(bob, pair(ChildOf, alice))
9131world:set(bob, Name, "bob")
9141world:add(sara, pair(ChildOf, alice))
9151world:set(sara, Name, "sara")
9161CHECK(world:parent(bob) :: number == alice :: number) -- O(1)
917N/A
9181local count = 0
9191for _, name in world:query(Name, pair(ChildOf, alice)) :: any do
9202count += 1
921N/Aend
9221CHECK(count == 2)
923N/Aend
924N/A
9250do
9261CASE("despawning while iterating")
9271local world = jecs.World.new()
9281local A = world:component()
9291local B = world:component()
930N/A
9311local e1 = world:entity()
9321local e2 = world:entity()
9331world:add(e1, A)
9341world:add(e2, A)
9351world:add(e2, B)
936N/A
9371local count = 0
9381for id in world:query(A) :: any do
9392world:clear(id)
9402count += 1
941N/Aend
9421CHECK(count == 2)
943N/Aend
944N/A
9451do CASE("iterator invalidation")
9461do CASE("adding")
9471SKIP()
9481local world = jecs.World.new()
9491local A = world:component()
9501local B = world:component()
951N/A
9521local e1 = world:entity()
9531local e2 = world:entity()
9541world:add(e1, A)
9551world:add(e2, A)
9561world:add(e2, B)
957N/A
9581local count = 0
9591for id in world:query(A) :: any do
9603world:add(id, B)
961N/A
9623count += 1
963N/Aend
964N/A
9651CHECK(count == 2)
966N/Aend
967N/A
9681do CASE("spawning")
9691local world = jecs.World.new()
9701local A = world:component()
9711local B = world:component()
972N/A
9731local e1 = world:entity()
9741local e2 = world:entity()
9751world:add(e1, A)
9761world:add(e2, A)
9771world:add(e2, B)
978N/A
9791for id in world:query(A) :: any do
9803local e = world:entity()
9813world:add(e, A)
9823world:add(e, B)
983N/Aend
984N/A
9851CHECK(true)
986N/Aend
987N/Aend
988N/A
9891do CASE("should not find any entities")
9901local world = jecs.World.new()
991N/A
9921local Hello = world:component()
9931local Bob = world:component()
994N/A
9951local helloBob = world:entity()
9961world:add(helloBob, pair(Hello, Bob))
9971world:add(helloBob, Bob)
998N/A
9991local withoutCount = 0
10001for _ in world:query(pair(Hello, Bob)):without(Bob) :: any do
10010withoutCount += 1
1002N/Aend
1003N/A
10041CHECK(withoutCount == 0)
1005N/Aend
1006N/A
10071do CASE("without")
1008N/A-- REGRESSION TEST
10091local world = jecs.World.new()
10101local _1, _2, _3 = world:component(), world:component(), world:component()
1011N/A
10121local counter = 0
10131for e, a, b in world:query(_1, _2):without(_3) :: any do
10140counter += 1
1015N/Aend
10161CHECK(counter == 0)
1017N/Aend
1018N/Aend)
1019N/A
10201TEST("world:each", function()
10211local world = world_new()
10221local A = world:component()
10231local B = world:component()
10241local C = world:component()
1025N/A
10261local e3 = world:entity()
10271local e1 = world:entity()
10281local e2 = world:entity()
1029N/A
10301world:set(e1, A, true)
1031N/A
10321world:set(e2, A, true)
10331world:set(e2, B, true)
1034N/A
10351world:set(e3, A, true)
10361world:set(e3, B, true)
10371world:set(e3, C, true)
1038N/A
10391for entity: number in world:each(A) do
10403if entity == e1 :: number or entity == e2 :: number or entity == e3 :: number then
10413CHECK(true)
10423continue
1043N/Aend
10440CHECK(false)
1045N/Aend
1046N/Aend)
1047N/A
10481TEST("world:children", function()
10491local world = world_new()
10501local C = world:component()
10511local T = world:entity()
1052N/A
10531local e1 = world:entity()
10541world:set(e1, C, true)
1055N/A
10561local e2 = world:entity() :: number
1057N/A
10581world:add(e2, T)
10591world:add(e2, pair(ChildOf, e1))
1060N/A
10611local e3 = world:entity() :: number
10621world:add(e3, pair(ChildOf, e1))
1063N/A
10641local count = 0
10651for entity: number in world:children(e1) do
10662count += 1
10672if entity == e2 or entity == e3 then
10682CHECK(true)
10692continue
1070N/Aend
10710CHECK(false)
1072N/Aend
10731CHECK(count == 2)
1074N/A
10751world:remove(e2, pair(ChildOf, e1))
1076N/A
10771count = 0
10781for entity in world:children(e1) do
10791count += 1
1080N/Aend
1081N/A
10821CHECK(count == 1)
1083N/Aend)
1084N/A
10851TEST("world:clear()", function()
10861do CASE("should remove its components")
10871local world = jecs.World.new() :: World
10881local A = world:component()
10891local B = world:component()
10901local C = world:component()
10911local D = world:component()
1092N/A
10931local e = world:entity()
10941local e1 = world:entity()
10951local e2 = world:entity()
1096N/A
10971world:set(e, A, true)
10981world:set(e, B, true)
1099N/A
11001world:set(e1, A, true)
11011world:set(e1, B, true)
1102N/A
11031CHECK(world:get(e, A))
11041CHECK(world:get(e, B))
1105N/A
11061world:clear(A)
11071CHECK(world:get(e, A) == nil)
11081CHECK(world:get(e, B))
11091CHECK(world:get(e1, A) == nil)
11101CHECK(world:get(e1, B))
1111N/Aend
1112N/A
11131do CASE("remove cleared ID from entities")
11141local world = world_new()
11151local A = world:component()
11161local B = world:component()
11171local C = world:component()
1118N/A
11190do
11201local id1 = world:entity()
11211local id2 = world:entity()
11221local id3 = world:entity()
1123N/A
11241world:set(id1, A, true)
1125N/A
11261world:set(id2, A, true)
11271world:set(id2, B, true)
1128N/A
11291world:set(id3, A, true)
11301world:set(id3, B, true)
11311world:set(id3, C, true)
1132N/A
11331world:clear(A)
1134N/A
11351CHECK(not world:has(id1, A))
11361CHECK(not world:has(id2, A))
11371CHECK(not world:has(id3, A))
1138N/A
11391CHECK(world:has(id2, B))
11401CHECK(world:has(id3, B, C))
1141N/A
11421world:clear(C)
1143N/A
11441CHECK(world:has(id2, B))
11451CHECK(world:has(id3, B))
1146N/A
11471CHECK(world:contains(A))
11481CHECK(world:contains(C))
11491CHECK(world:has(A, jecs.Component))
11501CHECK(world:has(B, jecs.Component))
1151N/Aend
1152N/A
11530do
11541local id1 = world:entity()
11551local id2 = world:entity()
11561local id3 = world:entity()
1157N/A
11581local tgt = world:entity()
1159N/A
11601world:add(id1, pair(A, tgt))
11611world:add(id1, pair(B, tgt))
11621world:add(id1, pair(C, tgt))
1163N/A
11641world:add(id2, pair(A, tgt))
11651world:add(id2, pair(B, tgt))
11661world:add(id2, pair(C, tgt))
1167N/A
11681world:add(id3, pair(A, tgt))
11691world:add(id3, pair(B, tgt))
11701world:add(id3, pair(C, tgt))
1171N/A
11721world:clear(B)
11731CHECK(world:has(id1, pair(A, tgt), pair(C, tgt)))
11741CHECK(not world:has(id1, pair(B, tgt)))
11751CHECK(world:has(id2, pair(A, tgt), pair(C, tgt)))
11761CHECK(not world:has(id1, pair(B, tgt)))
11771CHECK(world:has(id3, pair(A, tgt), pair(C, tgt)))
1178N/A
1179N/Aend
1180N/A
1181N/Aend
1182N/Aend)
1183N/A
11841TEST("world:has()", function()
11851do CASE("should find Tag on entity")
11861local world = jecs.World.new()
1187N/A
11881local Tag = world:entity()
1189N/A
11901local e = world:entity()
11911world:add(e, Tag)
1192N/A
11931CHECK(world:has(e, Tag))
1194N/Aend
1195N/A
11961do CASE("should return false when missing one tag")
11971local world = jecs.World.new()
1198N/A
11991local A = world:entity()
12001local B = world:entity()
12011local C = world:entity()
12021local D = world:entity()
1203N/A
12041local e = world:entity()
12051world:add(e, A)
12061world:add(e, C)
12071world:add(e, D)
1208N/A
12091CHECK(world:has(e, A, B, C, D) == false)
1210N/Aend
1211N/Aend)
1212N/A
12131TEST("world:component()", function()
12141do CASE("only components should have EcsComponent trait")
12151local world = jecs.World.new() :: World
12161local A = world:component()
12171local e = world:entity()
1218N/A
12191CHECK(world:has(A, jecs.Component))
12201CHECK(not world:has(e, jecs.Component))
1221N/Aend
1222N/A
12231do CASE("tag")
12241local world = jecs.World.new() :: World
12251local A = world:component()
12261local B = world:entity()
12271local C = world:entity()
12281local e = world:entity()
12291world:set(e, A, "test")
12301world:add(e, B)
12311CHECK_EXPECT_ERR(function()
12321world:set(e, C, 11 :: any)
1233N/Aend)
1234N/A
12351CHECK(world:has(e, A))
12361CHECK(world:get(e, A) == "test")
12371CHECK(world:get(e, B) == nil)
12381CHECK(world:get(e, C) == nil)
1239N/Aend
1240N/Aend)
1241N/A
12421TEST("world:delete", function()
12431do CASE "invoke OnRemove hooks"
12441local world = world_new()
1245N/A
12461local e1 = world:entity()
12471local e2 = world:entity()
1248N/A
12491local Stable = world:component()
12501world:set(Stable, jecs.OnRemove, function(e)
12511CHECK(e == e1)
1252N/Aend)
1253N/A
12541world:set(e1, Stable, true)
12551world:set(e2, Stable, true)
1256N/A
12571world:delete(e1)
1258N/Aend
12591do CASE "delete recycled entity id used as component"
12601local world = world_new()
12611local id = world:entity()
12621world:add(id, jecs.Component)
1263N/A
12641local e = world:entity()
12651world:set(e, id, 1)
12661CHECK(world:get(e, id) == 1)
12671world:delete(id)
12681local recycled = world:entity()
12691world:add(recycled, jecs.Component)
12701world:set(e, recycled, 1)
12711CHECK(world:has(recycled, jecs.Component))
12721CHECK(world:get(e, recycled) == 1)
1273N/Aend
12740do
12751CASE("bug: Empty entity does not respect cleanup policy")
12761local world = world_new()
12771local parent = world:entity()
12781local tag = world:entity()
1279N/A
12801local child = world:entity()
12811world:add(child, jecs.pair(jecs.ChildOf, parent))
12821world:delete(parent)
1283N/A
12841CHECK(not world:contains(parent))
12851CHECK(not world:contains(child))
1286N/A
12871local entity = world:entity()
12881world:add(entity, tag)
12891world:delete(tag)
12901CHECK(world:contains(entity))
12911CHECK(not world:contains(tag))
12921CHECK(not world:has(entity, tag)) -- => true
1293N/Aend
12941do CASE("should allow deleting components")
12951local world = jecs.World.new()
1296N/A
12971local Health = world:component()
12981local Poison = world:component()
1299N/A
13001local id = world:entity()
13011world:set(id, Poison, 5)
13021world:set(id, Health, 50)
13031local id1 = world:entity()
13041world:set(id1, Poison, 500)
13051world:set(id1, Health, 50)
1306N/A
13071world:delete(id)
13081CHECK(not world:contains(id))
13091CHECK(world:get(id, Poison) == nil)
13101CHECK(world:get(id, Health) == nil)
1311N/A
13121CHECK(world:get(id1, Poison) == 500)
13131CHECK(world:get(id1, Health) == 50)
1314N/Aend
1315N/A
13161do CASE("delete entities using another Entity as component with Delete cleanup action")
13171local world = jecs.World.new()
1318N/A
13191local Health = world:entity()
13201world:add(Health, pair(jecs.OnDelete, jecs.Delete))
13211local Poison = world:component()
1322N/A
13231local id = world:entity()
13241world:set(id, Poison, 5)
13251CHECK_EXPECT_ERR(function()
13261world:set(id, Health, 50 :: any)
1327N/Aend)
13281local id1 = world:entity()
13291world:set(id1, Poison, 500)
13301CHECK_EXPECT_ERR(function()
13311world:set(id1, Health, 50 :: any)
1332N/Aend)
1333N/A
13341CHECK(world:has(id, Poison, Health))
13351CHECK(world:has(id1, Poison, Health))
13361world:delete(Poison)
1337N/A
13381CHECK(world:contains(id))
13391CHECK(not world:has(id, Poison))
13401CHECK(not world:has(id1, Poison))
1341N/A
13421world:delete(Health)
13431CHECK(not world:contains(id))
13441CHECK(not world:contains(id1))
13451CHECK(not world:has(id, Health))
13461CHECK(not world:has(id1, Health))
1347N/Aend
1348N/A
1349N/A
13501do CASE("delete children")
13511local world = jecs.World.new()
1352N/A
13531local Health = world:component()
13541local Poison = world:component()
13551local FriendsWith = world:component()
1356N/A
13571local e = world:entity()
13581world:set(e, Poison, 5)
13591world:set(e, Health, 50)
1360N/A
13611local children = {}
13621for i = 1, 10 do
136310local child = world:entity()
136410world:set(child, Poison, 9999)
136510world:set(child, Health, 100)
136610world:add(child, pair(jecs.ChildOf, e))
136710table.insert(children, child)
1368N/Aend
1369N/A
13701BENCH("delete children of entity", function()
13711world:delete(e)
1372N/Aend)
1373N/A
13741for i, child in children do
137510CHECK(not world:contains(child))
137610CHECK(not world:has(child, pair(jecs.ChildOf, e)))
137710CHECK(not world:has(child, Health))
1378N/Aend
1379N/A
13801e = world:entity()
1381N/A
13821local friends = {}
13831for i = 1, 10 do
138410local friend = world:entity()
138510world:set(friend, Poison, 9999)
138610world:set(friend, Health, 100)
138710world:add(friend, pair(FriendsWith, e))
138810table.insert(friends, friend)
1389N/Aend
1390N/A
13911BENCH("remove friends of entity", function()
13921world:delete(e)
1393N/Aend)
1394N/A
13951local d = debug_world_inspect(world)
13961for i, friend in friends do
139710CHECK(not world:has(friend, pair(FriendsWith, e)))
139810CHECK(world:has(friend, Health))
139910CHECK(world:contains(friend))
1400N/Aend
1401N/Aend
1402N/A
14031do CASE("remove deleted ID from entities")
14041local world = world_new()
14050do
14061local A = world:component()
14071local B = world:component()
14081local C = world:component()
14091local id1 = world:entity()
14101local id2 = world:entity()
14111local id3 = world:entity()
1412N/A
14131world:set(id1, A, true)
1414N/A
14151world:set(id2, A, true)
14161world:set(id2, B, true)
1417N/A
14181world:set(id3, A, true)
14191world:set(id3, B, true)
14201world:set(id3, C, true)
1421N/A
14221world:delete(A)
1423N/A
14241CHECK(not world:has(id1, A))
14251CHECK(not world:has(id2, A))
14261CHECK(not world:has(id3, A))
1427N/A
14281CHECK(world:has(id2, B))
14291CHECK(world:has(id3, B, C))
1430N/A
14311world:delete(C)
1432N/A
14331CHECK(world:has(id2, B))
14341CHECK(world:has(id3, B))
1435N/A
14361CHECK(not world:contains(A))
14371CHECK(not world:contains(C))
1438N/Aend
1439N/A
14400do
14411local A = world:component()
14421world:add(A, pair(jecs.OnDeleteTarget, jecs.Delete))
14431local B = world:component()
14441local C = world:component()
14451world:add(C, pair(jecs.OnDeleteTarget, jecs.Delete))
1446N/A
14471local id1 = world:entity()
14481local id2 = world:entity()
14491local id3 = world:entity()
1450N/A
14511world:set(id1, C, true)
1452N/A
14531world:set(id2, pair(A, id1), true)
14541world:set(id2, B, true)
1455N/A
14561world:set(id3, B, true)
14571world:set(id3, pair(C, id2), true)
1458N/A
14591world:delete(id1)
1460N/A
14611CHECK(not world:contains(id1))
14621CHECK(not world:contains(id2))
14631CHECK(not world:contains(id3))
1464N/Aend
1465N/A
1466N/A
14670do
14681local A = world:component()
14691local B = world:component()
14701local C = world:component()
14711local id1 = world:entity()
14721local id2 = world:entity()
14731local id3 = world:entity()
1474N/A
1475N/A
14761world:set(id2, A, true)
14771world:set(id2, pair(B, id1), true)
1478N/A
14791world:set(id3, A, true)
14801world:set(id3, pair(B, id1), true)
14811world:set(id3, C, true)
1482N/A
14831world:delete(id1)
1484N/A
14851CHECK(not world:contains(id1))
14861CHECK(world:contains(id2))
14871CHECK(world:contains(id3))
1488N/A
14891CHECK(world:has(id2, A))
14901CHECK(world:has(id3, A, C))
1491N/A
14921CHECK(not world:target(id2, B))
14931CHECK(not world:target(id3, B))
1494N/Aend
1495N/Aend
1496N/A
14970do
14981CASE("fast delete")
14991local world = jecs.World.new()
1500N/A
15011local entities = {}
15021local Health = world:component()
15031local Poison = world:component()
1504N/A
15051for i = 1, 100 do
1506100local child = world:entity()
1507100world:set(child, Poison, 9999)
1508100world:set(child, Health, 100)
1509100table.insert(entities, child)
1510N/Aend
1511N/A
15121BENCH("simple deletion of entity", function()
15131for i = 1, START(100) do
1514100local e = entities[i]
1515100world:delete(e)
1516N/Aend
1517N/Aend)
1518N/A
15191for _, entity in entities do
1520100CHECK(not world:contains(entity))
1521N/Aend
1522N/Aend
1523N/A
15240do
15251CASE("cycle")
15261local world = jecs.World.new()
15271local Likes = world:component()
15281world:add(Likes, pair(jecs.OnDeleteTarget, jecs.Delete))
15291local bob = world:entity()
15301local alice = world:entity()
1531N/A
15321world:add(bob, pair(Likes, alice))
15331world:add(alice, pair(Likes, bob))
1534N/A
15351world:delete(bob)
15361CHECK(not world:contains(bob))
15371CHECK(not world:contains(alice))
1538N/Aend
1539N/Aend)
1540N/A
15411TEST("world:target", function()
15421do CASE("nth index")
15431local world = world_new()
15441local A = world:component()
15451world:set(A, jecs.Name, "A")
15461local B = world:component()
15471world:set(B, jecs.Name, "B")
15481local C = world:component()
15491world:set(C, jecs.Name, "C")
15501local D = world:component()
15511world:set(D, jecs.Name, "D")
15521local E = world:component()
15531world:set(E, jecs.Name, "E")
15541local e = world:entity()
1555N/A
15561world:add(e, pair(A, B))
15571world:add(e, pair(A, C))
15581world:add(e, pair(A, D))
15591world:add(e, pair(A, E))
15601world:add(e, pair(B, C))
15611world:add(e, pair(B, D))
15621world:add(e, pair(C, D))
1563N/A
15641CHECK(pair(A, B) < pair(A, C))
15651CHECK(pair(A, C) < pair(A, D))
15661CHECK(pair(C, A) < pair(C, D))
1567N/A
15681local records = debug_world_inspect(world).records(e)
15691CHECK(jecs.pair_first(world, pair(B, C)) == B)
15701local r = jecs.entity_index_try_get(world.entity_index, e)
15711local archetype = r.archetype
15721local counts = archetype.counts
15731CHECK(counts[pair(A, __)] == 4)
15741CHECK(records[pair(B, C)] > records[pair(A, E)])
15751CHECK(world:target(e, A, 0) == B)
15761CHECK(world:target(e, A, 1) == C)
15771CHECK(world:target(e, A, 2) == D)
15781CHECK(world:target(e, A, 3) == E)
15791CHECK(world:target(e, B, 0) == C)
15801CHECK(world:target(e, B, 1) == D)
15811CHECK(world:target(e, C, 0) == D)
15821CHECK(world:target(e, C, 1) == nil)
1583N/A
15841CHECK(archetype.records[pair(A, B)] == 1)
15851CHECK(archetype.records[pair(A, C)] == 2)
15861CHECK(archetype.records[pair(A, D)] == 3)
15871CHECK(archetype.records[pair(A, E)] == 4)
1588N/A
15891CHECK(world:target(e, C, 0) == D)
15901CHECK(world:target(e, C, 1) == nil)
1591N/Aend
1592N/A
15930do
15941CASE("infer index when unspecified")
15951local world = world_new()
15961local A = world:component()
15971local B = world:component()
15981local C = world:component()
15991local D = world:component()
16001local e = world:entity()
1601N/A
16021world:add(e, pair(A, B))
16031world:add(e, pair(A, C))
16041world:add(e, pair(B, C))
16051world:add(e, pair(B, D))
16061world:add(e, pair(C, D))
1607N/A
16081CHECK(world:target(e, A) == world:target(e, A, 0))
16091CHECK(world:target(e, B) == world:target(e, B, 0))
16101CHECK(world:target(e, C) == world:target(e, C, 0))
1611N/Aend
1612N/A
16130do
16141CASE("loop until no target")
16151local world = world_new()
1616N/A
16171local ROOT = world:entity()
16181local e1 = world:entity()
16191local targets = {}
1620N/A
16211for i = 1, 10 do
162210local target = world:entity()
162310targets[i] = target
162410world:add(e1, pair(ROOT, target))
1625N/Aend
1626N/A
16271local i = 0
16281local target = world:target(e1, ROOT, 0)
16291while target do
163010i += 1
163110CHECK(targets[i] == target)
163210target = world:target(e1, ROOT, i)
1633N/Aend
1634N/A
16351CHECK(i == 10)
1636N/Aend
1637N/Aend)
1638N/A
16391TEST("world:contains", function()
16401local world = jecs.World.new()
16411local id = world:entity()
16421CHECK(world:contains(id))
1643N/A
16440do
16451CASE("should not exist after delete")
16461world:delete(id)
16471CHECK(not world:contains(id))
1648N/Aend
1649N/Aend)
1650N/A
16511TEST("Hooks", function()
16521do CASE "OnAdd"
16531local world = jecs.World.new()
16541local Transform = world:component()
16551local e1 = world:entity()
16561world:set(Transform, jecs.OnAdd, function(entity)
16571CHECK(e1 == entity)
1658N/Aend)
16591world:add(e1, Transform)
1660N/Aend
1661N/A
16621do CASE "OnSet"
16631local world = jecs.World.new()
16641local Number = world:component()
16651local e1 = world:entity()
1666N/A
16671world:set(Number, jecs.OnSet, function(entity, data)
16681CHECK(e1 == entity)
16691CHECK(data == world:get(entity, Number))
16701CHECK(data == 1)
1671N/Aend)
16721world:set(e1, Number, 1)
1673N/Aend
1674N/A
16751do CASE("OnRemove")
16760do
1677N/A-- basic
16781local world = jecs.World.new()
16791local A = world:component() :: Entity
16801local e1 = world:entity()
16811world:set(A, jecs.OnRemove, function(entity)
16821CHECK(e1 == entity)
16831CHECK(world:has(e1, A))
1684N/Aend)
16851world:add(e1, A)
1686N/A
16871world:remove(e1, A)
16881CHECK(not world:has(e1, A))
1689N/Aend
16900do
1691N/A-- [BUG] https://github.com/Ukendio/jecs/issues/118
16921local world = world_new()
16931local A = world:component()
16941local B = world:component()
16951local e = world:entity()
1696N/A
16971world:set(A, jecs.OnRemove, function(entity)
16981world:set(entity, B, true)
16991CHECK(world:get(entity, A))
17001CHECK(world:get(entity, B))
1701N/Aend)
1702N/A
17031world:set(e, A, true)
17041world:remove(e, A)
17051CHECK(not world:get(e, A))
17061CHECK(world:get(e, B))
1707N/Aend
1708N/Aend
1709N/Aend)
1710N/A
17111TEST("change tracking", function()
17121do CASE "#1"
17131local world = world_new()
17141local Foo = world:component() :: Entity
17151local Previous = jecs.Rest
1716N/A
17171local q1 = world
17181:query(Foo)
17191:without(pair(Previous, Foo))
17200:cached()
1721N/A
17221local e1 = world:entity()
17231world:set(e1, Foo, 1)
17241local e2 = world:entity()
17251world:set(e2, Foo, 2)
1726N/A
17271local i = 0
17281for e, new in q1 :: any do
17292i += 1
17302world:set(e, pair(Previous, Foo), new)
1731N/Aend
1732N/A
17331CHECK(i == 2)
17341local j = 0
17351for e, new in q1 :: any do
17360j += 1
17370world:set(e, pair(Previous, Foo), new)
1738N/Aend
1739N/A
17401CHECK(j == 0)
1741N/Aend
1742N/A
17431do CASE "#2"
17441local world = world_new()
17451local component = world:component() :: Entity
17461local tag = world:entity()
17471local previous = jecs.Rest
1748N/A
17491local q1 = world:query(component):without(pair(previous, component), tag):cached()
1750N/A
17511local testEntity = world:entity()
1752N/A
17531world:set(testEntity, component, 10)
1754N/A
17551local i = 0
17561for entity, number in q1 :: any do
17571i += 1
17581world:add(testEntity, tag)
1759N/Aend
1760N/A
17611CHECK(i == 1)
1762N/A
17631for e, n in q1 :: any do
17640world:set(e, pair(previous, component), n)
1765N/Aend
1766N/Aend
1767N/A
1768N/Aend)
1769N/A
17701TEST("repro", function()
17711do CASE "#1"
17721local world = world_new()
17731local reproEntity = world:component()
17741local components = { Cooldown = world:component() :: jecs.Entity }
17751world:set(reproEntity, components.Cooldown, 2)
1776N/A
17771local function updateCooldowns(dt: number)
17782local toRemove = {}
1779N/A
17802local it = world:query(components.Cooldown):iter()
17812for id, cooldown in it do
17822cooldown -= dt
1783N/A
17842if cooldown <= 0 then
17851table.insert(toRemove, id)
1786N/A-- world:remove(id, components.Cooldown)
17870else
17881world:set(id, components.Cooldown, cooldown)
1789N/Aend
1790N/Aend
1791N/A
17922for _, id in toRemove do
17931world:remove(id, components.Cooldown)
17941CHECK(not world:get(id, components.Cooldown))
1795N/Aend
1796N/Aend
1797N/A
17981updateCooldowns(1.5)
17991updateCooldowns(1.5)
1800N/Aend
1801N/A
18021do CASE "#2" -- ISSUE #171
18031local world = world_new()
18041local component1 = world:component()
18051local tag1 = world:entity()
1806N/A
18071local query = world:query(component1):with(tag1):cached()
1808N/A
18091local entity = world:entity()
18101world:set(entity, component1, "some data")
1811N/A
18121local counter = 0
18131for x in query:iter() do
18140counter += 1
1815N/Aend
18161CHECK(counter == 0)
1817N/Aend
1818N/Aend)
1819N/A
18201TEST("wildcard query", function()
18211do CASE "#1"
18221local world = world_new()
18231local pair = jecs.pair
1824N/A
18251local Relation = world:entity()
18261local Wildcard = jecs.Wildcard
18271local A = world:entity()
1828N/A
18291local relationship = pair(Relation, Wildcard)
18301local query = world:query(relationship):cached()
1831N/A
18321local entity = world:entity()
1833N/A
18341local p = pair(Relation, A)
18351CHECK(jecs.pair_first(world, p) == Relation)
18361CHECK(jecs.pair_second(world, p) == A)
18371local w = dwi(world)
18381world:add(entity, pair(Relation, A))
1839N/A
18401local counter = 0
18411for e in query:iter() do
18421counter += 1
1843N/Aend
18441CHECK(counter == 1)
1845N/Aend
18461do CASE "#2"
18471local world = world_new()
18481local pair = jecs.pair
1849N/A
18501local Relation = world:entity()
18511local Wildcard = jecs.Wildcard
18521local A = world:entity()
1853N/A
18541local relationship = pair(Relation, Wildcard)
1855N/A
18561local entity = world:entity()
1857N/A
18581world:add(entity, pair(Relation, A))
1859N/A
18601local counter = 0
18611for e in world:query(relationship):iter() do
18621counter += 1
1863N/Aend
18641CHECK(counter == 1)
1865N/Aend
18661do CASE "#3"
18671local world = world_new()
18681local pair = jecs.pair
1869N/A
18701local Relation = world:entity()
18711local Wildcard = jecs.Wildcard
18721local A = world:entity()
1873N/A
18741local entity = world:entity()
1875N/A
18761world:add(entity, pair(Relation, A))
1877N/A
18781local relationship = pair(Relation, Wildcard)
18791local query = world:query(relationship):cached()
1880N/A
18811local counter = 0
18821for e in query:iter() do
18831counter += 1
1884N/Aend
18851CHECK(counter == 1)
1886N/Aend
1887N/Aend)
1888N/A
18891TEST("world:delete() invokes OnRemove hook", function()
18901do CASE "#1"
18911local world = world_new()
1892N/A
18931local A = world:entity()
18941local entity = world:entity()
1895N/A
18961local called = false
18971world:set(A, jecs.OnRemove, function(e)
18981called = true
1899N/Aend)
1900N/A
19011world:add(entity, A)
19021world:delete(entity)
1903N/A
19041CHECK(called)
1905N/Aend
19061do CASE "#2"
19071local world = world_new()
19081local pair = jecs.pair
1909N/A
19101local Relation = world:entity()
19111local A = world:entity()
19121local B = world:entity()
1913N/A
19141world:add(Relation, pair(jecs.OnDelete, jecs.Delete))
1915N/A
19161local entity = world:entity()
1917N/A
19181local called = false
19191world:set(A, jecs.OnRemove, function(e)
19201called = true
1921N/Aend)
1922N/A
19231world:add(entity, A)
19241world:add(entity, pair(Relation, B))
1925N/A
19261world:delete(B)
1927N/A
19281CHECK(called)
1929N/Aend
19301do CASE "#3"
19311local world = world_new()
19321local pair = jecs.pair
1933N/A
19341local viewingContainer = world:entity()
19351local character = world:entity()
19361local container = world:entity()
1937N/A
19381local called = false
19391world:set(viewingContainer, jecs.OnRemove, function(e)
19401called = true
1941N/Aend)
1942N/A
19431world:add(character, pair(viewingContainer, container))
1944N/A
19451world:delete(container)
1946N/A
19471CHECK(called)
1948N/Aend
1949N/Aend)
19501FINISH()