docs: migrate README to API-Doc-Convention (S2-cascade.libs test-lib)
Restructured per ADR-0038 with Coverage-Statement-Block per Fault- Injection-Audit-Convention. Topology auto-populated. Pre-commit hook installed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
13
.githooks/pre-commit
Normal file
13
.githooks/pre-commit
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Sporel API-Doc-Convention pre-commit hook.
|
||||||
|
SPOREL_EXE="${SPOREL_EXE:-$(command -v Sporel.exe 2>/dev/null || command -v sporel 2>/dev/null)}"
|
||||||
|
if [ -z "$SPOREL_EXE" ]; then
|
||||||
|
echo "INFO: Sporel.exe not on PATH. Skipping lint." >&2
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
"$SPOREL_EXE" --lint="$(pwd)" --fix
|
||||||
|
RC=$?
|
||||||
|
if git diff --cached --name-only | grep -qx 'README.md'; then
|
||||||
|
git add README.md
|
||||||
|
fi
|
||||||
|
exit $RC
|
||||||
119
README.md
119
README.md
@@ -1,48 +1,91 @@
|
|||||||
# sporel-module-lib-core.selection-test
|
# lib-core.selection-test
|
||||||
|
|
||||||
P.3.6 reference test-module — validates `lib-core.selection` API contracts
|
Test-lib for `lib-core.selection` (Tests-as-Libs per ADR-0037).
|
||||||
via the `engine.test.*` assertion surface.
|
|
||||||
|
|
||||||
- Module-ID: `lib-core.selection-test`
|
**Version:** 0.1.0
|
||||||
- Version: `0.1.0`
|
**Lib-ID:** lib-core.selection-test
|
||||||
- Kind: `test-module` (headless; runs init() once + exits)
|
**Requires:** lib-core.selection v>=0.1.0, lib-core.input v>=0.4.0, lib-core.camera v>=0.3.0
|
||||||
- Spec: `meta/docs/superpowers/specs/2026-05-14-p3-6-lib-selection-design.md`
|
**Tags:** test, selection
|
||||||
|
|
||||||
## Run
|
## Topology
|
||||||
|
|
||||||
|
<!-- topology:start (auto-generated; do not edit) -->
|
||||||
|
```mermaid
|
||||||
|
graph LR
|
||||||
|
this["lib-core.selection-test"]
|
||||||
|
lib_core_selection["lib-core.selection"]
|
||||||
|
this --> lib_core_selection
|
||||||
|
lib_core_input["lib-core.input"]
|
||||||
|
this --> lib_core_input
|
||||||
|
lib_core_camera["lib-core.camera"]
|
||||||
|
this --> lib_core_camera
|
||||||
|
```
|
||||||
|
<!-- topology:end -->
|
||||||
|
|
||||||
|
## Coverage Statement
|
||||||
|
|
||||||
|
Per Fault-Injection-Audit-Convention (2026-05-16):
|
||||||
|
|
||||||
|
| Section | Assertion-Count | Coverage |
|
||||||
|
|---|---|---|
|
||||||
|
| Register / Unregister | 3 | Handle positivity, `count_registered` after register/unregister |
|
||||||
|
| Set-Query (initial empty) | 2 | `is_empty()` true, `count() == 0` |
|
||||||
|
| Set-Mutation | 5 | `add`/`contains`, `count==1`, `remove`/`!contains`, `toggle` add/remove, `clear` -> empty |
|
||||||
|
| Unregister cascade | 1 | `unregister` removes from selection |
|
||||||
|
| Drag-Threshold | 2 | Default = 4, `set_drag_threshold` writes |
|
||||||
|
| Drag-State + box_rect | 2 | Initial `drag_state == "idle"`, `box_rect()` nil while idle |
|
||||||
|
| Enabled-Flag | 2 | Default true, `set_enabled(false)` writes |
|
||||||
|
| Visual-Setters | 2 | `set_drag_box_color` writes RGBA, `set_highlight_color` writes RGBA |
|
||||||
|
| Error-Handling | 3 | `set_drag_threshold(-1)` errors, `add(unregistered)` errors, `toggle(unregistered)` errors |
|
||||||
|
| **TOTAL** | **22** | |
|
||||||
|
|
||||||
|
### Tested-Surface
|
||||||
|
- `selection.register`, `selection.unregister`, `selection.count_registered`
|
||||||
|
- `selection.is_empty`, `selection.count`, `selection.contains`
|
||||||
|
- `selection.add`, `selection.remove`, `selection.toggle`, `selection.clear`
|
||||||
|
- `selection.drag_threshold`, `selection.set_drag_threshold`
|
||||||
|
- `selection.drag_state`, `selection.box_rect`
|
||||||
|
- `selection.enabled`, `selection.set_enabled`
|
||||||
|
- `selection.set_drag_box_color`, `selection.drag_box_color`
|
||||||
|
- `selection.set_highlight_color`, `selection.highlight_color`
|
||||||
|
|
||||||
|
### Fault-Injection Audit (2026-05-16)
|
||||||
|
|
||||||
|
Verified via deliberate-mutation in fixture-mirror; mutations reverted after observation.
|
||||||
|
|
||||||
|
#### Caught Mutations
|
||||||
|
|
||||||
|
| # | Mutation | Caught by |
|
||||||
|
|---|---|---|
|
||||||
|
| M1 | `M.contains` prefixed with `do return false end` (always false) | Assertions `add → contains` + `toggle adds when absent` fail (2 failures) |
|
||||||
|
| M2 | `M.count` prefixed with `do return 0 end` (always zero) | Assertion `count = 1 after add` fails (expected 1, got 0) |
|
||||||
|
|
||||||
|
#### Uncaught Mutations (Gaps) — Cheap-Fill Applied
|
||||||
|
|
||||||
|
| # | Original Gap | Fill | Re-Inject Verifies |
|
||||||
|
|---|---|---|---|
|
||||||
|
| M3 | `M.add` unregistered-handle guard removed | Added `pcall(add(99999))` + `pcall(toggle(99999))` error assertions | ✓ |
|
||||||
|
|
||||||
|
### Structurally Untestable
|
||||||
|
- Mouse-drag selection-box interaction — requires real mouse events; headless `drag_state` stays `idle`.
|
||||||
|
- Render output of highlight + drag-box visuals (verified manually).
|
||||||
|
|
||||||
|
### Deferred
|
||||||
|
- Integration test with simulated mouse events (Future-Slice B).
|
||||||
|
|
||||||
|
## Running
|
||||||
|
|
||||||
|
Via `--test-libs=lib-core.selection-test` (Smoke P0-S-batch Stage):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
Sporel.exe --test-libs=lib-core.selection-test
|
Sporel.exe --libs-dir=<libs-path> --test-libs=lib-core.selection-test
|
||||||
```
|
```
|
||||||
|
|
||||||
Output is TAP version 13 on stdout; exit-code = failure-count (0 = all pass).
|
Output is TAP version 13 on stdout; exit-code = failure-count (0 = all pass).
|
||||||
|
|
||||||
## Coverage Statements (2026-05-16 Fault-Injection Audit)
|
## References
|
||||||
|
|
||||||
Verified via deliberate-mutation in fixture-mirror; mutations reverted after observation.
|
- ADR-0037 (Tests-as-Libs Convention)
|
||||||
|
- ADR-0038 (API-Doc-Convention)
|
||||||
### Caught Mutations
|
- Production-Lib: `~/Projects/Sporel/sporel-libs/lib-core/selection/README.md`
|
||||||
|
- Spec: `meta/docs/superpowers/specs/2026-05-14-p3-6-lib-selection-design.md`
|
||||||
| # | Mutation | Caught by |
|
|
||||||
|---|---|---|
|
|
||||||
| M1 | `M.contains` prefixed with `do return false end` (always false) | Assertions "add → contains" (ok 6) and "toggle adds when absent" (ok 9) — 2 failures |
|
|
||||||
| M2 | `M.count` prefixed with `do return 0 end` (always zero) | Assertion "count = 1 after add" (ok 7) — expected 1 got 0 |
|
|
||||||
|
|
||||||
### Uncaught Mutations (Gaps)
|
|
||||||
|
|
||||||
| # | Mutation | Why uncaught | Severity |
|
|
||||||
|---|---|---|---|
|
|
||||||
| M3 | `M.add` unregistered-handle guard removed (any handle accepted silently) | No test calls `add(unregistered_handle)` and expects an error | Medium |
|
|
||||||
|
|
||||||
**Resolution per Gap:**
|
|
||||||
- Gap M3: Cheap-fill applied — two `pcall` error assertions added for `add(99999)` and `toggle(99999)` (commit see below).
|
|
||||||
|
|
||||||
### Cheap-Gap-Fills Applied
|
|
||||||
|
|
||||||
| Gap | New Assertion | Re-Inject Verifies |
|
|
||||||
|---|---|---|
|
|
||||||
| M3 | `add(unregistered) errors` — `pcall(function() selection.add(99999) end)` must return false | ✓ |
|
|
||||||
| M3 | `toggle(unregistered) errors` — `pcall(function() selection.toggle(99999) end)` must return false | ✓ |
|
|
||||||
|
|
||||||
### Tested Production-Lib Surface
|
|
||||||
|
|
||||||
`lib-core.selection` v0.1.0 — Tests cover: `register`, `unregister`, `count_registered`, `is_empty`, `count`, `contains`, `add`, `remove`, `toggle`, `clear`, `drag_threshold`, `set_drag_threshold`, `drag_state`, `box_rect`, `enabled`, `set_enabled`, `set_drag_box_color`, `drag_box_color`, `set_highlight_color`, `highlight_color`.
|
|
||||||
|
|||||||
3
scripts/install-hooks.sh
Normal file
3
scripts/install-hooks.sh
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
git config core.hooksPath .githooks
|
||||||
|
echo "Hooks activated."
|
||||||
Reference in New Issue
Block a user