Crafter.Build/interfaces/Crafter.Build-Test.cppm
Jorijn van der Graaf 603840879d
All checks were successful
CI / build-test-release (push) Successful in 1h4m52s
new tests
2026-05-27 19:45:05 +02:00

115 lines
5.2 KiB
C++

/*
Crafter® Build
Copyright (C) 2026 Catcrafts®
Catcrafts.net
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License version 3.0 as published by the Free Software Foundation;
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
module;
#include "Crafter.Build-Api.h"
export module Crafter.Build:Test;
import std;
import :Clang;
export namespace Crafter {
// One row in a SIMD-march fan-out: a single Test variant compiles the
// shared source with `-march=<march> -mtune=<mtune>`. Passed by span to
// Configuration::AddMarchVariants — see Crafter.Math/project.cpp.
struct MarchTier {
std::string march;
std::string mtune;
};
// Fluent helper returned by Configuration::AddTest. Holds a back-pointer
// to the parent Configuration and the index of the just-pushed Test, so
// mutations survive any vector reallocation that subsequent AddTest
// calls might trigger. Each setter returns `*this` to support chaining.
//
// Note: source files (interfaces + implementations) are fixed at
// AddTest-time — pass them via the AddTest overload that takes
// interfaces. The builder only mutates compile state, deps, runtime
// args, and requires; it deliberately does NOT expose Sources() to
// avoid the double-`GetInterfacesAndImplementations` footgun (calling
// it twice would re-parse the same .cppm files into duplicate Module
// entries).
struct CRAFTER_API TestBuilder {
Configuration* parent;
std::size_t index;
Test& test() const { return parent->tests[index]; }
// Override the path the test's sources resolve against. Defaults to
// "./" (project root, where tests/<name>/main.cpp lives). Override
// only when the test source layout sits under a subdir.
TestBuilder& Path(std::filesystem::path p);
TestBuilder& Target(std::string t);
TestBuilder& March(std::string m);
TestBuilder& Mtune(std::string m);
TestBuilder& Sysroot(std::filesystem::path s);
TestBuilder& Debug(bool d = true);
TestBuilder& Define(std::string name, std::string value = "");
TestBuilder& Timeout(std::chrono::seconds s);
// Replaces any previously-set args.
TestBuilder& Args(std::vector<std::string> a);
// Appends one require entry ("tool:foo" / "file:..." / "env:VAR").
TestBuilder& Requires(std::string r);
// Replaces the dependency list. Pointers must outlive the build.
TestBuilder& Dependencies(std::vector<Configuration*> deps);
// Append a single linker flag (-l..., -L..., etc.).
TestBuilder& LinkFlag(std::string f);
// Append a single compile flag.
TestBuilder& CompileFlag(std::string f);
};
struct RunTestsOptions {
std::vector<std::string> globs;
int jobs = 0;
std::optional<std::chrono::seconds> timeoutOverride;
bool listOnly = false;
// Single-target run: only tests whose Configuration::target matches
// are included. Empty (default) = run every distinct target declared
// across discovered tests, plus the host target. Set from --target=...
// (when omitted, the harness sweeps all declared targets so cross-arch
// tests run by default without the user having to know which targets
// exist).
std::string targetFilter;
// CLI override for --runner=<spec>: applies to every test in the run.
// Target scoping is unnecessary because targetFilter ensures the run
// contains only one target's tests.
std::optional<std::string> runnerOverride;
};
struct TestSummary {
int passed = 0;
int failed = 0;
int crashed = 0;
int timedOut = 0;
int skipped = 0;
std::vector<TestResult> results;
bool AllPassed() const { return failed == 0 && crashed == 0 && timedOut == 0; }
};
CRAFTER_API TestSummary RunTests(Configuration& projectCfg, const RunTestsOptions& opts, std::span<const std::string_view> projectArgs = {});
CRAFTER_API TestResult RunSingleTest(const Test& test, const std::filesystem::path& binary, std::chrono::seconds timeout);
// Parent-project access for test fixtures. Run() sets the pointer to the
// root project's Configuration before discovery; fixtures call ParentLib()
// to obtain a Configuration* for one of the parent's libraries (or the
// parent itself) so they can declare it as a dependency without rebuilding
// the lib config from scratch. Lookup is by Configuration::name and
// recurses through the parent's dependency graph.
CRAFTER_API void SetParentProject(Configuration* parent);
CRAFTER_API Configuration* ParentLib(std::string_view name);
}