Add UI core node lifetime handles
This commit is contained in:
225
tests/ui_core/node_lifetime_tests.cpp
Normal file
225
tests/ui_core/node_lifetime_tests.cpp
Normal file
@@ -0,0 +1,225 @@
|
||||
#include "ui_core/node_lifetime.h"
|
||||
#include "test_harness.h"
|
||||
|
||||
using pp::foundation::StatusCode;
|
||||
using pp::ui::NodeLifetimeTree;
|
||||
|
||||
namespace {
|
||||
|
||||
void creates_parent_child_handles(pp::tests::Harness& h)
|
||||
{
|
||||
NodeLifetimeTree tree;
|
||||
const auto root = tree.create_root();
|
||||
PP_EXPECT(h, root);
|
||||
if (!root) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto child = tree.create_child(root.value());
|
||||
PP_EXPECT(h, child);
|
||||
if (!child) {
|
||||
return;
|
||||
}
|
||||
|
||||
PP_EXPECT(h, tree.contains(root.value()));
|
||||
PP_EXPECT(h, tree.contains(child.value()));
|
||||
|
||||
const auto parent = tree.parent_of(child.value());
|
||||
PP_EXPECT(h, parent);
|
||||
if (parent) {
|
||||
PP_EXPECT(h, parent.value() == root.value());
|
||||
}
|
||||
|
||||
const auto count = tree.child_count(root.value());
|
||||
PP_EXPECT(h, count);
|
||||
if (count) {
|
||||
PP_EXPECT(h, count.value() == 1U);
|
||||
}
|
||||
|
||||
const auto first_child = tree.child_at(root.value(), 0);
|
||||
PP_EXPECT(h, first_child);
|
||||
if (first_child) {
|
||||
PP_EXPECT(h, first_child.value() == child.value());
|
||||
}
|
||||
}
|
||||
|
||||
void rejects_invalid_parent_and_child_lookup(pp::tests::Harness& h)
|
||||
{
|
||||
NodeLifetimeTree tree;
|
||||
const auto invalid_child = tree.create_child({});
|
||||
PP_EXPECT(h, !invalid_child.ok());
|
||||
PP_EXPECT(h, invalid_child.status().code == StatusCode::invalid_argument);
|
||||
|
||||
const auto root = tree.create_root();
|
||||
PP_EXPECT(h, root);
|
||||
if (!root) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto root_parent = tree.parent_of(root.value());
|
||||
const auto missing_child = tree.child_at(root.value(), 2);
|
||||
PP_EXPECT(h, !root_parent.ok());
|
||||
PP_EXPECT(h, root_parent.status().code == StatusCode::invalid_argument);
|
||||
PP_EXPECT(h, !missing_child.ok());
|
||||
PP_EXPECT(h, missing_child.status().code == StatusCode::out_of_range);
|
||||
}
|
||||
|
||||
void invalidates_destroyed_subtree_handles(pp::tests::Harness& h)
|
||||
{
|
||||
NodeLifetimeTree tree;
|
||||
const auto root = tree.create_root();
|
||||
PP_EXPECT(h, root);
|
||||
if (!root) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto child = tree.create_child(root.value());
|
||||
PP_EXPECT(h, child);
|
||||
if (!child) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto status = tree.destroy_subtree(root.value());
|
||||
PP_EXPECT(h, status.ok());
|
||||
PP_EXPECT(h, !tree.contains(root.value()));
|
||||
PP_EXPECT(h, !tree.contains(child.value()));
|
||||
|
||||
const auto reused = tree.create_root();
|
||||
PP_EXPECT(h, reused);
|
||||
if (reused) {
|
||||
PP_EXPECT(h, reused.value().slot == root.value().slot);
|
||||
PP_EXPECT(h, reused.value().generation != root.value().generation);
|
||||
PP_EXPECT(h, tree.contains(reused.value()));
|
||||
PP_EXPECT(h, !tree.contains(root.value()));
|
||||
}
|
||||
}
|
||||
|
||||
void scoped_connections_disconnect_on_reset(pp::tests::Harness& h)
|
||||
{
|
||||
NodeLifetimeTree tree;
|
||||
const auto root = tree.create_root();
|
||||
PP_EXPECT(h, root);
|
||||
if (!root) {
|
||||
return;
|
||||
}
|
||||
|
||||
int call_count = 0;
|
||||
auto scoped = tree.scoped_connect(root.value(), [&call_count](pp::ui::NodeHandle) {
|
||||
++call_count;
|
||||
});
|
||||
PP_EXPECT(h, scoped);
|
||||
if (!scoped) {
|
||||
return;
|
||||
}
|
||||
|
||||
PP_EXPECT(h, scoped.value().connected());
|
||||
PP_EXPECT(h, tree.dispatch(root.value()).ok());
|
||||
PP_EXPECT(h, call_count == 1);
|
||||
|
||||
scoped.value().reset();
|
||||
PP_EXPECT(h, !scoped.value().connected());
|
||||
PP_EXPECT(h, tree.dispatch(root.value()).ok());
|
||||
PP_EXPECT(h, call_count == 1);
|
||||
}
|
||||
|
||||
void destroying_node_disconnects_callbacks(pp::tests::Harness& h)
|
||||
{
|
||||
NodeLifetimeTree tree;
|
||||
const auto root = tree.create_root();
|
||||
PP_EXPECT(h, root);
|
||||
if (!root) {
|
||||
return;
|
||||
}
|
||||
|
||||
int call_count = 0;
|
||||
const auto connection = tree.connect(root.value(), [&call_count](pp::ui::NodeHandle) {
|
||||
++call_count;
|
||||
});
|
||||
PP_EXPECT(h, connection);
|
||||
if (!connection) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto live_count = tree.live_connection_count(root.value());
|
||||
PP_EXPECT(h, live_count);
|
||||
if (live_count) {
|
||||
PP_EXPECT(h, live_count.value() == 1U);
|
||||
}
|
||||
|
||||
PP_EXPECT(h, tree.destroy_subtree(root.value()).ok());
|
||||
PP_EXPECT(h, !tree.disconnect(connection.value()).ok());
|
||||
}
|
||||
|
||||
void dispatch_survives_destroy_during_callback(pp::tests::Harness& h)
|
||||
{
|
||||
NodeLifetimeTree tree;
|
||||
const auto root = tree.create_root();
|
||||
PP_EXPECT(h, root);
|
||||
if (!root) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto child = tree.create_child(root.value());
|
||||
PP_EXPECT(h, child);
|
||||
if (!child) {
|
||||
return;
|
||||
}
|
||||
|
||||
int first_count = 0;
|
||||
int second_count = 0;
|
||||
PP_EXPECT(h, tree.connect(child.value(), [&tree, &first_count](pp::ui::NodeHandle node) {
|
||||
++first_count;
|
||||
(void)tree.destroy_subtree(node);
|
||||
}));
|
||||
PP_EXPECT(h, tree.connect(child.value(), [&second_count](pp::ui::NodeHandle) {
|
||||
++second_count;
|
||||
}));
|
||||
|
||||
const auto dispatch = tree.dispatch(child.value());
|
||||
PP_EXPECT(h, dispatch.ok());
|
||||
PP_EXPECT(h, first_count == 1);
|
||||
PP_EXPECT(h, second_count == 0);
|
||||
PP_EXPECT(h, !tree.contains(child.value()));
|
||||
}
|
||||
|
||||
void dispatch_uses_stable_connection_snapshot(pp::tests::Harness& h)
|
||||
{
|
||||
NodeLifetimeTree tree;
|
||||
const auto root = tree.create_root();
|
||||
PP_EXPECT(h, root);
|
||||
if (!root) {
|
||||
return;
|
||||
}
|
||||
|
||||
int first_count = 0;
|
||||
int second_count = 0;
|
||||
PP_EXPECT(h, tree.connect(root.value(), [&tree, &root, &first_count, &second_count](pp::ui::NodeHandle) {
|
||||
++first_count;
|
||||
(void)tree.connect(root.value(), [&second_count](pp::ui::NodeHandle) {
|
||||
++second_count;
|
||||
});
|
||||
}));
|
||||
|
||||
PP_EXPECT(h, tree.dispatch(root.value()).ok());
|
||||
PP_EXPECT(h, first_count == 1);
|
||||
PP_EXPECT(h, second_count == 0);
|
||||
|
||||
PP_EXPECT(h, tree.dispatch(root.value()).ok());
|
||||
PP_EXPECT(h, first_count == 2);
|
||||
PP_EXPECT(h, second_count == 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
pp::tests::Harness harness;
|
||||
harness.run("creates_parent_child_handles", creates_parent_child_handles);
|
||||
harness.run("rejects_invalid_parent_and_child_lookup", rejects_invalid_parent_and_child_lookup);
|
||||
harness.run("invalidates_destroyed_subtree_handles", invalidates_destroyed_subtree_handles);
|
||||
harness.run("scoped_connections_disconnect_on_reset", scoped_connections_disconnect_on_reset);
|
||||
harness.run("destroying_node_disconnects_callbacks", destroying_node_disconnects_callbacks);
|
||||
harness.run("dispatch_survives_destroy_during_callback", dispatch_survives_destroy_during_callback);
|
||||
harness.run("dispatch_uses_stable_connection_snapshot", dispatch_uses_stable_connection_snapshot);
|
||||
return harness.finish();
|
||||
}
|
||||
Reference in New Issue
Block a user