Files
yaze/docs/z3ed/archive/IT-01-PHASE3-COMPLETE.md
scawful 286efdec6a Enhance ImGuiTestHarness with dynamic test integration and end-to-end validation
- Updated README.md to reflect the completion of IT-01 and the transition to end-to-end validation phase.
- Introduced a new end-to-end test script (scripts/test_harness_e2e.sh) for validating all RPC methods of the ImGuiTestHarness gRPC service.
- Implemented dynamic test functionality in ImGuiTestHarnessService for Type, Wait, and Assert methods, utilizing ImGuiTestEngine.
- Enhanced error handling and response messages for better clarity during test execution.
- Updated existing methods to support dynamic test registration and execution, ensuring robust interaction with the GUI elements.
2025-10-02 00:49:28 -04:00

9.7 KiB

IT-01 Phase 3: ImGuiTestEngine Integration - COMPLETE

Date: October 2, 2025
Status: COMPLETE - All RPC handlers implemented with ImGuiTestEngine
Time Spent: ~3 hours (implementation + testing)

Overview

Phase 3 successfully implements full ImGuiTestEngine integration for all gRPC RPC handlers. The test harness can now automate GUI interactions, wait for conditions, and validate widget state through remote procedure calls.

Implementation Summary

1. Type RPC

Purpose: Input text into GUI widgets
Implementation:

  • Uses ItemInfo() to find target widget (returns by value, check ID != 0)
  • Clicks widget to focus before typing
  • Supports clear_first flag using KeyPress(ImGuiMod_Shortcut | ImGuiKey_A) + KeyPress(ImGuiKey_Delete)
  • Types text using ItemInputValue()
  • Dynamic test registration with timeout and status polling

Example:

grpcurl -plaintext -d '{
  "target":"input:Filename",
  "text":"zelda3.sfc",
  "clear_first":true
}' 127.0.0.1:50052 yaze.test.ImGuiTestHarness/Type

2. Wait RPC

Purpose: Poll for UI conditions with timeout
Implementation:

  • Supports three condition types:
    • window_visible:WindowName - checks window exists and not hidden
    • element_visible:ElementLabel - checks element exists and has visible rect
    • element_enabled:ElementLabel - checks element not disabled
  • Configurable timeout (default 5000ms) and poll interval (default 100ms)
  • Uses ctx->Yield() to allow ImGui event processing during polling
  • Returns elapsed time in milliseconds

Example:

grpcurl -plaintext -d '{
  "condition":"window_visible:Overworld Editor",
  "timeout_ms":5000,
  "poll_interval_ms":100
}' 127.0.0.1:50052 yaze.test.ImGuiTestHarness/Wait

3. Assert RPC

Purpose: Validate GUI state and return actual vs expected values
Implementation:

  • Supports multiple assertion types:
    • visible:WindowName - checks window visibility
    • enabled:ElementLabel - checks if element is enabled
    • exists:ElementLabel - checks if element exists
    • text_contains:InputLabel:ExpectedText - validates text content (partial implementation)
  • Returns structured response with success, message, actual_value, expected_value
  • Detailed error messages for debugging

Example:

grpcurl -plaintext -d '{
  "condition":"visible:Main Window"
}' 127.0.0.1:50052 yaze.test.ImGuiTestHarness/Assert

Key API Learnings

