# 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
<anonymous>:120 | 1
<anonymous>:136 | 1
<anonymous>:174 | 1
<anonymous>:184 | 1
<anonymous>:189 | 1
getTargets:192 | 2
setAttacksAndEats:212 | 3
<anonymous>:240 | 1
<anonymous>:256 | 1
<anonymous>:312 | 1
<anonymous>:386 | 1
<anonymous>:435 | 1
<anonymous>:446 | 1
<anonymous>:453 | 1
<anonymous>:475 | 1
<anonymous>:514 | 1
<anonymous>:572 | 1
<anonymous>:596 | 1
<anonymous>:896 | 1
<anonymous>:1020 | 1
<anonymous>:1048 | 1
<anonymous>:1085 | 1
<anonymous>:1184 | 1
<anonymous>:1213 | 1
<anonymous>:1231 | 1
<anonymous>:1242 | 1
<anonymous>:1250 | 1
<anonymous>:1325 | 1
<anonymous>:1330 | 1
<anonymous>:1370 | 1
<anonymous>:1391 | 1
<anonymous>:1512 | 1
<anonymous>:1541 | 1
<anonymous>:1639 | 1
<anonymous>:1651 | 1
<anonymous>:1656 | 1
<anonymous>:1667 | 1
<anonymous>:1681 | 1
<anonymous>:1697 | 1
<anonymous>:1711 | 1
<anonymous>:1770 | 1
updateCooldowns:1777 | 2
<anonymous>:1820 | 1
<anonymous>:1889 | 1
<anonymous>:1897 | 1
<anonymous>:1919 | 1
<anonymous>: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<T=nil> = jecs.Entity<T>
>
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<number>
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<boolean>
259 | 1 | local B = world:component() :: jecs.Id<boolean>
260 | 1 | local C = world:component() :: jecs.Id<boolean>
>
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<boolean>
586 | 1 | local C2 = world:component() :: jecs.Id<boolean>
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<string>, "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<boolean>
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<number>
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<number>
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<number> }
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()