#include #include #include #include #include #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 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); } TEST(triple_state_strategy_test, returns_render_state_snapshot) { Triple_State_Strategy strategy; const auto snapshot = strategy.render_use_state(); strategy.set<&Triple_State_Test_State::value>(7); strategy.publish(); strategy.acquire_render_state(); EXPECT_EQ(snapshot.value, 0); 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 {}; struct Multi_State_B_Strategy : Double_State_Strategy {}; 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); } TEST(triple_state_strategy_test, concurrently_acquires_render_revision_without_data_race) { Triple_State_Strategy strategy; std::atomic finished{}; auto acquire = [&] { while (!finished.load(std::memory_order_acquire)) { strategy.acquire_render_state(); } strategy.acquire_render_state(); }; std::thread first(acquire); std::thread second(acquire); for (int value = 1; value <= 1000; ++value) { strategy.set<&Triple_State_Test_State::value>(value); strategy.publish(); } finished.store(true, std::memory_order_release); first.join(); second.join(); EXPECT_EQ(strategy.acquire_render_state(), 1000); EXPECT_EQ(strategy.render_use_state().value, 1000); } struct Triple_State_Throwing_Assignment_State { int first{}; int second{}; Triple_State_Throwing_Assignment_State() = default; Triple_State_Throwing_Assignment_State(int first, int second) : first(first), second(second) {} Triple_State_Throwing_Assignment_State(const Triple_State_Throwing_Assignment_State&) = default; Triple_State_Throwing_Assignment_State(Triple_State_Throwing_Assignment_State&&) noexcept = default; Triple_State_Throwing_Assignment_State& operator=(const Triple_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; } Triple_State_Throwing_Assignment_State& operator=(Triple_State_Throwing_Assignment_State&&) noexcept = default; inline static bool throw_on_copy_assignment{}; }; TEST(triple_state_strategy_test, failed_publish_keeps_published_revision_and_render_state_unchanged) { using Strategy = Triple_State_Strategy; Triple_State_Throwing_Assignment_State::throw_on_copy_assignment = false; Strategy strategy(Triple_State_Throwing_Assignment_State(1, 2)); strategy.set<&Triple_State_Throwing_Assignment_State::first>(10); strategy.set<&Triple_State_Throwing_Assignment_State::second>(20); Triple_State_Throwing_Assignment_State::throw_on_copy_assignment = true; EXPECT_THROW(strategy.publish(), std::runtime_error); Triple_State_Throwing_Assignment_State::throw_on_copy_assignment = false; EXPECT_EQ(strategy.state_revision(), 0); EXPECT_EQ(strategy.acquire_render_state(), 0); EXPECT_EQ(strategy.render_use_state().first, 1); EXPECT_EQ(strategy.render_use_state().second, 2); strategy.publish(); EXPECT_EQ(strategy.state_revision(), 1); EXPECT_EQ(strategy.acquire_render_state(), 1); EXPECT_EQ(strategy.render_use_state().first, 10); EXPECT_EQ(strategy.render_use_state().second, 20); }