整体错误处理添加
This commit is contained in:
@@ -104,6 +104,8 @@ exception
|
||||
|
||||
`fast_fail` 只改变 Unknown Failure 的处置方式, **不得改变 Known Result / Unknown Failure 的分类。**
|
||||
|
||||
Unknown Failure Policy 是进程级唯一配置,必须在启动异步工作前设置,任务在途时不得切换。实际失败点只向统一处理函数提交原始 `std::exception_ptr`,不得再逐次传入策略或在模块内保存策略副本。
|
||||
|
||||
## 4. 第三方库处理规则
|
||||
|
||||
第三方库自身使用返回码还是异常,不决定本系统的处理方式。
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
#include "Failure_Policy.hpp"
|
||||
#include <atomic>
|
||||
|
||||
namespace aethera {
|
||||
[[noreturn]] void Failure_Policy::handle_unknown_failure(Unknown_Failure_Policy policy, std::exception_ptr failure) {
|
||||
if (policy == Unknown_Failure_Policy::exception && failure) std::rethrow_exception(failure);
|
||||
namespace {
|
||||
std::atomic<Unknown_Failure_Policy> unknown_failure_policy{Unknown_Failure_Policy::fast_fail};
|
||||
}
|
||||
void Failure_Policy::configure(Unknown_Failure_Policy policy) noexcept {
|
||||
unknown_failure_policy.store(policy, std::memory_order_relaxed);
|
||||
}
|
||||
[[noreturn]] void Failure_Policy::handle_unknown_failure(std::exception_ptr failure) {
|
||||
if (unknown_failure_policy.load(std::memory_order_relaxed) == Unknown_Failure_Policy::exception && failure) std::rethrow_exception(failure);
|
||||
std::terminate();
|
||||
}
|
||||
} // namespace aethera
|
||||
|
||||
@@ -7,6 +7,9 @@ enum struct Unknown_Failure_Policy : std::uint8_t {
|
||||
exception /* 原样重抛 Unknown Failure,由上层隔离边界处理。 */
|
||||
};
|
||||
struct Failure_Policy final {
|
||||
[[noreturn]] static void handle_unknown_failure(Unknown_Failure_Policy policy, std::exception_ptr failure);
|
||||
/* 设置进程级策略;必须在启动异步工作前完成,不得在任务在途时切换。 */
|
||||
static void configure(Unknown_Failure_Policy policy) noexcept;
|
||||
/* 按当前进程级策略处理原始 Unknown Failure;空 failure 无法传播,只能终止。 */
|
||||
[[noreturn]] static void handle_unknown_failure(std::exception_ptr failure);
|
||||
};
|
||||
} // namespace aethera
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include "Error_handling_specification/Failure_Policy.hpp"
|
||||
#include <algorithm>
|
||||
#include <concurrentqueue.h>
|
||||
#include <deque>
|
||||
@@ -72,7 +73,7 @@ template <typename Node_Facade> Dag<Node_Facade>::Dag(Dag&&) noexcept = default;
|
||||
template <typename Node_Facade> auto Dag<Node_Facade>::operator=(Dag&&) noexcept -> Dag& = default;
|
||||
template <typename Node_Facade> template <typename Callback> void Dag<Node_Facade>::for_each_topological(Callback&& callback) {
|
||||
const auto order = detail::dag_topological_indices(d->nodes, d->edges);
|
||||
if (!order) throw std::logic_error("authoritative DAG is invalid");
|
||||
if (!order) Failure_Policy::handle_unknown_failure(std::make_exception_ptr(std::logic_error("authoritative DAG is invalid")));
|
||||
for (const auto index : *order) std::invoke(callback, d->nodes[index].id, d->nodes[index].object);
|
||||
}
|
||||
template <typename Node_Facade> template <typename Callback> void Dag<Node_Facade>::for_each_dependency(Callback&& callback) const { for (const auto& edge : d->edges) std::invoke(callback, edge.target, edge.source); }
|
||||
@@ -144,7 +145,7 @@ template <typename Node_Facade> void Owned_Dag<Node_Facade>::Internal::advance()
|
||||
}
|
||||
template <typename Node_Facade> void Owned_Dag<Node_Facade>::set(Graph graph) {
|
||||
typename Internal::Private::Deferred_Command command = [graph = std::move(graph)](Graph& current) mutable { current = std::move(graph); };
|
||||
if (!internal.d->queue.enqueue(std::move(command))) throw std::bad_alloc{};
|
||||
if (!internal.d->queue.enqueue(std::move(command))) Failure_Policy::handle_unknown_failure(std::make_exception_ptr(std::bad_alloc{}));
|
||||
}
|
||||
template <typename Node_Facade> void Owned_Dag<Node_Facade>::set(Edit edit, Edit_Completion completion) {
|
||||
typename Internal::Private::Deferred_Command command = [edit = std::move(edit), completion = std::move(completion)](Graph& current) mutable {
|
||||
@@ -255,10 +256,10 @@ template <typename Node_Facade> void Owned_Dag<Node_Facade>::set(Edit edit, Edit
|
||||
transaction_open = false;
|
||||
} catch (...) {
|
||||
if (transaction_open) rollback();
|
||||
throw;
|
||||
Failure_Policy::handle_unknown_failure(std::current_exception());
|
||||
}
|
||||
if (completion) completion({});
|
||||
};
|
||||
if (!internal.d->queue.enqueue(std::move(command))) throw std::bad_alloc{};
|
||||
if (!internal.d->queue.enqueue(std::move(command))) Failure_Policy::handle_unknown_failure(std::make_exception_ptr(std::bad_alloc{}));
|
||||
}
|
||||
} // namespace aethera
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "Throttled_Latest_Only.hpp"
|
||||
#include "Error_handling_specification/Failure_Policy.hpp"
|
||||
#include <algorithm>
|
||||
#include <concepts>
|
||||
#include <exception>
|
||||
@@ -35,7 +36,7 @@ auto Throttled_Latest_only::Private::start() -> std::expected<void, Start_Result
|
||||
}
|
||||
catch (...) {
|
||||
rollback_start();
|
||||
throw;
|
||||
Failure_Policy::handle_unknown_failure(std::current_exception());
|
||||
}
|
||||
timer_started(scheduled_timer_id);
|
||||
return {};
|
||||
@@ -88,7 +89,7 @@ auto Throttled_Latest_only::Private::prepare_start() -> std::expected<std::chron
|
||||
}
|
||||
catch (...) {
|
||||
rollback_start();
|
||||
throw;
|
||||
Failure_Policy::handle_unknown_failure(std::current_exception());
|
||||
}
|
||||
}
|
||||
void Throttled_Latest_only::Private::rollback_start() {
|
||||
@@ -145,7 +146,7 @@ auto Throttled_Latest_only::Private::stop(Stop_Completion completion) -> std::ex
|
||||
}
|
||||
catch (...) {
|
||||
cancel_failed();
|
||||
throw;
|
||||
Failure_Policy::handle_unknown_failure(std::current_exception());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@@ -214,7 +215,7 @@ void Throttled_Latest_only::Private::frame_due() {
|
||||
const auto result = scene->render(frames[selected_index].frame, [this, selected_index](proxy<scene::Frame>& completed_frame, std::exception_ptr failure) {
|
||||
if (failure) {
|
||||
abandon_render(selected_index);
|
||||
std::rethrow_exception(failure);
|
||||
Failure_Policy::handle_unknown_failure(failure);
|
||||
}
|
||||
rendered(selected_index, completed_frame);
|
||||
});
|
||||
@@ -223,7 +224,7 @@ void Throttled_Latest_only::Private::frame_due() {
|
||||
}
|
||||
catch (...) {
|
||||
abandon_render(selected_index);
|
||||
throw;
|
||||
Failure_Policy::handle_unknown_failure(std::current_exception());
|
||||
}
|
||||
}
|
||||
void Throttled_Latest_only::Private::record_render_submission(scene::Render_Result result) {
|
||||
@@ -296,7 +297,7 @@ void Throttled_Latest_only::Private::dispatch_send(std::size_t slot_index) {
|
||||
}
|
||||
catch (...) {
|
||||
abandon_send(slot_index);
|
||||
throw;
|
||||
Failure_Policy::handle_unknown_failure(std::current_exception());
|
||||
}
|
||||
}
|
||||
void Throttled_Latest_only::Private::sent(std::size_t slot_index, proxy<scene::Frame>& completed_frame) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "frame_policy/src/Throttled_Latest_Only.hpp"
|
||||
#include "Error_handling_specification/Failure_Policy.hpp"
|
||||
#include "statistics/Sliding_Statistics.hpp"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
@@ -458,6 +459,7 @@ TEST(Throttled_Latest_Only, Reports_Invalid_Configuration) {
|
||||
}
|
||||
|
||||
TEST(Throttled_Latest_Only, Render_Failure_Releases_Slot_Before_It_Escapes) {
|
||||
aethera::Failure_Policy::configure(aethera::Unknown_Failure_Policy::exception);
|
||||
auto control = std::make_shared<Frame_Policy_Test_Control>();
|
||||
auto policy = make_policy(control, 60.0);
|
||||
ASSERT_TRUE(policy->start());
|
||||
@@ -477,6 +479,7 @@ TEST(Throttled_Latest_Only, Render_Failure_Releases_Slot_Before_It_Escapes) {
|
||||
}
|
||||
|
||||
TEST(Throttled_Latest_Only, Synchronous_Completions_And_Failure_Restore_Slot) {
|
||||
aethera::Failure_Policy::configure(aethera::Unknown_Failure_Policy::exception);
|
||||
auto control = std::make_shared<Frame_Policy_Test_Control>();
|
||||
control->complete_render_synchronously = true;
|
||||
control->throw_from_send = true;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "Task_Graph.hpp"
|
||||
#include "detail/Taskflow_Execution.ipp"
|
||||
#include "Error_handling_specification/Failure_Policy.hpp"
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <exception>
|
||||
@@ -151,7 +152,7 @@ detail::Task_Graph_Execution::~Task_Graph_Execution() {
|
||||
}
|
||||
}
|
||||
std::shared_ptr<detail::Task_Graph_Execution> detail::Task_Graph_Execution::create(Task_Graph& graph) {
|
||||
if (!graph.d) throw std::invalid_argument("Task graph is moved from");
|
||||
if (!graph.d) Failure_Policy::handle_unknown_failure(std::make_exception_ptr(std::invalid_argument("Task graph is moved from")));
|
||||
auto execution = std::make_shared<Task_Graph_Execution>(Construction_Key{});
|
||||
execution->root = graph.d;
|
||||
std::unordered_set<Task_Graph::Private*> visited;
|
||||
@@ -228,7 +229,7 @@ auto Task_Graph::add(std::string task_name, std::function<void()> work) -> std::
|
||||
}
|
||||
catch (...) {
|
||||
private_data.get().record_failure(std::current_exception());
|
||||
throw;
|
||||
Failure_Policy::handle_unknown_failure(std::current_exception());
|
||||
}
|
||||
});
|
||||
const auto node_id = d->unique_node_id(task_name);
|
||||
@@ -247,7 +248,7 @@ auto Task_Graph::add_condition(std::string task_name, std::function<int()> work)
|
||||
}
|
||||
catch (...) {
|
||||
private_data.get().record_failure(std::current_exception());
|
||||
throw;
|
||||
Failure_Policy::handle_unknown_failure(std::current_exception());
|
||||
}
|
||||
});
|
||||
const auto node_id = d->unique_node_id(task_name);
|
||||
|
||||
@@ -11,18 +11,22 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
namespace aethera {
|
||||
detail::Taskflow_Completion_Boundary::Taskflow_Completion_Boundary(Unknown_Failure_Policy selected_failure_policy, Taskflow_Exception_Boundary selected_exception_boundary) : failure_policy(selected_failure_policy), exception_boundary(std::move(selected_exception_boundary)) {}
|
||||
detail::Taskflow_Completion_Boundary::Taskflow_Completion_Boundary(Taskflow_Exception_Boundary selected_exception_boundary) : exception_boundary(std::move(selected_exception_boundary)) {}
|
||||
void detail::Taskflow_Completion_Boundary::invoke(Taskflow_Completion& completion, std::exception_ptr failure) const noexcept {
|
||||
try {
|
||||
completion(std::move(failure));
|
||||
}
|
||||
catch (...) {
|
||||
if (failure_policy == Unknown_Failure_Policy::fast_fail) Failure_Policy::handle_unknown_failure(failure_policy, std::current_exception());
|
||||
try {
|
||||
exception_boundary(std::current_exception());
|
||||
Failure_Policy::handle_unknown_failure(std::current_exception());
|
||||
}
|
||||
catch (...) {
|
||||
std::terminate();
|
||||
try {
|
||||
exception_boundary(std::current_exception());
|
||||
}
|
||||
catch (...) {
|
||||
std::terminate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -179,8 +183,9 @@ public:
|
||||
? Initialize_Task_Runtime_Result::already_initialized
|
||||
: Initialize_Task_Runtime_Result::initialization_in_progress;
|
||||
}
|
||||
Failure_Policy::configure(failure_policy);
|
||||
try {
|
||||
auto next_completion_boundary = std::make_unique<detail::Taskflow_Completion_Boundary>(failure_policy, std::move(exception_boundary));
|
||||
auto next_completion_boundary = std::make_unique<detail::Taskflow_Completion_Boundary>(std::move(exception_boundary));
|
||||
auto next_executor = std::make_unique<tf::Executor>(workers);
|
||||
(void)next_executor->make_observer<Task_Observer>();
|
||||
completion_boundary = std::move(next_completion_boundary);
|
||||
@@ -191,7 +196,7 @@ public:
|
||||
phase.store(
|
||||
Runtime_Phase::uninitialized,
|
||||
std::memory_order_release);
|
||||
throw;
|
||||
Failure_Policy::handle_unknown_failure(std::current_exception());
|
||||
}
|
||||
return Initialize_Task_Runtime_Result::initialized;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "Taskflow_Observation.hpp"
|
||||
#include "detail/Taskflow_Execution.ipp"
|
||||
#include "Error_handling_specification/Failure_Policy.hpp"
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <expected>
|
||||
@@ -93,7 +94,7 @@ std::expected<Taskflow_Execution_Trace, Take_Taskflow_Observation_Result> Taskfl
|
||||
: Take_Taskflow_Observation_Result::recording};
|
||||
}
|
||||
if (auto failure = d->take_failure()) {
|
||||
std::rethrow_exception(failure);
|
||||
Failure_Policy::handle_unknown_failure(failure);
|
||||
}
|
||||
auto result = std::move(d->trace);
|
||||
d->worker_states.clear();
|
||||
@@ -149,7 +150,7 @@ std::expected<detail::Taskflow_Observation_Execution,
|
||||
observation.d->phase.store(
|
||||
Observation_Phase::requested,
|
||||
std::memory_order_release);
|
||||
throw;
|
||||
Failure_Policy::handle_unknown_failure(std::current_exception());
|
||||
}
|
||||
return Taskflow_Observation_Execution{std::move(observation.d)};
|
||||
}
|
||||
|
||||
@@ -19,10 +19,9 @@ class Worker;
|
||||
namespace aethera::detail {
|
||||
/* Process-lifetime worker boundary. The handler is immutable after runtime initialization and executes on the completing Taskflow worker. */
|
||||
struct Taskflow_Completion_Boundary final {
|
||||
Taskflow_Completion_Boundary(Unknown_Failure_Policy failure_policy, Taskflow_Exception_Boundary exception_boundary);
|
||||
explicit Taskflow_Completion_Boundary(Taskflow_Exception_Boundary exception_boundary);
|
||||
void invoke(Taskflow_Completion& completion, std::exception_ptr failure) const noexcept;
|
||||
private:
|
||||
Unknown_Failure_Policy failure_policy;
|
||||
Taskflow_Exception_Boundary exception_boundary;
|
||||
};
|
||||
/* 单帧执行期间持有根图及全部子图;调用方保证同一张图不并发执行或重建。 */
|
||||
|
||||
@@ -169,9 +169,10 @@ TEST(Task_Runtime, Validates_Optional_Exception_Boundary) {
|
||||
}
|
||||
|
||||
TEST(Taskflow_Completion_Boundary, Exception_Policy_Forwards_Completion_Failure) {
|
||||
aethera::Failure_Policy::configure(aethera::Unknown_Failure_Policy::exception);
|
||||
std::exception_ptr forwarded;
|
||||
aethera::detail::Taskflow_Completion_Boundary boundary(aethera::Unknown_Failure_Policy::exception, [&forwarded](std::exception_ptr failure) { forwarded = std::move(failure); });
|
||||
aethera::Taskflow_Completion completion = [](std::exception_ptr failure) { std::rethrow_exception(failure); };
|
||||
aethera::detail::Taskflow_Completion_Boundary boundary([&forwarded](std::exception_ptr failure) { forwarded = std::move(failure); });
|
||||
aethera::Taskflow_Completion completion = [](std::exception_ptr failure) { aethera::Failure_Policy::handle_unknown_failure(failure); };
|
||||
boundary.invoke(completion, std::make_exception_ptr(std::runtime_error("task failure")));
|
||||
ASSERT_TRUE(forwarded);
|
||||
EXPECT_THROW(std::rethrow_exception(forwarded), std::runtime_error);
|
||||
@@ -179,15 +180,15 @@ TEST(Taskflow_Completion_Boundary, Exception_Policy_Forwards_Completion_Failure)
|
||||
|
||||
#if GTEST_HAS_DEATH_TEST
|
||||
TEST(Taskflow_Completion_Boundary, Fast_Fail_Policy_Terminates_On_Completion_Failure) {
|
||||
aethera::detail::Taskflow_Completion_Boundary boundary(aethera::Unknown_Failure_Policy::fast_fail, {});
|
||||
aethera::detail::Taskflow_Completion_Boundary boundary({});
|
||||
aethera::Taskflow_Completion completion = [](std::exception_ptr) { throw std::runtime_error("completion failure"); };
|
||||
EXPECT_DEATH({ boundary.invoke(completion, {}); }, "");
|
||||
EXPECT_DEATH({ aethera::Failure_Policy::configure(aethera::Unknown_Failure_Policy::fast_fail); boundary.invoke(completion, {}); }, "");
|
||||
}
|
||||
|
||||
TEST(Taskflow_Completion_Boundary, Exception_Policy_Terminates_If_The_Receiver_Throws) {
|
||||
aethera::detail::Taskflow_Completion_Boundary boundary(aethera::Unknown_Failure_Policy::exception, [](std::exception_ptr) { throw std::runtime_error("receiver failure"); });
|
||||
aethera::detail::Taskflow_Completion_Boundary boundary([](std::exception_ptr) { throw std::runtime_error("receiver failure"); });
|
||||
aethera::Taskflow_Completion completion = [](std::exception_ptr) { throw std::runtime_error("completion failure"); };
|
||||
EXPECT_DEATH({ boundary.invoke(completion, {}); }, "");
|
||||
EXPECT_DEATH({ aethera::Failure_Policy::configure(aethera::Unknown_Failure_Policy::exception); boundary.invoke(completion, {}); }, "");
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "Timer_Scheduler.hpp"
|
||||
#include "Error_handling_specification/Failure_Policy.hpp"
|
||||
|
||||
#include "timer-wheel.h"
|
||||
|
||||
@@ -41,7 +42,7 @@ struct Timer_Scheduler::Impl {
|
||||
explicit Impl(proxy<Timer_Time_Source> time_source) :
|
||||
time_source(std::move(time_source)) {
|
||||
if (!this->time_source) {
|
||||
throw std::invalid_argument("Timer time source is unavailable");
|
||||
Failure_Policy::handle_unknown_failure(std::make_exception_ptr(std::invalid_argument("Timer time source is unavailable")));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
|
||||
namespace {
|
||||
TEST(Failure_Policy, Exception_Rethrows_The_Original_Unknown_Failure) {
|
||||
aethera::Failure_Policy::configure(aethera::Unknown_Failure_Policy::exception);
|
||||
const auto failure = std::make_exception_ptr(std::runtime_error("unknown failure"));
|
||||
try {
|
||||
aethera::Failure_Policy::handle_unknown_failure(aethera::Unknown_Failure_Policy::exception, failure);
|
||||
aethera::Failure_Policy::handle_unknown_failure(failure);
|
||||
} catch (const std::runtime_error& error) {
|
||||
EXPECT_STREQ(error.what(), "unknown failure");
|
||||
return;
|
||||
@@ -16,10 +17,10 @@ TEST(Failure_Policy, Exception_Rethrows_The_Original_Unknown_Failure) {
|
||||
|
||||
TEST(Failure_Policy, Fast_Fail_Terminates) {
|
||||
const auto failure = std::make_exception_ptr(std::runtime_error("unknown failure"));
|
||||
EXPECT_DEATH(aethera::Failure_Policy::handle_unknown_failure(aethera::Unknown_Failure_Policy::fast_fail, failure), "");
|
||||
EXPECT_DEATH({ aethera::Failure_Policy::configure(aethera::Unknown_Failure_Policy::fast_fail); aethera::Failure_Policy::handle_unknown_failure(failure); }, "");
|
||||
}
|
||||
|
||||
TEST(Failure_Policy, Missing_Unknown_Failure_Terminates) {
|
||||
EXPECT_DEATH(aethera::Failure_Policy::handle_unknown_failure(aethera::Unknown_Failure_Policy::exception, {}), "");
|
||||
EXPECT_DEATH({ aethera::Failure_Policy::configure(aethera::Unknown_Failure_Policy::exception); aethera::Failure_Policy::handle_unknown_failure({}); }, "");
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -4,15 +4,22 @@
|
||||
#include <vector>
|
||||
namespace {
|
||||
PRO_DEF_MEM_DISPATCH(Test_Model_Private_Read, private_value);
|
||||
struct Test_Model_Private_Facade : aethera::facade_builder::add_convention<Test_Model_Private_Read, int() const>::support_relocation<pro::constraint_level::nothrow>::build {};
|
||||
struct Test_Model_Private_Facade : aethera::facade_builder
|
||||
::add_convention<Test_Model_Private_Read, int() const>
|
||||
::support_relocation<pro::constraint_level::nothrow>
|
||||
::build {};
|
||||
PRO_DEF_MEM_DISPATCH(Other_Test_Model_Private_Read, other_private_value);
|
||||
struct Other_Test_Model_Private_Facade : aethera::facade_builder::add_convention<Other_Test_Model_Private_Read, int() const>::support_relocation<pro::constraint_level::nothrow>::build {};
|
||||
struct Other_Test_Model_Private_Facade : aethera::facade_builder
|
||||
::add_convention<Other_Test_Model_Private_Read, int() const>
|
||||
::support_relocation<pro::constraint_level::nothrow>
|
||||
::build {};
|
||||
struct Number_List_Tag {
|
||||
template <typename Layer> using Value = typename Layer::Numbers;
|
||||
};
|
||||
struct Test_Model_Base : aethera::Model_Layer<aethera::Root, aethera::Storage_Registration<aethera::Prop_Tag, aethera::Import_Struct_With_Dirty>, aethera::Storage_Registration<aethera::State_Tag, aethera::Export_Struct>, aethera::Storage_Registration<Number_List_Tag, aethera::Import_List>> {
|
||||
struct Prop : Prev<aethera::Prop_Tag> {
|
||||
int base_number{};
|
||||
int same_name{};
|
||||
};
|
||||
struct State : Prev<aethera::State_Tag> {
|
||||
int base_state{};
|
||||
@@ -36,6 +43,7 @@ struct Test_Model : aethera::Def<Test_Model, Test_Model_Middle> {
|
||||
struct Prop : Prev<aethera::Prop_Tag> {
|
||||
int number{};
|
||||
int other{};
|
||||
int same_name{};
|
||||
};
|
||||
struct State : Prev<aethera::State_Tag> {
|
||||
int state{};
|
||||
@@ -114,6 +122,23 @@ TEST(Model, Registered_Struct_Set_Publishes_To_The_Same_Tagged_Storage) {
|
||||
properties.internal.advance();
|
||||
EXPECT_EQ(properties.internal.use()->number, 13);
|
||||
}
|
||||
TEST(Model, Same_Name_Properties_In_Base_And_Derived_Layers_Are_Set_Independently) {
|
||||
Test_Model::Builder builder;
|
||||
builder
|
||||
.set<aethera::Import_Struct_With_Dirty, aethera::Prop_Tag>(&Test_Model_Base::Prop::same_name, 29)
|
||||
.set<aethera::Import_Struct_With_Dirty, aethera::Prop_Tag>(&Test_Model::Prop::same_name, 31);
|
||||
auto model = builder.build();
|
||||
const auto* properties = model->d->get<aethera::Prop_Tag>().internal.use();
|
||||
EXPECT_EQ(static_cast<const Test_Model_Base::Prop&>(*properties).same_name, 29);
|
||||
EXPECT_EQ(properties->same_name, 31);
|
||||
model->set<aethera::Import_Struct_With_Dirty, aethera::Prop_Tag>(&Test_Model_Base::Prop::same_name, 37);
|
||||
model->set<aethera::Import_Struct_With_Dirty, aethera::Prop_Tag>(&Test_Model::Prop::same_name, 41);
|
||||
auto& storage = model->d->get<aethera::Prop_Tag>();
|
||||
storage.internal.advance();
|
||||
properties = storage.internal.use();
|
||||
EXPECT_EQ(static_cast<const Test_Model_Base::Prop&>(*properties).same_name, 37);
|
||||
EXPECT_EQ(properties->same_name, 41);
|
||||
}
|
||||
TEST(Model, Consumer_Facade_Selects_One_Model_Private_View) {
|
||||
auto model = aethera::make_model_proxy<Test_Model_Private_Facade, Test_Model>();
|
||||
EXPECT_EQ(model->private_value(), 41);
|
||||
|
||||
Reference in New Issue
Block a user