psemek/libs/ui/tests/reconciliation.cpp

364 lines
8.5 KiB
C++

#include <psemek/test/test.hpp>
#include <psemek/ui/impl/component_factory_base.hpp>
#include <psemek/ui/label.hpp>
#include <psemek/ui/rectangle.hpp>
#include <psemek/ui/button.hpp>
#include <psemek/ui/stack_layout.hpp>
#include <type_traits>
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<rectangle, rectangle_impl>([this]{ ++rectangle_count; return std::make_unique<rectangle_impl>(); });
register_type<label, label_impl>([this]{ ++label_count; return std::make_unique<label_impl>(); });
register_type<button, button_impl>([this]{ ++button_count; return std::make_unique<button_impl>(); });
register_type<stack_layout, stack_layout_impl>([this]{ ++stack_layout_count; return std::make_unique<stack_layout_impl>(); });
}
};
struct check_children_helper
{
impl::component & component;
std::size_t i = 0;
template <typename Type>
void check()
{
if constexpr (std::is_same_v<Type, std::nullptr_t>)
{
expect_null(component.children()[i]);
}
else
{
expect_non_null(component.children()[i]);
expect_dynamic_type(*component.children()[i], Type);
}
++i;
}
};
template <typename ... Types>
void check_children(impl::component & component)
{
expect_equal(component.children().size(), sizeof...(Types));
check_children_helper helper{component};
(void)helper;
(helper.check<Types>(), ...);
}
}
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<label_impl>(*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<std::nullptr_t>(*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<label_impl, button_impl, label_impl>(*ui_root);
check_children<label_impl>(*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<std::nullptr_t, std::nullptr_t>(*ui_root);
expect_equal(factory.stack_layout_count, 1);
}
test_case(ui_impl_reconciliate_single__container)
{
test_component_factory factory;
auto child = react::source<std::any>(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<label_impl>(*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<label_impl>(*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<rectangle_impl>(*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<label_impl>(*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<std::vector<stack_layout::element>> 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<label_impl>(*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<label_impl, label_impl>(*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<std::vector<stack_layout::element>> 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<label_impl>(*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<label_impl, label_impl>(*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<label_impl, label_impl>(*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<label_impl, label_impl>(*ui_root);
expect_equal(ui_root->children()[0].get(), label0);
expect_equal(factory.stack_layout_count, 1);
expect_equal(factory.label_count, 3);
}