117 lines
5.4 KiB
C++
117 lines
5.4 KiB
C++
#include <gtest/gtest.h>
|
|
#include <cstdint>
|
|
#include <memory>
|
|
#include "State_Test_Types.hpp"
|
|
TEST(published_state_strategy_test, constructs_from_state) {
|
|
State_Plain_Strategy state({.count = 10, .name = "alpha"});
|
|
EXPECT_EQ(state.get<&State_Plain_Value::count>(), 10);
|
|
EXPECT_EQ(state.get<&State_Plain_Value::name>(), "alpha");
|
|
}
|
|
TEST(published_state_strategy_test, calls_optional_state_validator_when_present) {
|
|
State_Checked_Value state{.minimum = 10, .maximum = 5};
|
|
EXPECT_THROW(State_Checked_Validator::validate_state(state),
|
|
std::invalid_argument);
|
|
}
|
|
TEST(published_state_strategy_test, keeps_cache_separate_until_publish) {
|
|
State_Render_State_Reader render;
|
|
State_Checked_Strategy state(
|
|
State_Checked_Value{.percent = 20, .batch_size = 4});
|
|
state.set<&State_Checked_Value::percent>(60);
|
|
EXPECT_EQ(state.get<&State_Checked_Value::percent>(), 60);
|
|
EXPECT_EQ(render.read(state).percent.get(), 20);
|
|
state.publish();
|
|
EXPECT_EQ(render.read(state).percent.get(), 60);
|
|
}
|
|
TEST(published_state_strategy_test, keeps_acquired_render_reference_stable_until_publish) {
|
|
State_Render_State_Reader render;
|
|
State_Plain_Strategy strategy(State_Plain_Value{.count = 1, .name = "snapshot"});
|
|
const auto snapshot = render.read(strategy);
|
|
strategy.set<&State_Plain_Value::count>(2);
|
|
strategy.publish();
|
|
EXPECT_EQ(snapshot.count, 1);
|
|
EXPECT_EQ(render.read(strategy).count, 2);
|
|
}
|
|
TEST(published_state_strategy_test, keeps_cached_value_when_runtime_validation_fails) {
|
|
State_Checked_Strategy state(
|
|
State_Checked_Value{.percent = 20, .batch_size = 4});
|
|
EXPECT_THROW((state.set<&State_Checked_Value::percent>(200)),
|
|
std::out_of_range);
|
|
EXPECT_EQ(state.get<&State_Checked_Value::percent>(), 20);
|
|
}
|
|
TEST(published_state_strategy_test, supports_atomic_wait_mutex) {
|
|
State_Render_State_Reader render;
|
|
State_Wait_Strategy state({.count = 1, .name = "wait"});
|
|
state.set<&State_Plain_Value::count>(2);
|
|
state.publish();
|
|
EXPECT_EQ(render.read(state).count, 2);
|
|
}
|
|
struct Published_State_Observer_Test_Time_Source {
|
|
std::uint64_t value{100};
|
|
std::uint64_t now_ns() const noexcept {
|
|
return value;
|
|
}
|
|
};
|
|
struct Published_State_Observer_Test_Data {
|
|
std::uint64_t event_count{};
|
|
std::uint64_t publish_count{};
|
|
int value{};
|
|
};
|
|
struct Published_State_Observer_Test_Recorder {
|
|
static constexpr bool enabled = true;
|
|
std::shared_ptr<Published_State_Observer_Test_Data> data{std::make_shared<Published_State_Observer_Test_Data>()};
|
|
template <class Observation>
|
|
void observe(const Observation& observation) noexcept {
|
|
++data->event_count;
|
|
data->publish_count = observation.publish_count;
|
|
data->value = observation.state.count;
|
|
}
|
|
};
|
|
TEST(published_state_strategy_test, notifies_observer_for_cache_update_and_publish) {
|
|
using Observer = Observer_State<Published_State_Observer_Test_Recorder, Published_State_Observer_Test_Time_Source>;
|
|
using Strategy =
|
|
Published_State_Storage<State_Plain_Value, Atomic_Spin_Mutex, Observer>;
|
|
Published_State_Observer_Test_Recorder recorder;
|
|
Strategy strategy(State_Plain_Value{}, With_Observer(Observer(recorder, Published_State_Observer_Test_Time_Source{})));
|
|
strategy.set<&State_Plain_Value::count>(8);
|
|
strategy.publish();
|
|
EXPECT_EQ(recorder.data->event_count, 2);
|
|
EXPECT_EQ(recorder.data->publish_count, 1);
|
|
EXPECT_EQ(recorder.data->value, 8);
|
|
}
|
|
struct Published_State_Throwing_Assignment_State {
|
|
int first{};
|
|
int second{};
|
|
Published_State_Throwing_Assignment_State() = default;
|
|
Published_State_Throwing_Assignment_State(int first, int second) : first(first), second(second) {}
|
|
Published_State_Throwing_Assignment_State(const Published_State_Throwing_Assignment_State&) = default;
|
|
Published_State_Throwing_Assignment_State(Published_State_Throwing_Assignment_State&&) noexcept = default;
|
|
Published_State_Throwing_Assignment_State& operator=(const Published_State_Throwing_Assignment_State& other) {
|
|
first = other.first;
|
|
if (throw_on_copy_assignment) {
|
|
throw std::runtime_error("state assignment failed");
|
|
}
|
|
second = other.second;
|
|
return *this;
|
|
}
|
|
Published_State_Throwing_Assignment_State& operator=(Published_State_Throwing_Assignment_State&&) noexcept = default;
|
|
inline static bool throw_on_copy_assignment{};
|
|
};
|
|
TEST(published_state_strategy_test, failed_publish_keeps_render_state_and_revision_unchanged) {
|
|
State_Render_State_Reader render;
|
|
using Strategy = Published_State_Storage<Published_State_Throwing_Assignment_State>;
|
|
Published_State_Throwing_Assignment_State::throw_on_copy_assignment = false;
|
|
Strategy strategy(Published_State_Throwing_Assignment_State(1, 2));
|
|
strategy.set<&Published_State_Throwing_Assignment_State::first>(10);
|
|
strategy.set<&Published_State_Throwing_Assignment_State::second>(20);
|
|
Published_State_Throwing_Assignment_State::throw_on_copy_assignment = true;
|
|
EXPECT_THROW(strategy.publish(), std::runtime_error);
|
|
Published_State_Throwing_Assignment_State::throw_on_copy_assignment = false;
|
|
EXPECT_EQ(strategy.state_revision(), 0);
|
|
EXPECT_EQ(render.read(strategy).first, 1);
|
|
EXPECT_EQ(render.read(strategy).second, 2);
|
|
strategy.publish();
|
|
EXPECT_EQ(strategy.state_revision(), 1);
|
|
EXPECT_EQ(render.read(strategy).first, 10);
|
|
EXPECT_EQ(render.read(strategy).second, 20);
|
|
}
|