#ifndef POKEMONSCRIPTTESTER_TESTRUNNER_HPP #define POKEMONSCRIPTTESTER_TESTRUNNER_HPP #include #include #include #include #include "../../extern/termcolor.hpp" #include "Test.hpp" class TestRunner { AngelScriptResolver* _scriptResolver; ArbUt::Dictionary> _tests; public: TestRunner(AngelScriptResolver* scriptResolver) : _scriptResolver(scriptResolver) { const auto* module = scriptResolver->GetMainModule(); auto builder = scriptResolver->GetBuilder(); for (u32 i = 0; i < module->GetFunctionCount(); ++i) { auto* func = module->GetFunctionByIndex(i); auto metaData = builder.GetMetadataForFunc(func); for (const auto& m : metaData) { auto meta = AngelscriptMetadata(m); if (meta.GetIdentifier() == "Test"_cnc) { auto name = meta.GetParameter("name"_cnc); if (_tests.Has(name)) { std::cout << termcolor::red << "Duplicate test name: '" << name << "'." << termcolor::reset << std::endl; continue; } _tests.GetStdMap().insert({name, std::make_unique(name, func)}); } } } } i32 RunAll([[maybe_unused]]bool waitForDebugger) { auto ctx = _scriptResolver->GetContextPool()->RequestContext(); ctx->SetUserData(_scriptResolver->GetUserdata()); #if ANGELSCRIPT_DEBUGGER _scriptResolver->SetDebugger(new AngelscriptDebugger()); auto& debugger = *_scriptResolver->GetDebugger().GetValue(); debugger.Run(8684); debugger.RegisterContext(ctx); if (waitForDebugger) { std::cout << "Waiting for debugger" << std::endl; while (!debugger.HasDebuggerAttached()) { continue; } std::this_thread::sleep_for(std::chrono::milliseconds(500)); } #endif for (auto& test : _tests) { test.second->Run(ctx); } ctx->Release(); size_t notRunTests = 0; size_t successfulTests = 0; size_t failedTests = 0; size_t totalRequirements = 0; size_t failedRequirements = 0; for (auto& test : _tests) { auto result = test.second->GetResult(); switch (result) { case TestResult::NotRan: notRunTests += 1; break; case TestResult::Success: successfulTests += 1; break; case TestResult::Failed: { std::cout << termcolor::red << "Test '" << termcolor::yellow << test.first << termcolor::red << "' failed with message: " << termcolor::reset << std::endl; std::cout << test.second->GetErrorMessage() << std::endl << std::endl; failedTests += 1; break; } } totalRequirements += test.second->GetTotalRequirements(); failedRequirements += test.second->GetFailedRequirements(); } auto totalTests = successfulTests + failedTests; auto digitWidth = std::log10(std::max(totalTests, totalRequirements)) + 2; std::cout << "Running tests in library folder: " << std::filesystem::current_path().stem() << std::endl; std::cout << termcolor::blue << "====================================================================="; for (int i = 0; i < digitWidth; ++i) { std::cout << "==="; } std::cout << termcolor::reset << std::endl; ReportResults("Test cases", digitWidth, totalTests, successfulTests, failedTests, notRunTests); ReportResults("Requirements", digitWidth, totalRequirements, totalRequirements - failedRequirements, failedRequirements, {}); if (failedTests > 0) { return 1; } return 0; } static void ReportResults(const std::string& label, double digitWidth, size_t total, size_t passed, size_t failed, std::optional skipped) { std::cout << std::left // Left align << std::setw(16) << label + ":" << std::setw(digitWidth) << total << std::setw(3) << "|"; if (passed > 0) { std::cout << termcolor::green; } std::cout << std::left << std::setw(14) << "Passed:" << std::setw(digitWidth) << passed << termcolor::reset << std::setw(3) << "|"; if (failed > 0) { std::cout << termcolor::red; } std::cout << std::setw(14) << "Failed:" << std::setw(digitWidth) << failed << termcolor::reset; std::cout << std::setw(3) << "|"; if (skipped.has_value()) { std::cout << std::setw(14) << "Skipped:" << std::setw(digitWidth) << skipped.value(); } std::cout << std::endl; } }; #endif // POKEMONSCRIPTTESTER_TESTRUNNER_HPP