|
5 | 5 | #include <thread> |
6 | 6 | #include <iostream> |
7 | 7 | #include <vector> |
| 8 | +#include <atomic> |
| 9 | +#include <mutex> |
| 10 | +#include <condition_variable> |
8 | 11 |
|
9 | 12 | #include "TestElements.h" |
10 | 13 | #include "../../src/Mouse.h" |
11 | 14 | #include "../../src/Utils.h" |
12 | 15 |
|
13 | 16 | namespace RobotTest { |
14 | 17 |
|
| 18 | +// Test states for thread communication |
| 19 | +enum class TestState { |
| 20 | + IDLE, |
| 21 | + INITIALIZING, |
| 22 | + MOVING_TO_START, |
| 23 | + CLICKING, |
| 24 | + PRESSING_MOUSE, |
| 25 | + MOVING_TO_END, |
| 26 | + RELEASING_MOUSE, |
| 27 | + VALIDATING, |
| 28 | + COMPLETED, |
| 29 | + FAILED |
| 30 | +}; |
| 31 | + |
15 | 32 | class MouseTests { |
16 | 33 | public: |
17 | 34 | MouseTests(SDL_Renderer* renderer, SDL_Window* window) |
18 | | - : renderer(renderer), window(window) { |
| 35 | + : renderer(renderer), window(window), testPassed(false), |
| 36 | + testState(TestState::IDLE), testNeedsRendering(false) { |
19 | 37 |
|
20 | 38 | // Initialize drag elements for testing - make it larger and more visible |
21 | 39 | dragElements.push_back(DragElement( |
@@ -59,16 +77,30 @@ class MouseTests { |
59 | 77 | SDL_RenderDrawLine(renderer, localMouseX, localMouseY-10, localMouseX, localMouseY+10); |
60 | 78 |
|
61 | 79 | // Draw status box with info about mouse position |
62 | | - SDL_Rect posRect = {10, 10, 180, 40}; |
| 80 | + SDL_Rect posRect = {10, 10, 280, 40}; |
63 | 81 | SDL_SetRenderDrawColor(renderer, 40, 40, 40, 255); |
64 | 82 | SDL_RenderFillRect(renderer, &posRect); |
65 | 83 |
|
66 | 84 | // Draw border around status box |
67 | 85 | SDL_SetRenderDrawColor(renderer, 100, 100, 100, 255); |
68 | 86 | SDL_RenderDrawRect(renderer, &posRect); |
69 | 87 |
|
70 | | - // Unfortunately, we can't draw text directly as we're not using SDL_ttf library |
71 | | - // But we leave the box to show where coordinates would be displayed |
| 88 | + // Optional: Draw test state information |
| 89 | + std::string stateText; |
| 90 | + switch (testState) { |
| 91 | + case TestState::IDLE: stateText = "IDLE"; break; |
| 92 | + case TestState::INITIALIZING: stateText = "INITIALIZING"; break; |
| 93 | + case TestState::MOVING_TO_START: stateText = "MOVING TO START"; break; |
| 94 | + case TestState::CLICKING: stateText = "CLICKING"; break; |
| 95 | + case TestState::PRESSING_MOUSE: stateText = "PRESSING MOUSE"; break; |
| 96 | + case TestState::MOVING_TO_END: stateText = "MOVING TO END"; break; |
| 97 | + case TestState::RELEASING_MOUSE: stateText = "RELEASING MOUSE"; break; |
| 98 | + case TestState::VALIDATING: stateText = "VALIDATING"; break; |
| 99 | + case TestState::COMPLETED: stateText = "COMPLETED"; break; |
| 100 | + case TestState::FAILED: stateText = "FAILED"; break; |
| 101 | + } |
| 102 | + |
| 103 | + // Draw test state - in a real app we'd use SDL_ttf, but we're just showing the approach |
72 | 104 | } |
73 | 105 |
|
74 | 106 | void handleEvent(const SDL_Event& event) { |
@@ -121,96 +153,187 @@ class MouseTests { |
121 | 153 | return {x + windowX, y + windowY}; |
122 | 154 | } |
123 | 155 |
|
124 | | - // Test drag functionality |
125 | | - bool testMouseDragging() { |
126 | | - std::cout << "Testing mouse dragging..." << std::endl; |
127 | | - reset(); |
| 156 | + // This function runs in a separate thread and performs the mouse actions |
| 157 | + // without directly calling SDL functions |
| 158 | + void runDragTestThread() { |
| 159 | + std::cout << "Starting mouse drag test in a thread..." << std::endl; |
128 | 160 |
|
129 | | - if (dragElements.empty()) { |
130 | | - std::cout << "No drag elements to test" << std::endl; |
131 | | - return false; |
132 | | - } |
| 161 | + // Set initial state |
| 162 | + testState = TestState::INITIALIZING; |
| 163 | + testNeedsRendering = true; |
133 | 164 |
|
134 | | - // Get first drag element |
135 | | - auto& dragElement = dragElements[0]; |
136 | | - SDL_Rect startRect = dragElement.getRect(); |
| 165 | + // Wait for main thread to process this state |
| 166 | + std::this_thread::sleep_for(std::chrono::milliseconds(500)); |
137 | 167 |
|
138 | | - // Process pending SDL events |
139 | | - SDL_Event event; |
140 | | - while (SDL_PollEvent(&event)) { |
141 | | - handleEvent(event); |
142 | | - } |
| 168 | + // Get first drag element position (we'll calculate using window coordinates in main thread) |
| 169 | + int startX = 0, startY = 0, expectedX = 0, expectedY = 0; |
| 170 | + { |
| 171 | + std::lock_guard<std::mutex> lock(testMutex); |
143 | 172 |
|
144 | | - // Get window position |
145 | | - int windowX, windowY; |
146 | | - SDL_GetWindowPosition(window, &windowX, &windowY); |
147 | | - std::cout << "Window position: (" << windowX << ", " << windowY << ")" << std::endl; |
| 173 | + if (dragElements.empty()) { |
| 174 | + std::cout << "No drag elements to test" << std::endl; |
| 175 | + testState = TestState::FAILED; |
| 176 | + testNeedsRendering = true; |
| 177 | + return; |
| 178 | + } |
148 | 179 |
|
149 | | - // Start position (center of element) in window coordinates |
150 | | - int startX = startRect.x + startRect.w/2; |
151 | | - int startY = startRect.y + startRect.h/2; |
| 180 | + auto& dragElement = dragElements[0]; |
| 181 | + SDL_Rect startRect = dragElement.getRect(); |
| 182 | + |
| 183 | + // Start position (center of element) in window coordinates |
| 184 | + startX = startRect.x + startRect.w/2; |
| 185 | + startY = startRect.y + startRect.h/2; |
| 186 | + |
| 187 | + // Calculate expected end position |
| 188 | + expectedX = startRect.x + 100; // 100px to the right |
| 189 | + expectedY = startRect.y + 50; // 50px down |
| 190 | + } |
152 | 191 |
|
153 | 192 | // Convert to screen coordinates |
154 | 193 | Robot::Point startPos = windowToScreen(startX, startY); |
155 | | - |
156 | | - // End position (100px to the right) in screen coordinates |
157 | 194 | Robot::Point endPos = windowToScreen(startX + 100, startY + 50); |
158 | 195 |
|
159 | 196 | std::cout << "Start position (screen): (" << startPos.x << ", " << startPos.y << ")" << std::endl; |
160 | 197 | std::cout << "End position (screen): (" << endPos.x << ", " << endPos.y << ")" << std::endl; |
161 | 198 |
|
162 | 199 | // Move to start position |
| 200 | + testState = TestState::MOVING_TO_START; |
| 201 | + testNeedsRendering = true; |
| 202 | + std::cout << "Moving to start position..." << std::endl; |
163 | 203 | Robot::Mouse::Move(startPos); |
164 | 204 | Robot::delay(300); |
165 | 205 |
|
166 | | - // click on the screen |
167 | | - Robot::Mouse::Click(Robot::MouseButton::LEFT_BUTTON); |
| 206 | + // Click to ensure element is ready for dragging |
| 207 | + testState = TestState::CLICKING; |
| 208 | + testNeedsRendering = true; |
| 209 | + std::cout << "Clicking to select drag element..." << std::endl; |
168 | 210 | Robot::Mouse::Click(Robot::MouseButton::LEFT_BUTTON); |
169 | | - Robot::Mouse::Click(Robot::MouseButton::LEFT_BUTTON); |
170 | | - Robot::delay(3000); |
| 211 | + Robot::delay(300); |
171 | 212 |
|
172 | | - // Perform drag operation |
173 | | - Robot::Mouse::Drag(endPos); |
174 | | - Robot::delay(500); // Give a bit more time for the drag to complete |
| 213 | + // Perform drag operation with states for main thread rendering |
| 214 | + std::cout << "Starting drag operation..." << std::endl; |
175 | 215 |
|
176 | | - // Process events to register the drag |
177 | | - while (SDL_PollEvent(&event)) { |
178 | | - handleEvent(event); |
| 216 | + // Press the mouse button |
| 217 | + testState = TestState::PRESSING_MOUSE; |
| 218 | + testNeedsRendering = true; |
| 219 | + Robot::Mouse::ToggleButton(true, Robot::MouseButton::LEFT_BUTTON); |
| 220 | + Robot::delay(300); |
| 221 | + |
| 222 | + // Move to the target position |
| 223 | + testState = TestState::MOVING_TO_END; |
| 224 | + testNeedsRendering = true; |
| 225 | + std::cout << "Moving to end position..." << std::endl; |
| 226 | + Robot::Mouse::Move(endPos); |
| 227 | + Robot::delay(300); |
| 228 | + |
| 229 | + // Release the mouse button |
| 230 | + testState = TestState::RELEASING_MOUSE; |
| 231 | + testNeedsRendering = true; |
| 232 | + Robot::Mouse::ToggleButton(false, Robot::MouseButton::LEFT_BUTTON); |
| 233 | + Robot::delay(500); // Give time for the drag to complete |
| 234 | + |
| 235 | + // Validate results |
| 236 | + testState = TestState::VALIDATING; |
| 237 | + testNeedsRendering = true; |
| 238 | + std::this_thread::sleep_for(std::chrono::milliseconds(500)); |
| 239 | + |
| 240 | + // Let the main thread process events before evaluating results |
| 241 | + std::this_thread::sleep_for(std::chrono::milliseconds(500)); |
| 242 | + |
| 243 | + // Validate the results (in a thread-safe way) |
| 244 | + { |
| 245 | + std::lock_guard<std::mutex> lock(testMutex); |
| 246 | + |
| 247 | + if (dragElements.empty()) { |
| 248 | + testPassed = false; |
| 249 | + testState = TestState::FAILED; |
| 250 | + testNeedsRendering = true; |
| 251 | + return; |
| 252 | + } |
| 253 | + |
| 254 | + auto& dragElement = dragElements[0]; |
| 255 | + SDL_Rect currentRect = dragElement.getRect(); |
| 256 | + |
| 257 | + std::cout << "Element position after drag: (" << currentRect.x << ", " << currentRect.y << ")" << std::endl; |
| 258 | + |
| 259 | + // Check if element was dragged (should be close to the target position) |
| 260 | + const int tolerance = 20; // pixels |
| 261 | + |
| 262 | + if (abs(currentRect.x - expectedX) > tolerance || |
| 263 | + abs(currentRect.y - expectedY) > tolerance) { |
| 264 | + std::cout << "Drag test failed. Expected pos: (" << expectedX << ", " << expectedY |
| 265 | + << "), Actual: (" << currentRect.x << ", " << currentRect.y << ")" << std::endl; |
| 266 | + testPassed = false; |
| 267 | + testState = TestState::FAILED; |
| 268 | + } else { |
| 269 | + std::cout << "Mouse dragging test passed" << std::endl; |
| 270 | + testPassed = true; |
| 271 | + testState = TestState::COMPLETED; |
| 272 | + } |
179 | 273 | } |
180 | 274 |
|
181 | | - // Additional processing to ensure events are processed |
182 | | - SDL_PumpEvents(); |
183 | | - Robot::delay(200); |
| 275 | + testNeedsRendering = true; |
| 276 | + } |
184 | 277 |
|
185 | | - // Get new position |
186 | | - SDL_Rect currentRect = dragElement.getRect(); |
187 | | - std::cout << "Element position after drag: (" << currentRect.x << ", " << currentRect.y << ")" << std::endl; |
| 278 | + // Start test in a separate thread and return immediately |
| 279 | + void startDragTest() { |
| 280 | + // Reset test state |
| 281 | + testState = TestState::IDLE; |
| 282 | + testPassed = false; |
| 283 | + testNeedsRendering = true; |
188 | 284 |
|
189 | | - // Check if element was dragged (should be close to the target position) |
190 | | - const int tolerance = 20; // pixels (increased tolerance slightly) |
191 | | - int expectedX = startRect.x + 100; // 100px to the right |
192 | | - int expectedY = startRect.y + 50; // 50px down |
| 285 | + // Start the test thread |
| 286 | + if (testThread.joinable()) { |
| 287 | + testThread.join(); |
| 288 | + } |
193 | 289 |
|
194 | | - if (abs(currentRect.x - expectedX) > tolerance || |
195 | | - abs(currentRect.y - expectedY) > tolerance) { |
196 | | - std::cout << "Drag test failed. Expected pos: (" << expectedX << ", " << expectedY |
197 | | - << "), Actual: (" << currentRect.x << ", " << currentRect.y << ")" << std::endl; |
198 | | - return false; |
| 290 | + testThread = std::thread(&MouseTests::runDragTestThread, this); |
| 291 | + } |
| 292 | + |
| 293 | + // Process any test-related events/updates in the main thread |
| 294 | + void updateFromMainThread() { |
| 295 | + // No SDL API calls in test thread - just handle any pending state changes |
| 296 | + if (testNeedsRendering) { |
| 297 | + testNeedsRendering = false; |
| 298 | + // Main thread has now processed this state |
199 | 299 | } |
| 300 | + } |
200 | 301 |
|
201 | | - std::cout << "Mouse dragging test passed" << std::endl; |
202 | | - return true; |
| 302 | + // Check if test is completed |
| 303 | + bool isTestCompleted() const { |
| 304 | + return (testState == TestState::COMPLETED || testState == TestState::FAILED); |
| 305 | + } |
| 306 | + |
| 307 | + // Get test result |
| 308 | + bool getTestResult() const { |
| 309 | + return testPassed; |
| 310 | + } |
| 311 | + |
| 312 | + // Clean up test thread |
| 313 | + void cleanup() { |
| 314 | + if (testThread.joinable()) { |
| 315 | + testThread.join(); |
| 316 | + } |
203 | 317 | } |
204 | 318 |
|
205 | 319 | bool runAllTests() { |
206 | | - // Only run the drag test |
207 | | - return testMouseDragging(); |
| 320 | + startDragTest(); |
| 321 | + |
| 322 | + // Main thread will handle SDL events and rendering |
| 323 | + // This function will be used by RobotTestApp |
| 324 | + |
| 325 | + return true; // Return value not used - test status is checked separately |
208 | 326 | } |
209 | 327 |
|
210 | 328 | private: |
211 | 329 | SDL_Renderer* renderer; |
212 | 330 | SDL_Window* window; |
213 | 331 | std::vector<DragElement> dragElements; |
| 332 | + std::thread testThread; |
| 333 | + std::atomic<bool> testPassed; |
| 334 | + std::atomic<TestState> testState; |
| 335 | + std::atomic<bool> testNeedsRendering; |
| 336 | + std::mutex testMutex; |
214 | 337 | }; |
215 | 338 |
|
216 | 339 | } // namespace RobotTest |
0 commit comments