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