Files
MeanField_SERiF/tests/utils/dependencies/context.cpp
Emily Boudreaux cb20faeb3d 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.
2026-11-09 13:53:17 -05:00

104 lines
4.5 KiB
C++

#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);
}