#include #include #include #include #include #include #include using namespace psemek; using namespace psemek::ui; namespace { struct rectangle_impl : impl::component { void update(rectangle const &) {} }; struct label_impl : impl::component { void update(label const &) {} }; struct button_impl : impl::single_container { void update(button const &) {} }; struct stack_layout_impl : impl::container { void update(stack_layout const &) {} }; struct test_component_factory : impl::component_factory_base { int rectangle_count = 0; int label_count = 0; int button_count = 0; int stack_layout_count = 0; test_component_factory() { register_type([this]{ ++rectangle_count; return std::make_unique(); }); register_type([this]{ ++label_count; return std::make_unique(); }); register_type([this]{ ++button_count; return std::make_unique(); }); register_type([this]{ ++stack_layout_count; return std::make_unique(); }); } }; struct check_children_helper { impl::component & component; std::size_t i = 0; template void check() { if constexpr (std::is_same_v) { expect_null(component.children()[i]); } else { expect_non_null(component.children()[i]); expect_dynamic_type(*component.children()[i], Type); } ++i; } }; template void check_children(impl::component & component) { expect_equal(component.children().size(), sizeof...(Types)); check_children_helper helper{component}; (void)helper; (helper.check(), ...); } } test_case(ui_impl_factory_element) { test_component_factory factory; auto test_ui = label{}; auto ui_root = factory.reconciliate(nullptr, test_ui); expect_non_null(ui_root); expect_dynamic_type(*ui_root, label_impl); expect_equal(factory.label_count, 1); } test_case(ui_impl_factory_single__container) { test_component_factory factory; auto test_ui = button{ .child = label{} }; auto ui_root = factory.reconciliate(nullptr, test_ui); expect_non_null(ui_root); expect_dynamic_type(*ui_root, button_impl); check_children(*ui_root); expect_equal(factory.label_count, 1); expect_equal(factory.button_count, 1); } test_case(ui_impl_factory_single__container__null) { test_component_factory factory; auto test_ui = button{}; auto ui_root = factory.reconciliate(nullptr, test_ui); expect_non_null(ui_root); expect_dynamic_type(*ui_root, button_impl); check_children(*ui_root); expect_equal(factory.button_count, 1); } test_case(ui_impl_factory_container) { test_component_factory factory; auto test_ui = stack_layout{{ {label{}}, {button{.child = label{}}}, {label{}}, }}; auto ui_root = factory.reconciliate(nullptr, test_ui); expect_non_null(ui_root); expect_dynamic_type(*ui_root, stack_layout_impl); check_children(*ui_root); check_children(*ui_root->children()[1]); expect_equal(factory.label_count, 3); expect_equal(factory.button_count, 1); expect_equal(factory.stack_layout_count, 1); } test_case(ui_impl_factory_container__null) { test_component_factory factory; auto test_ui = stack_layout{}; auto ui_root = factory.reconciliate(nullptr, test_ui); expect_non_null(ui_root); expect_dynamic_type(*ui_root, stack_layout_impl); check_children<>(*ui_root); expect_equal(factory.stack_layout_count, 1); } test_case(ui_impl_factory_container__null__child) { test_component_factory factory; auto test_ui = stack_layout{{ {}, {}, }}; auto ui_root = factory.reconciliate(nullptr, test_ui); expect_non_null(ui_root); expect_dynamic_type(*ui_root, stack_layout_impl); check_children(*ui_root); expect_equal(factory.stack_layout_count, 1); } test_case(ui_impl_reconciliate_single__container) { test_component_factory factory; auto child = react::source(label{}); expect_equal((*child).type(), typeid(label{})); auto test_ui = button{ .child = child }; expect_equal((*test_ui.child).type(), typeid(label{})); auto ui_root = factory.reconciliate(nullptr, test_ui); expect_non_null(ui_root); expect_dynamic_type(*ui_root, button_impl); check_children(*ui_root); expect_equal(factory.rectangle_count, 0); expect_equal(factory.label_count, 1); expect_equal(factory.button_count, 1); child.set(label{}); check_children(*ui_root); expect_equal(factory.rectangle_count, 0); expect_equal(factory.label_count, 1); expect_equal(factory.button_count, 1); child.set(rectangle{}); check_children(*ui_root); expect_equal(factory.rectangle_count, 1); expect_equal(factory.label_count, 1); expect_equal(factory.button_count, 1); child.set(label{}); check_children(*ui_root); expect_equal(factory.rectangle_count, 1); expect_equal(factory.label_count, 2); expect_equal(factory.button_count, 1); } test_case(ui_impl_reconciliate_container__no__keys) { test_component_factory factory; react::source> children; auto test_ui = stack_layout{children}; auto ui_root = factory.reconciliate(nullptr, test_ui); expect_equal(factory.stack_layout_count, 1); expect_equal(factory.label_count, 0); expect_equal(factory.button_count, 0); expect_non_null(ui_root); expect_dynamic_type(*ui_root, stack_layout_impl); check_children<>(*ui_root); children.set({{label{}}}); check_children(*ui_root); expect_equal(factory.stack_layout_count, 1); expect_equal(factory.label_count, 1); expect_equal(factory.button_count, 0); children.set({{label{}}, {label{}}}); check_children(*ui_root); expect_equal(factory.stack_layout_count, 1); expect_equal(factory.label_count, 2); expect_equal(factory.button_count, 0); children.set({{label{}}, {label{}}, {label{}}}); expect_equal(factory.stack_layout_count, 1); expect_equal(factory.label_count, 3); expect_equal(factory.button_count, 0); children.set({{label{}}, {button{}}, {label{}}}); expect_equal(factory.stack_layout_count, 1); expect_equal(factory.label_count, 3); expect_equal(factory.button_count, 1); children.set({{button{}}, {button{}}, {label{}}}); expect_equal(factory.stack_layout_count, 1); expect_equal(factory.label_count, 3); expect_equal(factory.button_count, 2); children.set({{button{}}, {button{}}}); expect_equal(factory.stack_layout_count, 1); expect_equal(factory.label_count, 3); expect_equal(factory.button_count, 2); children.set({{button{}}}); expect_equal(factory.stack_layout_count, 1); expect_equal(factory.label_count, 3); expect_equal(factory.button_count, 2); children.set({}); expect_equal(factory.stack_layout_count, 1); expect_equal(factory.label_count, 3); expect_equal(factory.button_count, 2); } test_case(ui_impl_reconciliate_container__keys) { test_component_factory factory; react::source> children; auto test_ui = stack_layout{children}; auto ui_root = factory.reconciliate(nullptr, test_ui); expect_equal(factory.stack_layout_count, 1); expect_equal(factory.label_count, 0); expect_non_null(ui_root); expect_dynamic_type(*ui_root, stack_layout_impl); check_children<>(*ui_root); children.set({{label{}, "Label 0"}}); check_children(*ui_root); auto label0 = ui_root->children()[0].get(); expect_equal(factory.stack_layout_count, 1); expect_equal(factory.label_count, 1); children.set({{label{}, "Label 0"}, {label{}, "Label 1"}}); check_children(*ui_root); expect_equal(ui_root->children()[0].get(), label0); auto label1 = ui_root->children()[1].get(); expect_equal(factory.stack_layout_count, 1); expect_equal(factory.label_count, 2); children.set({{label{}, "Label 1"}, {label{}, "Label 0"}}); check_children(*ui_root); expect_equal(ui_root->children()[0].get(), label1); expect_equal(ui_root->children()[1].get(), label0); expect_equal(factory.stack_layout_count, 1); expect_equal(factory.label_count, 2); children.set({{label{}, "Label 0"}, {label{}, "Label 2"}}); check_children(*ui_root); expect_equal(ui_root->children()[0].get(), label0); expect_equal(factory.stack_layout_count, 1); expect_equal(factory.label_count, 3); }