ImGuiTestEngine API Patterns

  1. ItemInfo Returns By Value:

    // WRONG: ImGuiTestItemInfo* item = ctx->ItemInfo(label);
    // RIGHT:
    ImGuiTestItemInfo item = ctx->ItemInfo(label);
    if (item.ID == 0) { /* not found */ }
    
  2. Flag Names Changed:

    • Use ItemFlags instead of InFlags (obsolete)
    • Use ImGuiItemFlags_Disabled instead of ImGuiItemStatusFlags_Disabled
  3. Visibility Check:

    // Check if element is visible (has non-zero clipped rect)
    bool visible = (item.ID != 0 && 
                    item.RectClipped.GetWidth() > 0 && 
                    item.RectClipped.GetHeight() > 0);
    
  4. Dynamic Test Registration:

    auto test_data = std::make_shared<DynamicTestData>();
    test_data->test_func = [=](ImGuiTestContext* ctx) { /* test logic */ };
    
    ImGuiTest* test = IM_REGISTER_TEST(engine, "grpc", test_name.c_str());
    test->TestFunc = RunDynamicTest;
    test->UserData = test_data.get();
    
    ImGuiTestEngine_QueueTest(engine, test, ImGuiTestRunFlags_RunFromGui);
    
  5. Test Status Polling:

    while (test->Output.Status == ImGuiTestStatus_Queued || 
           test->Output.Status == ImGuiTestStatus_Running) {
      if (timeout_reached) break;
      std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
    
    // Cleanup
    ImGuiTestEngine_UnregisterTest(engine, test);
    

Build and Test

Build Command

cd /Users/scawful/Code/yaze
cmake --build build-grpc-test --target yaze -j$(sysctl -n hw.ncpu)

Build Status: Success (with deprecation warnings in imgui_memory_editor.h - unrelated)

Start Test Harness

./build-grpc-test/bin/yaze.app/Contents/MacOS/yaze \
  --enable_test_harness \
  --test_harness_port=50052 \
  --rom_file=assets/zelda3.sfc &

Test All RPCs

# 1. Ping - Health check
grpcurl -plaintext -import-path src/app/core/proto -proto imgui_test_harness.proto \
  -d '{"message":"test"}' 127.0.0.1:50052 yaze.test.ImGuiTestHarness/Ping

# 2. Click - Click button
grpcurl -plaintext -import-path src/app/core/proto -proto imgui_test_harness.proto \
  -d '{"target":"button:Overworld","type":"LEFT"}' \
  127.0.0.1:50052 yaze.test.ImGuiTestHarness/Click

# 3. Type - Input text
grpcurl -plaintext -import-path src/app/core/proto -proto imgui_test_harness.proto \
  -d '{"target":"input:Filename","text":"zelda3.sfc","clear_first":true}' \
  127.0.0.1:50052 yaze.test.ImGuiTestHarness/Type

# 4. Wait - Wait for condition
grpcurl -plaintext -import-path src/app/core/proto -proto imgui_test_harness.proto \
  -d '{"condition":"window_visible:Main Window","timeout_ms":5000}' \
  127.0.0.1:50052 yaze.test.ImGuiTestHarness/Wait

# 5. Assert - Validate state
grpcurl -plaintext -import-path src/app/core/proto -proto imgui_test_harness.proto \
  -d '{"condition":"visible:Main Window"}' \
  127.0.0.1:50052 yaze.test.ImGuiTestHarness/Assert

# 6. Screenshot - Capture (not yet implemented)
grpcurl -plaintext -import-path src/app/core/proto -proto imgui_test_harness.proto \
  -d '{"region":"full","format":"PNG"}' \
  127.0.0.1:50052 yaze.test.ImGuiTestHarness/Screenshot

Files Modified

Core Implementation

  1. src/app/core/imgui_test_harness_service.cc
    • Type RPC: Full implementation with ItemInfo, focus, clear, and input
    • Wait RPC: Polling loop with multiple condition types
    • Assert RPC: State validation with structured responses
    • Total: ~300 lines of new GUI automation code

No Changes Required

  • src/app/core/imgui_test_harness_service.h - interface unchanged
  • src/app/test/test_manager.{h,cc} - initialization already correct
  • Proto files - schema unchanged

Known Limitations

1. Text Retrieval (Assert RPC)

  • text_contains assertion returns placeholder string
  • Requires deeper investigation of ImGuiTestEngine text query APIs
  • Current workaround: manually check text after typing with Type RPC

2. Screenshot RPC

  • Not implemented (returns "not yet implemented" message)
  • Requires framebuffer access and image encoding
  • Planned for future enhancement

3. Error Handling

  • Test failures may not always provide detailed context
  • Consider adding more verbose logging in debug mode

Success Metrics

All Core RPCs Implemented: Ping, Click, Type, Wait, Assert
Build Successful: No compilation errors
API Compatibility: Correct ImGuiTestEngine usage patterns
Dynamic Test Registration: Tests created on-demand without pre-registration
Timeout Handling: All RPCs have configurable timeouts
Stub Fallback: Works without ImGuiTestEngine (compile-time flag)

Next Steps (Phase 4)

Priority 1: End-to-End Testing (2-3 hours)

  1. Manual Workflow Testing:

    • Start YAZE with test harness
    • Execute full workflow: Click → Type → Wait → Assert
    • Validate responses match expected behavior
    • Test error cases (widget not found, timeout, etc.)
  2. Create Test Script:

    #!/bin/bash
    # test_harness_e2e.sh
    
    # Start YAZE in background
    ./build-grpc-test/bin/yaze.app/Contents/MacOS/yaze \
      --enable_test_harness --test_harness_port=50052 \
      --rom_file=assets/zelda3.sfc &
    YAZE_PID=$!
    
    sleep 2  # Wait for startup
    
    # Test Ping
    grpcurl -plaintext -d '{"message":"test"}' \
      127.0.0.1:50052 yaze.test.ImGuiTestHarness/Ping
    
    # Test Click
    grpcurl -plaintext -d '{"target":"button:Overworld","type":"LEFT"}' \
      127.0.0.1:50052 yaze.test.ImGuiTestHarness/Click
    
    # Test Wait
    grpcurl -plaintext -d '{"condition":"window_visible:Overworld Editor","timeout_ms":5000}' \
      127.0.0.1:50052 yaze.test.ImGuiTestHarness/Wait
    
    # Cleanup
    kill $YAZE_PID
    

Priority 2: CLI Agent Integration (3-4 hours)

  1. Create z3ed agent test command:

    • Translate natural language prompts to gRPC calls
    • Chain multiple RPCs for complex workflows
    • Capture screenshots for LLM feedback
  2. Example Agent Test:

    z3ed agent test --prompt "Open the Overworld editor and verify it's visible" \
      --rom zelda3.sfc
    
    # Generated workflow:
    # 1. Click "button:Overworld"
    # 2. Wait "window_visible:Overworld Editor" (5s timeout)
    # 3. Assert "visible:Overworld Editor"
    # 4. Screenshot "full"
    

Priority 3: Documentation Update (1 hour)

  • Update E6-z3ed-implementation-plan.md with Phase 3 completion
  • Update STATE_SUMMARY_2025-10-01.mdSTATE_SUMMARY_2025-10-02.md
  • Add IT-01-PHASE3-COMPLETE.md to documentation index

Priority 4: Windows Testing (2-3 hours)

  • Build on Windows with vcpkg
  • Validate gRPC service startup
  • Test all RPCs on Windows
  • Document platform-specific issues

Conclusion

Phase 3 is complete with all core GUI automation capabilities implemented. The ImGuiTestHarness can now:

  • Click buttons and interactive elements
  • Type text into input fields
  • Wait for UI conditions (window visibility, element state)
  • Assert widget state with detailed validation
  • Handle timeouts and errors gracefully

The foundation is ready for AI-driven GUI testing and automation workflows. Next phase will focus on end-to-end integration and CLI agent tooling.


Completed: October 2, 2025
Contributors: @scawful, GitHub Copilot
License: Same as YAZE (see ../../LICENSE)