核心模板架构
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include "State_Test_Types.hpp"
|
||||
static_assert(State_Value<State_Plain_Value>);
|
||||
static_assert(std::derived_from<State_Plain_Strategy, State_Strategy_Base>);
|
||||
static_assert(Double_State_Strategy_Type<State_Plain_Strategy>);
|
||||
static_assert(!Has_State_Validator<State_Plain_Strategy, State_Plain_Value>);
|
||||
static_assert(Has_State_Validator<State_Checked_Strategy, State_Checked_Value>);
|
||||
TEST(state_concepts_test, concepts_compile) {
|
||||
SUCCEED();
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include "State_Test_Types.hpp"
|
||||
TEST(double_state_strategy_test, builds_without_state_validator) {
|
||||
auto product = State_Plain_Product::Builder{}.set<&State_Plain_Value::count>(10).set<&State_Plain_Value::name>("alpha").build();
|
||||
EXPECT_EQ(product->get<&State_Plain_Value::count>(), 10);
|
||||
EXPECT_EQ(product->get<&State_Plain_Value::name>(), "alpha");
|
||||
}
|
||||
TEST(double_state_strategy_test, calls_optional_state_validator_when_present) {
|
||||
auto builder = State_Checked_Product::Builder{}.set<&State_Checked_Value::minimum>(10).set<&State_Checked_Value::maximum>(5);
|
||||
EXPECT_THROW(builder.build(1, "invalid"), std::invalid_argument);
|
||||
}
|
||||
TEST(double_state_strategy_test, keeps_cache_separate_until_publish) {
|
||||
auto product = State_Checked_Product::Builder{}.set<&State_Checked_Value::percent>(20).set<&State_Checked_Value::batch_size>(4).build(1, "state");
|
||||
product->set<&State_Checked_Value::percent>(60);
|
||||
EXPECT_EQ(product->get<&State_Checked_Value::percent>(), 60);
|
||||
EXPECT_EQ(product->render_use_state().percent.get(), 20);
|
||||
product->publish();
|
||||
EXPECT_EQ(product->render_use_state().percent.get(), 60);
|
||||
}
|
||||
TEST(double_state_strategy_test, keeps_cached_value_when_runtime_validation_fails) {
|
||||
auto product = State_Checked_Product::Builder{}.set<&State_Checked_Value::percent>(20).set<&State_Checked_Value::batch_size>(4).build(1, "state");
|
||||
EXPECT_THROW((product->set<&State_Checked_Value::percent>(200)), std::out_of_range);
|
||||
EXPECT_EQ(product->get<&State_Checked_Value::percent>(), 20);
|
||||
}
|
||||
TEST(double_state_strategy_test, supports_atomic_wait_mutex) {
|
||||
auto product = State_Wait_Product::Builder{State_Plain_Value{.count = 1, .name = "wait"}}.build();
|
||||
product->set<&State_Plain_Value::count>(2);
|
||||
product->publish();
|
||||
EXPECT_EQ(product->render_use_state().count, 2);
|
||||
}
|
||||
TEST(double_state_strategy_test, passes_additional_builder_template_arguments) {
|
||||
static_assert(std::same_as<typename State_Tagged_Product::Builder::Tag, State_Builder_Tag>);
|
||||
static_assert(std::same_as<typename State_Tagged_Product::Builder::Mode, State_Builder_Mode>);
|
||||
auto product = State_Tagged_Product::Builder{State_Plain_Value{.count = 3, .name = "tagged"}}.build();
|
||||
EXPECT_EQ(product->get<&State_Plain_Value::count>(), 3);
|
||||
}
|
||||
struct Double_State_Observer_Test_Time_Source {
|
||||
std::uint64_t value{100};
|
||||
std::uint64_t now_ns() const noexcept {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
struct Double_State_Observer_Test_Data {
|
||||
std::uint64_t event_count{};
|
||||
std::uint64_t publish_count{};
|
||||
int value{};
|
||||
};
|
||||
struct Double_State_Observer_Test_Recorder {
|
||||
static constexpr bool enabled = true;
|
||||
std::shared_ptr<Double_State_Observer_Test_Data> data{std::make_shared<Double_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(double_state_strategy_test, notifies_observer_for_cache_update_and_publish) {
|
||||
using Observer = Observer_State<Double_State_Observer_Test_Recorder, Double_State_Observer_Test_Time_Source>;
|
||||
using Strategy = Double_State_Strategy<State_Plain_Base, State_Plain_Value, Atomic_Spin_Mutex, Observer>;
|
||||
Double_State_Observer_Test_Recorder recorder;
|
||||
Strategy strategy(State_Plain_Value{}, With_Observer(Observer(recorder, Double_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);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include "renderive/base/property/Property.hpp"
|
||||
#include "renderive/state/State_Strategy.hpp"
|
||||
struct State_Plain_Value {
|
||||
int count{};
|
||||
std::string name;
|
||||
};
|
||||
struct State_Plain_Base {};
|
||||
using State_Plain_Strategy = Double_State_Strategy<State_Plain_Base, State_Plain_Value>;
|
||||
using State_Plain_Product = Attach_State_Builder<State_Plain_Strategy, State_Plain_Value>;
|
||||
struct State_Checked_Value {
|
||||
struct Even_Validator {
|
||||
void operator()(const int& value) const {
|
||||
if (value % 2 != 0) {
|
||||
throw std::invalid_argument("value must be even");
|
||||
}
|
||||
}
|
||||
};
|
||||
Range_Value<int, 0, 100> percent{0};
|
||||
Validated_Value<int, Even_Validator> batch_size{0};
|
||||
int minimum{};
|
||||
int maximum{};
|
||||
};
|
||||
struct State_Checked_Base {
|
||||
State_Checked_Base(int id, std::string name) : id(id), name(std::move(name)) {}
|
||||
static void validate_state(const State_Checked_Value& state) {
|
||||
if (state.minimum > state.maximum) {
|
||||
throw std::invalid_argument("minimum must not exceed maximum");
|
||||
}
|
||||
}
|
||||
int id;
|
||||
std::string name;
|
||||
};
|
||||
using State_Checked_Strategy = Double_State_Strategy<State_Checked_Base, State_Checked_Value>;
|
||||
using State_Checked_Product = Attach_State_Builder<State_Checked_Strategy, State_Checked_Value>;
|
||||
using State_Wait_Product = Attach_State_Builder<Double_State_Strategy<State_Plain_Base, State_Plain_Value, Atomic_Wait_Mutex>, State_Plain_Value>;
|
||||
struct State_Builder_Tag {};
|
||||
struct State_Builder_Mode {};
|
||||
template <Property_Product Product, Property_Set Properties, Property_Validator<Properties> Validator, class Tag_Type, class Mode_Type>
|
||||
class State_Tagged_Builder : public Property_Builder<Product, Properties, Validator> {
|
||||
public:
|
||||
using Base = Property_Builder<Product, Properties, Validator>;
|
||||
using Tag = Tag_Type;
|
||||
using Mode = Mode_Type;
|
||||
using Base::Base;
|
||||
};
|
||||
using State_Tagged_Product = Attach_State_Builder<State_Plain_Strategy, State_Plain_Value, State_Tagged_Builder, State_Builder_Tag, State_Builder_Mode>;
|
||||
@@ -0,0 +1,36 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include "renderive/state/State_Strategy.hpp"
|
||||
struct Triple_State_Test_Base {};
|
||||
struct Triple_State_Test_State {
|
||||
int value{};
|
||||
};
|
||||
TEST(triple_state_strategy_test, publishes_then_acquires_render_state) {
|
||||
Triple_State_Strategy<Triple_State_Test_Base, Triple_State_Test_State> strategy;
|
||||
strategy.set<&Triple_State_Test_State::value>(7);
|
||||
strategy.publish();
|
||||
EXPECT_EQ(strategy.render_use_state().value, 0);
|
||||
EXPECT_EQ(strategy.acquire_render_state(), 1);
|
||||
EXPECT_EQ(strategy.render_use_state().value, 7);
|
||||
}
|
||||
struct Multi_State_A_Base {};
|
||||
struct Multi_State_B_Base {};
|
||||
struct Multi_State_A {
|
||||
int value{};
|
||||
};
|
||||
struct Multi_State_B {
|
||||
int value{};
|
||||
};
|
||||
struct Multi_State_A_Strategy : Double_State_Strategy<Multi_State_A_Base, Multi_State_A> {};
|
||||
struct Multi_State_B_Strategy : Double_State_Strategy<Multi_State_B_Base, Multi_State_B> {};
|
||||
struct Multi_State_Product : Multi_State_A_Strategy, Multi_State_B_Strategy {};
|
||||
TEST(double_state_strategy_test, supports_named_base_access_in_multiple_inheritance) {
|
||||
Multi_State_Product product;
|
||||
product.Multi_State_A_Strategy::set<&Multi_State_A::value>(3);
|
||||
product.Multi_State_B_Strategy::set<&Multi_State_B::value>(8);
|
||||
product.Multi_State_A_Strategy::publish();
|
||||
product.Multi_State_B_Strategy::publish();
|
||||
EXPECT_EQ(product.Multi_State_A_Strategy::render_use_state().value, 3);
|
||||
EXPECT_EQ(product.Multi_State_B_Strategy::render_use_state().value, 8);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include "../State_Test_Types.hpp"
|
||||
TEST(state_strategy_base_test, publishes_through_virtual_interface) {
|
||||
State_Plain_Strategy strategy;
|
||||
State_Strategy_Base* base = &strategy;
|
||||
strategy.set<&State_Plain_Value::count>(7);
|
||||
base->publish();
|
||||
EXPECT_EQ(base->state_revision(), 1);
|
||||
EXPECT_EQ(strategy.render_use_state().count, 7);
|
||||
}
|
||||
Reference in New Issue
Block a user