#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(); }