From ebb4d8132ffc4754577aca470ddd6dc5129410f9 Mon Sep 17 00:00:00 2001 From: Calic Date: Sat, 13 Jun 2026 23:09:50 +0200 Subject: [PATCH] =?UTF-8?q?initial:=20panel-test=20v0.1.0=20=E2=80=94=2010?= =?UTF-8?q?=20assertions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- LICENSE | 24 +++++++++ README.md | 33 ++++++++++++ init.lua | 147 ++++++++++++++++++++++++++++++++++++++++++++++++++ manifest.core | 6 +++ manifest.lib | 9 ++++ 5 files changed, 219 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 init.lua create mode 100644 manifest.core create mode 100644 manifest.lib diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..81c9d65 --- /dev/null +++ b/LICENSE @@ -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 diff --git a/README.md b/README.md new file mode 100644 index 0000000..835b963 --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +# lib-core.panel-test + +Test-module for `lib-core.panel`. 10 assertions covering registry lifecycle, +theme validation, error handling, pause-gate, and context-menu dispatch. + +**Version:** 0.1.0 +**Lib-ID:** lib-core.panel-test +**Requires:** lib-core.panel v0.1.0 +**Tags:** test, panel, ui + +## 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.panel-test 2>&1 | tail -15 +``` + +Expected: `pass=10 fail=0`. + +## Assertion Summary + +1. register + open / close round-trip +2. close after open works +3. toggle twice opens then closes +4. open with unknown widget_id raises an error +5. set_theme with valid override is visible via get_theme +6. set_theme with unknown key raises an error +7. register without render function raises an error +8. pause_on_open=true widget makes is_pausing() true; close makes it false +9. context-menu action callback invoked on click inside its row +10. non-pause widget: is_pausing() is false diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..234fe4e --- /dev/null +++ b/init.lua @@ -0,0 +1,147 @@ +-- lib-core.panel-test v0.1.0 — 10 assertions +-- Exercises: registry lifecycle, theme, errors, pause-gate, context-menu dispatch. +-- Render paths are NOT tested here: engine.render.* is blocked in test-mode +-- (assert_in_render_phase guard in engine_lua_render.c). Input dispatch is +-- exercised via panel._dispatch_event_for_test(event). + +local panel = require("lib-core.panel") +local T = engine.test + +local M = {} + +-- ----------------------------------------------------------------------- +-- Shared dummy widget factories +-- ----------------------------------------------------------------------- + +local function make_widget(overrides) + local w = { + title = "Test Widget", + render = function(_ctx) end, + handle_input = function(_ctx, _event) end, + } + if overrides then + for k, v in pairs(overrides) do w[k] = v end + end + return w +end + +-- ----------------------------------------------------------------------- +-- Test suite +-- ----------------------------------------------------------------------- + +function M.run_tests(_ctx) + + -- ------------------------------------------------------------------ + -- 1. register + open / close round-trip + -- ------------------------------------------------------------------ + panel.register("w1", make_widget()) + panel.open("w1") + T.assert(panel.is_open(), "register + open makes is_open() true") + + -- ------------------------------------------------------------------ + -- 2. close after open works + -- ------------------------------------------------------------------ + panel.close() + T.assert(not panel.is_open(), "close() after open makes is_open() false") + + -- ------------------------------------------------------------------ + -- 3. toggle twice → opens then closes + -- ------------------------------------------------------------------ + panel.toggle("w1") + T.assert(panel.is_open(), "toggle (first) opens panel") + panel.toggle("w1") + T.assert(not panel.is_open(), "toggle (second) closes panel") + + -- ------------------------------------------------------------------ + -- 4. open("unknown_id") → loud-error + -- ------------------------------------------------------------------ + local ok_unknown = pcall(panel.open, "does_not_exist") + T.assert(not ok_unknown, + "panel.open with unknown widget_id raises an error") + + -- ------------------------------------------------------------------ + -- 5. set_theme with valid override + get_theme reads merged + -- ------------------------------------------------------------------ + panel.set_theme({ padding = 16 }) + local t = panel.get_theme() + T.equals(t.padding, 16, "set_theme override is visible via get_theme") + + -- Restore for subsequent tests + panel.set_theme({ padding = 8 }) + + -- ------------------------------------------------------------------ + -- 6. set_theme with unknown key → loud-error + -- ------------------------------------------------------------------ + local ok_bad_key = pcall(panel.set_theme, { nonexistent_key = 99 }) + T.assert(not ok_bad_key, + "set_theme with unknown key raises an error") + + -- ------------------------------------------------------------------ + -- 7. register without render function → loud-error + -- ------------------------------------------------------------------ + local ok_no_render = pcall(panel.register, "w_bad", { + title = "Bad", + handle_input = function() end, + -- render intentionally missing + }) + T.assert(not ok_no_render, + "panel.register without render function raises an error") + + -- ------------------------------------------------------------------ + -- 8. pause_on_open=true widget makes is_pausing()=true; close → false + -- ------------------------------------------------------------------ + panel.register("w_pause", make_widget({ pause_on_open = true })) + panel.open("w_pause") + T.assert(panel.is_pausing(), + "is_pausing() is true when active widget has pause_on_open=true") + panel.close() + T.assert(not panel.is_pausing(), + "is_pausing() is false after close()") + + -- ------------------------------------------------------------------ + -- 9. show_context_menu + simulated click on action row → callback invoked + -- ------------------------------------------------------------------ + + -- Need an active widget for context-menu dispatch path + panel.open("w1") + + local callback_fired = false + + -- The context-menu renders at a known position; compute the click coords: + -- show_context_menu is called at (100, 100). With padding=8, row_height=24: + -- row 1 spans y=100..(100+24). Use x=120, y=110 (inside row 1). + panel.show_context_menu(100, 100, { + { + label = "Do Thing", + callback = function(_info) + callback_fired = true + end, + }, + }) + + -- Simulate a left-click inside the first action row + panel._dispatch_event_for_test({ + kind = "click", + x = 120, + y = 110, + button = "left", + }) + + T.assert(callback_fired, + "context-menu action callback is invoked on click inside its row") + + -- ------------------------------------------------------------------ + -- 10. non-pause widget: is_pausing() = false + -- ------------------------------------------------------------------ + panel.close() -- close w1 from test 9 + panel.open("w1") -- w1 has no pause_on_open + T.assert(not panel.is_pausing(), + "is_pausing() is false for widget without pause_on_open") + + -- Cleanup + panel.close() + panel.unregister("w1") + panel.unregister("w_pause") +end + +return M diff --git a/manifest.core b/manifest.core new file mode 100644 index 0000000..0350f4f --- /dev/null +++ b/manifest.core @@ -0,0 +1,6 @@ +{ + "id": "lib-core.panel-test.core", + "kind": "lib", + "version": "0.1.0", + "license": "Proprietary" +} diff --git a/manifest.lib b/manifest.lib new file mode 100644 index 0000000..385bdc6 --- /dev/null +++ b/manifest.lib @@ -0,0 +1,9 @@ +{ + "id": "lib-core.panel-test", + "role": "test", + "version": "0.1.0", + "api_min": "0.1", + "deps": [ + {"id": "lib-core.panel", "version": "0.1.0"} + ] +}