test(tests): added test suite

The test suite is extensive. Note that LLMs were used to aid in the development of the test suite. All LLM generated code was manually reviewed.
This commit is contained in:
2026-11-09 13:53:17 -05:00
parent 74e55a6859
commit cb20faeb3d
202 changed files with 35505 additions and 953 deletions

View File

@@ -0,0 +1,103 @@
#include "serif/utils/dependencies/dependencies.hpp"
#include <catch2/catch_test_macros.hpp>
#include <memory>
#include <stdexcept>
#include <utility>
namespace {
namespace dependencies = serif::utils::dependencies;
struct Owned {};
struct Borrowed {};
struct Result {};
}
TEST_CASE("Dependency contexts distinguish ownership and borrowed roles", "[unit][utils][dependencies]") {
int value = 3;
auto context = dependencies::make_dependency_context(dependencies::own<Owned>(value), dependencies::borrow<Borrowed>(value));
auto selection = context.select(dependencies::KeyList<Borrowed>{});
value = 8;
CHECK(context.get<Owned>() == 3);
CHECK(context.get<Borrowed>() == 8);
CHECK(std::addressof(selection.get<Borrowed>()) == std::addressof(value));
auto graph = dependencies::make_computation_graph(dependencies::make_computation<Result, dependencies::InputList<Borrowed>>([](int input) { return 2 * input; }));
auto cache = dependencies::make_computation_cache(std::move(graph), selection);
REQUIRE(cache.try_prepare(selection));
CHECK(cache.get<Result>() == 16);
value = 9;
CHECK(cache.get<Result>() == 16); // An accepted result remains frozen until explicitly refreshed.
REQUIRE(cache.try_prepare(selection));
CHECK(cache.get<Result>() == 18);
}
TEST_CASE("Replacing a borrowed source invalidates its identity even at equal value", "[unit][utils][dependencies]") {
int original = 4;
int replacement = 4;
int calls = 0;
auto context = dependencies::make_dependency_context(dependencies::borrow<Borrowed>(original));
auto graph = dependencies::make_computation_graph(dependencies::make_computation<Result, dependencies::InputList<Borrowed>>([&calls](int input) { ++calls; return input; }));
auto cache = dependencies::make_computation_cache(std::move(graph), context);
REQUIRE(cache.try_prepare(context));
context = dependencies::make_dependency_context(dependencies::borrow<Borrowed>(replacement));
auto report = cache.try_prepare(context);
REQUIRE(report);
CHECK(report->source_changed<Borrowed>());
CHECK(calls == 2);
original = 7;
REQUIRE(cache.try_prepare(context));
CHECK(calls == 2);
CHECK(cache.get<Result>() == 4);
}
TEST_CASE("ComputationCache dependency results can move without leaving readable moved-from state", "[unit][utils][dependencies]") {
const auto context = dependencies::make_dependency_context();
auto graph = dependencies::make_computation_graph(dependencies::make_computation<Result, dependencies::InputList<>>([coefficient = std::make_unique<int>(7)] { return std::make_unique<int>(*coefficient); }));
auto original = dependencies::make_computation_cache(std::move(graph), context);
REQUIRE(original.try_prepare(context));
auto moved = std::move(original);
CHECK_FALSE(original.ready());
CHECK_FALSE(original.contains<Result>());
CHECK_THROWS_AS(original.get<Result>(), std::logic_error);
REQUIRE(moved.contains<Result>());
CHECK(*moved.get<Result>() == 7);
const auto report = moved.try_prepare(context);
REQUIRE(report);
CHECK(report->reused_count() == 1);
}
TEST_CASE("Combining disjoint contexts preserves owned values borrowed identities and selected tracking policies", "[unit][utils][dependencies]")
{
int value = 4;
const auto borrowed = dependencies::make_dependency_context(dependencies::borrow<Borrowed>(value, dependencies::track_by([](const int input)
{
return input / 10;
})));
const auto context = dependencies::combine_dependency_contexts(
dependencies::make_dependency_context(dependencies::own<Owned>(3)), borrowed.select(dependencies::KeyList<Borrowed>{}));
CHECK(context.get<Owned>() == 3);
CHECK(&context.get<Borrowed>() == &value);
STATIC_CHECK(std::same_as<typename decltype(context)::Keys, dependencies::KeyList<Owned, Borrowed>>);
auto cache = dependencies::make_computation_cache(context, dependencies::make_computation<Result, dependencies::InputList<Owned, Borrowed>>([](const int owned, const int input)
{
return owned + input / 10;
}));
REQUIRE(cache.try_prepare(context));
CHECK(cache.get<Result>() == 3);
value = 8;
const auto unchanged = cache.try_prepare(context);
REQUIRE(unchanged);
CHECK(unchanged->reused_count() == 1);
value = 12;
const auto changed = cache.try_prepare(context);
REQUIRE(changed);
CHECK(changed->source_changed<Borrowed>());
CHECK(cache.get<Result>() == 4);
}