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.
This commit is contained in:
scawful
2025-10-02 00:49:28 -04:00
parent 4320b67da1
commit 286efdec6a
19 changed files with 7325 additions and 222 deletions

View File

@@ -0,0 +1,299 @@
# IT-01 Phase 2: File Modification Checklist
**Quick Reference**: Exactly which files to edit and what to change
## Files to Modify (4 files)
### 1. `src/app/core/imgui_test_harness_service.h`
**What to change**: Add TestManager member to service class
**Line ~20-30** (in ImGuiTestHarnessServiceImpl class):
```cpp
class ImGuiTestHarnessServiceImpl {
public:
// ADD THIS LINE:
explicit ImGuiTestHarnessServiceImpl(TestManager* test_manager)
: test_manager_(test_manager) {}
absl::Status Ping(const PingRequest* request, PingResponse* response);
absl::Status Click(const ClickRequest* request, ClickResponse* response);
absl::Status Type(const TypeRequest* request, TypeResponse* response);
absl::Status Wait(const WaitRequest* request, WaitResponse* response);
absl::Status Assert(const AssertRequest* request, AssertResponse* response);
absl::Status Screenshot(const ScreenshotRequest* request,
ScreenshotResponse* response);
private:
TestManager* test_manager_; // ADD THIS LINE
};
```
**Line ~50** (in ImGuiTestHarnessServer class):
```cpp
class ImGuiTestHarnessServer {
public:
static ImGuiTestHarnessServer& Instance();
// CHANGE THIS LINE - add second parameter:
absl::Status Start(int port, TestManager* test_manager);
// ... rest stays the same
};
```
---
### 2. `src/app/core/imgui_test_harness_service.cc`
**What to change**: Add includes and implement Click handler
**Top of file** (after existing includes, around line 10):
```cpp
// ADD THESE INCLUDES:
#include "app/test/test_manager.h"
#include "imgui_test_engine/imgui_te_engine.h"
#include "imgui_test_engine/imgui_te_context.h"
```
**In `Start()` method** (around line 100):
```cpp
absl::Status ImGuiTestHarnessServer::Start(int port, TestManager* test_manager) {
if (server_) {
return absl::FailedPreconditionError("Server already running");
}
// ADD THESE LINES:
if (!test_manager) {
return absl::InvalidArgumentError("TestManager cannot be null");
}
// CHANGE THIS LINE to pass test_manager:
service_ = std::make_unique<ImGuiTestHarnessServiceImpl>(test_manager);
// ... rest of method stays the same
```
**In `Click()` method** (around line 130):
Replace the entire stub implementation with the code from `IT-01-PHASE2-IMPLEMENTATION-GUIDE.md` (Step 2.2).
Quick version (see full guide for complete code):
```cpp
absl::Status ImGuiTestHarnessServiceImpl::Click(
const ClickRequest* request,
ClickResponse* response) {
auto start = std::chrono::steady_clock::now();
// Get TestEngine
ImGuiTestEngine* engine = test_manager_->GetUITestEngine();
if (!engine) {
response->set_success(false);
response->set_message("ImGuiTestEngine not initialized");
return absl::OkStatus();
}
// Parse target: "button:Open ROM"
std::string target = request->target();
size_t colon_pos = target.find(':');
if (colon_pos == std::string::npos) {
response->set_success(false);
response->set_message("Invalid target format. Use 'type:label'");
return absl::OkStatus();
}
std::string widget_label = target.substr(colon_pos + 1);
// Create test context
std::string context_name = absl::StrFormat("grpc_click_%lld",
std::chrono::system_clock::now().time_since_epoch().count());
ImGuiTestContext* ctx = ImGuiTestEngine_CreateContext(engine, context_name.c_str());
if (!ctx) {
response->set_success(false);
response->set_message("Failed to create test context");
return absl::OkStatus();
}
// Find widget
ImGuiTestItemInfo* item = ImGuiTestEngine_FindItemByLabel(
ctx, widget_label.c_str(), NULL);
bool success = false;
std::string message;
if (!item) {
message = absl::StrFormat("Widget not found: %s", widget_label);
} else {
// Convert click type
ImGuiMouseButton mouse_button = ImGuiMouseButton_Left;
switch (request->type()) {
case ClickRequest::LEFT: mouse_button = ImGuiMouseButton_Left; break;
case ClickRequest::RIGHT: mouse_button = ImGuiMouseButton_Right; break;
case ClickRequest::MIDDLE: mouse_button = ImGuiMouseButton_Middle; break;
}
// Click it!
ImGuiTestEngine_ItemClick(ctx, item->ID, mouse_button);
success = true;
message = absl::StrFormat("Clicked '%s'", widget_label);
}
// Cleanup
ImGuiTestEngine_DestroyContext(ctx);
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - start);
response->set_success(success);
response->set_message(message);
response->set_execution_time_ms(elapsed.count());
return absl::OkStatus();
}
```
---
### 3. `src/app/main.cc`
**What to change**: Pass TestManager when starting gRPC server
**Find the section** with `#ifdef YAZE_WITH_GRPC` (probably around line 200-300):
```cpp
#ifdef YAZE_WITH_GRPC
if (absl::GetFlag(FLAGS_enable_test_harness)) {
// ADD THESE LINES:
auto* test_manager = yaze::test::TestManager::GetInstance();
if (!test_manager) {
std::cerr << "ERROR: TestManager not initialized. "
<< "Cannot start test harness.\n";
return 1;
}
auto& harness = yaze::test::ImGuiTestHarnessServer::Instance();
// CHANGE THIS LINE to pass test_manager:
auto status = harness.Start(
absl::GetFlag(FLAGS_test_harness_port),
test_manager // ADD THIS ARGUMENT
);
if (!status.ok()) {
std::cerr << "Failed to start test harness: "
<< status.message() << "\n";
return 1;
}
}
#endif
```
---
### 4. Build and Test
After making the above changes:
```bash
# Rebuild
cd /Users/scawful/Code/yaze
cmake --build build-grpc-test --target yaze -j8
# Should compile without errors
# If compilation fails, check:
# - Include paths are correct
# - TestManager header is found
# - ImGuiTestEngine headers are found
# Start YAZE with test harness
./build-grpc-test/bin/yaze.app/Contents/MacOS/yaze --enable_test_harness &
# Wait for startup (2-3 seconds)
sleep 3
# Test Ping first
grpcurl -plaintext -import-path src/app/core/proto -proto imgui_test_harness.proto \
-d '{"message":"Hello"}' 127.0.0.1:50051 yaze.test.ImGuiTestHarness/Ping
# Should return: {"message":"Pong: Hello", "timestampMs":"...", "yazeVersion":"0.3.2"}
# Test Click (adjust button label to match YAZE UI)
grpcurl -plaintext -import-path src/app/core/proto -proto imgui_test_harness.proto \
-d '{"target":"button:Overworld","type":"LEFT"}' \
127.0.0.1:50051 yaze.test.ImGuiTestHarness/Click
# Expected: {"success":true, "message":"Clicked 'Overworld'", "executionTimeMs":5}
# And in YAZE GUI: The Overworld button should actually click!
```
---
## Compilation Troubleshooting
### Error: "TestManager not found"
**Fix**: Add include to service file:
```cpp
#include "app/test/test_manager.h"
```
### Error: "ImGuiTestEngine_CreateContext not declared"
**Fix**: Add includes:
```cpp
#include "imgui_test_engine/imgui_te_engine.h"
#include "imgui_test_engine/imgui_te_context.h"
```
### Error: "no matching function for call to Start"
**Fix**: Update main.cc to pass test_manager as second argument.
### Linker error: "undefined reference to ImGuiTestEngine"
**Fix**: Ensure CMake links ImGuiTestEngine:
```cmake
if(YAZE_WITH_GRPC AND TARGET ImGuiTestEngine)
target_link_libraries(yaze PRIVATE ImGuiTestEngine)
endif()
```
---
## Testing Checklist
After compilation succeeds:
- [ ] Server starts without errors
- [ ] Ping RPC returns version
- [ ] Click RPC with fake button returns "Widget not found" (expected)
- [ ] Click RPC with real button returns success
- [ ] Button actually clicks in YAZE GUI
If first 4 work but button doesn't click:
- Check button label is exact match
- Try with a different button
- Enable ImGuiTestEngine debug output
---
## What's Next
Once Click works:
1. Implement Type handler (similar pattern)
2. Implement Wait handler (polling loop)
3. Implement Assert handler (state queries)
4. Create end-to-end test script
See `IT-01-PHASE2-IMPLEMENTATION-GUIDE.md` for full implementations.
---
**Estimated Time**:
- Code changes: 1-2 hours
- Testing/debugging: 1-2 hours
- **Total: 2-4 hours for Click handler working end-to-end**
Good luck! 🚀