yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
ai_gui_controller_test.cc
Go to the documentation of this file.
1// Integration tests for AIGUIController
2// Tests the gRPC GUI automation with vision feedback
3
5
6#include <gmock/gmock.h>
7#include <gtest/gtest.h>
8
11
12namespace yaze {
13namespace cli {
14namespace ai {
15namespace {
16
17using ::testing::_;
18using ::testing::Return;
19
20// Mock GuiAutomationClient for testing without actual GUI
22 public:
24
25 MOCK_METHOD(absl::Status, Connect, ());
26 MOCK_METHOD(absl::StatusOr<AutomationResult>, Ping, (const std::string&));
27 MOCK_METHOD(absl::StatusOr<AutomationResult>, Click,
28 (const std::string&, ClickType));
29 MOCK_METHOD(absl::StatusOr<AutomationResult>, Type,
30 (const std::string&, const std::string&, bool));
31 MOCK_METHOD(absl::StatusOr<AutomationResult>, Wait,
32 (const std::string&, int, int));
33 MOCK_METHOD(absl::StatusOr<AutomationResult>, Assert,
34 (const std::string&));
35};
36
37class AIGUIControllerTest : public ::testing::Test {
38 protected:
39 void SetUp() override {
40 // Create mock services
41 GeminiConfig config;
42 config.api_key = "test_key";
43 config.model = "gemini-2.5-flash";
44 gemini_service_ = std::make_unique<GeminiAIService>(config);
45
46 gui_client_ = std::make_unique<MockGuiAutomationClient>();
47
48 controller_ = std::make_unique<AIGUIController>(
49 gemini_service_.get(), gui_client_.get());
50
51 ControlLoopConfig loop_config;
52 loop_config.max_iterations = 5;
53 loop_config.enable_vision_verification = false; // Disable for unit tests
54 loop_config.enable_iterative_refinement = false;
55 controller_->Initialize(loop_config);
56 }
57
58 std::unique_ptr<GeminiAIService> gemini_service_;
59 std::unique_ptr<MockGuiAutomationClient> gui_client_;
60 std::unique_ptr<AIGUIController> controller_;
61};
62
63// ============================================================================
64// Basic Action Execution Tests
65// ============================================================================
66
67TEST_F(AIGUIControllerTest, ExecuteClickAction_Success) {
69 action.parameters["target"] = "button:Test";
70 action.parameters["click_type"] = "left";
71
72 AutomationResult result;
73 result.success = true;
74 result.message = "Click successful";
75
76 EXPECT_CALL(*gui_client_, Click("button:Test", ClickType::kLeft))
77 .WillOnce(Return(result));
78
79 auto status = controller_->ExecuteSingleAction(action, false);
80
81 ASSERT_TRUE(status.ok()) << status.status().message();
82 EXPECT_TRUE(status->action_successful);
83}
84
85TEST_F(AIGUIControllerTest, ExecuteClickAction_Failure) {
87 action.parameters["target"] = "button:NonExistent";
88
89 AutomationResult result;
90 result.success = false;
91 result.message = "Button not found";
92
93 EXPECT_CALL(*gui_client_, Click("button:NonExistent", ClickType::kLeft))
94 .WillOnce(Return(result));
95
96 auto status = controller_->ExecuteSingleAction(action, false);
97
98 EXPECT_FALSE(status.ok());
99 EXPECT_THAT(status.status().message(),
100 ::testing::HasSubstr("Click action failed"));
101}
102
103// ============================================================================
104// Type Action Tests
105// ============================================================================
106
107TEST_F(AIGUIControllerTest, ExecuteTypeAction_Success) {
108 AIAction action(AIActionType::kSelectTile); // Using SelectTile as a type action
109 action.parameters["target"] = "input:TileID";
110 action.parameters["text"] = "0x42";
111 action.parameters["clear_first"] = "true";
112
113 AutomationResult result;
114 result.success = true;
115 result.message = "Text entered";
116
117 EXPECT_CALL(*gui_client_, Type("input:TileID", "0x42", true))
118 .WillOnce(Return(result));
119
120 auto status = controller_->ExecuteSingleAction(action, false);
121
122 ASSERT_TRUE(status.ok());
123 EXPECT_TRUE(status->action_successful);
124}
125
126// ============================================================================
127// Wait Action Tests
128// ============================================================================
129
130TEST_F(AIGUIControllerTest, ExecuteWaitAction_Success) {
132 action.parameters["condition"] = "window:OverworldEditor";
133 action.parameters["timeout_ms"] = "2000";
134
135 AutomationResult result;
136 result.success = true;
137 result.message = "Condition met";
138
139 EXPECT_CALL(*gui_client_, Wait("window:OverworldEditor", 2000, 100))
140 .WillOnce(Return(result));
141
142 auto status = controller_->ExecuteSingleAction(action, false);
143
144 ASSERT_TRUE(status.ok());
145 EXPECT_TRUE(status->action_successful);
146}
147
148TEST_F(AIGUIControllerTest, ExecuteWaitAction_Timeout) {
150 action.parameters["condition"] = "window:NonExistentWindow";
151 action.parameters["timeout_ms"] = "100";
152
153 AutomationResult result;
154 result.success = false;
155 result.message = "Timeout waiting for condition";
156
157 EXPECT_CALL(*gui_client_, Wait("window:NonExistentWindow", 100, 100))
158 .WillOnce(Return(result));
159
160 auto status = controller_->ExecuteSingleAction(action, false);
161
162 EXPECT_FALSE(status.ok());
163}
164
165// ============================================================================
166// Verify/Assert Action Tests
167// ============================================================================
168
169TEST_F(AIGUIControllerTest, ExecuteVerifyAction_Success) {
171 action.parameters["condition"] = "tile_placed";
172
173 AutomationResult result;
174 result.success = true;
175 result.message = "Assertion passed";
176 result.expected_value = "0x42";
177 result.actual_value = "0x42";
178
179 EXPECT_CALL(*gui_client_, Assert("tile_placed"))
180 .WillOnce(Return(result));
181
182 auto status = controller_->ExecuteSingleAction(action, false);
183
184 ASSERT_TRUE(status.ok());
185 EXPECT_TRUE(status->action_successful);
186}
187
188TEST_F(AIGUIControllerTest, ExecuteVerifyAction_Failure) {
190 action.parameters["condition"] = "tile_placed";
191
192 AutomationResult result;
193 result.success = false;
194 result.message = "Assertion failed";
195 result.expected_value = "0x42";
196 result.actual_value = "0x00";
197
198 EXPECT_CALL(*gui_client_, Assert("tile_placed"))
199 .WillOnce(Return(result));
200
201 auto status = controller_->ExecuteSingleAction(action, false);
202
203 EXPECT_FALSE(status.ok());
204 EXPECT_THAT(status.status().message(),
205 ::testing::HasSubstr("Assert action failed"));
206 EXPECT_THAT(status.status().message(),
207 ::testing::HasSubstr("expected: 0x42"));
208 EXPECT_THAT(status.status().message(),
209 ::testing::HasSubstr("actual: 0x00"));
210}
211
212// ============================================================================
213// Complex Tile Placement Action Tests
214// ============================================================================
215
216TEST_F(AIGUIControllerTest, ExecutePlaceTileAction_CompleteFlow) {
218 action.parameters["map_id"] = "5";
219 action.parameters["x"] = "10";
220 action.parameters["y"] = "20";
221 action.parameters["tile"] = "0x42";
222
223 AutomationResult result;
224 result.success = true;
225
226 // Expect sequence: open menu, wait for window, set map ID, click position
227 testing::InSequence seq;
228
229 EXPECT_CALL(*gui_client_, Click("menu:Overworld", ClickType::kLeft))
230 .WillOnce(Return(result));
231
232 EXPECT_CALL(*gui_client_, Wait("window:Overworld Editor", 2000, 100))
233 .WillOnce(Return(result));
234
235 EXPECT_CALL(*gui_client_, Type("input:Map ID", "5", true))
236 .WillOnce(Return(result));
237
238 EXPECT_CALL(*gui_client_, Click(::testing::_, ClickType::kLeft))
239 .WillOnce(Return(result));
240
241 auto status = controller_->ExecuteSingleAction(action, false);
242
243 ASSERT_TRUE(status.ok()) << status.status().message();
244 EXPECT_TRUE(status->action_successful);
245}
246
247// ============================================================================
248// Multiple Actions Execution Tests
249// ============================================================================
250
251TEST_F(AIGUIControllerTest, ExecuteActions_MultipleActionsSuccess) {
252 std::vector<AIAction> actions;
253
255 action1.parameters["target"] = "button:Overworld";
256 actions.push_back(action1);
257
259 action2.parameters["condition"] = "window:OverworldEditor";
260 actions.push_back(action2);
261
262 AutomationResult success_result;
263 success_result.success = true;
264
265 EXPECT_CALL(*gui_client_, Click("button:Overworld", ClickType::kLeft))
266 .WillOnce(Return(success_result));
267
268 EXPECT_CALL(*gui_client_, Wait("window:OverworldEditor", 5000, 100))
269 .WillOnce(Return(success_result));
270
271 auto result = controller_->ExecuteActions(actions);
272
273 ASSERT_TRUE(result.ok()) << result.status().message();
274 EXPECT_TRUE(result->success);
275 EXPECT_EQ(result->actions_executed.size(), 2);
276}
277
278TEST_F(AIGUIControllerTest, ExecuteActions_StopsOnFirstFailure) {
279 std::vector<AIAction> actions;
280
282 action1.parameters["target"] = "button:Test";
283 actions.push_back(action1);
284
286 action2.parameters["target"] = "button:NeverReached";
287 actions.push_back(action2);
288
289 AutomationResult failure_result;
290 failure_result.success = false;
291 failure_result.message = "First action failed";
292
293 EXPECT_CALL(*gui_client_, Click("button:Test", ClickType::kLeft))
294 .WillOnce(Return(failure_result));
295
296 // Second action should never be called
297 EXPECT_CALL(*gui_client_, Click("button:NeverReached", _))
298 .Times(0);
299
300 auto result = controller_->ExecuteActions(actions);
301
302 EXPECT_FALSE(result.ok());
303 EXPECT_EQ(result->actions_executed.size(), 1);
304}
305
306// ============================================================================
307// Error Handling Tests
308// ============================================================================
309
310TEST_F(AIGUIControllerTest, ExecuteAction_InvalidActionType) {
312
313 auto status = controller_->ExecuteSingleAction(action, false);
314
315 EXPECT_FALSE(status.ok());
316 EXPECT_THAT(status.status().message(),
317 ::testing::HasSubstr("Action type not implemented"));
318}
319
320TEST_F(AIGUIControllerTest, ExecutePlaceTileAction_MissingParameters) {
322 // Missing required parameters
323
324 auto status = controller_->ExecuteSingleAction(action, false);
325
326 EXPECT_FALSE(status.ok());
327 EXPECT_THAT(status.status().message(),
328 ::testing::HasSubstr("requires map_id, x, y, and tile"));
329}
330
331} // namespace
332} // namespace ai
333} // namespace cli
334} // namespace yaze
Client for automating YAZE GUI through gRPC.
MOCK_METHOD(absl::StatusOr< AutomationResult >, Assert,(const std::string &))
MOCK_METHOD(absl::StatusOr< AutomationResult >, Click,(const std::string &, ClickType))
MOCK_METHOD(absl::StatusOr< AutomationResult >, Type,(const std::string &, const std::string &, bool))
MOCK_METHOD(absl::StatusOr< AutomationResult >, Wait,(const std::string &, int, int))
MOCK_METHOD(absl::StatusOr< AutomationResult >, Ping,(const std::string &))
ClickType
Type of click action to perform.
Main namespace for the application.
Definition controller.cc:20
Result of a GUI automation action.
Represents a single action to be performed in the GUI.
std::map< std::string, std::string > parameters
Configuration for the AI GUI control loop.