# tests.luau Coverage

## Total Execution Hits: 100

## Function Coverage Overview: 83.58%

## Function Coverage:

Function | Hits
--- | ---
| 1
white_underline:30 | 0
white:34 | 0
green:38 | 0
red:42 | 0
yellow:46 | 0
red_highlight:50 | 0
green_highlight:54 | 0
gray:58 | 0
pe:63 | 0
pp:68 | 0
debug_world_inspect:73 | 7
record:74 | 17
tbl:77 | 12
archetype:80 | 4
records:83 | 1
columns:86 | 1
row:89 | 5
tuple:94 | 1
name:116 | 0
:120 | 1
:136 | 1
:174 | 1
:184 | 1
:189 | 1
getTargets:192 | 2
setAttacksAndEats:212 | 3
:240 | 1
:256 | 1
:312 | 1
:386 | 1
:435 | 1
:446 | 1
:453 | 1
:475 | 1
:514 | 1
:572 | 1
:596 | 1
:896 | 1
:1020 | 1
:1048 | 1
:1085 | 1
:1184 | 1
:1213 | 1
:1231 | 1
:1242 | 1
:1250 | 1
:1325 | 1
:1330 | 1
:1370 | 1
:1391 | 1
:1512 | 1
:1541 | 1
:1639 | 1
:1651 | 1
:1656 | 1
:1667 | 1
:1681 | 1
:1697 | 1
:1711 | 1
:1770 | 1
updateCooldowns:1777 | 2
:1820 | 1
:1889 | 1
:1897 | 1
:1919 | 1
:1939 | 1

## Source Code:

