원클릭으로
lxa-testing
General TDD workflow, integration/unit testing strategies, and test requirements checklist.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
General TDD workflow, integration/unit testing strategies, and test requirements checklist.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
AmigaOS implementation rules, code style, memory management, and debugging tips.
Specific strategies for headless Graphics and Intuition library testing.
Roadmap-driven development process, version management, and build system instructions.
| name | lxa-testing |
| description | General TDD workflow, integration/unit testing strategies, and test requirements checklist. |
This skill details the testing requirements and strategies for the lxa project.
Location: tests/drivers/
Host-side test drivers use the liblxa API and Google Test framework. This is the standard approach for all interactive UI testing.
Key Features:
LxaUITest base classCurrent helper surface:
WaitForWindowDrawn() / lxa_wait_window_drawn() for visible-content readinessGetGadgetCount(), GetGadgetInfo(), and GetGadgets() for Intuition gadget introspectionClickGadget() for geometry-driven interactionCaptureWindow() / lxa_capture_screen() for screenshots on failureExample: tests/drivers/simplegad_gtest.cpp
#include "lxa_test.h"
using namespace lxa::testing;
class SimpleGadgetTest : public LxaUITest {
protected:
void SetUp() override {
LxaUITest::SetUp();
ASSERT_EQ(lxa_load_program("SYS:SimpleGad", ""), 0);
ASSERT_TRUE(WaitForWindows(1, 5000));
ASSERT_TRUE(GetWindowInfo(0, &window_info));
// Let task reach event loop
RunCyclesWithVBlank(20);
}
};
TEST_F(SimpleGadgetTest, ClickButton) {
ClearOutput();
Click(window_info.x + 50, window_info.y + 40);
RunCyclesWithVBlank(20);
std::string output = GetOutput();
EXPECT_NE(output.find("GADGETUP"), std::string::npos);
}
Location: tests/dos/, tests/exec/, etc.
exec_gtest.cpp, dos_gtest.cpp).Location: tests/unit/
Before completing a task:
ctest or make lxa_tests../build.sh # From project root (/home/guenter/projects/amiga/lxa/src/lxa)
# ALWAYS use -j16 for parallel execution, ALWAYS use --timeout 180 to detect hangs
cd build && ctest --output-on-failure --timeout 180 -j16
# Equivalent from project root:
ctest --test-dir build --output-on-failure --timeout 180 -j16
| Flag | Wall Time | When to Use |
|---|---|---|
-j1 | Slowest | Only for debugging unusual resource conflicts |
-j4 | Good | Acceptable local parallelism |
-j16 | Best default | Project standard for routine runs |
-j16 | Similar to -j16 | Usually no meaningful wall-time gain |
-j38 | Similar to -j16 | Safe, but typically unnecessary |
Why -j16 is optimal: the formerly oversized interactive suites are now
split into shards and use persistent fixtures, which keeps the full-suite wall
time around ~145 seconds on this machine class. Higher parallelism remains safe,
but usually does not improve the end-to-end runtime enough to justify using it
as the default.
Tests are fully isolated: Each test runs its own emulator instance with independent memory. No shared files, ports, or state. Any parallelism level is safe.
# By test name (via ctest):
ctest --test-dir build --output-on-failure --timeout 180 -R shell_gtest
# By GTest filter (direct binary execution):
./build/tests/drivers/shell_gtest --gtest_filter="ShellTest.Variables"
# Multiple filters:
./build/tests/drivers/dos_gtest --gtest_filter="DosTest.Lock*"
# List all tests in a binary:
./build/tests/drivers/exec_gtest --gtest_list_tests
When debugging intermittent failures, use a loop:
# Run a specific test N times with timeout
for i in $(seq 1 50); do
timeout 30 ./build/tests/drivers/shell_gtest \
--gtest_filter="ShellTest.Variables" 2>&1 | tail -1
done
# Full suite reliability (N consecutive runs):
for run in $(seq 1 5); do
echo "=== Run $run ==="
ctest --test-dir build --output-on-failure --timeout 180 -j16 2>&1 | grep "tests passed"
done
Tests with explicit CTest timeouts (in tests/drivers/CMakeLists.txt):
| Test | Timeout | Typical Use |
|---|---|---|
simplegad_behavior_gtest | 60s | behavior shard |
simplegad_pixels_gtest | 90s | pixel shard |
easyrequest_gtest | 120s | requester flow |
rgbboxes_gtest | 25s | graphics regression |
cluster2_navigation_gtest | 60s | interactive app shard |
All other tests use CTest's default timeout (1500s). If adding a new test
that takes >60 seconds, add an explicit TIMEOUT property in CMakeLists.txt.
Execution:
RunProgram(path, args) - Load and run program until exitRunCycles(n) - Run n CPU cyclesRunCyclesWithVBlank(iterations, cycles_per_iteration) - Run cycles with VBlank interruptsWaitForWindows(count, timeout_ms) - Wait for windows to openGetWindowInfo(index, info) - Get window position/sizeWaitForWindowDrawn(index, timeout_ms) - Wait for non-empty visible window contentEvent Injection:
Click(x, y, button) - Click at positionPressKey(rawkey, qualifier) - Key pressTypeString(str) - Type stringClickGadget(index, window_index, button) - Click gadget by queried geometryIntrospection / Artifacts:
GetGadgetCount(window_index) - Get tracked gadget countGetGadgetInfo(gadget_index, info, window_index) - Query one gadgetGetGadgets(window_index) - Enumerate gadgetsCaptureWindow(path, index) - Save tracked window imagelxa_capture_screen(path) - Save full screen imageOutput Capture:
GetOutput() - Get program output as stringClearOutput() - Clear output buffer-v (verbose).doc/test-reliability-report.md for methodology.
Use the reliability loop from Section 5.5 to reproduce.The test suite runs 63 tests in ~145 seconds wall-time with -j16. Previous
optimizations (Phases 106-107) reduced this from ~210s through:
display_update_planar()/display_refresh_all()
are skipped in headless VBlank; s_display_dirty flag auto-flushes on
lxa_read_pixel()/lxa_capture_*() calls.lxa_is_idle() checks if TaskReady list is empty.
lxa_run_until_idle() returns early when all tasks are blocked.SetUpTestSuite() /
TearDownTestSuite() to load the app once per binary, not once per test.lxa_inject_string() uses 10x50K with idle
early-return. lxa_inject_mouse_click() uses 6 settle iterations with idle.
lxa_inject_drag() uses 3x200K per step (VBlank-driven, not idle-detected,
because Intuition renders in interrupt context).| Operation | Current Budget | Real 68000 Need | Over-Provision |
|---|---|---|---|
| Key press (inject_string) | 500,000 (10×50K) | ~2,000 | 250× |
| Mouse click | ~300,000 (6 iters) | ~10,000 | 30× |
| Drag step (inject_drag) | 600,000 (3×200K) | ~70,000 | 9× |
| App startup (typical) | 5-10M | ~1-2M | 5× |
| Settling after action | 1-3M | ~200K | 5-15× |
WaitForWindowDrawn(), window count
checks, or pixel change detection instead of flat RunCyclesWithVBlank(200, 50000).SetUpTestSuite() to load the app once. Follow the pattern in
simplegad_gtest.cpp or cluster2_gtest.cpp.30 seconds, shard it from the start.
TEST_F to a sharded driver, you MUST
update the corresponding add_gtest_driver_shard FILTER string in
tests/drivers/CMakeLists.txt. Missing this silently orphans the test — ctest
runs the binary but the new test is never executed. The tools/check_shard_coverage.py
CI check (Phase 0) will catch violations, but update FILTER in the same commit.GetGadgetCount() returns 0 for these. Tests
must use raw coordinate clicks and pixel-change verification instead of
gadget introspection.These APIs are planned but not yet implemented. Do not use until the corresponding phase lands.
// After Phase 130: semantic text assertions without pixel-counting
std::vector<std::string> text_log;
lxa_set_text_hook([](const char *s, int n, int, int, void *ud) {
((std::vector<std::string>*)ud)->push_back(std::string(s, n));
}, &text_log);
// ... run app ...
lxa_clear_text_hook();
EXPECT_TRUE(std::any_of(text_log.begin(), text_log.end(),
[](const auto &s){ return s.find("EDITOR") != std::string::npos; }));
// After Phase 131: detect open/close cycles without window-count polling
lxa_drain_events(events, &count);
// events[] contains LXA_EVENT_OPEN_WINDOW, LXA_EVENT_CLOSE_WINDOW, etc.
// After Phase 131: assert menu structure without hardcoded coordinates
lxa_menu_strip_t *strip = lxa_get_menu_strip(0);
lxa_menu_info_t info = lxa_get_menu_info(strip, 0, -1); // menu 0, no item
EXPECT_STREQ(info.name, "Project");
EXPECT_TRUE(info.enabled);