yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
test_manager.h
Go to the documentation of this file.
1#ifndef YAZE_APP_TEST_TEST_MANAGER_H
2#define YAZE_APP_TEST_TEST_MANAGER_H
3
4#include <chrono>
5#include <deque>
6#include <functional>
7#include <map>
8#include <memory>
9#include <optional>
10#include <string>
11#include <unordered_map>
12#include <vector>
13
14#include "absl/status/status.h"
15#include "absl/status/statusor.h"
16#include "absl/strings/string_view.h"
17#include "absl/synchronization/mutex.h"
18#include "absl/time/time.h"
20#include "rom/rom.h"
21
22#define IMGUI_DEFINE_MATH_OPERATORS
23#include "imgui.h"
24#include "util/log.h"
25
26// Forward declarations
27namespace yaze {
28namespace editor {
29class EditorManager;
30}
31} // namespace yaze
32
33#if defined(YAZE_ENABLE_IMGUI_TEST_ENGINE) && YAZE_ENABLE_IMGUI_TEST_ENGINE
34#if __has_include("imgui_test_engine/imgui_te_engine.h")
35#include "imgui_test_engine/imgui_te_engine.h"
36#elif __has_include("imgui_te_engine.h")
37#include "imgui_te_engine.h"
38#else
39#error "ImGui Test Engine headers not found"
40#endif
41#else
42// Forward declaration when ImGui Test Engine is not available
43struct ImGuiTest;
44struct ImGuiTestEngine;
45#endif
46
47namespace yaze {
48namespace test {
49
50// Test execution status
52
53// Test categories for organization
55
56// Individual test result
57struct TestResult {
58 std::string name;
59 std::string suite_name;
62 std::string error_message;
63 std::chrono::milliseconds duration;
64 std::chrono::time_point<std::chrono::steady_clock> timestamp;
65};
66
67// Overall test results summary
69 std::vector<TestResult> individual_results;
70 size_t total_tests = 0;
71 size_t passed_tests = 0;
72 size_t failed_tests = 0;
73 size_t skipped_tests = 0;
74 std::chrono::milliseconds total_duration{0};
75
76 void AddResult(const TestResult& result) {
77 individual_results.push_back(result);
79 switch (result.status) {
82 break;
85 break;
88 break;
89 default:
90 break;
91 }
92 total_duration += result.duration;
93 }
94
95 void Clear() {
96 individual_results.clear();
98 total_duration = std::chrono::milliseconds{0};
99 }
100
101 float GetPassRate() const {
102 return total_tests > 0 ? static_cast<float>(passed_tests) / total_tests
103 : 0.0f;
104 }
105};
106
107// Base class for test suites
109 public:
110 virtual ~TestSuite() = default;
111 virtual std::string GetName() const = 0;
112 virtual TestCategory GetCategory() const = 0;
113 virtual absl::Status RunTests(TestResults& results) = 0;
114 virtual void DrawConfiguration() {}
115 virtual bool IsEnabled() const { return enabled_; }
116 virtual void SetEnabled(bool enabled) { enabled_ = enabled; }
117
118 protected:
119 bool enabled_ = true;
120};
121
122// Resource monitoring for performance and memory tests
124 size_t texture_count = 0;
125 size_t surface_count = 0;
126 size_t memory_usage_mb = 0;
127 float frame_rate = 0.0f;
128 std::chrono::time_point<std::chrono::steady_clock> timestamp;
129};
130
131// Test harness execution tracking for gRPC automation (IT-05)
132#if defined(YAZE_WITH_GRPC)
133enum class HarnessTestStatus {
134 kUnspecified,
135 kQueued,
136 kRunning,
137 kPassed,
138 kFailed,
139 kTimeout,
140};
141
142const char* HarnessStatusToString(HarnessTestStatus status);
143HarnessTestStatus HarnessStatusFromString(absl::string_view status);
144
145struct HarnessTestExecution {
146 std::string test_id;
147 std::string name;
148 std::string category;
149 HarnessTestStatus status = HarnessTestStatus::kUnspecified;
150 absl::Time queued_at;
151 absl::Time started_at;
152 absl::Time completed_at;
153 absl::Duration duration = absl::ZeroDuration();
154 std::string error_message;
155 std::vector<std::string> assertion_failures;
156 std::vector<std::string> logs;
157 std::map<std::string, int32_t> metrics;
158
159 // IT-08b: Failure diagnostics
160 std::string screenshot_path;
161 int64_t screenshot_size_bytes = 0;
162 std::string failure_context;
163 std::string widget_state; // IT-08c (future)
164};
165
166struct HarnessTestSummary {
167 HarnessTestExecution latest_execution;
168 int total_runs = 0;
169 int pass_count = 0;
170 int fail_count = 0;
171 absl::Duration total_duration = absl::ZeroDuration();
172};
173
174class HarnessListener {
175 public:
176 virtual ~HarnessListener() = default;
177 virtual void OnHarnessTestUpdated(const HarnessTestExecution& execution) = 0;
178 virtual void OnHarnessPlanSummary(const std::string& summary) = 0;
179};
180#endif // defined(YAZE_WITH_GRPC)
181
182// Main test manager - singleton
184 public:
185 static TestManager& Get();
186
187 // Core test execution
188 absl::Status RunAllTests();
189 absl::Status RunTestsByCategory(TestCategory category);
190 absl::Status RunTestSuite(const std::string& suite_name);
191
192 // Test suite management
193 void RegisterTestSuite(std::unique_ptr<TestSuite> suite);
194 std::vector<std::string> GetTestSuiteNames() const;
195 TestSuite* GetTestSuite(const std::string& name);
196
197 // Results access
198 const TestResults& GetLastResults() const { return last_results_; }
200
201 // Configuration
202 void SetMaxConcurrentTests(size_t max_concurrent) {
203 max_concurrent_tests_ = max_concurrent;
204 }
205 void SetTestTimeout(std::chrono::seconds timeout) { test_timeout_ = timeout; }
206
207 // Resource monitoring
208 void UpdateResourceStats();
209 const std::vector<ResourceStats>& GetResourceHistory() const {
210 return resource_history_;
211 }
212
213 // UI Testing (ImGui Test Engine integration)
214#if defined(YAZE_ENABLE_IMGUI_TEST_ENGINE) && YAZE_ENABLE_IMGUI_TEST_ENGINE
215 ImGuiTestEngine* GetUITestEngine() { return ui_test_engine_; }
216 void InitializeUITesting();
217 void OnPostSwap();
218 // Replay jobs retain popup/focus state between remote calls. All engine
219 // configuration changes happen on the UI thread, after queue admission.
220 void RegisterRemoteReplayTest(ImGuiTest* test, const std::string& harness_id);
221 absl::Status BeginRemoteReplayFocus(ImGuiTest* test);
222 void StopUITesting(); // Stop test engine while ImGui context is valid
223 void DestroyUITestingContext(); // Destroy test engine after ImGui context is
224 // destroyed
225 void ShutdownUITesting(); // Complete shutdown (calls both Stop and Destroy)
226#else
227 void* GetUITestEngine() { return nullptr; }
229 void OnPostSwap() {}
233#endif
234
235 // Status queries
236 bool IsTestRunning() const { return is_running_; }
237 const std::string& GetCurrentTestName() const { return current_test_name_; }
238 float GetProgress() const { return progress_; }
239
240 // UI Interface
241 void DrawTestDashboard(bool* show_dashboard = nullptr);
242
243 // ROM-dependent testing
244 void SetCurrentRom(Rom* rom) {
245 LOG_INFO("TestManager", "SetCurrentRom called with ROM: %p", (void*)rom);
246 if (rom) {
247 LOG_INFO("TestManager", "ROM title: '%s', loaded: %s",
248 rom->title().c_str(), rom->is_loaded() ? "true" : "false");
249 }
250 current_rom_ = rom;
251 }
252 Rom* GetCurrentRom() const { return current_rom_; }
253 void RefreshCurrentRom(); // Refresh ROM pointer from editor manager
254 // Remove EditorManager dependency to avoid circular includes
255
256 // Enhanced ROM testing
257 absl::Status LoadRomForTesting(const std::string& filename);
258 void ShowRomComparisonResults(const Rom& before, const Rom& after);
259
260 // Test ROM management
261 absl::Status CreateTestRomCopy(Rom* source_rom,
262 std::unique_ptr<Rom>& test_rom);
263 std::string GenerateTestRomFilename(const std::string& base_name);
264 void OfferTestSessionCreation(const std::string& test_rom_path);
265
266 public:
267 // ROM testing methods (work on copies, not originals)
268 absl::Status TestRomSaveLoad(Rom* rom);
269 absl::Status TestRomDataIntegrity(Rom* rom);
270 absl::Status TestRomWithCopy(Rom* source_rom,
271 std::function<absl::Status(Rom*)> test_function);
272
273 // Test configuration management
274 void DisableTest(const std::string& test_name) {
275 disabled_tests_[test_name] = true;
276 }
277 void EnableTest(const std::string& test_name) {
278 disabled_tests_[test_name] = false;
279 }
280 bool IsTestEnabled(const std::string& test_name) const {
281 auto it = disabled_tests_.find(test_name);
282 return it == disabled_tests_.end() || !it->second;
283 }
284 // File dialog mode now uses global feature flags
285
286 // Harness test introspection (IT-05)
287#if defined(YAZE_WITH_GRPC)
288 using FailureScreenshotCallback =
289 std::function<void(absl::StatusOr<ScreenshotArtifact>)>;
290 using FailureScreenshotRequester =
291 std::function<void(const std::string&, FailureScreenshotCallback)>;
292
293 std::string RegisterHarnessTest(const std::string& name,
294 const std::string& category)
295 ABSL_LOCKS_EXCLUDED(harness_history_mutex_);
296 void MarkHarnessTestRunning(const std::string& test_id)
297 ABSL_LOCKS_EXCLUDED(harness_history_mutex_);
298 void MarkHarnessTestCompleted(
299 const std::string& test_id, HarnessTestStatus status,
300 const std::string& error_message = "",
301 const std::vector<std::string>& assertion_failures = {},
302 const std::vector<std::string>& logs = {},
303 const std::map<std::string, int32_t>& metrics = {})
304 ABSL_LOCKS_EXCLUDED(harness_history_mutex_);
305 void AppendHarnessTestLog(const std::string& test_id,
306 const std::string& log_entry)
307 ABSL_LOCKS_EXCLUDED(harness_history_mutex_);
308 absl::StatusOr<HarnessTestExecution> GetHarnessTestExecution(
309 const std::string& test_id) const
310 ABSL_LOCKS_EXCLUDED(harness_history_mutex_);
311 std::vector<HarnessTestSummary> ListHarnessTestSummaries(
312 const std::string& category_filter = "") const
313 ABSL_LOCKS_EXCLUDED(harness_history_mutex_);
314
315 // IT-08b: Capture failure diagnostics
316 void CaptureFailureContext(const std::string& test_id)
317 ABSL_LOCKS_EXCLUDED(harness_history_mutex_);
318
319 void SetHarnessListener(std::shared_ptr<HarnessListener> listener);
320 void ClearHarnessListener(const HarnessListener* listener);
321 void SetFailureScreenshotRequester(FailureScreenshotRequester requester);
322
323 absl::Status ReplayLastPlan();
324#else
325 // Stub implementations when GRPC is not available
326 std::string RegisterHarnessTest(const std::string& name,
327 const std::string& category);
328 void CaptureFailureContext(const std::string& test_id);
329 absl::Status ReplayLastPlan();
330#endif
331
332 // These methods are always available
333 absl::Status ShowHarnessDashboard();
334 absl::Status ShowHarnessActiveTests();
335 void RecordPlanSummary(const std::string& summary);
336
337 private:
338 TestManager();
339 ~TestManager();
340
341 // Test execution helpers
342 absl::Status ExecuteTestSuite(TestSuite* suite);
343 void UpdateProgress();
344
345 // Resource monitoring helpers
347 void TrimResourceHistory();
348
349 // Member variables
350 std::vector<std::unique_ptr<TestSuite>> test_suites_;
351 std::unordered_map<std::string, TestSuite*> suite_lookup_;
352
354 bool is_running_ = false;
356 float progress_ = 0.0f;
357
358 // Configuration
360 std::chrono::seconds test_timeout_{30};
361
362 // Resource monitoring
363 std::vector<ResourceStats> resource_history_;
364 static constexpr size_t kMaxResourceHistorySize = 1000;
365
366 // UI Testing. Data members stay unconditional: libraries that include this
367 // header without the test engine define must agree on the object layout.
368 ImGuiTestEngine* ui_test_engine_ = nullptr;
369 std::unordered_map<ImGuiTest*, std::string> remote_replay_tests_
371 std::optional<bool> remote_replay_restore_focus_;
372#if defined(YAZE_ENABLE_IMGUI_TEST_ENGINE) && YAZE_ENABLE_IMGUI_TEST_ENGINE
373 void UpdateRemoteReplayFocus();
374 void RestoreRemoteReplayFocus();
375#endif
376
377 // UI State
378 bool show_dashboard_ = false;
380 std::string test_filter_;
382
383 // ROM-dependent testing
384 Rom* current_rom_ = nullptr;
385 // Removed editor_manager_ to avoid circular dependency
386
387 // UI state
388 bool show_google_tests_ = false;
394
395 // Test selection and configuration
396 std::unordered_map<std::string, bool> disabled_tests_;
397
398 // Harness test tracking
399#if defined(YAZE_WITH_GRPC)
400 struct HarnessAggregate {
401 int total_runs = 0;
402 int pass_count = 0;
403 int fail_count = 0;
404 absl::Duration total_duration = absl::ZeroDuration();
405 std::string category;
406 absl::Time last_run;
407 HarnessTestExecution latest_execution;
408 };
409
410 std::unordered_map<std::string, HarnessTestExecution> harness_history_
411 ABSL_GUARDED_BY(harness_history_mutex_);
412 std::unordered_map<std::string, HarnessAggregate> harness_aggregates_
413 ABSL_GUARDED_BY(harness_history_mutex_);
414 std::deque<std::string> harness_history_order_;
415 size_t harness_history_limit_ = 200;
416 mutable absl::Mutex harness_history_mutex_;
417#if defined(YAZE_WITH_GRPC)
418 std::shared_ptr<HarnessListener> harness_listener_ ABSL_GUARDED_BY(mutex_);
419 FailureScreenshotRequester failure_screenshot_requester_
421#endif
422#endif // defined(YAZE_WITH_GRPC)
423
424#if defined(YAZE_WITH_GRPC)
425 std::string GenerateHarnessTestIdLocked(absl::string_view prefix)
426 ABSL_EXCLUSIVE_LOCKS_REQUIRED(harness_history_mutex_);
427 void TrimHarnessHistoryLocked()
428 ABSL_EXCLUSIVE_LOCKS_REQUIRED(harness_history_mutex_);
429 void NotifyHarnessListener(const HarnessTestExecution& execution)
430 ABSL_LOCKS_EXCLUDED(mutex_);
431#endif
432
433 absl::Mutex mutex_;
434};
435
436// Utility functions for test result formatting
437const char* TestStatusToString(TestStatus status);
438const char* TestCategoryToString(TestCategory category);
439ImVec4 GetTestStatusColor(TestStatus status);
440
441} // namespace test
442} // namespace yaze
443
444#endif // YAZE_APP_TEST_TEST_MANAGER_H
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
Definition rom.h:52
bool is_loaded() const
Definition rom.h:199
auto title() const
Definition rom.h:211
std::string GenerateTestRomFilename(const std::string &base_name)
void CaptureFailureContext(const std::string &test_id)
std::string current_test_name_
Rom * GetCurrentRom() const
const TestResults & GetLastResults() const
absl::Status ShowHarnessActiveTests()
TestCategory category_filter_
absl::Status ShowHarnessDashboard()
std::vector< ResourceStats > resource_history_
void RegisterTestSuite(std::unique_ptr< TestSuite > suite)
void RecordPlanSummary(const std::string &summary)
absl::Status TestRomWithCopy(Rom *source_rom, std::function< absl::Status(Rom *)> test_function)
std::unordered_map< std::string, TestSuite * > suite_lookup_
absl::Status CreateTestRomCopy(Rom *source_rom, std::unique_ptr< Rom > &test_rom)
bool IsTestEnabled(const std::string &test_name) const
absl::Status RunTestSuite(const std::string &suite_name)
std::string RegisterHarnessTest(const std::string &name, const std::string &category)
std::unordered_map< ImGuiTest *, std::string > remote_replay_tests_ ABSL_GUARDED_BY(mutex_)
absl::Status ExecuteTestSuite(TestSuite *suite)
std::optional< bool > remote_replay_restore_focus_
void SetCurrentRom(Rom *rom)
std::string test_rom_path_for_session_
absl::Status TestRomDataIntegrity(Rom *rom)
static constexpr size_t kMaxResourceHistorySize
ImGuiTestEngine * ui_test_engine_
float GetProgress() const
void DrawTestDashboard(bool *show_dashboard=nullptr)
TestSuite * GetTestSuite(const std::string &name)
absl::Status RunAllTests()
static TestManager & Get()
void OfferTestSessionCreation(const std::string &test_rom_path)
void EnableTest(const std::string &test_name)
const std::vector< ResourceStats > & GetResourceHistory() const
void DisableTest(const std::string &test_name)
void SetTestTimeout(std::chrono::seconds timeout)
std::vector< std::unique_ptr< TestSuite > > test_suites_
absl::Status TestRomSaveLoad(Rom *rom)
void SetMaxConcurrentTests(size_t max_concurrent)
std::chrono::seconds test_timeout_
absl::Status RunTestsByCategory(TestCategory category)
void ShowRomComparisonResults(const Rom &before, const Rom &after)
std::unordered_map< std::string, bool > disabled_tests_
const std::string & GetCurrentTestName() const
absl::Status ReplayLastPlan()
std::vector< std::string > GetTestSuiteNames() const
absl::Status LoadRomForTesting(const std::string &filename)
virtual void DrawConfiguration()
virtual ~TestSuite()=default
virtual void SetEnabled(bool enabled)
virtual std::string GetName() const =0
virtual bool IsEnabled() const
virtual TestCategory GetCategory() const =0
virtual absl::Status RunTests(TestResults &results)=0
#define LOG_INFO(category, format,...)
Definition log.h:136
const char * TestStatusToString(TestStatus status)
const char * TestCategoryToString(TestCategory category)
ImVec4 GetTestStatusColor(TestStatus status)
std::chrono::time_point< std::chrono::steady_clock > timestamp
std::chrono::milliseconds duration
std::string error_message
std::chrono::time_point< std::chrono::steady_clock > timestamp
std::vector< TestResult > individual_results
std::chrono::milliseconds total_duration
void AddResult(const TestResult &result)
float GetPassRate() const