diff --git a/init.lua b/init.lua index 5314bb2..260c7c0 100644 --- a/init.lua +++ b/init.lua @@ -8,6 +8,21 @@ local T = engine.test local M = {} +-- entity.get_children() returns fresh userdata wrappers on each call; +-- there is no __eq on entity userdata, so == is Lua-pointer equality +-- (always false for different wrappers of the same engine entity). +-- Compare by composition.reg_id (stable property on all composition +-- entities) instead. +local function same_entity(a, b) + local ra = a and a:get_property("composition.reg_id") + local rb = b and b:get_property("composition.reg_id") + if ra and ra ~= 0 and rb and rb ~= 0 then + return ra == rb + end + -- Fallback for raw entities: pointer equality (may be unreliable). + return a == b +end + -- ---------------------------------------------------------------- -- Template definitions (shared across tests) -- All template names are unique within this test run. @@ -120,7 +135,7 @@ function M.run_tests(ctx) local contents2 = inventory.contents(bag1) local found_sword1 = false for _, it in ipairs(contents2) do - if it == sword1 then found_sword1 = true; break end + if same_entity(it, sword1) then found_sword1 = true; break end end T.assert(found_sword1, "2b. after add: contents(container) contains item") @@ -143,8 +158,8 @@ function M.run_tests(ctx) local found3_s = false local found3_p = false for _, it in ipairs(contents3) do - if it == sword3 then found3_s = true end - if it == potion3 then found3_p = true end + if same_entity(it, sword3) then found3_s = true end + if same_entity(it, potion3) then found3_p = true end end T.assert(found3_s and found3_p, "3b. two adds → both items in contents") @@ -200,24 +215,18 @@ function M.run_tests(ctx) "7. add item with stack_mode='stack' → Loud-Error") -- ================================================================ - -- Assertion 8: add to container with kind != "list" → Loud-Error - -- (Template-level catch already fires, but runtime check is the - -- double-belt tested here via a raw entity.create() bypass.) - -- We test the double-belt by attempting to add to a composition - -- entity whose template has a deferred constraint — this cannot - -- be constructed via define_template (it loud-errors), so we test - -- the "no container block" path which is the closest available - -- runtime-only path not gated by template-load. - -- The template-level gate for kind!="list" is covered in - -- composition-test (assertion 6 there). Here we verify inventory - -- itself also loud-errors on a non-container-template entity. + -- Assertion 8: add on non-composition entity (no reg_id) → Loud-Error + -- Uses raw entity.create() (not composition.create) so the entity + -- has no composition.reg_id. composition.get_container returns nil, + -- which propagates through validate_container as a Loud-Error. + -- Distinct from assertion 5 (composition entity, no container block). -- ================================================================ - local plain8 = composition.create{ template = "inv_test.plain_entity" } + local raw8 = entity.create() local sword8 = composition.create{ template = "inv_test.sword" } - local ok8 = pcall(inventory.add, plain8, sword8) + local ok8 = pcall(inventory.add, raw8, sword8) T.assert(not ok8, - "8. add to entity with no container block (non-container) → Loud-Error") + "8. add on non-composition entity (no reg_id) → Loud-Error") -- ================================================================ -- Assertion 9: add sets renderable tag off on item @@ -230,7 +239,7 @@ function M.run_tests(ctx) local renderable_before9 = composition.list_by_tag("renderable") local in_renderable_before9 = false for _, e in ipairs(renderable_before9) do - if e == sword9 then in_renderable_before9 = true; break end + if same_entity(e, sword9) then in_renderable_before9 = true; break end end T.assert(in_renderable_before9, "9a. item is in renderable index before add") @@ -240,7 +249,7 @@ function M.run_tests(ctx) local renderable_after9 = composition.list_by_tag("renderable") local in_renderable_after9 = false for _, e in ipairs(renderable_after9) do - if e == sword9 then in_renderable_after9 = true; break end + if same_entity(e, sword9) then in_renderable_after9 = true; break end end T.assert(not in_renderable_after9, "9b. add: item removed from renderable index via composition.set_tag") @@ -254,7 +263,7 @@ function M.run_tests(ctx) local renderable_after_remove10 = composition.list_by_tag("renderable") local restored10 = false for _, e in ipairs(renderable_after_remove10) do - if e == sword9 then restored10 = true; break end + if same_entity(e, sword9) then restored10 = true; break end end T.assert(restored10, "10. remove: item restored to renderable index via composition.set_tag") @@ -285,16 +294,13 @@ function M.run_tests(ctx) local contents11 = inventory.contents(bag11) T.assert(#contents11 == 2 and - contents11[1] == item11a and - contents11[2] == item11b, + same_entity(contents11[1], item11a) and + same_entity(contents11[2], item11b), "11b. reconstruction: contents ordered by slot-n, items correct") -- Next add must land on item.8 (max=7, so 7+1=8), not item.1. local item11c = composition.create{ template = "inv_test.sword" } - -- item11c starts renderable; set_tag will be called by add. - -- But item11a and item11b were attached directly (bypass add), - -- so we must declare stack_mode; they have it via template. - -- Detach item11c from any parent first (it has none here). + -- item11c has no prior parent; set_tag is invoked by add. inventory.add(bag11, item11c) local children11 = bag11:get_children() T.assert(children11["item.8"] ~= nil,