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

288 lines
9.7 KiB
Markdown

# 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**:
```bash
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**:
```bash
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**:
```bash
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**:
```cpp
// 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**:
```cpp
// 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**:
```cpp
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**:
```cpp
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
```bash
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
```bash
./build-grpc-test/bin/yaze.app/Contents/MacOS/yaze \
--enable_test_harness \
--test_harness_port=50052 \
--rom_file=assets/zelda3.sfc &
```
### Test All RPCs
```bash
# 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**:
```bash
#!/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**:
```bash
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.md` → `STATE_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)