blob: 90ecd62d65d7d9c6281659a57e87f064e6bc9a93 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/ui/scenic/lib/flatland/global_topology_data.h"
#include <lib/syslog/cpp/macros.h>
#include <memory>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
using flatland::TransformGraph;
using flatland::TransformHandle;
namespace {
constexpr TransformHandle::InstanceId kLinkInstanceId = 0;
// Gets the test-standard link handle to link to a graph rooted at |instance_id:0|.
TransformHandle GetLinkHandle(uint64_t instance_id) { return {kLinkInstanceId, instance_id}; }
// Creates a link in |links| to the the graph rooted at |instance_id:0|.
void MakeLink(flatland::GlobalTopologyData::LinkTopologyMap& links, uint64_t instance_id) {
links[GetLinkHandle(instance_id)] = {instance_id, 0};
}
} // namespace
namespace flatland {
namespace test {
// This is a macro so that, if the various test macros fail, we get a line number associated with a
// particular call in a unit test.
//
// |data| is a :GlobalTopologyData object. |link_id| is the instance ID for link handles.
#define CHECK_GLOBAL_TOPOLOGY_DATA(data, link_id) \
{ \
std::unordered_set<TransformHandle> all_handles; \
for (auto handle : data.topology_vector) { \
all_handles.insert(handle); \
EXPECT_NE(handle.GetInstanceId(), link_id); \
} \
EXPECT_EQ(all_handles, data.live_handles); \
}
TEST(GlobalTopologyDataTest, GlobalTopologyUnknownGraph) {
TransformHandle unknown_handle = {1, 1};
auto output =
GlobalTopologyData::ComputeGlobalTopologyData({}, {}, kLinkInstanceId, unknown_handle);
EXPECT_TRUE(output.topology_vector.empty());
EXPECT_TRUE(output.child_counts.empty());
EXPECT_TRUE(output.parent_indices.empty());
EXPECT_TRUE(output.live_handles.empty());
}
TEST(GlobalTopologyDataTest, GlobalTopologyLinkExpansion) {
UberStruct::InstanceMap uber_structs;
GlobalTopologyData::LinkTopologyMap links;
auto link_2 = GetLinkHandle(2);
TransformGraph::TopologyVector vectors[] = {{{{1, 0}, 1}, {link_2, 0}}, // 1:0 - 0:2
{{{2, 0}, 0}}}; // 2:0
MakeLink(links, 2); // 0:2 - 2:0
for (const auto& v : vectors) {
auto uber_struct = std::make_unique<UberStruct>();
uber_struct->local_topology = v;
uber_structs[v[0].handle.GetInstanceId()] = std::move(uber_struct);
}
// Combined, the global vector looks like this (the link handle is ommitted):
//
// 1:0 - 2:0
GlobalTopologyData::TopologyVector expected_topology = {{1, 0}, {2, 0}};
GlobalTopologyData::ChildCountVector expected_child_counts = {1, 0};
GlobalTopologyData::ParentIndexVector expected_parent_indices = {0, 0};
auto output =
GlobalTopologyData::ComputeGlobalTopologyData(uber_structs, links, kLinkInstanceId, {1, 0});
CHECK_GLOBAL_TOPOLOGY_DATA(output, 0u);
EXPECT_THAT(output.topology_vector, ::testing::ElementsAreArray(expected_topology));
EXPECT_THAT(output.child_counts, ::testing::ElementsAreArray(expected_child_counts));
EXPECT_THAT(output.parent_indices, ::testing::ElementsAreArray(expected_parent_indices));
}
TEST(GlobalTopologyDataTest, GlobalTopologyIncompleteLink) {
UberStruct::InstanceMap uber_structs;
GlobalTopologyData::LinkTopologyMap links;
auto link_2 = GetLinkHandle(2);
// The link is in the middle of the topology to demonstrate that the topology it links to replaces
// it in the correct order.
TransformGraph::TopologyVector vectors[] = {
{{{1, 0}, 3}, {{1, 1}, 0}, {link_2, 0}, {{1, 2}, 0}}, // 1:0 - 1:1
// \ \
// \ 0:2
// \
// 1:2
//
{{{2, 0}, 1}, {{2, 1}, 0}}}; // 2:0 - 2:1
// With only the first vector updated, we get the same result as the original topology, excluding
// the link handle.
//
// 1:0 - 1:1
// \
// 1:2
GlobalTopologyData::TopologyVector expected_topology = {{1, 0}, {1, 1}, {1, 2}};
GlobalTopologyData::ChildCountVector expected_child_counts = {2, 0, 0};
GlobalTopologyData::ParentIndexVector expected_parent_indices = {0, 0, 0};
auto uber_struct = std::make_unique<UberStruct>();
uber_struct->local_topology = vectors[0];
uber_structs[vectors[0][0].handle.GetInstanceId()] = std::move(uber_struct);
auto output =
GlobalTopologyData::ComputeGlobalTopologyData(uber_structs, links, kLinkInstanceId, {1, 0});
CHECK_GLOBAL_TOPOLOGY_DATA(output, 0u);
EXPECT_THAT(output.topology_vector, ::testing::ElementsAreArray(expected_topology));
EXPECT_THAT(output.child_counts, ::testing::ElementsAreArray(expected_child_counts));
EXPECT_THAT(output.parent_indices, ::testing::ElementsAreArray(expected_parent_indices));
// With the second vector updated, we still get the same result because the two are not linked.
//
// 1:0 - 1:1
// \
// 1:2
uber_struct = std::make_unique<UberStruct>();
uber_struct->local_topology = vectors[1];
uber_structs[vectors[1][0].handle.GetInstanceId()] = std::move(uber_struct);
output =
GlobalTopologyData::ComputeGlobalTopologyData(uber_structs, links, kLinkInstanceId, {1, 0});
CHECK_GLOBAL_TOPOLOGY_DATA(output, 0u);
EXPECT_THAT(output.topology_vector, ::testing::ElementsAreArray(expected_topology));
EXPECT_THAT(output.child_counts, ::testing::ElementsAreArray(expected_child_counts));
EXPECT_THAT(output.parent_indices, ::testing::ElementsAreArray(expected_parent_indices));
// When the link becomes available, the full topology is available, excluding the link handle.
//
// 1:0 - 1:1
// \ \
// \ 2:0 - 2:1
// \
// 1:2
expected_topology = {{1, 0}, {1, 1}, {2, 0}, {2, 1}, {1, 2}};
expected_child_counts = {3, 0, 1, 0, 0};
expected_parent_indices = {0, 0, 0, 2, 0};
MakeLink(links, 2); // 0:2 - 2:0
output =
GlobalTopologyData::ComputeGlobalTopologyData(uber_structs, links, kLinkInstanceId, {1, 0});
CHECK_GLOBAL_TOPOLOGY_DATA(output, 0u);
EXPECT_THAT(output.topology_vector, ::testing::ElementsAreArray(expected_topology));
EXPECT_THAT(output.child_counts, ::testing::ElementsAreArray(expected_child_counts));
EXPECT_THAT(output.parent_indices, ::testing::ElementsAreArray(expected_parent_indices));
}
TEST(GlobalTopologyDataTest, GlobalTopologyLinksMismatchedUberStruct) {
UberStruct::InstanceMap uber_structs;
GlobalTopologyData::LinkTopologyMap links;
auto link_2 = GetLinkHandle(2);
TransformGraph::TopologyVector vectors[] = {{{{1, 0}, 1}, {link_2, 0}}, // 1:0 - 0:2
//
{{{2, 0}, 0}}}; // 2:0
// Explicitly make an incorrect link for 0:2 to 2:1, which is not the start of the topology vector
// for instance ID 2. The link is skipped, leaving the expected topology as just 1:0.
links[{0, 2}] = {2, 1}; // 0:2 - 2:1
for (const auto& v : vectors) {
auto uber_struct = std::make_unique<UberStruct>();
uber_struct->local_topology = v;
uber_structs[v[0].handle.GetInstanceId()] = std::move(uber_struct);
}
GlobalTopologyData::TopologyVector expected_topology = {{1, 0}};
GlobalTopologyData::ChildCountVector expected_child_counts = {0};
GlobalTopologyData::ParentIndexVector expected_parent_indices = {0};
auto output =
GlobalTopologyData::ComputeGlobalTopologyData(uber_structs, links, kLinkInstanceId, {1, 0});
CHECK_GLOBAL_TOPOLOGY_DATA(output, 0u);
EXPECT_THAT(output.topology_vector, ::testing::ElementsAreArray(expected_topology));
EXPECT_THAT(output.child_counts, ::testing::ElementsAreArray(expected_child_counts));
EXPECT_THAT(output.parent_indices, ::testing::ElementsAreArray(expected_parent_indices));
// Changing the link to the right root handle of 2:0 completes the topology.
MakeLink(links, 2); // 0:2 - 2:0
// So the expected topology, excluding the link handle:
//
// 1:0 - 2:0
expected_topology = {{1, 0}, {2, 0}};
expected_child_counts = {1, 0};
expected_parent_indices = {0, 0};
output =
GlobalTopologyData::ComputeGlobalTopologyData(uber_structs, links, kLinkInstanceId, {1, 0});
CHECK_GLOBAL_TOPOLOGY_DATA(output, 0u);
EXPECT_THAT(output.topology_vector, ::testing::ElementsAreArray(expected_topology));
EXPECT_THAT(output.child_counts, ::testing::ElementsAreArray(expected_child_counts));
EXPECT_THAT(output.parent_indices, ::testing::ElementsAreArray(expected_parent_indices));
}
TEST(GlobalTopologyDataTest, GlobalTopologyDiamondInheritance) {
UberStruct::InstanceMap uber_structs;
GlobalTopologyData::LinkTopologyMap links;
auto link_2 = GetLinkHandle(2);
auto link_3 = GetLinkHandle(3);
TransformGraph::TopologyVector vectors[] = {{{{1, 0}, 2}, {link_2, 0}, {link_3, 0}}, // 1:0 - 0:2
// \
// 0:3
//
{{{2, 0}, 2}, {{2, 1}, 0}, {link_3, 0}}, // 2:0 - 2:1
// \
// 0:3
//
{{{3, 0}, 0}}}; // 3:0
for (const auto& v : vectors) {
auto uber_struct = std::make_unique<UberStruct>();
uber_struct->local_topology = v;
uber_structs[v[0].handle.GetInstanceId()] = std::move(uber_struct);
}
MakeLink(links, 2); // 0:2 - 2:0
MakeLink(links, 3); // 0:3 - 3:0
// When fully combined, we expect to find two copies of the third subgraph.
//
// 1:0 - 2:0 - 2:1
// \ \
// \ 3:0
// \
// 3:0
GlobalTopologyData::TopologyVector expected_topology = {{1, 0}, {2, 0}, {2, 1}, {3, 0}, {3, 0}};
GlobalTopologyData::ChildCountVector expected_child_counts = {2, 2, 0, 0, 0};
GlobalTopologyData::ParentIndexVector expected_parent_indices = {0, 0, 1, 1, 0};
auto output =
GlobalTopologyData::ComputeGlobalTopologyData(uber_structs, links, kLinkInstanceId, {1, 0});
CHECK_GLOBAL_TOPOLOGY_DATA(output, 0u);
EXPECT_THAT(output.topology_vector, ::testing::ElementsAreArray(expected_topology));
EXPECT_THAT(output.child_counts, ::testing::ElementsAreArray(expected_child_counts));
EXPECT_THAT(output.parent_indices, ::testing::ElementsAreArray(expected_parent_indices));
}
#undef CHECK_GLOBAL_TOPOLOGY_DATA
} // namespace test
} // namespace flatland