Line | Hits | Code
--- | --- | ---
1 | 1 | local jecs = require("@jecs")
2 | N/A | >
3 | 1 | local testkit = require("@testkit")
4 | 1 | local BENCH, START = testkit.benchmark()
5 | 1 | local __ = jecs.Wildcard
6 | 1 | local ECS_ID, ECS_GENERATION = jecs.ECS_ID, jecs.ECS_GENERATION
7 | 1 | local ECS_GENERATION_INC = jecs.ECS_GENERATION_INC
8 | 1 | local IS_PAIR = jecs.IS_PAIR
9 | 1 | local pair = jecs.pair
10 | 1 | local ecs_pair_first = jecs.pair_first
11 | 1 | local ecs_pair_second = jecs.pair_second
12 | 1 | local entity_index_try_get_any = jecs.entity_index_try_get_any
13 | 1 | local entity_index_get_alive = jecs.entity_index_get_alive
14 | 1 | local entity_index_is_alive = jecs.entity_index_is_alive
15 | 1 | local ChildOf = jecs.ChildOf
16 | 1 | local world_new = jecs.World.new
17 | N/A | >
18 | 1 | local it = testkit.test()
19 | 1 | local TEST, CASE = it.TEST, it.CASE
20 | 1 | local CHECK, FINISH = it.CHECK, it.FINISH
21 | 1 | local SKIP, FOCUS = it.SKIP, it.FOCUS
22 | 1 | local CHECK_EXPECT_ERR = it.CHECK_EXPECT_ERR
23 | N/A | >
24 | 1 | local N = 2 ^ 8
25 | N/A | >
26 | 0 | type World = jecs.World
27 | 0 | type Entity = jecs.Entity
28 | N/A | >
29 | 1 | local c = {
30 | 1 | white_underline = function(s: any)
31 | 0 | return `\27[1;4m{s}\27[0m`
32 | N/A | end,>
33 | N/A | >
34 | 1 | white = function(s: any)
35 | 0 | return `\27[37;1m{s}\27[0m`
36 | N/A | end,>
37 | N/A | >
38 | 1 | green = function(s: any)
39 | 0 | return `\27[32;1m{s}\27[0m`
40 | N/A | end,>
41 | N/A | >
42 | 1 | red = function(s: any)
43 | 0 | return `\27[31;1m{s}\27[0m`
44 | N/A | end,>
45 | N/A | >
46 | 1 | yellow = function(s: any)
47 | 0 | return `\27[33;1m{s}\27[0m`
48 | N/A | end,>
49 | N/A | >
50 | 1 | red_highlight = function(s: any)
51 | 0 | return `\27[41;1;30m{s}\27[0m`
52 | N/A | end,>
53 | N/A | >
54 | 1 | green_highlight = function(s: any)
55 | 0 | return `\27[42;1;30m{s}\27[0m`
56 | N/A | end,>
57 | N/A | >
58 | 1 | gray = function(s: any)
59 | 0 | return `\27[30;1m{s}\27[0m`
60 | N/A | end,>
61 | 0 | }
62 | N/A | >
63 | 1 | local function pe(e)
64 | 0 | local gen = ECS_GENERATION(e)
65 | 0 | return c.green(`e{ECS_ID(e)}`)..c.yellow(`v{gen}`)
66 | N/A | end>
67 | N/A | >
68 | 1 | local function pp(e)
69 | 0 | local gen = ECS_GENERATION(e)
70 | 0 | return c.green(`e{ECS_ID(e)}`)..c.yellow(`v{jecs.ECS_ENTITY_T_HI(e)}`)
71 | N/A | end>
72 | N/A | >
73 | 1 | local function debug_world_inspect(world: World)
74 | 7 | local function record(e): jecs.Record
75 | 17 | return entity_index_try_get_any(world.entity_index, e) :: any
76 | N/A | end>
77 | 7 | local function tbl(e)
78 | 12 | return record(e).archetype
79 | N/A | end>
80 | 7 | local function archetype(e)
81 | 4 | return tbl(e).type
82 | N/A | end>
83 | 7 | local function records(e)
84 | 1 | return tbl(e).records
85 | N/A | end>
86 | 7 | local function columns(e)
87 | 1 | return tbl(e).columns
88 | N/A | end>
89 | 7 | local function row(e)
90 | 5 | return record(e).row
91 | N/A | end>
92 | N/A | >
93 | N/A | -- Important to order them in the order of their columns>
94 | 7 | local function tuple(e, ...)
95 | 1 | for i, column in columns(e) do
96 | 2 | if select(i, ...) ~= column[row(e)] then
97 | 0 | return false
98 | N/A | end>
99 | N/A | end>
100 | 1 | return true
101 | N/A | end>
102 | N/A | >
103 | 7 | return {
104 | 7 | record = record,
105 | 7 | tbl = tbl,
106 | 7 | archetype = archetype,
107 | 7 | records = records,
108 | 7 | row = row,
109 | 7 | tuple = tuple,
110 | 7 | columns = columns
111 | 0 | }
112 | N/A | end>
113 | N/A | >
114 | 1 | local dwi = debug_world_inspect
115 | N/A | >
116 | 1 | local function name(world, e)
117 | 0 | return world:get(e, jecs.Name)
118 | N/A | end>
119 | N/A | >
120 | 1 | TEST("#adding a recycled target", function()
121 | 1 | local world = world_new()
122 | 1 | local R = world:component()
123 | N/A | >
124 | 1 | local e = world:entity()
125 | 1 | local T = world:entity()
126 | 1 | world:add(e, pair(R, T))
127 | 1 | world:delete(T)
128 | 1 | CHECK(not world:has(e, pair(R, T)))
129 | 1 | local T2 = world:entity()
130 | 1 | world:add(e, pair(R, T2))
131 | 1 | CHECK(world:target(e, R) ~= T)
132 | 1 | CHECK(world:target(e, R) ~= 0)
133 | N/A | >
134 | N/A | end)>
135 | N/A | >
136 | 1 | TEST("#repro2", function()
137 | 1 | local world = world_new()
138 | 1 | local Lifetime = world:component() :: jecs.Id
139 | 1 | local Particle = world:entity()
140 | 1 | local Beam = world:entity()
141 | N/A | >
142 | 1 | local entity = world:entity()
143 | 1 | world:set(entity, pair(Lifetime, Particle), 1)
144 | 1 | world:set(entity, pair(Lifetime, Beam), 2)
145 | 1 | world:set(entity, pair(4, 5), 6) -- noise
146 | N/A | >
147 | 1 | local entity_visualizer = require("@tools/entity_visualiser")
148 | N/A | -- entity_visualizer.components(world, entity)>
149 | N/A | >
150 | 1 | for e in world:each(pair(Lifetime, __)) do
151 | 2 | local i = 0
152 | 2 | local nth = world:target(e, Lifetime, i)
153 | 2 | while nth do
154 | N/A | -- entity_visualizer.components(world, e)>
155 | N/A | >
156 | 2 | local data = world:get(e, pair(Lifetime, nth))
157 | 2 | data -= 1
158 | 2 | if data <= 0 then
159 | 1 | world:remove(e, pair(Lifetime, nth))
160 | 0 | else
161 | 1 | world:set(e, pair(Lifetime, nth), data)
162 | N/A | end>
163 | 2 | i += 1
164 | 2 | nth = world:target(e, Lifetime, i)
165 | N/A | end>
166 | N/A | end>
167 | N/A | >
168 | 1 | CHECK(not world:has(entity, pair(Lifetime, Particle)))
169 | 1 | CHECK(world:get(entity, pair(Lifetime, Beam)) == 1)
170 | N/A | end)>
171 | N/A | >
172 | 1 | local lifetime_tracker_add = require("@tools/lifetime_tracker")
173 | N/A | >
174 | 1 | TEST("another", function()
175 | 1 | local world = world_new()
176 | N/A | -- world = lifetime_tracker_add(world, {padding_enabled=false})>
177 | 1 | local e1 = world:entity()
178 | 1 | local e2 = world:entity()
179 | 1 | local e3 = world:entity()
180 | 1 | world:delete(e2)
181 | 1 | local e2_e3 = pair(e2, e3)
182 | 1 | CHECK(jecs.pair_first(world, e2_e3) == 0)
183 | 1 | CHECK(jecs.pair_second(world, e2_e3) == e3)
184 | 1 | CHECK_EXPECT_ERR(function()
185 | 1 | world:add(e1, pair(e2, e3))
186 | N/A | end)>
187 | N/A | end)>
188 | N/A | >
189 | 1 | TEST("#repro", function()
190 | 1 | local world = world_new()
191 | N/A | >
192 | 1 | local function getTargets(relation)
193 | 2 | local tgts = {}
194 | 2 | local pairwildcard = pair(relation, jecs.Wildcard)
195 | 2 | for _, archetype in world:query(pairwildcard):archetypes() do
196 | 2 | local tr = archetype.records[pairwildcard]
197 | 2 | local count = archetype.counts[pairwildcard]
198 | 2 | local types = archetype.types
199 | 2 | for _, entity in archetype.entities do
200 | 2 | for i = 0, count - 1 do
201 | 2 | local tgt = jecs.pair_second(world, types[i + tr])
202 | 2 | table.insert(tgts, tgt)
203 | N/A | end>
204 | N/A | end>
205 | N/A | end>
206 | 2 | return tgts
207 | N/A | end>
208 | N/A | >
209 | 1 | local Attacks = world:component()
210 | 1 | local Eats = world:component()
211 | N/A | >
212 | 1 | local function setAttacksAndEats(entity1, entity2)
213 | 3 | world:add(entity1, pair(Attacks, entity2))
214 | 3 | world:add(entity1, pair(Eats, entity2))
215 | N/A | end>
216 | N/A | >
217 | 1 | local e1 = world:entity()
218 | 1 | local e2 = world:entity()
219 | 1 | local e3 = world:entity()
220 | 1 | setAttacksAndEats(e3, e1)
221 | 1 | setAttacksAndEats(e3, e2)
222 | 1 | setAttacksAndEats(e1, e2)
223 | 1 | local d = dwi(world)
224 | 1 | world:delete(e2)
225 | 1 | local types1 = { pair(Attacks, e1), pair(Eats, e1) }
226 | 1 | table.sort(types1)
227 | N/A | >
228 | N/A | >
229 | 1 | CHECK(d.tbl(e1).type == "")
230 | 1 | CHECK(d.tbl(e3).type == table.concat(types1, "_"))
231 | N/A | >
232 | 1 | for _, entity in getTargets(Attacks) do
233 | 1 | CHECK(entity == e1)
234 | N/A | end>
235 | 1 | for _, entity in getTargets(Eats) do
236 | 1 | CHECK(entity == e1)
237 | N/A | end>
238 | N/A | end)>
239 | N/A | >
240 | 1 | TEST("archetype", function()
241 | 1 | local archetype_traverse_add = jecs.archetype_traverse_add
242 | 1 | local archetype_traverse_remove = jecs.archetype_traverse_remove
243 | N/A | >
244 | 1 | local world = world_new()
245 | 1 | local root = world.ROOT_ARCHETYPE
246 | 1 | local c1 = world:component()
247 | 1 | local c2 = world:component()
248 | 1 | local c3 = world:component()
249 | N/A | >
250 | 1 | local a1 = archetype_traverse_add(world, c1, nil :: any)
251 | 1 | local a2 = archetype_traverse_remove(world, c1, a1)
252 | 1 | CHECK(root.add[c1].to == a1)
253 | 1 | CHECK(root == a2)
254 | N/A | end)>
255 | N/A | >
256 | 1 | TEST("world:cleanup()", function()
257 | 1 | local world = world_new()
258 | 1 | local A = world:component() :: jecs.Id
259 | 1 | local B = world:component() :: jecs.Id
260 | 1 | local C = world:component() :: jecs.Id
261 | N/A | >
262 | 1 | local e1 = world:entity()
263 | 1 | local e2 = world:entity()
264 | 1 | local e3 = world:entity()
265 | N/A | >
266 | 1 | world:set(e1, A, true)
267 | N/A | >
268 | 1 | world:set(e2, A, true)
269 | 1 | world:set(e2, B, true)
270 | N/A | >
271 | N/A | >
272 | 1 | world:set(e3, A, true)
273 | 1 | world:set(e3, B, true)
274 | 1 | world:set(e3, C, true)
275 | N/A | >
276 | 1 | local archetype_index = world.archetype_index
277 | N/A | >
278 | 1 | CHECK(#archetype_index["1"].entities == 1)
279 | 1 | CHECK(#archetype_index["1_2"].entities == 1)
280 | 1 | CHECK(#archetype_index["1_2_3"].entities == 1)
281 | N/A | >
282 | 1 | world:delete(e1)
283 | 1 | world:delete(e2)
284 | 1 | world:delete(e3)
285 | N/A | >
286 | 1 | world:cleanup()
287 | N/A | >
288 | 1 | archetype_index = world.archetype_index
289 | N/A | >
290 | 1 | CHECK((archetype_index["1"] :: jecs.Archetype?) == nil)
291 | 1 | CHECK((archetype_index["1_2"] :: jecs.Archetype?) == nil)
292 | 1 | CHECK((archetype_index["1_2_3"] :: jecs.Archetype?) == nil)
293 | N/A | >
294 | 1 | local e4 = world:entity()
295 | 1 | world:set(e4, A, true)
296 | 1 | CHECK(#archetype_index["1"].entities == 1)
297 | 1 | CHECK((archetype_index["1_2"] :: jecs.Archetype?) == nil)
298 | 1 | CHECK((archetype_index["1_2_3"] :: jecs.Archetype?) == nil)
299 | 1 | world:set(e4, B, true)
300 | 1 | CHECK(#archetype_index["1"].entities == 0)
301 | 1 | CHECK(#archetype_index["1_2"].entities == 1)
302 | 1 | CHECK((archetype_index["1_2_3"] :: jecs.Archetype?) == nil)
303 | 1 | world:set(e4, C, true)
304 | 1 | CHECK(#archetype_index["1"].entities == 0)
305 | 1 | CHECK(#archetype_index["1_2"].entities == 0)
306 | 1 | CHECK(#archetype_index["1_2_3"].entities == 1)
307 | N/A | end)>
308 | N/A | >
309 | 1 | local pe = require("@tools/entity_visualiser").prettify
310 | 1 | local lifetime_tracker_add = require("@tools/lifetime_tracker")
311 | N/A | >
312 | 1 | TEST("world:entity()", function()
313 | 0 | do
314 | 1 | CASE("unique IDs")
315 | 1 | local world = jecs.World.new()
316 | 1 | local set = {}
317 | 1 | for i = 1, N do
318 | 256 | local e = world:entity()
319 | 256 | CHECK(not set[e])
320 | 256 | set[e] = true
321 | N/A | end>
322 | N/A | end>
323 | 0 | do
324 | 1 | CASE("generations")
325 | 1 | local world = jecs.World.new()
326 | 1 | local e = world:entity() :: number
327 | 1 | CHECK(ECS_ID(e) == 1 + jecs.Rest :: number)
328 | 1 | CHECK(ECS_GENERATION(e) == 0) -- 0
329 | 1 | e = ECS_GENERATION_INC(e)
330 | 1 | CHECK(ECS_GENERATION(e) == 1) -- 1
331 | N/A | end>
332 | N/A | >
333 | 1 | do CASE "pairs"
334 | 1 | local world = jecs.World.new()
335 | 1 | local _e = world:entity()
336 | 1 | local e2 = world:entity()
337 | 1 | local e3 = world:entity()
338 | N/A | >
339 | N/A | -- Incomplete pair, must have a bit flag that notes it is a pair>
340 | 1 | CHECK(IS_PAIR(world:entity()) == false)
341 | N/A | >
342 | 1 | local p = pair(e2, e3)
343 | 1 | CHECK(IS_PAIR(p) == true)
344 | N/A | >
345 | 1 | CHECK(ecs_pair_first(world, p) == e2 :: number)
346 | 1 | CHECK(ecs_pair_second(world, p) == e3 :: number)
347 | N/A | >
348 | 1 | world:delete(e2)
349 | 1 | local e2v2 = world:entity()
350 | 1 | CHECK(IS_PAIR(e2v2) == false)
351 | N/A | >
352 | 1 | CHECK(IS_PAIR(pair(e2v2, e3)) == true)
353 | N/A | end>
354 | N/A | >
355 | 1 | do CASE "Recycling"
356 | 1 | local world = world_new()
357 | 1 | local e = world:entity()
358 | 1 | world:delete(e)
359 | 1 | local e1 = world:entity()
360 | 1 | world:delete(e1)
361 | 1 | local e2 = world:entity()
362 | 1 | CHECK(ECS_ID(e2) == e :: number)
363 | 1 | CHECK(ECS_GENERATION(e2) == 2)
364 | 1 | CHECK(world:contains(e2))
365 | 1 | CHECK(not world:contains(e1))
366 | 1 | CHECK(not world:contains(e))
367 | N/A | end>
368 | N/A | >
369 | 1 | do CASE "Recycling max generation"
370 | 1 | local world = world_new()
371 | 1 | local pin = (jecs.Rest :: any) :: number + 1
372 | 1 | for i = 1, 2^16-1 do
373 | 65535 | local e = world:entity()
374 | 65535 | world:delete(e)
375 | N/A | end>
376 | 1 | local e = world:entity()
377 | 1 | CHECK(ECS_ID(e) == pin)
378 | 1 | CHECK(ECS_GENERATION(e) == 2^16-1)
379 | 1 | world:delete(e)
380 | 1 | e = world:entity()
381 | 1 | CHECK(ECS_ID(e) == pin)
382 | 1 | CHECK(ECS_GENERATION(e) == 0)
383 | N/A | end>
384 | N/A | end)>
385 | N/A | >
386 | 1 | TEST("world:set()", function()
387 | 1 | do CASE "archetype move"
388 | 0 | do
389 | 1 | local world = jecs.World.new()
390 | N/A | >
391 | 1 | local d = debug_world_inspect(world)
392 | N/A | >
393 | 1 | local _1 = world:component()
394 | 1 | local _2 = world:component()
395 | 1 | local e = world:entity()
396 | N/A | -- An entity starts without an archetype or row>
397 | N/A | -- should therefore not need to copy over data>
398 | 1 | CHECK(d.tbl(e) == nil)
399 | 1 | CHECK(d.row(e) == nil)
400 | N/A | >
401 | 1 | local archetypes = #world.archetypes
402 | N/A | -- This should create a new archetype since it is the first>
403 | N/A | -- entity to have moved there>
404 | 1 | world:set(e, _1, 1)
405 | 1 | local oldRow = d.row(e)
406 | 1 | local oldArchetype = d.archetype(e)
407 | 1 | CHECK(#world.archetypes == archetypes + 1)
408 | 1 | CHECK(oldArchetype == "1")
409 | 1 | CHECK(d.tbl(e))
410 | 1 | CHECK(oldRow == 1)
411 | N/A | >
412 | 1 | world:set(e, _2, 2)
413 | 1 | CHECK(d.archetype(e) == "1_2")
414 | N/A | -- Should have tuple of fields to the next archetype and set the component data>
415 | 1 | CHECK(d.tuple(e, 1, 2))
416 | N/A | -- Should have moved the data from the old archetype>
417 | 1 | CHECK(world.archetype_index[oldArchetype].columns[_1][oldRow] == nil)
418 | N/A | end>
419 | N/A | end>
420 | N/A | >
421 | 1 | do CASE "pairs"
422 | 1 | local world = jecs.World.new()
423 | N/A | >
424 | 1 | local C1 = world:component()
425 | 1 | local C2 = world:component()
426 | 1 | local T1 = world:entity()
427 | 1 | local T2 = world:entity()
428 | N/A | >
429 | 1 | local e = world:entity()
430 | N/A | >
431 | 1 | world:set(e, pair(C1, C2), true)
432 | 1 | world:set(e, pair(C1, T1), true)
433 | 1 | world:set(e, pair(T1, C1), true)
434 | N/A | >
435 | 1 | CHECK_EXPECT_ERR(function()
436 | 1 | world:set(e, pair(T1, T2), true :: any)
437 | N/A | end)>
438 | N/A | >
439 | 1 | CHECK(world:get(e, pair(C1, C2)))
440 | 1 | CHECK(world:get(e, pair(C1, T1)))
441 | 1 | CHECK(world:get(e, pair(T1, C1)))
442 | 1 | CHECK(not world:get(e, pair(T1, T2)))
443 | N/A | >
444 | 1 | local e2 = world:entity()
445 | N/A | >
446 | 1 | CHECK_EXPECT_ERR(function()
447 | 1 | world:set(e2, pair(jecs.ChildOf, e), true :: any)
448 | N/A | end)>
449 | 1 | CHECK(not world:get(e2, pair(jecs.ChildOf, e)))
450 | N/A | end>
451 | N/A | end)>
452 | N/A | >
453 | 1 | TEST("world:remove()", function()
454 | 0 | do
455 | 1 | CASE("should allow remove a component that doesn't exist on entity")
456 | 1 | local world = jecs.World.new()
457 | N/A | >
458 | 1 | local Health = world:component()
459 | 1 | local Poison = world:component()
460 | N/A | >
461 | 1 | local id = world:entity()
462 | 0 | do
463 | 1 | world:remove(id, Poison)
464 | 1 | CHECK(true) -- Didn't error
465 | N/A | end>
466 | N/A | >
467 | 1 | world:set(id, Health, 50)
468 | 1 | world:remove(id, Poison)
469 | N/A | >
470 | 1 | CHECK(world:get(id, Poison) == nil)
471 | 1 | CHECK(world:get(id, Health) == 50)
472 | N/A | end>
473 | N/A | end)>
474 | N/A | >
475 | 1 | TEST("world:add()", function()
476 | 0 | do
477 | 1 | CASE("idempotent")
478 | 1 | local world = jecs.World.new()
479 | 1 | local d = debug_world_inspect(world)
480 | 1 | local _1, _2 = world:component(), world:component()
481 | N/A | >
482 | 1 | local e = world:entity()
483 | 1 | world:add(e, _1)
484 | 1 | world:add(e, _2)
485 | 1 | world:add(e, _2) -- should have 0 effects
486 | 1 | CHECK(d.archetype(e) == "1_2")
487 | N/A | end>
488 | N/A | >
489 | 0 | do
490 | 1 | CASE("archetype move")
491 | 0 | do
492 | 1 | local world = jecs.World.new()
493 | N/A | >
494 | 1 | local d = debug_world_inspect(world)
495 | N/A | >
496 | 1 | local _1 = world:component()
497 | 1 | local e = world:entity()
498 | N/A | -- An entity starts without an archetype or row>
499 | N/A | -- should therefore not need to copy over data>
500 | 1 | CHECK(d.tbl(e) == nil)
501 | 1 | CHECK(d.row(e) == nil)
502 | N/A | >
503 | 1 | local archetypes = #world.archetypes
504 | N/A | -- This should create a new archetype>
505 | 1 | world:add(e, _1)
506 | 1 | CHECK(#world.archetypes == archetypes + 1)
507 | N/A | >
508 | 1 | CHECK(d.archetype(e) == "1")
509 | 1 | CHECK(d.tbl(e))
510 | N/A | end>
511 | N/A | end>
512 | N/A | end)>
513 | N/A | >
514 | 1 | TEST("world:query()", function()
515 | 1 | do CASE "cached"
516 | 1 | local world = world_new()
517 | 1 | local Foo = world:component()
518 | 1 | local Bar = world:component()
519 | 1 | local Baz = world:component()
520 | 1 | local e = world:entity()
521 | 1 | local q = world:query(Foo, Bar):without(Baz):cached()
522 | 1 | world:set(e, Foo, true)
523 | 1 | world:set(e, Bar, false)
524 | 1 | local i = 0
525 | N/A | >
526 | 1 | local iter = 0
527 | 1 | for _, e in q:iter() do
528 | 1 | iter += 1
529 | 1 | i=1
530 | N/A | end>
531 | 1 | CHECK (iter == 1)
532 | 1 | CHECK(i == 1)
533 | 1 | for _, e in q:iter() do
534 | 1 | i=2
535 | N/A | end>
536 | 1 | CHECK(i == 2)
537 | 1 | for _, e in q :: any do
538 | 1 | i=3
539 | N/A | end>
540 | 1 | CHECK(i == 3)
541 | 1 | for _, e in q :: any do
542 | 1 | i=4
543 | N/A | end>
544 | 1 | CHECK(i == 4)
545 | N/A | >
546 | 1 | CHECK(#q:archetypes() == 1)
547 | 1 | CHECK(not table.find(q:archetypes(), world.archetype_index[table.concat({Foo, Bar, Baz}, "_")]))
548 | 1 | world:delete(Foo)
549 | 1 | CHECK(#q:archetypes() == 0)
550 | N/A | end>
551 | 1 | do CASE "multiple iter"
552 | 1 | local world = jecs.World.new()
553 | 1 | local A = world:component()
554 | 1 | local B = world:component()
555 | 1 | local e = world:entity()
556 | 1 | world:add(e, A)
557 | 1 | world:add(e, B)
558 | 1 | local q = world:query(A, B)
559 | 1 | local counter = 0
560 | 1 | for x in q:iter() do
561 | 1 | counter += 1
562 | N/A | end>
563 | 1 | for x in q:iter() do
564 | 1 | counter += 1
565 | N/A | end>
566 | 1 | CHECK(counter == 2)
567 | N/A | end>
568 | 1 | do CASE "tag"
569 | 1 | local world = jecs.World.new()
570 | 1 | local A = world:entity()
571 | 1 | local e = world:entity()
572 | 1 | CHECK_EXPECT_ERR(function()
573 | 1 | world:set(e, A, "test" :: any)
574 | N/A | end)>
575 | 1 | local count = 0
576 | 1 | for id, a in world:query(A) :: any do
577 | 1 | count += 1
578 | 1 | CHECK(a == nil)
579 | N/A | end>
580 | 1 | CHECK(count == 1)
581 | N/A | end>
582 | 1 | do CASE "pairs"
583 | 1 | local world = jecs.World.new()
584 | N/A | >
585 | 1 | local C1 = world:component() :: jecs.Id
586 | 1 | local C2 = world:component() :: jecs.Id
587 | 1 | local T1 = world:entity()
588 | 1 | local T2 = world:entity()
589 | N/A | >
590 | 1 | local e = world:entity()
591 | N/A | >
592 | 1 | local C1_C2 = pair(C1, C2)
593 | 1 | world:set(e, C1_C2, true)
594 | 1 | world:set(e, pair(C1, T1), true)
595 | 1 | world:set(e, pair(T1, C1), true)
596 | 1 | CHECK_EXPECT_ERR(function()
597 | 1 | world:set(e, pair(T1, T2), true :: any)
598 | N/A | end)>
599 | N/A | >
600 | 1 | for id, a, b, c, d in world:query(pair(C1, C2), pair(C1, T1), pair(T1, C1), pair(T1, T2)):iter() do
601 | 1 | CHECK(a == true)
602 | 1 | CHECK(b == true)
603 | 1 | CHECK(c == true)
604 | 1 | CHECK(d == nil)
605 | N/A | end>
606 | N/A | end>
607 | 0 | do
608 | 1 | CASE("query single component")
609 | 0 | do
610 | 1 | local world = jecs.World.new()
611 | 1 | local A = world:component()
612 | 1 | local B = world:component()
613 | N/A | >
614 | 1 | local entities = {}
615 | 1 | for i = 1, N do
616 | 256 | local id = world:entity()
617 | N/A | >
618 | 256 | world:set(id, A, true)
619 | 256 | if i > 5 then
620 | 251 | world:set(id, B, true)
621 | N/A | end>
622 | 256 | entities[i] = id
623 | N/A | end>
624 | N/A | >
625 | 1 | for id in world:query(A) :: any do
626 | 256 | table.remove(entities, CHECK(table.find(entities, id)))
627 | N/A | end>
628 | N/A | >
629 | 1 | CHECK(#entities == 0)
630 | N/A | end>
631 | N/A | >
632 | 0 | do
633 | 1 | local world = jecs.World.new() :: World
634 | 1 | local A = world:component()
635 | 1 | local B = world:component()
636 | 1 | local eA = world:entity()
637 | 1 | world:set(eA, A, true)
638 | 1 | local eB = world:entity()
639 | 1 | world:set(eB, B, true)
640 | 1 | local eAB = world:entity()
641 | 1 | world:set(eAB, A, true)
642 | 1 | world:set(eAB, B, true)
643 | N/A | >
644 | N/A | -- Should drain the iterator>
645 | 1 | local q = world:query(A)
646 | N/A | >
647 | 1 | local i = 0
648 | 1 | local j = 0
649 | 1 | for _ in q :: any do
650 | 2 | i += 1
651 | N/A | end>
652 | 1 | for _ in q :: any do
653 | 0 | j += 1
654 | N/A | end>
655 | 1 | CHECK(i == 2)
656 | 1 | CHECK(j == 0)
657 | N/A | end>
658 | N/A | end>
659 | N/A | >
660 | 0 | do
661 | 1 | CASE("query missing component")
662 | 1 | local world = jecs.World.new()
663 | 1 | local A = world:component()
664 | 1 | local B = world:component()
665 | 1 | local C = world:component()
666 | N/A | >
667 | 1 | local e1 = world:entity()
668 | 1 | local e2 = world:entity()
669 | N/A | >
670 | 1 | world:set(e1, A, "abc")
671 | 1 | world:set(e2, A, "def")
672 | 1 | world:set(e1, B, 123)
673 | 1 | world:set(e2, B, 457)
674 | N/A | >
675 | 1 | local counter = 0
676 | 1 | for _ in world:query(B, C) :: any do
677 | 0 | counter += 1
678 | N/A | end>
679 | 1 | CHECK(counter == 0)
680 | N/A | end>
681 | N/A | >
682 | 0 | do
683 | 1 | CASE("query more than 8 components")
684 | 1 | local world = jecs.World.new()
685 | 1 | local components = {}
686 | N/A | >
687 | 1 | for i = 1, 9 do
688 | 9 | local id = world:component()
689 | 9 | components[i] = id
690 | N/A | end>
691 | 1 | local e = world:entity()
692 | 1 | for i, id in components do
693 | 9 | world:set(e, id, 13 ^ i)
694 | N/A | end>
695 | N/A | >
696 | 1 | for entity, a, b, c, d, e, f, g, h, i in world:query(unpack(components)) :: any do
697 | 1 | CHECK(a == 13 ^ 1)
698 | 1 | CHECK(b == 13 ^ 2)
699 | 1 | CHECK(c == 13 ^ 3)
700 | 1 | CHECK(d == 13 ^ 4)
701 | 1 | CHECK(e == 13 ^ 5)
702 | 1 | CHECK(f == 13 ^ 6)
703 | 1 | CHECK(g == 13 ^ 7)
704 | 1 | CHECK(h == 13 ^ 8)
705 | 1 | CHECK(i == 13 ^ 9)
706 | N/A | end>
707 | N/A | end>
708 | N/A | >
709 | 0 | do
710 | 1 | CASE("should be able to get next results")
711 | 1 | local world = jecs.World.new() :: World
712 | 1 | world:component()
713 | 1 | local A = world:component()
714 | 1 | local B = world:component()
715 | 1 | local eA = world:entity()
716 | 1 | world:set(eA, A, true)
717 | 1 | local eB = world:entity()
718 | 1 | world:set(eB, B, true)
719 | 1 | local eAB = world:entity()
720 | 1 | world:set(eAB, A, true)
721 | 1 | world:set(eAB, B, true)
722 | N/A | >
723 | 1 | local it = world:query(A):iter()
724 | N/A | >
725 | 1 | local e: number, data = it()
726 | 1 | while e do
727 | 2 | if e == eA :: number then
728 | 1 | CHECK(data)
729 | 1 | elseif e == eAB :: number then
730 | 1 | CHECK(data)
731 | 0 | else
732 | 0 | CHECK(false)
733 | N/A | end>
734 | N/A | >
735 | 2 | e, data = it()
736 | N/A | end>
737 | 1 | CHECK(true)
738 | N/A | end>
739 | N/A | >
740 | 1 | do CASE "should query all matching entities when irrelevant component is removed"
741 | 1 | local world = jecs.World.new()
742 | 1 | local A = world:component()
743 | 1 | local B = world:component()
744 | 1 | local C = world:component()
745 | N/A | >
746 | 1 | local entities = {}
747 | 1 | for i = 1, N do
748 | 256 | local id = world:entity()
749 | N/A | >
750 | N/A | -- specifically put them in disorder to track regression>
751 | N/A | -- https://github.com/Ukendio/jecs/pull/15>
752 | 256 | world:set(id, B, true)
753 | 256 | world:set(id, A, true)
754 | 256 | if i > 5 then
755 | 251 | world:remove(id, B)
756 | N/A | end>
757 | 256 | entities[i] = id
758 | N/A | end>
759 | N/A | >
760 | 1 | local added = 0
761 | 1 | for id in world:query(A) :: any do
762 | 256 | added += 1
763 | 256 | table.remove(entities, CHECK(table.find(entities, id)))
764 | N/A | end>
765 | N/A | >
766 | 1 | CHECK(added == N)
767 | N/A | end>
768 | N/A | >
769 | 0 | do
770 | 1 | CASE("should query all entities without B")
771 | 1 | local world = jecs.World.new()
772 | 1 | local A = world:component()
773 | 1 | local B = world:component()
774 | N/A | >
775 | 1 | local entities = {}
776 | 1 | for i = 1, N do
777 | 256 | local id = world:entity()
778 | N/A | >
779 | 256 | world:set(id, A, true)
780 | 256 | if i < 5 then
781 | 4 | entities[i] = id
782 | 0 | else
783 | 252 | world:set(id, B, true)
784 | N/A | end>
785 | N/A | end>
786 | N/A | >
787 | 1 | for id in world:query(A):without(B) :: any do
788 | 4 | table.remove(entities, CHECK(table.find(entities, id)))
789 | N/A | end>
790 | N/A | >
791 | 1 | CHECK(#entities == 0)
792 | N/A | end>
793 | N/A | >
794 | 0 | do
795 | 1 | CASE("should allow querying for relations")
796 | 1 | local world = jecs.World.new()
797 | 1 | local Eats = world:component()
798 | 1 | local Apples = world:component()
799 | 1 | local bob = world:entity()
800 | N/A | >
801 | 1 | world:set(bob, pair(Eats, Apples), true)
802 | 1 | for e, bool in world:query(pair(Eats, Apples)) :: any do
803 | 1 | CHECK(e == bob)
804 | 1 | CHECK(bool)
805 | N/A | end>
806 | N/A | end>
807 | N/A | >
808 | 0 | do
809 | 1 | CASE("should allow wildcards in queries")
810 | 1 | local world = jecs.World.new()
811 | 1 | local Eats = world:component()
812 | 1 | local Apples = world:entity()
813 | 1 | local bob = world:entity()
814 | N/A | >
815 | 1 | world:set(bob, pair(Eats, Apples), "bob eats apples")
816 | N/A | >
817 | 1 | local w = jecs.Wildcard
818 | 1 | for e, data in world:query(pair(Eats, w)) :: any do
819 | 1 | CHECK(e == bob)
820 | 1 | CHECK(data == "bob eats apples")
821 | N/A | end>
822 | 1 | for e, data in world:query(pair(w, Apples)) :: any do
823 | 1 | CHECK(e == bob)
824 | 1 | CHECK(data == "bob eats apples")
825 | N/A | end>
826 | N/A | end>
827 | N/A | >
828 | 0 | do
829 | 1 | CASE("should match against multiple pairs")
830 | 1 | local world = jecs.World.new()
831 | 1 | local Eats = world:component()
832 | 1 | local Apples = world:entity()
833 | 1 | local Oranges = world:entity()
834 | 1 | local bob = world:entity()
835 | 1 | local alice = world:entity()
836 | N/A | >
837 | 1 | world:set(bob, pair(Eats, Apples), "bob eats apples")
838 | 1 | world:set(alice, pair(Eats, Oranges), "alice eats oranges")
839 | N/A | >
840 | 1 | local w = jecs.Wildcard
841 | 1 | local count = 0
842 | 1 | for e, data in world:query(pair(Eats, w)) :: any do
843 | 2 | count += 1
844 | 2 | if e == bob then
845 | 1 | CHECK(data == "bob eats apples")
846 | 0 | else
847 | 1 | CHECK(data == "alice eats oranges")
848 | N/A | end>
849 | N/A | end>
850 | N/A | >
851 | 1 | CHECK(count == 2)
852 | 1 | count = 0
853 | N/A | >
854 | 1 | for e, data in world:query(pair(w, Apples)) :: any do
855 | 1 | count += 1
856 | 1 | CHECK(data == "bob eats apples")
857 | N/A | end>
858 | 1 | CHECK(count == 1)
859 | N/A | end>
860 | N/A | >
861 | 1 | do CASE "should only relate alive entities"
862 | 1 | local world = jecs.World.new()
863 | 1 | local Eats = world:entity()
864 | 1 | local Apples = world:component()
865 | 1 | local Oranges = world:component()
866 | 1 | local bob = world:entity()
867 | 1 | local alice = world:entity()
868 | N/A | >
869 | 1 | world:set(bob, Apples, "apples")
870 | 1 | world:set(bob, pair(Eats, Apples), "bob eats apples")
871 | 1 | world:set(alice, pair(Eats, Oranges) :: Entity, "alice eats oranges")
872 | N/A | >
873 | 1 | world:delete(Apples)
874 | 1 | local Wildcard = jecs.Wildcard
875 | N/A | >
876 | 1 | local count = 0
877 | 1 | for _, data in world:query(pair(Wildcard, Apples)) :: any do
878 | 0 | count += 1
879 | N/A | end>
880 | N/A | >
881 | 1 | world:delete(pair(Eats, Apples))
882 | N/A | >
883 | 1 | CHECK(count == 0)
884 | 1 | CHECK(world:get(bob, pair(Eats, Apples)) == nil)
885 | N/A | >
886 | N/A | end>
887 | N/A | >
888 | 0 | do
889 | 1 | CASE("should error when setting invalid pair")
890 | 1 | local world = jecs.World.new()
891 | 1 | local Eats = world:component()
892 | 1 | local Apples = world:component()
893 | 1 | local bob = world:entity()
894 | N/A | >
895 | 1 | world:delete(Apples)
896 | 1 | CHECK_EXPECT_ERR(function()
897 | 1 | world:set(bob, pair(Eats, Apples), "bob eats apples")
898 | N/A | end)>
899 | N/A | end>
900 | N/A | >
901 | 0 | do
902 | 1 | CASE("should find target for ChildOf")
903 | 1 | local world = jecs.World.new()
904 | 1 | local ChildOf = jecs.ChildOf
905 | N/A | >
906 | 1 | local Name = world:component()
907 | N/A | >
908 | 1 | local bob = world:entity()
909 | 1 | local alice = world:entity()
910 | 1 | local sara = world:entity()
911 | N/A | >
912 | 1 | world:add(bob, pair(ChildOf, alice))
913 | 1 | world:set(bob, Name, "bob")
914 | 1 | world:add(sara, pair(ChildOf, alice))
915 | 1 | world:set(sara, Name, "sara")
916 | 1 | CHECK(world:parent(bob) :: number == alice :: number) -- O(1)
917 | N/A | >
918 | 1 | local count = 0
919 | 1 | for _, name in world:query(Name, pair(ChildOf, alice)) :: any do
920 | 2 | count += 1
921 | N/A | end>
922 | 1 | CHECK(count == 2)
923 | N/A | end>
924 | N/A | >
925 | 0 | do
926 | 1 | CASE("despawning while iterating")
927 | 1 | local world = jecs.World.new()
928 | 1 | local A = world:component()
929 | 1 | local B = world:component()
930 | N/A | >
931 | 1 | local e1 = world:entity()
932 | 1 | local e2 = world:entity()
933 | 1 | world:add(e1, A)
934 | 1 | world:add(e2, A)
935 | 1 | world:add(e2, B)
936 | N/A | >
937 | 1 | local count = 0
938 | 1 | for id in world:query(A) :: any do
939 | 2 | world:clear(id)
940 | 2 | count += 1
941 | N/A | end>
942 | 1 | CHECK(count == 2)
943 | N/A | end>
944 | N/A | >
945 | 1 | do CASE("iterator invalidation")
946 | 1 | do CASE("adding")
947 | 1 | SKIP()
948 | 1 | local world = jecs.World.new()
949 | 1 | local A = world:component()
950 | 1 | local B = world:component()
951 | N/A | >
952 | 1 | local e1 = world:entity()
953 | 1 | local e2 = world:entity()
954 | 1 | world:add(e1, A)
955 | 1 | world:add(e2, A)
956 | 1 | world:add(e2, B)
957 | N/A | >
958 | 1 | local count = 0
959 | 1 | for id in world:query(A) :: any do
960 | 3 | world:add(id, B)
961 | N/A | >
962 | 3 | count += 1
963 | N/A | end>
964 | N/A | >
965 | 1 | CHECK(count == 2)
966 | N/A | end>
967 | N/A | >
968 | 1 | do CASE("spawning")
969 | 1 | local world = jecs.World.new()
970 | 1 | local A = world:component()
971 | 1 | local B = world:component()
972 | N/A | >
973 | 1 | local e1 = world:entity()
974 | 1 | local e2 = world:entity()
975 | 1 | world:add(e1, A)
976 | 1 | world:add(e2, A)
977 | 1 | world:add(e2, B)
978 | N/A | >
979 | 1 | for id in world:query(A) :: any do
980 | 3 | local e = world:entity()
981 | 3 | world:add(e, A)
982 | 3 | world:add(e, B)
983 | N/A | end>
984 | N/A | >
985 | 1 | CHECK(true)
986 | N/A | end>
987 | N/A | end>
988 | N/A | >
989 | 1 | do CASE("should not find any entities")
990 | 1 | local world = jecs.World.new()
991 | N/A | >
992 | 1 | local Hello = world:component()
993 | 1 | local Bob = world:component()
994 | N/A | >
995 | 1 | local helloBob = world:entity()
996 | 1 | world:add(helloBob, pair(Hello, Bob))
997 | 1 | world:add(helloBob, Bob)
998 | N/A | >
999 | 1 | local withoutCount = 0
1000 | 1 | for _ in world:query(pair(Hello, Bob)):without(Bob) :: any do
1001 | 0 | withoutCount += 1
1002 | N/A | end>
1003 | N/A | >
1004 | 1 | CHECK(withoutCount == 0)
1005 | N/A | end>
1006 | N/A | >
1007 | 1 | do CASE("without")
1008 | N/A | -- REGRESSION TEST>
1009 | 1 | local world = jecs.World.new()
1010 | 1 | local _1, _2, _3 = world:component(), world:component(), world:component()
1011 | N/A | >
1012 | 1 | local counter = 0
1013 | 1 | for e, a, b in world:query(_1, _2):without(_3) :: any do
1014 | 0 | counter += 1
1015 | N/A | end>
1016 | 1 | CHECK(counter == 0)
1017 | N/A | end>
1018 | N/A | end)>
1019 | N/A | >
1020 | 1 | TEST("world:each", function()
1021 | 1 | local world = world_new()
1022 | 1 | local A = world:component()
1023 | 1 | local B = world:component()
1024 | 1 | local C = world:component()
1025 | N/A | >
1026 | 1 | local e3 = world:entity()
1027 | 1 | local e1 = world:entity()
1028 | 1 | local e2 = world:entity()
1029 | N/A | >
1030 | 1 | world:set(e1, A, true)
1031 | N/A | >
1032 | 1 | world:set(e2, A, true)
1033 | 1 | world:set(e2, B, true)
1034 | N/A | >
1035 | 1 | world:set(e3, A, true)
1036 | 1 | world:set(e3, B, true)
1037 | 1 | world:set(e3, C, true)
1038 | N/A | >
1039 | 1 | for entity: number in world:each(A) do
1040 | 3 | if entity == e1 :: number or entity == e2 :: number or entity == e3 :: number then
1041 | 3 | CHECK(true)
1042 | 3 | continue
1043 | N/A | end>
1044 | 0 | CHECK(false)
1045 | N/A | end>
1046 | N/A | end)>
1047 | N/A | >
1048 | 1 | TEST("world:children", function()
1049 | 1 | local world = world_new()
1050 | 1 | local C = world:component()
1051 | 1 | local T = world:entity()
1052 | N/A | >
1053 | 1 | local e1 = world:entity()
1054 | 1 | world:set(e1, C, true)
1055 | N/A | >
1056 | 1 | local e2 = world:entity() :: number
1057 | N/A | >
1058 | 1 | world:add(e2, T)
1059 | 1 | world:add(e2, pair(ChildOf, e1))
1060 | N/A | >
1061 | 1 | local e3 = world:entity() :: number
1062 | 1 | world:add(e3, pair(ChildOf, e1))
1063 | N/A | >
1064 | 1 | local count = 0
1065 | 1 | for entity: number in world:children(e1) do
1066 | 2 | count += 1
1067 | 2 | if entity == e2 or entity == e3 then
1068 | 2 | CHECK(true)
1069 | 2 | continue
1070 | N/A | end>
1071 | 0 | CHECK(false)
1072 | N/A | end>
1073 | 1 | CHECK(count == 2)
1074 | N/A | >
1075 | 1 | world:remove(e2, pair(ChildOf, e1))
1076 | N/A | >
1077 | 1 | count = 0
1078 | 1 | for entity in world:children(e1) do
1079 | 1 | count += 1
1080 | N/A | end>
1081 | N/A | >
1082 | 1 | CHECK(count == 1)
1083 | N/A | end)>
1084 | N/A | >
1085 | 1 | TEST("world:clear()", function()
1086 | 1 | do CASE("should remove its components")
1087 | 1 | local world = jecs.World.new() :: World
1088 | 1 | local A = world:component()
1089 | 1 | local B = world:component()
1090 | 1 | local C = world:component()
1091 | 1 | local D = world:component()
1092 | N/A | >
1093 | 1 | local e = world:entity()
1094 | 1 | local e1 = world:entity()
1095 | 1 | local e2 = world:entity()
1096 | N/A | >
1097 | 1 | world:set(e, A, true)
1098 | 1 | world:set(e, B, true)
1099 | N/A | >
1100 | 1 | world:set(e1, A, true)
1101 | 1 | world:set(e1, B, true)
1102 | N/A | >
1103 | 1 | CHECK(world:get(e, A))
1104 | 1 | CHECK(world:get(e, B))
1105 | N/A | >
1106 | 1 | world:clear(A)
1107 | 1 | CHECK(world:get(e, A) == nil)
1108 | 1 | CHECK(world:get(e, B))
1109 | 1 | CHECK(world:get(e1, A) == nil)
1110 | 1 | CHECK(world:get(e1, B))
1111 | N/A | end>
1112 | N/A | >
1113 | 1 | do CASE("remove cleared ID from entities")
1114 | 1 | local world = world_new()
1115 | 1 | local A = world:component()
1116 | 1 | local B = world:component()
1117 | 1 | local C = world:component()
1118 | N/A | >
1119 | 0 | do
1120 | 1 | local id1 = world:entity()
1121 | 1 | local id2 = world:entity()
1122 | 1 | local id3 = world:entity()
1123 | N/A | >
1124 | 1 | world:set(id1, A, true)
1125 | N/A | >
1126 | 1 | world:set(id2, A, true)
1127 | 1 | world:set(id2, B, true)
1128 | N/A | >
1129 | 1 | world:set(id3, A, true)
1130 | 1 | world:set(id3, B, true)
1131 | 1 | world:set(id3, C, true)
1132 | N/A | >
1133 | 1 | world:clear(A)
1134 | N/A | >
1135 | 1 | CHECK(not world:has(id1, A))
1136 | 1 | CHECK(not world:has(id2, A))
1137 | 1 | CHECK(not world:has(id3, A))
1138 | N/A | >
1139 | 1 | CHECK(world:has(id2, B))
1140 | 1 | CHECK(world:has(id3, B, C))
1141 | N/A | >
1142 | 1 | world:clear(C)
1143 | N/A | >
1144 | 1 | CHECK(world:has(id2, B))
1145 | 1 | CHECK(world:has(id3, B))
1146 | N/A | >
1147 | 1 | CHECK(world:contains(A))
1148 | 1 | CHECK(world:contains(C))
1149 | 1 | CHECK(world:has(A, jecs.Component))
1150 | 1 | CHECK(world:has(B, jecs.Component))
1151 | N/A | end>
1152 | N/A | >
1153 | 0 | do
1154 | 1 | local id1 = world:entity()
1155 | 1 | local id2 = world:entity()
1156 | 1 | local id3 = world:entity()
1157 | N/A | >
1158 | 1 | local tgt = world:entity()
1159 | N/A | >
1160 | 1 | world:add(id1, pair(A, tgt))
1161 | 1 | world:add(id1, pair(B, tgt))
1162 | 1 | world:add(id1, pair(C, tgt))
1163 | N/A | >
1164 | 1 | world:add(id2, pair(A, tgt))
1165 | 1 | world:add(id2, pair(B, tgt))
1166 | 1 | world:add(id2, pair(C, tgt))
1167 | N/A | >
1168 | 1 | world:add(id3, pair(A, tgt))
1169 | 1 | world:add(id3, pair(B, tgt))
1170 | 1 | world:add(id3, pair(C, tgt))
1171 | N/A | >
1172 | 1 | world:clear(B)
1173 | 1 | CHECK(world:has(id1, pair(A, tgt), pair(C, tgt)))
1174 | 1 | CHECK(not world:has(id1, pair(B, tgt)))
1175 | 1 | CHECK(world:has(id2, pair(A, tgt), pair(C, tgt)))
1176 | 1 | CHECK(not world:has(id1, pair(B, tgt)))
1177 | 1 | CHECK(world:has(id3, pair(A, tgt), pair(C, tgt)))
1178 | N/A | >
1179 | N/A | end>
1180 | N/A | >
1181 | N/A | end>
1182 | N/A | end)>
1183 | N/A | >
1184 | 1 | TEST("world:has()", function()
1185 | 1 | do CASE("should find Tag on entity")
1186 | 1 | local world = jecs.World.new()
1187 | N/A | >
1188 | 1 | local Tag = world:entity()
1189 | N/A | >
1190 | 1 | local e = world:entity()
1191 | 1 | world:add(e, Tag)
1192 | N/A | >
1193 | 1 | CHECK(world:has(e, Tag))
1194 | N/A | end>
1195 | N/A | >
1196 | 1 | do CASE("should return false when missing one tag")
1197 | 1 | local world = jecs.World.new()
1198 | N/A | >
1199 | 1 | local A = world:entity()
1200 | 1 | local B = world:entity()
1201 | 1 | local C = world:entity()
1202 | 1 | local D = world:entity()
1203 | N/A | >
1204 | 1 | local e = world:entity()
1205 | 1 | world:add(e, A)
1206 | 1 | world:add(e, C)
1207 | 1 | world:add(e, D)
1208 | N/A | >
1209 | 1 | CHECK(world:has(e, A, B, C, D) == false)
1210 | N/A | end>
1211 | N/A | end)>
1212 | N/A | >
1213 | 1 | TEST("world:component()", function()
1214 | 1 | do CASE("only components should have EcsComponent trait")
1215 | 1 | local world = jecs.World.new() :: World
1216 | 1 | local A = world:component()
1217 | 1 | local e = world:entity()
1218 | N/A | >
1219 | 1 | CHECK(world:has(A, jecs.Component))
1220 | 1 | CHECK(not world:has(e, jecs.Component))
1221 | N/A | end>
1222 | N/A | >
1223 | 1 | do CASE("tag")
1224 | 1 | local world = jecs.World.new() :: World
1225 | 1 | local A = world:component()
1226 | 1 | local B = world:entity()
1227 | 1 | local C = world:entity()
1228 | 1 | local e = world:entity()
1229 | 1 | world:set(e, A, "test")
1230 | 1 | world:add(e, B)
1231 | 1 | CHECK_EXPECT_ERR(function()
1232 | 1 | world:set(e, C, 11 :: any)
1233 | N/A | end)>
1234 | N/A | >
1235 | 1 | CHECK(world:has(e, A))
1236 | 1 | CHECK(world:get(e, A) == "test")
1237 | 1 | CHECK(world:get(e, B) == nil)
1238 | 1 | CHECK(world:get(e, C) == nil)
1239 | N/A | end>
1240 | N/A | end)>
1241 | N/A | >
1242 | 1 | TEST("world:delete", function()
1243 | 1 | do CASE "invoke OnRemove hooks"
1244 | 1 | local world = world_new()
1245 | N/A | >
1246 | 1 | local e1 = world:entity()
1247 | 1 | local e2 = world:entity()
1248 | N/A | >
1249 | 1 | local Stable = world:component()
1250 | 1 | world:set(Stable, jecs.OnRemove, function(e)
1251 | 1 | CHECK(e == e1)
1252 | N/A | end)>
1253 | N/A | >
1254 | 1 | world:set(e1, Stable, true)
1255 | 1 | world:set(e2, Stable, true)
1256 | N/A | >
1257 | 1 | world:delete(e1)
1258 | N/A | end>
1259 | 1 | do CASE "delete recycled entity id used as component"
1260 | 1 | local world = world_new()
1261 | 1 | local id = world:entity()
1262 | 1 | world:add(id, jecs.Component)
1263 | N/A | >
1264 | 1 | local e = world:entity()
1265 | 1 | world:set(e, id, 1)
1266 | 1 | CHECK(world:get(e, id) == 1)
1267 | 1 | world:delete(id)
1268 | 1 | local recycled = world:entity()
1269 | 1 | world:add(recycled, jecs.Component)
1270 | 1 | world:set(e, recycled, 1)
1271 | 1 | CHECK(world:has(recycled, jecs.Component))
1272 | 1 | CHECK(world:get(e, recycled) == 1)
1273 | N/A | end>
1274 | 0 | do
1275 | 1 | CASE("bug: Empty entity does not respect cleanup policy")
1276 | 1 | local world = world_new()
1277 | 1 | local parent = world:entity()
1278 | 1 | local tag = world:entity()
1279 | N/A | >
1280 | 1 | local child = world:entity()
1281 | 1 | world:add(child, jecs.pair(jecs.ChildOf, parent))
1282 | 1 | world:delete(parent)
1283 | N/A | >
1284 | 1 | CHECK(not world:contains(parent))
1285 | 1 | CHECK(not world:contains(child))
1286 | N/A | >
1287 | 1 | local entity = world:entity()
1288 | 1 | world:add(entity, tag)
1289 | 1 | world:delete(tag)
1290 | 1 | CHECK(world:contains(entity))
1291 | 1 | CHECK(not world:contains(tag))
1292 | 1 | CHECK(not world:has(entity, tag)) -- => true
1293 | N/A | end>
1294 | 1 | do CASE("should allow deleting components")
1295 | 1 | local world = jecs.World.new()
1296 | N/A | >
1297 | 1 | local Health = world:component()
1298 | 1 | local Poison = world:component()
1299 | N/A | >
1300 | 1 | local id = world:entity()
1301 | 1 | world:set(id, Poison, 5)
1302 | 1 | world:set(id, Health, 50)
1303 | 1 | local id1 = world:entity()
1304 | 1 | world:set(id1, Poison, 500)
1305 | 1 | world:set(id1, Health, 50)
1306 | N/A | >
1307 | 1 | world:delete(id)
1308 | 1 | CHECK(not world:contains(id))
1309 | 1 | CHECK(world:get(id, Poison) == nil)
1310 | 1 | CHECK(world:get(id, Health) == nil)
1311 | N/A | >
1312 | 1 | CHECK(world:get(id1, Poison) == 500)
1313 | 1 | CHECK(world:get(id1, Health) == 50)
1314 | N/A | end>
1315 | N/A | >
1316 | 1 | do CASE("delete entities using another Entity as component with Delete cleanup action")
1317 | 1 | local world = jecs.World.new()
1318 | N/A | >
1319 | 1 | local Health = world:entity()
1320 | 1 | world:add(Health, pair(jecs.OnDelete, jecs.Delete))
1321 | 1 | local Poison = world:component()
1322 | N/A | >
1323 | 1 | local id = world:entity()
1324 | 1 | world:set(id, Poison, 5)
1325 | 1 | CHECK_EXPECT_ERR(function()
1326 | 1 | world:set(id, Health, 50 :: any)
1327 | N/A | end)>
1328 | 1 | local id1 = world:entity()
1329 | 1 | world:set(id1, Poison, 500)
1330 | 1 | CHECK_EXPECT_ERR(function()
1331 | 1 | world:set(id1, Health, 50 :: any)
1332 | N/A | end)>
1333 | N/A | >
1334 | 1 | CHECK(world:has(id, Poison, Health))
1335 | 1 | CHECK(world:has(id1, Poison, Health))
1336 | 1 | world:delete(Poison)
1337 | N/A | >
1338 | 1 | CHECK(world:contains(id))
1339 | 1 | CHECK(not world:has(id, Poison))
1340 | 1 | CHECK(not world:has(id1, Poison))
1341 | N/A | >
1342 | 1 | world:delete(Health)
1343 | 1 | CHECK(not world:contains(id))
1344 | 1 | CHECK(not world:contains(id1))
1345 | 1 | CHECK(not world:has(id, Health))
1346 | 1 | CHECK(not world:has(id1, Health))
1347 | N/A | end>
1348 | N/A | >
1349 | N/A | >
1350 | 1 | do CASE("delete children")
1351 | 1 | local world = jecs.World.new()
1352 | N/A | >
1353 | 1 | local Health = world:component()
1354 | 1 | local Poison = world:component()
1355 | 1 | local FriendsWith = world:component()
1356 | N/A | >
1357 | 1 | local e = world:entity()
1358 | 1 | world:set(e, Poison, 5)
1359 | 1 | world:set(e, Health, 50)
1360 | N/A | >
1361 | 1 | local children = {}
1362 | 1 | for i = 1, 10 do
1363 | 10 | local child = world:entity()
1364 | 10 | world:set(child, Poison, 9999)
1365 | 10 | world:set(child, Health, 100)
1366 | 10 | world:add(child, pair(jecs.ChildOf, e))
1367 | 10 | table.insert(children, child)
1368 | N/A | end>
1369 | N/A | >
1370 | 1 | BENCH("delete children of entity", function()
1371 | 1 | world:delete(e)
1372 | N/A | end)>
1373 | N/A | >
1374 | 1 | for i, child in children do
1375 | 10 | CHECK(not world:contains(child))
1376 | 10 | CHECK(not world:has(child, pair(jecs.ChildOf, e)))
1377 | 10 | CHECK(not world:has(child, Health))
1378 | N/A | end>
1379 | N/A | >
1380 | 1 | e = world:entity()
1381 | N/A | >
1382 | 1 | local friends = {}
1383 | 1 | for i = 1, 10 do
1384 | 10 | local friend = world:entity()
1385 | 10 | world:set(friend, Poison, 9999)
1386 | 10 | world:set(friend, Health, 100)
1387 | 10 | world:add(friend, pair(FriendsWith, e))
1388 | 10 | table.insert(friends, friend)
1389 | N/A | end>
1390 | N/A | >
1391 | 1 | BENCH("remove friends of entity", function()
1392 | 1 | world:delete(e)
1393 | N/A | end)>
1394 | N/A | >
1395 | 1 | local d = debug_world_inspect(world)
1396 | 1 | for i, friend in friends do
1397 | 10 | CHECK(not world:has(friend, pair(FriendsWith, e)))
1398 | 10 | CHECK(world:has(friend, Health))
1399 | 10 | CHECK(world:contains(friend))
1400 | N/A | end>
1401 | N/A | end>
1402 | N/A | >
1403 | 1 | do CASE("remove deleted ID from entities")
1404 | 1 | local world = world_new()
1405 | 0 | do
1406 | 1 | local A = world:component()
1407 | 1 | local B = world:component()
1408 | 1 | local C = world:component()
1409 | 1 | local id1 = world:entity()
1410 | 1 | local id2 = world:entity()
1411 | 1 | local id3 = world:entity()
1412 | N/A | >
1413 | 1 | world:set(id1, A, true)
1414 | N/A | >
1415 | 1 | world:set(id2, A, true)
1416 | 1 | world:set(id2, B, true)
1417 | N/A | >
1418 | 1 | world:set(id3, A, true)
1419 | 1 | world:set(id3, B, true)
1420 | 1 | world:set(id3, C, true)
1421 | N/A | >
1422 | 1 | world:delete(A)
1423 | N/A | >
1424 | 1 | CHECK(not world:has(id1, A))
1425 | 1 | CHECK(not world:has(id2, A))
1426 | 1 | CHECK(not world:has(id3, A))
1427 | N/A | >
1428 | 1 | CHECK(world:has(id2, B))
1429 | 1 | CHECK(world:has(id3, B, C))
1430 | N/A | >
1431 | 1 | world:delete(C)
1432 | N/A | >
1433 | 1 | CHECK(world:has(id2, B))
1434 | 1 | CHECK(world:has(id3, B))
1435 | N/A | >
1436 | 1 | CHECK(not world:contains(A))
1437 | 1 | CHECK(not world:contains(C))
1438 | N/A | end>
1439 | N/A | >
1440 | 0 | do
1441 | 1 | local A = world:component()
1442 | 1 | world:add(A, pair(jecs.OnDeleteTarget, jecs.Delete))
1443 | 1 | local B = world:component()
1444 | 1 | local C = world:component()
1445 | 1 | world:add(C, pair(jecs.OnDeleteTarget, jecs.Delete))
1446 | N/A | >
1447 | 1 | local id1 = world:entity()
1448 | 1 | local id2 = world:entity()
1449 | 1 | local id3 = world:entity()
1450 | N/A | >
1451 | 1 | world:set(id1, C, true)
1452 | N/A | >
1453 | 1 | world:set(id2, pair(A, id1), true)
1454 | 1 | world:set(id2, B, true)
1455 | N/A | >
1456 | 1 | world:set(id3, B, true)
1457 | 1 | world:set(id3, pair(C, id2), true)
1458 | N/A | >
1459 | 1 | world:delete(id1)
1460 | N/A | >
1461 | 1 | CHECK(not world:contains(id1))
1462 | 1 | CHECK(not world:contains(id2))
1463 | 1 | CHECK(not world:contains(id3))
1464 | N/A | end>
1465 | N/A | >
1466 | N/A | >
1467 | 0 | do
1468 | 1 | local A = world:component()
1469 | 1 | local B = world:component()
1470 | 1 | local C = world:component()
1471 | 1 | local id1 = world:entity()
1472 | 1 | local id2 = world:entity()
1473 | 1 | local id3 = world:entity()
1474 | N/A | >
1475 | N/A | >
1476 | 1 | world:set(id2, A, true)
1477 | 1 | world:set(id2, pair(B, id1), true)
1478 | N/A | >
1479 | 1 | world:set(id3, A, true)
1480 | 1 | world:set(id3, pair(B, id1), true)
1481 | 1 | world:set(id3, C, true)
1482 | N/A | >
1483 | 1 | world:delete(id1)
1484 | N/A | >
1485 | 1 | CHECK(not world:contains(id1))
1486 | 1 | CHECK(world:contains(id2))
1487 | 1 | CHECK(world:contains(id3))
1488 | N/A | >
1489 | 1 | CHECK(world:has(id2, A))
1490 | 1 | CHECK(world:has(id3, A, C))
1491 | N/A | >
1492 | 1 | CHECK(not world:target(id2, B))
1493 | 1 | CHECK(not world:target(id3, B))
1494 | N/A | end>
1495 | N/A | end>
1496 | N/A | >
1497 | 0 | do
1498 | 1 | CASE("fast delete")
1499 | 1 | local world = jecs.World.new()
1500 | N/A | >
1501 | 1 | local entities = {}
1502 | 1 | local Health = world:component()
1503 | 1 | local Poison = world:component()
1504 | N/A | >
1505 | 1 | for i = 1, 100 do
1506 | 100 | local child = world:entity()
1507 | 100 | world:set(child, Poison, 9999)
1508 | 100 | world:set(child, Health, 100)
1509 | 100 | table.insert(entities, child)
1510 | N/A | end>
1511 | N/A | >
1512 | 1 | BENCH("simple deletion of entity", function()
1513 | 1 | for i = 1, START(100) do
1514 | 100 | local e = entities[i]
1515 | 100 | world:delete(e)
1516 | N/A | end>
1517 | N/A | end)>
1518 | N/A | >
1519 | 1 | for _, entity in entities do
1520 | 100 | CHECK(not world:contains(entity))
1521 | N/A | end>
1522 | N/A | end>
1523 | N/A | >
1524 | 0 | do
1525 | 1 | CASE("cycle")
1526 | 1 | local world = jecs.World.new()
1527 | 1 | local Likes = world:component()
1528 | 1 | world:add(Likes, pair(jecs.OnDeleteTarget, jecs.Delete))
1529 | 1 | local bob = world:entity()
1530 | 1 | local alice = world:entity()
1531 | N/A | >
1532 | 1 | world:add(bob, pair(Likes, alice))
1533 | 1 | world:add(alice, pair(Likes, bob))
1534 | N/A | >
1535 | 1 | world:delete(bob)
1536 | 1 | CHECK(not world:contains(bob))
1537 | 1 | CHECK(not world:contains(alice))
1538 | N/A | end>
1539 | N/A | end)>
1540 | N/A | >
1541 | 1 | TEST("world:target", function()
1542 | 1 | do CASE("nth index")
1543 | 1 | local world = world_new()
1544 | 1 | local A = world:component()
1545 | 1 | world:set(A, jecs.Name, "A")
1546 | 1 | local B = world:component()
1547 | 1 | world:set(B, jecs.Name, "B")
1548 | 1 | local C = world:component()
1549 | 1 | world:set(C, jecs.Name, "C")
1550 | 1 | local D = world:component()
1551 | 1 | world:set(D, jecs.Name, "D")
1552 | 1 | local E = world:component()
1553 | 1 | world:set(E, jecs.Name, "E")
1554 | 1 | local e = world:entity()
1555 | N/A | >
1556 | 1 | world:add(e, pair(A, B))
1557 | 1 | world:add(e, pair(A, C))
1558 | 1 | world:add(e, pair(A, D))
1559 | 1 | world:add(e, pair(A, E))
1560 | 1 | world:add(e, pair(B, C))
1561 | 1 | world:add(e, pair(B, D))
1562 | 1 | world:add(e, pair(C, D))
1563 | N/A | >
1564 | 1 | CHECK(pair(A, B) < pair(A, C))
1565 | 1 | CHECK(pair(A, C) < pair(A, D))
1566 | 1 | CHECK(pair(C, A) < pair(C, D))
1567 | N/A | >
1568 | 1 | local records = debug_world_inspect(world).records(e)
1569 | 1 | CHECK(jecs.pair_first(world, pair(B, C)) == B)
1570 | 1 | local r = jecs.entity_index_try_get(world.entity_index, e)
1571 | 1 | local archetype = r.archetype
1572 | 1 | local counts = archetype.counts
1573 | 1 | CHECK(counts[pair(A, __)] == 4)
1574 | 1 | CHECK(records[pair(B, C)] > records[pair(A, E)])
1575 | 1 | CHECK(world:target(e, A, 0) == B)
1576 | 1 | CHECK(world:target(e, A, 1) == C)
1577 | 1 | CHECK(world:target(e, A, 2) == D)
1578 | 1 | CHECK(world:target(e, A, 3) == E)
1579 | 1 | CHECK(world:target(e, B, 0) == C)
1580 | 1 | CHECK(world:target(e, B, 1) == D)
1581 | 1 | CHECK(world:target(e, C, 0) == D)
1582 | 1 | CHECK(world:target(e, C, 1) == nil)
1583 | N/A | >
1584 | 1 | CHECK(archetype.records[pair(A, B)] == 1)
1585 | 1 | CHECK(archetype.records[pair(A, C)] == 2)
1586 | 1 | CHECK(archetype.records[pair(A, D)] == 3)
1587 | 1 | CHECK(archetype.records[pair(A, E)] == 4)
1588 | N/A | >
1589 | 1 | CHECK(world:target(e, C, 0) == D)
1590 | 1 | CHECK(world:target(e, C, 1) == nil)
1591 | N/A | end>
1592 | N/A | >
1593 | 0 | do
1594 | 1 | CASE("infer index when unspecified")
1595 | 1 | local world = world_new()
1596 | 1 | local A = world:component()
1597 | 1 | local B = world:component()
1598 | 1 | local C = world:component()
1599 | 1 | local D = world:component()
1600 | 1 | local e = world:entity()
1601 | N/A | >
1602 | 1 | world:add(e, pair(A, B))
1603 | 1 | world:add(e, pair(A, C))
1604 | 1 | world:add(e, pair(B, C))
1605 | 1 | world:add(e, pair(B, D))
1606 | 1 | world:add(e, pair(C, D))
1607 | N/A | >
1608 | 1 | CHECK(world:target(e, A) == world:target(e, A, 0))
1609 | 1 | CHECK(world:target(e, B) == world:target(e, B, 0))
1610 | 1 | CHECK(world:target(e, C) == world:target(e, C, 0))
1611 | N/A | end>
1612 | N/A | >
1613 | 0 | do
1614 | 1 | CASE("loop until no target")
1615 | 1 | local world = world_new()
1616 | N/A | >
1617 | 1 | local ROOT = world:entity()
1618 | 1 | local e1 = world:entity()
1619 | 1 | local targets = {}
1620 | N/A | >
1621 | 1 | for i = 1, 10 do
1622 | 10 | local target = world:entity()
1623 | 10 | targets[i] = target
1624 | 10 | world:add(e1, pair(ROOT, target))
1625 | N/A | end>
1626 | N/A | >
1627 | 1 | local i = 0
1628 | 1 | local target = world:target(e1, ROOT, 0)
1629 | 1 | while target do
1630 | 10 | i += 1
1631 | 10 | CHECK(targets[i] == target)
1632 | 10 | target = world:target(e1, ROOT, i)
1633 | N/A | end>
1634 | N/A | >
1635 | 1 | CHECK(i == 10)
1636 | N/A | end>
1637 | N/A | end)>
1638 | N/A | >
1639 | 1 | TEST("world:contains", function()
1640 | 1 | local world = jecs.World.new()
1641 | 1 | local id = world:entity()
1642 | 1 | CHECK(world:contains(id))
1643 | N/A | >
1644 | 0 | do
1645 | 1 | CASE("should not exist after delete")
1646 | 1 | world:delete(id)
1647 | 1 | CHECK(not world:contains(id))
1648 | N/A | end>
1649 | N/A | end)>
1650 | N/A | >
1651 | 1 | TEST("Hooks", function()
1652 | 1 | do CASE "OnAdd"
1653 | 1 | local world = jecs.World.new()
1654 | 1 | local Transform = world:component()
1655 | 1 | local e1 = world:entity()
1656 | 1 | world:set(Transform, jecs.OnAdd, function(entity)
1657 | 1 | CHECK(e1 == entity)
1658 | N/A | end)>
1659 | 1 | world:add(e1, Transform)
1660 | N/A | end>
1661 | N/A | >
1662 | 1 | do CASE "OnSet"
1663 | 1 | local world = jecs.World.new()
1664 | 1 | local Number = world:component()
1665 | 1 | local e1 = world:entity()
1666 | N/A | >
1667 | 1 | world:set(Number, jecs.OnSet, function(entity, data)
1668 | 1 | CHECK(e1 == entity)
1669 | 1 | CHECK(data == world:get(entity, Number))
1670 | 1 | CHECK(data == 1)
1671 | N/A | end)>
1672 | 1 | world:set(e1, Number, 1)
1673 | N/A | end>
1674 | N/A | >
1675 | 1 | do CASE("OnRemove")
1676 | 0 | do
1677 | N/A | -- basic>
1678 | 1 | local world = jecs.World.new()
1679 | 1 | local A = world:component() :: Entity
1680 | 1 | local e1 = world:entity()
1681 | 1 | world:set(A, jecs.OnRemove, function(entity)
1682 | 1 | CHECK(e1 == entity)
1683 | 1 | CHECK(world:has(e1, A))
1684 | N/A | end)>
1685 | 1 | world:add(e1, A)
1686 | N/A | >
1687 | 1 | world:remove(e1, A)
1688 | 1 | CHECK(not world:has(e1, A))
1689 | N/A | end>
1690 | 0 | do
1691 | N/A | -- [BUG] https://github.com/Ukendio/jecs/issues/118>
1692 | 1 | local world = world_new()
1693 | 1 | local A = world:component()
1694 | 1 | local B = world:component()
1695 | 1 | local e = world:entity()
1696 | N/A | >
1697 | 1 | world:set(A, jecs.OnRemove, function(entity)
1698 | 1 | world:set(entity, B, true)
1699 | 1 | CHECK(world:get(entity, A))
1700 | 1 | CHECK(world:get(entity, B))
1701 | N/A | end)>
1702 | N/A | >
1703 | 1 | world:set(e, A, true)
1704 | 1 | world:remove(e, A)
1705 | 1 | CHECK(not world:get(e, A))
1706 | 1 | CHECK(world:get(e, B))
1707 | N/A | end>
1708 | N/A | end>
1709 | N/A | end)>
1710 | N/A | >
1711 | 1 | TEST("change tracking", function()
1712 | 1 | do CASE "#1"
1713 | 1 | local world = world_new()
1714 | 1 | local Foo = world:component() :: Entity
1715 | 1 | local Previous = jecs.Rest
1716 | N/A | >
1717 | 1 | local q1 = world
1718 | 1 | :query(Foo)
1719 | 1 | :without(pair(Previous, Foo))
1720 | 0 | :cached()
1721 | N/A | >
1722 | 1 | local e1 = world:entity()
1723 | 1 | world:set(e1, Foo, 1)
1724 | 1 | local e2 = world:entity()
1725 | 1 | world:set(e2, Foo, 2)
1726 | N/A | >
1727 | 1 | local i = 0
1728 | 1 | for e, new in q1 :: any do
1729 | 2 | i += 1
1730 | 2 | world:set(e, pair(Previous, Foo), new)
1731 | N/A | end>
1732 | N/A | >
1733 | 1 | CHECK(i == 2)
1734 | 1 | local j = 0
1735 | 1 | for e, new in q1 :: any do
1736 | 0 | j += 1
1737 | 0 | world:set(e, pair(Previous, Foo), new)
1738 | N/A | end>
1739 | N/A | >
1740 | 1 | CHECK(j == 0)
1741 | N/A | end>
1742 | N/A | >
1743 | 1 | do CASE "#2"
1744 | 1 | local world = world_new()
1745 | 1 | local component = world:component() :: Entity
1746 | 1 | local tag = world:entity()
1747 | 1 | local previous = jecs.Rest
1748 | N/A | >
1749 | 1 | local q1 = world:query(component):without(pair(previous, component), tag):cached()
1750 | N/A | >
1751 | 1 | local testEntity = world:entity()
1752 | N/A | >
1753 | 1 | world:set(testEntity, component, 10)
1754 | N/A | >
1755 | 1 | local i = 0
1756 | 1 | for entity, number in q1 :: any do
1757 | 1 | i += 1
1758 | 1 | world:add(testEntity, tag)
1759 | N/A | end>
1760 | N/A | >
1761 | 1 | CHECK(i == 1)
1762 | N/A | >
1763 | 1 | for e, n in q1 :: any do
1764 | 0 | world:set(e, pair(previous, component), n)
1765 | N/A | end>
1766 | N/A | end>
1767 | N/A | >
1768 | N/A | end)>
1769 | N/A | >
1770 | 1 | TEST("repro", function()
1771 | 1 | do CASE "#1"
1772 | 1 | local world = world_new()
1773 | 1 | local reproEntity = world:component()
1774 | 1 | local components = { Cooldown = world:component() :: jecs.Entity }
1775 | 1 | world:set(reproEntity, components.Cooldown, 2)
1776 | N/A | >
1777 | 1 | local function updateCooldowns(dt: number)
1778 | 2 | local toRemove = {}
1779 | N/A | >
1780 | 2 | local it = world:query(components.Cooldown):iter()
1781 | 2 | for id, cooldown in it do
1782 | 2 | cooldown -= dt
1783 | N/A | >
1784 | 2 | if cooldown <= 0 then
1785 | 1 | table.insert(toRemove, id)
1786 | N/A | -- world:remove(id, components.Cooldown)>
1787 | 0 | else
1788 | 1 | world:set(id, components.Cooldown, cooldown)
1789 | N/A | end>
1790 | N/A | end>
1791 | N/A | >
1792 | 2 | for _, id in toRemove do
1793 | 1 | world:remove(id, components.Cooldown)
1794 | 1 | CHECK(not world:get(id, components.Cooldown))
1795 | N/A | end>
1796 | N/A | end>
1797 | N/A | >
1798 | 1 | updateCooldowns(1.5)
1799 | 1 | updateCooldowns(1.5)
1800 | N/A | end>
1801 | N/A | >
1802 | 1 | do CASE "#2" -- ISSUE #171
1803 | 1 | local world = world_new()
1804 | 1 | local component1 = world:component()
1805 | 1 | local tag1 = world:entity()
1806 | N/A | >
1807 | 1 | local query = world:query(component1):with(tag1):cached()
1808 | N/A | >
1809 | 1 | local entity = world:entity()
1810 | 1 | world:set(entity, component1, "some data")
1811 | N/A | >
1812 | 1 | local counter = 0
1813 | 1 | for x in query:iter() do
1814 | 0 | counter += 1
1815 | N/A | end>
1816 | 1 | CHECK(counter == 0)
1817 | N/A | end>
1818 | N/A | end)>
1819 | N/A | >
1820 | 1 | TEST("wildcard query", function()
1821 | 1 | do CASE "#1"
1822 | 1 | local world = world_new()
1823 | 1 | local pair = jecs.pair
1824 | N/A | >
1825 | 1 | local Relation = world:entity()
1826 | 1 | local Wildcard = jecs.Wildcard
1827 | 1 | local A = world:entity()
1828 | N/A | >
1829 | 1 | local relationship = pair(Relation, Wildcard)
1830 | 1 | local query = world:query(relationship):cached()
1831 | N/A | >
1832 | 1 | local entity = world:entity()
1833 | N/A | >
1834 | 1 | local p = pair(Relation, A)
1835 | 1 | CHECK(jecs.pair_first(world, p) == Relation)
1836 | 1 | CHECK(jecs.pair_second(world, p) == A)
1837 | 1 | local w = dwi(world)
1838 | 1 | world:add(entity, pair(Relation, A))
1839 | N/A | >
1840 | 1 | local counter = 0
1841 | 1 | for e in query:iter() do
1842 | 1 | counter += 1
1843 | N/A | end>
1844 | 1 | CHECK(counter == 1)
1845 | N/A | end>
1846 | 1 | do CASE "#2"
1847 | 1 | local world = world_new()
1848 | 1 | local pair = jecs.pair
1849 | N/A | >
1850 | 1 | local Relation = world:entity()
1851 | 1 | local Wildcard = jecs.Wildcard
1852 | 1 | local A = world:entity()
1853 | N/A | >
1854 | 1 | local relationship = pair(Relation, Wildcard)
1855 | N/A | >
1856 | 1 | local entity = world:entity()
1857 | N/A | >
1858 | 1 | world:add(entity, pair(Relation, A))
1859 | N/A | >
1860 | 1 | local counter = 0
1861 | 1 | for e in world:query(relationship):iter() do
1862 | 1 | counter += 1
1863 | N/A | end>
1864 | 1 | CHECK(counter == 1)
1865 | N/A | end>
1866 | 1 | do CASE "#3"
1867 | 1 | local world = world_new()
1868 | 1 | local pair = jecs.pair
1869 | N/A | >
1870 | 1 | local Relation = world:entity()
1871 | 1 | local Wildcard = jecs.Wildcard
1872 | 1 | local A = world:entity()
1873 | N/A | >
1874 | 1 | local entity = world:entity()
1875 | N/A | >
1876 | 1 | world:add(entity, pair(Relation, A))
1877 | N/A | >
1878 | 1 | local relationship = pair(Relation, Wildcard)
1879 | 1 | local query = world:query(relationship):cached()
1880 | N/A | >
1881 | 1 | local counter = 0
1882 | 1 | for e in query:iter() do
1883 | 1 | counter += 1
1884 | N/A | end>
1885 | 1 | CHECK(counter == 1)
1886 | N/A | end>
1887 | N/A | end)>
1888 | N/A | >
1889 | 1 | TEST("world:delete() invokes OnRemove hook", function()
1890 | 1 | do CASE "#1"
1891 | 1 | local world = world_new()
1892 | N/A | >
1893 | 1 | local A = world:entity()
1894 | 1 | local entity = world:entity()
1895 | N/A | >
1896 | 1 | local called = false
1897 | 1 | world:set(A, jecs.OnRemove, function(e)
1898 | 1 | called = true
1899 | N/A | end)>
1900 | N/A | >
1901 | 1 | world:add(entity, A)
1902 | 1 | world:delete(entity)
1903 | N/A | >
1904 | 1 | CHECK(called)
1905 | N/A | end>
1906 | 1 | do CASE "#2"
1907 | 1 | local world = world_new()
1908 | 1 | local pair = jecs.pair
1909 | N/A | >
1910 | 1 | local Relation = world:entity()
1911 | 1 | local A = world:entity()
1912 | 1 | local B = world:entity()
1913 | N/A | >
1914 | 1 | world:add(Relation, pair(jecs.OnDelete, jecs.Delete))
1915 | N/A | >
1916 | 1 | local entity = world:entity()
1917 | N/A | >
1918 | 1 | local called = false
1919 | 1 | world:set(A, jecs.OnRemove, function(e)
1920 | 1 | called = true
1921 | N/A | end)>
1922 | N/A | >
1923 | 1 | world:add(entity, A)
1924 | 1 | world:add(entity, pair(Relation, B))
1925 | N/A | >
1926 | 1 | world:delete(B)
1927 | N/A | >
1928 | 1 | CHECK(called)
1929 | N/A | end>
1930 | 1 | do CASE "#3"
1931 | 1 | local world = world_new()
1932 | 1 | local pair = jecs.pair
1933 | N/A | >
1934 | 1 | local viewingContainer = world:entity()
1935 | 1 | local character = world:entity()
1936 | 1 | local container = world:entity()
1937 | N/A | >
1938 | 1 | local called = false
1939 | 1 | world:set(viewingContainer, jecs.OnRemove, function(e)
1940 | 1 | called = true
1941 | N/A | end)>
1942 | N/A | >
1943 | 1 | world:add(character, pair(viewingContainer, container))
1944 | N/A | >
1945 | 1 | world:delete(container)
1946 | N/A | >
1947 | 1 | CHECK(called)
1948 | N/A | end>
1949 | N/A | end)>
1950 | 1 | FINISH()