initial: inventory-list-display-test v0.1.0 — 9 assertions
Covers: create round-trip, non-container loud-error, action register/unregister lifecycle, duplicate label + non-function callback loud-errors, empty default action-set, contents ordering via _test_get_rows, default icon-resolver property reads, custom icon-resolver override via set_icon_resolver.
This commit is contained in:
24
LICENSE
Normal file
24
LICENSE
Normal file
@@ -0,0 +1,24 @@
|
||||
Copyright (c) 2026 Calic. All rights reserved.
|
||||
|
||||
This software is part of the Sporel platform — **Tier 1 (Official /
|
||||
Proprietary)** content per the Three-Tier Licensing Model documented in
|
||||
`meta/docs/archive/design/vision.md §Licensing Model` (current source;
|
||||
migration to `meta/docs/architecture/licensing-model.md` pending).
|
||||
|
||||
⚠ **WIP — Legal review required before public launch.** The terms below
|
||||
reflect design intent only; the formalized license framework will be
|
||||
finalized through legal counsel before the first public release. Until
|
||||
then, this notice serves as a placeholder defending the platform owner's
|
||||
rights against unintentional re-licensing.
|
||||
|
||||
No license is granted to copy, modify, distribute, sublicense, or otherwise
|
||||
use this software in any form without prior written permission from the
|
||||
copyright holder.
|
||||
|
||||
References:
|
||||
- Tier 1 (this file): all rights reserved, proprietary, sold/distributed
|
||||
via official channels (Steam, etc.)
|
||||
- Tier 2 (Semi-Commercial Co-Development): bilateral contracts, revenue-
|
||||
share — see vision.md §Licensing Model
|
||||
- Tier 3 (Community Content): CC BY-NC-SA 4.0 + asymmetric CLA — applies
|
||||
to community-uploaded libs/modules/assets, not this repo
|
||||
32
README.md
Normal file
32
README.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# lib-core.inventory-list-display-test
|
||||
|
||||
Test module for `lib-core.inventory-list-display`. Runs 9 assertions
|
||||
headless via `engine.test.*` (TAP output).
|
||||
|
||||
**Version:** 0.1.0
|
||||
**Lib-ID:** lib-core.inventory-list-display-test
|
||||
**Role:** test
|
||||
**Requires:** lib-core.inventory-list-display v0.1.0, lib-core.inventory-list v0.1.0, lib-core.composition v0.2.0
|
||||
|
||||
## Running
|
||||
|
||||
```bash
|
||||
export PATH="/c/msys64/mingw64/bin:$PATH"
|
||||
cd ~/Projects/Sporel/sporel-engine
|
||||
SPOREL_LIBS_DIR=~/Projects/Sporel/sporel-libs SPOREL_CI=1 SPOREL_LOG_STDERR=1 \
|
||||
./build/Sporel.exe --test-libs=lib-core.inventory-list-display-test 2>&1 | tail -15
|
||||
```
|
||||
|
||||
Expected: `pass=N fail=0` where N >= 9.
|
||||
|
||||
## Assertions
|
||||
|
||||
1. `create` returns widget_def with `render`, `handle_input`, and correct `title`
|
||||
2. `create` with a non-container entity loud-errors
|
||||
3. `register_action` + `unregister_action` round-trip completes without error
|
||||
4. Duplicate action label loud-errors
|
||||
5. Non-function callback loud-errors
|
||||
6. Default action-set is empty; `unregister_action` on unknown label is a no-op
|
||||
7. `_test_get_rows` returns items in `inventory.contents` order
|
||||
8. Default icon-resolver reads `sprite_atlas` and `sprite_uv.*` from item properties
|
||||
9. Custom icon-resolver is invoked after `set_icon_resolver`
|
||||
138
init.lua
Normal file
138
init.lua
Normal file
@@ -0,0 +1,138 @@
|
||||
-- lib-core.inventory-list-display-test — v0.1.0
|
||||
-- 9 assertions covering widget creation, action lifecycle, resolver
|
||||
-- overrides, and content ordering.
|
||||
-- Pattern follows P.2.4 Test-Module-Pattern; TAP output via engine.test.*
|
||||
|
||||
local panel = require("lib-core.panel")
|
||||
local composition = require("lib-core.composition")
|
||||
local inventory = require("lib-core.inventory-list")
|
||||
local display = require("lib-core.inventory-list-display")
|
||||
local T = engine.test
|
||||
|
||||
local M = {}
|
||||
|
||||
-- ----------------------------------------------------------------
|
||||
-- Template definitions (shared across all assertions in this run)
|
||||
-- All template IDs are unique to this test lib to avoid clashes.
|
||||
-- ----------------------------------------------------------------
|
||||
|
||||
-- Container template: kind="list", no constraints.
|
||||
composition.define_template{
|
||||
id = "test_backpack",
|
||||
properties = { position = {x = 0, y = 0} },
|
||||
tags = {"renderable"},
|
||||
container = { kind = "list" },
|
||||
}
|
||||
|
||||
-- Item template: individual stack_mode + Atlas-UV properties for icon tests.
|
||||
composition.define_template{
|
||||
id = "test_item",
|
||||
properties = {
|
||||
stack_mode = "individual",
|
||||
sprite_atlas = "tex.png",
|
||||
sprite_uv = {x = 0, y = 0, w = 8, h = 8},
|
||||
position = {x = 0, y = 0},
|
||||
},
|
||||
tags = {"renderable", "item"},
|
||||
}
|
||||
|
||||
function M.run_tests(ctx)
|
||||
local bp = composition.create{template = "test_backpack"}
|
||||
|
||||
-- ================================================================
|
||||
-- 1. create returns widget_def with render + handle_input + title
|
||||
-- ================================================================
|
||||
|
||||
local widget = display.create(bp, { title = "Backpack" })
|
||||
T.assert(widget ~= nil, "create returns non-nil")
|
||||
T.equals(type(widget.render), "function", "widget has render fn")
|
||||
T.equals(type(widget.handle_input), "function", "widget has handle_input")
|
||||
T.equals(widget.title, "Backpack", "title set from opts")
|
||||
|
||||
-- ================================================================
|
||||
-- 2. create with non-container entity -> loud-error
|
||||
-- test_item has no container block, so composition.get_container
|
||||
-- returns nil, and display.create loud-errors.
|
||||
-- ================================================================
|
||||
|
||||
local plain = composition.create{template = "test_item"}
|
||||
local ok2 = pcall(display.create, plain)
|
||||
T.assert(not ok2, "create(non_container) loud-errors")
|
||||
|
||||
-- ================================================================
|
||||
-- 3. register_action + unregister_action round-trip (no error)
|
||||
-- ================================================================
|
||||
|
||||
display.register_action(widget, "Drop", function(item, c) end)
|
||||
display.register_action(widget, "Inspect", function(item, c) end)
|
||||
display.unregister_action(widget, "Drop")
|
||||
T.assert(true, "register/unregister round-trip completes without error")
|
||||
|
||||
-- ================================================================
|
||||
-- 4. Duplicate action label -> loud-error
|
||||
-- "Drop" was unregistered above; re-register then attempt duplicate.
|
||||
-- ================================================================
|
||||
|
||||
display.register_action(widget, "Drop", function() end)
|
||||
local ok4 = pcall(display.register_action, widget, "Drop", function() end)
|
||||
T.assert(not ok4, "duplicate action label loud-errors")
|
||||
|
||||
-- ================================================================
|
||||
-- 5. Non-function callback -> loud-error
|
||||
-- ================================================================
|
||||
|
||||
local ok5 = pcall(display.register_action, widget, "X", "not_fn")
|
||||
T.assert(not ok5, "non-function callback loud-errors")
|
||||
|
||||
-- ================================================================
|
||||
-- 6. Default action-set is empty; unregister of unknown label is no-op
|
||||
-- A fresh widget has no implicit actions. Unregistering a missing
|
||||
-- label must not error (idempotent).
|
||||
-- ================================================================
|
||||
|
||||
local widget2 = display.create(composition.create{template = "test_backpack"})
|
||||
display.unregister_action(widget2, "Drop") -- no-op: not registered
|
||||
T.assert(true, "default action-set empty; unregister of unknown label is no-op")
|
||||
|
||||
-- ================================================================
|
||||
-- 7. Rows match inventory.contents order (_test_get_rows)
|
||||
-- ================================================================
|
||||
|
||||
local item_a = composition.create{template = "test_item"}
|
||||
local item_b = composition.create{template = "test_item"}
|
||||
inventory.add(bp, item_a)
|
||||
inventory.add(bp, item_b)
|
||||
local rows = display._test_get_rows(widget)
|
||||
T.assert(#rows == 2, "rows reflect inventory.contents count")
|
||||
|
||||
-- Verify ordering: item_a was added first, must appear first.
|
||||
-- Compare by composition.reg_id (stable identity; entity userdata has no __eq).
|
||||
local rid_a = item_a:get_property("composition.reg_id")
|
||||
local rid_r1 = rows[1]:get_property("composition.reg_id")
|
||||
T.assert(rid_a ~= nil and rid_r1 == rid_a,
|
||||
"rows[1] matches item_a (insertion order preserved)")
|
||||
|
||||
-- ================================================================
|
||||
-- 8. Default icon-resolver reads sprite_atlas + sprite_uv from properties
|
||||
-- ================================================================
|
||||
|
||||
local icon = display._test_resolve_icon(widget, item_a)
|
||||
T.assert(icon ~= nil, "default icon-resolver returns non-nil table")
|
||||
T.equals(icon.atlas, "tex.png", "icon.atlas matches sprite_atlas property")
|
||||
T.equals(icon.uv.x, 0, "icon.uv.x matches sprite_uv.x property")
|
||||
|
||||
-- ================================================================
|
||||
-- 9. Custom icon-resolver invoked after set_icon_resolver
|
||||
-- ================================================================
|
||||
|
||||
local custom_called = false
|
||||
display.set_icon_resolver(widget, function(item)
|
||||
custom_called = true
|
||||
return {atlas = "custom.png", uv = {x = 1, y = 2, w = 3, h = 4}}
|
||||
end)
|
||||
local icon2 = display._test_resolve_icon(widget, item_a)
|
||||
T.assert(custom_called, "custom icon-resolver was invoked after set_icon_resolver")
|
||||
T.equals(icon2.atlas, "custom.png", "custom resolver returns custom atlas")
|
||||
end
|
||||
|
||||
return M
|
||||
6
manifest.core
Normal file
6
manifest.core
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"id": "lib-core.inventory-list-display-test.core",
|
||||
"kind": "lib",
|
||||
"version": "0.1.0",
|
||||
"license": "Proprietary"
|
||||
}
|
||||
11
manifest.lib
Normal file
11
manifest.lib
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"id": "lib-core.inventory-list-display-test",
|
||||
"role": "test",
|
||||
"version": "0.1.0",
|
||||
"api_min": "0.1",
|
||||
"deps": [
|
||||
{"id": "lib-core.inventory-list-display", "version": "0.1.0"},
|
||||
{"id": "lib-core.inventory-list", "version": "0.1.0"},
|
||||
{"id": "lib-core.composition", "version": "0.2.0"}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user