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.
104 lines
4.5 KiB
C++
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);
|
|
}
|