aboutsummaryrefslogtreecommitdiff
path: root/src/zencore/testing.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/zencore/testing.cpp')
-rw-r--r--src/zencore/testing.cpp134
1 files changed, 122 insertions, 12 deletions
diff --git a/src/zencore/testing.cpp b/src/zencore/testing.cpp
index 936424e0f..089e376bb 100644
--- a/src/zencore/testing.cpp
+++ b/src/zencore/testing.cpp
@@ -1,11 +1,22 @@
// Copyright Epic Games, Inc. All Rights Reserved.
+#define ZEN_TEST_WITH_RUNNER 1
+
#include "zencore/testing.h"
+
+#include "zencore/filesystem.h"
#include "zencore/logging.h"
+#include "zencore/process.h"
+#include "zencore/trace.h"
#if ZEN_WITH_TESTS
-# include <doctest/doctest.h>
+# include <chrono>
+# include <clocale>
+# include <cstdlib>
+# include <cstdio>
+# include <string>
+# include <vector>
namespace zen::testing {
@@ -21,9 +32,35 @@ struct TestListener : public doctest::IReporter
void report_query(const doctest::QueryData& /*in*/) override {}
- void test_run_start() override {}
+ void test_run_start() override { RunStart = std::chrono::steady_clock::now(); }
- void test_run_end(const doctest::TestRunStats& /*in*/) override {}
+ void test_run_end(const doctest::TestRunStats& in) override
+ {
+ auto elapsed = std::chrono::steady_clock::now() - RunStart;
+ double elapsedSeconds = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count() / 1000.0;
+
+ // Write machine-readable summary to file if requested (used by xmake test summary table)
+ const char* summaryFile = std::getenv("ZEN_TEST_SUMMARY_FILE");
+ if (summaryFile && summaryFile[0] != '\0')
+ {
+ if (FILE* f = std::fopen(summaryFile, "w"))
+ {
+ std::fprintf(f,
+ "cases_total=%u\ncases_passed=%u\nassertions_total=%d\nassertions_passed=%d\n"
+ "elapsed_seconds=%.3f\n",
+ in.numTestCasesPassingFilters,
+ in.numTestCasesPassingFilters - in.numTestCasesFailed,
+ in.numAsserts,
+ in.numAsserts - in.numAssertsFailed,
+ elapsedSeconds);
+ for (const auto& failure : FailedTests)
+ {
+ std::fprintf(f, "failed=%s|%s|%u\n", failure.Name.c_str(), failure.File.c_str(), failure.Line);
+ }
+ std::fclose(f);
+ }
+ }
+ }
void test_case_start(const doctest::TestCaseData& in) override
{
@@ -37,7 +74,14 @@ struct TestListener : public doctest::IReporter
ZEN_CONSOLE("{}-------------------------------------------------------------------------------{}", ColorYellow, ColorNone);
}
- void test_case_end(const doctest::CurrentTestCaseStats& /*in*/) override { Current = nullptr; }
+ void test_case_end(const doctest::CurrentTestCaseStats& in) override
+ {
+ if (!in.testCaseSuccess && Current)
+ {
+ FailedTests.push_back({Current->m_name, Current->m_file.c_str(), Current->m_line});
+ }
+ Current = nullptr;
+ }
void test_case_exception(const doctest::TestCaseException& /*in*/) override {}
@@ -57,7 +101,16 @@ struct TestListener : public doctest::IReporter
void test_case_skipped(const doctest::TestCaseData& /*in*/) override {}
- const doctest::TestCaseData* Current = nullptr;
+ const doctest::TestCaseData* Current = nullptr;
+ std::chrono::steady_clock::time_point RunStart = {};
+
+ struct FailedTestInfo
+ {
+ std::string Name;
+ std::string File;
+ unsigned Line;
+ };
+ std::vector<FailedTestInfo> FailedTests;
};
struct TestRunner::Impl
@@ -75,20 +128,26 @@ TestRunner::~TestRunner()
{
}
+void
+TestRunner::SetDefaultSuiteFilter(const char* Pattern)
+{
+ m_Impl->Session.setOption("test-suite", Pattern);
+}
+
int
-TestRunner::ApplyCommandLine(int argc, char const* const* argv)
+TestRunner::ApplyCommandLine(int Argc, char const* const* Argv)
{
- m_Impl->Session.applyCommandLine(argc, argv);
+ m_Impl->Session.applyCommandLine(Argc, Argv);
- for (int i = 1; i < argc; ++i)
+ for (int i = 1; i < Argc; ++i)
{
- if (argv[i] == "--debug"sv)
+ if (Argv[i] == "--debug"sv)
{
- zen::logging::SetLogLevel(zen::logging::level::Debug);
+ zen::logging::SetLogLevel(zen::logging::Debug);
}
- else if (argv[i] == "--verbose"sv)
+ else if (Argv[i] == "--verbose"sv)
{
- zen::logging::SetLogLevel(zen::logging::level::Trace);
+ zen::logging::SetLogLevel(zen::logging::Trace);
}
}
@@ -101,6 +160,57 @@ TestRunner::Run()
return m_Impl->Session.run();
}
+int
+RunTestMain(int Argc, char* Argv[], const char* ExecutableName, void (*ForceLink)())
+{
+# if ZEN_PLATFORM_WINDOWS
+ setlocale(LC_ALL, "en_us.UTF8");
+# endif
+
+ ForceLink();
+
+# if ZEN_PLATFORM_LINUX
+ zen::IgnoreChildSignals();
+# endif
+
+# if ZEN_WITH_TRACE
+ zen::TraceInit(ExecutableName);
+ zen::TraceOptions TraceCommandlineOptions;
+ if (GetTraceOptionsFromCommandline(TraceCommandlineOptions))
+ {
+ TraceConfigure(TraceCommandlineOptions);
+ }
+# endif
+
+ zen::logging::InitializeLogging();
+ zen::MaximizeOpenFileCount();
+
+ TestRunner Runner;
+
+ // Derive default suite filter from ExecutableName: "zencore-test" -> "core.*"
+ if (ExecutableName)
+ {
+ std::string_view Name = ExecutableName;
+ if (Name.starts_with("zen"))
+ {
+ Name.remove_prefix(3);
+ }
+ if (Name.ends_with("-test"))
+ {
+ Name.remove_suffix(5);
+ }
+ if (!Name.empty())
+ {
+ std::string Filter(Name);
+ Filter += ".*";
+ Runner.SetDefaultSuiteFilter(Filter.c_str());
+ }
+ }
+
+ Runner.ApplyCommandLine(Argc, Argv);
+ return Runner.Run();
+}
+
} // namespace zen::testing
#endif // ZEN_WITH_TESTS