ワンクリックで
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);