106 lines
4.7 KiB
C++
106 lines
4.7 KiB
C++
#include <gtest/gtest.h>
|
|
#include <atomic>
|
|
#include <cstdint>
|
|
#include <memory>
|
|
#include <thread>
|
|
#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);
|
|
}
|
|
TEST(triple_state_strategy_test, returns_render_state_snapshot) {
|
|
Triple_State_Strategy<Triple_State_Test_Base, Triple_State_Test_State> 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<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);
|
|
}
|
|
TEST(triple_state_strategy_test, concurrently_acquires_render_revision_without_data_race) {
|
|
Triple_State_Strategy<Triple_State_Test_Base, Triple_State_Test_State> strategy;
|
|
std::atomic<bool> 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_Test_Base, Triple_State_Throwing_Assignment_State>;
|
|
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);
|
|
}
|