226 lines
6.3 KiB
C++
226 lines
6.3 KiB
C++
#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();
|
|
}
|