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

  1. Open the editor (tap the Editor icon in the NavBar).
  2. In the file tree, navigate to the scratch directory.
  3. Open run_all_tests.lua.
  4. 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 assert for hard failures. If an assertion fails, the test stops and the error appears in red.
  • Use console.log for diagnostic output.
  • Use pcall for 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 like TEST_marker_1, memory keys like TEST_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/