#include "double_buffer/model.hpp" #include namespace { struct Object_Buffer_Tag {}; struct Test_Object : double_buffer::Def> { struct Prop : Prev_Prop { int first{}; int second{}; }; struct State : Prev_State { int first{}; int second{}; bool operator==(const State&) const = default; }; struct Private : Prev_Private {}; auto& data_for_test(); }; using Object = double_buffer::Impl; inline auto& Test_Object::data_for_test() { return static_cast(*d); } struct Derived_Object : double_buffer::Def { struct Prop : Prev_Prop {}; struct State : Prev_State { int derived{}; bool operator==(const State&) const = default; }; struct Private : Prev_Private {}; }; using Derived = double_buffer::Impl; struct Lifetime_Object : double_buffer::Def { struct Prop : Prev_Prop {}; struct State : Prev_State { bool operator==(const State&) const = default; }; struct Private : Prev_Private { inline static int destruction_count{}; /* 测试最终 Private 是否经公共虚析构链释放。 */ ~Private() override { ++destruction_count; } }; }; using Lifetime = double_buffer::Impl; template std::unique_ptr build_object() { auto result = typename Object_Type::Builder{}.build(); if (!result) std::terminate(); return std::move(result).value(); } static_assert(!std::constructible_from); } TEST(object_lifetime, root_destroys_attached_private_through_virtual_base) { Lifetime_Object::Private::destruction_count = 0; { auto object = build_object(); EXPECT_EQ(Lifetime_Object::Private::destruction_count, 0); } EXPECT_EQ(Lifetime_Object::Private::destruction_count, 1); } TEST(object_builder, build_attaches_private_and_commits_staged_values) { Object::Builder builder; builder.set(&Test_Object::Prop::first, 29).set(31); auto result = builder.build(); ASSERT_TRUE(result.has_value()); auto object = std::move(result).value(); EXPECT_EQ(object->data_for_test().current->first, 29); EXPECT_EQ(object->data_for_test().pending->first, 0); EXPECT_EQ(object->current_buffer(), 31); EXPECT_EQ(object->pending_buffer(), 31); } TEST(object_buffer, state_commits_and_keeps_incremental_baseline) { auto object = build_object(); object->update_state<&Test_Object::State::first>(3); object->advance(); EXPECT_EQ(object->data_for_test().state.current->first, 3); EXPECT_EQ(object->data_for_test().state.pending->first, 3); object->update_state<&Test_Object::State::second>(5); object->advance(); EXPECT_EQ(object->data_for_test().state.current->first, 3); EXPECT_EQ(object->data_for_test().state.current->second, 5); } TEST(object_buffer, prop_publishes_and_keeps_incremental_baseline) { auto object = build_object(); object->set<&Test_Object::Prop::first>(17); EXPECT_EQ(object->data_for_test().current->first, 17); EXPECT_EQ(object->data_for_test().pending->first, 0); object->advance(); EXPECT_EQ(object->data_for_test().pending->first, 17); EXPECT_EQ(object->data_for_test().current->first, 17); object->set<&Test_Object::Prop::second>(19); object->advance(); EXPECT_EQ(object->data_for_test().pending->first, 17); EXPECT_EQ(object->data_for_test().pending->second, 19); } TEST(state_tag, callback_publishes_only_requested_layer) { auto object = build_object(); int calls = 0; object->set_state_callback( [&](const auto& state) { ++calls; EXPECT_EQ(state.first, 23); } ); object->update_state<&Test_Object::State::first>(23); object->advance(); EXPECT_EQ(calls, 0); object->notify_state(); EXPECT_EQ(calls, 1); } static_assert(requires(Object& object) { object.template access_state([](const auto&) {}); }); static_assert(std::same_as< decltype(std::declval().template read_state()), const Test_Object::State& >); struct Missing_State_Tag {}; static_assert(!double_buffer::detail::State_Tag_In); TEST(state_tag, inherited_tags_remain_independently_addressable) { auto object = build_object(); int base_calls = 0; int derived_calls = 0; object->set_state_callback([&](const auto&) { ++base_calls; }); object->set_state_callback([&](const auto&) { ++derived_calls; }); object->notify_state(); EXPECT_EQ(base_calls, 1); EXPECT_EQ(derived_calls, 0); object->notify_state(); EXPECT_EQ(base_calls, 1); EXPECT_EQ(derived_calls, 1); } TEST(state_tag, committed_layer_is_directly_readable_by_tag) { auto object = build_object(); object->update_state<&Test_Object::State::first>(41); object->update_state<&Derived_Object::State::derived>(43); object->advance(); EXPECT_EQ(object->read_state().first, 41); EXPECT_EQ(object->read_state().derived, 43); } static_assert(double_buffer::detail::State_Tag_In); static_assert(double_buffer::detail::State_Tag_In); static_assert(double_buffer::detail::State_Tag_In); static_assert(std::same_as< decltype(std::declval>().template get()), Test_Object::State& >); static_assert(std::same_as< decltype(std::declval>().template get()), const Derived_Object::State& >); TEST(state_tag, state_access_selects_mutable_and_const_layers_by_tag) { Derived::State state; double_buffer::State_Access states{state}; states.get().first = 31; states.get().derived = 47; double_buffer::State_Access base_states = states; EXPECT_EQ(base_states.get().first, 31); const auto& const_state = state; double_buffer::State_Access current_states{const_state}; EXPECT_EQ(current_states.get().first, 31); EXPECT_EQ(current_states.get().derived, 47); } TEST(state_tag, state_chain_keeps_default_equality_usable) { Test_Object::State first; Test_Object::State second; EXPECT_TRUE(first == second); second.first = 1; EXPECT_FALSE(first == second); } static_assert(std::same_as< decltype(std::declval>().template get()), Test_Object::Prop& >); static_assert(std::same_as< decltype(std::declval>().template get()), Test_Object::Private& >); TEST(base_tag, prop_state_and_private_domains_are_independently_addressable) { Derived::Prop prop; double_buffer::Prop_Access props{prop}; props.get().first = 53; EXPECT_EQ(props.get().first, 53); Derived_Object::Private private_data; double_buffer::Private_Access private_layers{private_data}; EXPECT_EQ(&private_layers.get(), static_cast(&private_data)); EXPECT_EQ(&private_layers.get(), static_cast(&private_data)); }