Files
yaze/src/app/test/unit_test_suite.h
scawful 8ab30dd5ad Enhance testing framework and CMake integration for YAZE
- Conditionally include Google Test support in the build configuration, allowing for integrated testing when enabled.
- Refactor ImGui Test Engine setup to be conditional based on the YAZE_ENABLE_UI_TESTS flag, improving modularity.
- Update EditorManager to register new test suites, including integrated and performance tests, enhancing test coverage.
- Improve the test dashboard UI with additional options for filtering and viewing test results, providing a better user experience.
- Introduce a new integrated test suite for comprehensive testing of core functionalities, ensuring robustness and reliability.
2025-09-25 13:29:39 -04:00

303 lines
9.7 KiB
C++

#ifndef YAZE_APP_TEST_UNIT_TEST_SUITE_H
#define YAZE_APP_TEST_UNIT_TEST_SUITE_H
#include <chrono>
#include <memory>
#include "app/gfx/arena.h"
#include "app/test/test_manager.h"
#ifdef YAZE_ENABLE_GTEST
#include <gtest/gtest.h>
#endif
// Note: ImGui Test Engine is handled through YAZE_ENABLE_IMGUI_TEST_ENGINE in TestManager
namespace yaze {
namespace test {
#ifdef YAZE_ENABLE_GTEST
// Custom test listener to capture Google Test results
class TestResultCapture : public ::testing::TestEventListener {
public:
explicit TestResultCapture(TestResults* results) : results_(results) {}
void OnTestStart(const ::testing::TestInfo& test_info) override {
current_test_start_ = std::chrono::steady_clock::now();
current_test_name_ =
std::string(test_info.test_case_name()) + "." + test_info.name();
}
void OnTestEnd(const ::testing::TestInfo& test_info) override {
auto end_time = std::chrono::steady_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
end_time - current_test_start_);
TestResult result;
result.name = test_info.name();
result.suite_name = test_info.test_case_name();
result.category = TestCategory::kUnit;
result.duration = duration;
result.timestamp = current_test_start_;
if (test_info.result()->Passed()) {
result.status = TestStatus::kPassed;
} else if (test_info.result()->Skipped()) {
result.status = TestStatus::kSkipped;
} else {
result.status = TestStatus::kFailed;
// Capture failure message
std::stringstream error_stream;
for (int i = 0; i < test_info.result()->total_part_count(); ++i) {
const auto& part = test_info.result()->GetTestPartResult(i);
if (part.failed()) {
error_stream << part.file_name() << ":" << part.line_number() << " "
<< part.message() << "\n";
}
}
result.error_message = error_stream.str();
}
if (results_) {
results_->AddResult(result);
}
}
// Required overrides (can be empty)
void OnTestProgramStart(const ::testing::UnitTest&) override {}
void OnTestIterationStart(const ::testing::UnitTest&, int) override {}
void OnEnvironmentsSetUpStart(const ::testing::UnitTest&) override {}
void OnEnvironmentsSetUpEnd(const ::testing::UnitTest&) override {}
void OnTestCaseStart(const ::testing::TestCase&) override {}
void OnTestCaseEnd(const ::testing::TestCase&) override {}
void OnTestPartResult(const ::testing::TestPartResult& test_part_result) override {
// Handle individual test part results (can be empty for our use case)
}
void OnEnvironmentsTearDownStart(const ::testing::UnitTest&) override {}
void OnEnvironmentsTearDownEnd(const ::testing::UnitTest&) override {}
void OnTestIterationEnd(const ::testing::UnitTest&, int) override {}
void OnTestProgramEnd(const ::testing::UnitTest&) override {}
private:
TestResults* results_;
std::chrono::time_point<std::chrono::steady_clock> current_test_start_;
std::string current_test_name_;
};
#endif // YAZE_ENABLE_GTEST
// Unit test suite that runs Google Test cases
class UnitTestSuite : public TestSuite {
public:
UnitTestSuite() = default;
~UnitTestSuite() override = default;
std::string GetName() const override { return "Google Test Unit Tests"; }
TestCategory GetCategory() const override { return TestCategory::kUnit; }
absl::Status RunTests(TestResults& results) override {
#ifdef YAZE_ENABLE_GTEST
// Set up Google Test to capture results
auto& listeners = ::testing::UnitTest::GetInstance()->listeners();
// Remove default console output (we'll capture it ourselves)
delete listeners.Release(listeners.default_result_printer());
// Add our custom listener
auto capture_listener = new TestResultCapture(&results);
listeners.Append(capture_listener);
// Configure test execution
int argc = 1;
const char* argv[] = {"yaze_tests"};
::testing::InitGoogleTest(&argc, const_cast<char**>(argv));
// Run the tests
int result = RUN_ALL_TESTS();
// Clean up
listeners.Release(capture_listener);
delete capture_listener;
return result == 0 ? absl::OkStatus()
: absl::InternalError("Some unit tests failed");
#else
// Google Test not available - add a placeholder test
TestResult result;
result.name = "Placeholder Test";
result.suite_name = GetName();
result.category = GetCategory();
result.status = TestStatus::kSkipped;
result.error_message = "Google Test not available in this build";
result.duration = std::chrono::milliseconds{0};
result.timestamp = std::chrono::steady_clock::now();
results.AddResult(result);
return absl::OkStatus();
#endif
}
void DrawConfiguration() override {
ImGui::Text("Google Test Configuration");
ImGui::Checkbox("Run disabled tests", &run_disabled_tests_);
ImGui::Checkbox("Shuffle tests", &shuffle_tests_);
ImGui::InputInt("Repeat count", &repeat_count_);
if (repeat_count_ < 1) repeat_count_ = 1;
ImGui::InputText("Test filter", test_filter_, sizeof(test_filter_));
ImGui::SameLine();
if (ImGui::Button("Clear")) {
test_filter_[0] = '\0';
}
}
private:
bool run_disabled_tests_ = false;
bool shuffle_tests_ = false;
int repeat_count_ = 1;
char test_filter_[256] = "";
};
// Arena-specific test suite for memory management
class ArenaTestSuite : public TestSuite {
public:
ArenaTestSuite() = default;
~ArenaTestSuite() override = default;
std::string GetName() const override { return "Arena Memory Tests"; }
TestCategory GetCategory() const override { return TestCategory::kMemory; }
absl::Status RunTests(TestResults& results) override {
// Test Arena resource management
RunArenaAllocationTest(results);
RunArenaCleanupTest(results);
RunArenaResourceTrackingTest(results);
return absl::OkStatus();
}
void DrawConfiguration() override {
ImGui::Text("Arena Test Configuration");
ImGui::InputInt("Test allocations", &test_allocation_count_);
ImGui::InputInt("Test texture size", &test_texture_size_);
ImGui::Checkbox("Test cleanup order", &test_cleanup_order_);
}
private:
void RunArenaAllocationTest(TestResults& results) {
auto start_time = std::chrono::steady_clock::now();
TestResult result;
result.name = "Arena_Allocation_Test";
result.suite_name = GetName();
result.category = GetCategory();
result.timestamp = start_time;
try {
auto& arena = gfx::Arena::Get();
size_t initial_texture_count = arena.GetTextureCount();
size_t initial_surface_count = arena.GetSurfaceCount();
// Test texture allocation (would need a valid renderer)
// This is a simplified test - in real implementation we'd mock the
// renderer
size_t final_texture_count = arena.GetTextureCount();
size_t final_surface_count = arena.GetSurfaceCount();
// For now, just verify the Arena can be accessed
result.status = TestStatus::kPassed;
} catch (const std::exception& e) {
result.status = TestStatus::kFailed;
result.error_message =
"Arena allocation test failed: " + std::string(e.what());
}
auto end_time = std::chrono::steady_clock::now();
result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
end_time - start_time);
results.AddResult(result);
}
void RunArenaCleanupTest(TestResults& results) {
auto start_time = std::chrono::steady_clock::now();
TestResult result;
result.name = "Arena_Cleanup_Test";
result.suite_name = GetName();
result.category = GetCategory();
result.timestamp = start_time;
try {
auto& arena = gfx::Arena::Get();
// Test that shutdown doesn't crash
// Note: We can't actually call Shutdown() here as it would affect the
// running app This test verifies the methods exist and are callable
size_t texture_count = arena.GetTextureCount();
size_t surface_count = arena.GetSurfaceCount();
result.status = TestStatus::kPassed;
} catch (const std::exception& e) {
result.status = TestStatus::kFailed;
result.error_message =
"Arena cleanup test failed: " + std::string(e.what());
}
auto end_time = std::chrono::steady_clock::now();
result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
end_time - start_time);
results.AddResult(result);
}
void RunArenaResourceTrackingTest(TestResults& results) {
auto start_time = std::chrono::steady_clock::now();
TestResult result;
result.name = "Arena_Resource_Tracking_Test";
result.suite_name = GetName();
result.category = GetCategory();
result.timestamp = start_time;
try {
auto& arena = gfx::Arena::Get();
// Test resource tracking methods
size_t texture_count = arena.GetTextureCount();
size_t surface_count = arena.GetSurfaceCount();
// Verify tracking methods work
if (texture_count >= 0 && surface_count >= 0) {
result.status = TestStatus::kPassed;
} else {
result.status = TestStatus::kFailed;
result.error_message = "Invalid resource counts returned";
}
} catch (const std::exception& e) {
result.status = TestStatus::kFailed;
result.error_message =
"Resource tracking test failed: " + std::string(e.what());
}
auto end_time = std::chrono::steady_clock::now();
result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
end_time - start_time);
results.AddResult(result);
}
int test_allocation_count_ = 10;
int test_texture_size_ = 64;
bool test_cleanup_order_ = true;
};
} // namespace test
} // namespace yaze
#endif // YAZE_APP_TEST_UNIT_TEST_SUITE_H