Run Skill Tests
CUST/OS ships with test files covering all tool scripts. Tests validate that tool functions load, accept parameters, return expected structures, and do not crash.
Run the full test suite
- Open the editor (tap the Editor icon in the NavBar).
- In the file tree, navigate to the scratch directory.
- Open
run_all_tests.lua. - Tap Run.
The console shows pass/fail for each test file as it executes. At the end, a summary line reports total passed, failed, and skipped. Cleanup runs automatically after all tests complete.
Run an individual test
Open any *_test.lua file from the scratch directory and tap Run. The test executes in isolation and reports its result in the console. This is faster than running the full suite when you are iterating on a specific tool.
Test categories
The test runner groups tests into three informal buckets based on how safe it is to actually invoke the tool:
| Category | Behavior | Example |
|---|---|---|
| READ_ONLY | Calls the tool and validates the return structure. No side effects. | get_self_position_test.lua |
| PROCEDURAL | Creates something, validates it, then cleans up. | place_marker_test.lua |
| EXISTENCE_ONLY | Checks the tool function is loaded and callable. Does not actually invoke it. Used for high-impact tools (comms broadcasts, package distribution, beacons) whose side effects are irreversible. | send_chat_test.lua |
The category is a grouping convention inside run_all_tests.lua, not a header on individual test files.
Write a new test
Follow this pattern:
-- my_new_tool_test.lua
-- Tests that my_new_tool returns a well-formed table for a benign query
local result = my_new_tool({query = "TEST_something"})
-- Check return type
assert(type(result) == "table", "expected table, got " .. type(result))
-- Check expected keys
assert(result.status ~= nil, "missing status field")
assert(result.status == "success", "expected success, got " .. tostring(result.status))
-- Log diagnostics for the console
console.log("result count: " .. tostring(#(result.items or {})))
return "PASS"
Key points:
- Return
"PASS"at the end. The test runner checks for this exact string. - Use
assertfor hard failures. If an assertion fails, the test stops and the error appears in red. - Use
console.logfor diagnostic output. - Use
pcallfor tools that may fail depending on map state:
local ok, result = pcall(find_items, {query = "TEST_probe"})
if not ok then
console.log("skipped — no map items: " .. tostring(result))
return "PASS"
end
- Prefix test artifacts with
TEST_so cleanup can find them. Use callsigns likeTEST_marker_1, memory keys likeTEST_mem_entry, etc.
Cleanup
The test runner cleans up after the suite completes:
- Removes markers with
TEST_-prefixed callsigns - Clears test memory entries with
TEST_-prefixed keys
If you run an individual PROCEDURAL test and it fails mid-execution, test artifacts may be left on the map. Run the full suite once to clean up, or manually delete TEST_-prefixed markers.
Pushing scratches to a device
Scratch tests ship in the CUST/OS skill kit at github.com/nimbusxr/custos under scratches/. The kit's install.sh pushes them for you:
git clone https://github.com/nimbusxr/custos.git
cd custos
./install.sh
To push just the scratches without re-pushing the rest of the kit:
adb push scratches/*.lua /sdcard/atak/custos/scratch/