summaryrefslogtreecommitdiff
path: root/chromium/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc')
-rw-r--r--chromium/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc529
1 files changed, 529 insertions, 0 deletions
diff --git a/chromium/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc b/chromium/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc
new file mode 100644
index 00000000000..8fd888d7ba1
--- /dev/null
+++ b/chromium/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc
@@ -0,0 +1,529 @@
+// Copyright 2016 The Chromium 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 <stddef.h>
+
+#include "cc/scheduler/begin_frame_source.h"
+#include "cc/test/begin_frame_source_test.h"
+#include "cc/test/fake_external_begin_frame_source.h"
+#include "components/viz/service/frame_sinks/frame_sink_manager_client.h"
+#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace viz {
+
+namespace {
+
+constexpr FrameSinkId kArbitraryFrameSinkId(1, 1);
+}
+
+class FakeFrameSinkManagerClient : public FrameSinkManagerClient {
+ public:
+ explicit FakeFrameSinkManagerClient(const FrameSinkId& frame_sink_id)
+ : source_(nullptr), manager_(nullptr), frame_sink_id_(frame_sink_id) {}
+
+ FakeFrameSinkManagerClient(const FrameSinkId& frame_sink_id,
+ FrameSinkManagerImpl* manager)
+ : source_(nullptr), manager_(nullptr), frame_sink_id_(frame_sink_id) {
+ DCHECK(manager);
+ Register(manager);
+ }
+
+ ~FakeFrameSinkManagerClient() override {
+ if (manager_) {
+ Unregister();
+ }
+ EXPECT_EQ(nullptr, source_);
+ }
+
+ cc::BeginFrameSource* source() { return source_; }
+ const FrameSinkId& frame_sink_id() { return frame_sink_id_; }
+
+ void Register(FrameSinkManagerImpl* manager) {
+ EXPECT_EQ(nullptr, manager_);
+ manager_ = manager;
+ manager_->RegisterFrameSinkManagerClient(frame_sink_id_, this);
+ }
+
+ void Unregister() {
+ EXPECT_NE(manager_, nullptr);
+ manager_->UnregisterFrameSinkManagerClient(frame_sink_id_);
+ manager_ = nullptr;
+ }
+
+ // FrameSinkManagerClient implementation.
+ void SetBeginFrameSource(cc::BeginFrameSource* begin_frame_source) override {
+ DCHECK(!source_ || !begin_frame_source);
+ source_ = begin_frame_source;
+ }
+
+ private:
+ cc::BeginFrameSource* source_;
+ FrameSinkManagerImpl* manager_;
+ FrameSinkId frame_sink_id_;
+};
+
+class FrameSinkManagerTest : public testing::Test {
+ public:
+ FrameSinkManagerTest() = default;
+
+ ~FrameSinkManagerTest() override = default;
+
+ protected:
+ FrameSinkManagerImpl manager_;
+};
+
+TEST_F(FrameSinkManagerTest, SingleClients) {
+ FakeFrameSinkManagerClient client(FrameSinkId(1, 1));
+ FakeFrameSinkManagerClient other_client(FrameSinkId(2, 2));
+ cc::StubBeginFrameSource source;
+
+ EXPECT_EQ(nullptr, client.source());
+ EXPECT_EQ(nullptr, other_client.source());
+ client.Register(&manager_);
+ other_client.Register(&manager_);
+ EXPECT_EQ(nullptr, client.source());
+ EXPECT_EQ(nullptr, other_client.source());
+
+ // Test setting unsetting BFS
+ manager_.RegisterBeginFrameSource(&source, client.frame_sink_id());
+ EXPECT_EQ(&source, client.source());
+ EXPECT_EQ(nullptr, other_client.source());
+ manager_.UnregisterBeginFrameSource(&source);
+ EXPECT_EQ(nullptr, client.source());
+ EXPECT_EQ(nullptr, other_client.source());
+
+ // Set BFS for other namespace
+ manager_.RegisterBeginFrameSource(&source, other_client.frame_sink_id());
+ EXPECT_EQ(&source, other_client.source());
+ EXPECT_EQ(nullptr, client.source());
+ manager_.UnregisterBeginFrameSource(&source);
+ EXPECT_EQ(nullptr, client.source());
+ EXPECT_EQ(nullptr, other_client.source());
+
+ // Re-set BFS for original
+ manager_.RegisterBeginFrameSource(&source, client.frame_sink_id());
+ EXPECT_EQ(&source, client.source());
+ manager_.UnregisterBeginFrameSource(&source);
+ EXPECT_EQ(nullptr, client.source());
+}
+
+// This test verifies that a client is still connected to the BeginFrameSource
+// after restart.
+TEST_F(FrameSinkManagerTest, ClientRestart) {
+ FakeFrameSinkManagerClient client(kArbitraryFrameSinkId);
+ cc::StubBeginFrameSource source;
+ client.Register(&manager_);
+
+ manager_.RegisterBeginFrameSource(&source, kArbitraryFrameSinkId);
+ EXPECT_EQ(&source, client.source());
+
+ // |client| is disconnect from |source| after Unregister.
+ client.Unregister();
+ EXPECT_EQ(nullptr, client.source());
+
+ // |client| is reconnected with |source| after re-Register.
+ client.Register(&manager_);
+ EXPECT_EQ(&source, client.source());
+
+ manager_.UnregisterBeginFrameSource(&source);
+ EXPECT_EQ(nullptr, client.source());
+}
+
+// This test verifies that a PrimaryBeginFrameSource will receive BeginFrames
+// from the first BeginFrameSource registered. If that BeginFrameSource goes
+// away then it will receive BeginFrames from the second BeginFrameSource.
+TEST_F(FrameSinkManagerTest, PrimaryBeginFrameSource) {
+ // This PrimaryBeginFrameSource should track the first BeginFrameSource
+ // registered with the SurfaceManager.
+ testing::NiceMock<cc::MockBeginFrameObserver> obs;
+ cc::BeginFrameSource* begin_frame_source =
+ manager_.GetPrimaryBeginFrameSource();
+ begin_frame_source->AddObserver(&obs);
+
+ FakeFrameSinkManagerClient root1(FrameSinkId(1, 1), &manager_);
+ std::unique_ptr<cc::FakeExternalBeginFrameSource> external_source1 =
+ base::MakeUnique<cc::FakeExternalBeginFrameSource>(60.f, false);
+ manager_.RegisterBeginFrameSource(external_source1.get(),
+ root1.frame_sink_id());
+
+ FakeFrameSinkManagerClient root2(FrameSinkId(2, 2), &manager_);
+ std::unique_ptr<cc::FakeExternalBeginFrameSource> external_source2 =
+ base::MakeUnique<cc::FakeExternalBeginFrameSource>(60.f, false);
+ manager_.RegisterBeginFrameSource(external_source2.get(),
+ root2.frame_sink_id());
+
+ cc::BeginFrameArgs args =
+ cc::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
+
+ // Ticking |external_source2| does not propagate to |begin_frame_source|.
+ {
+ EXPECT_CALL(obs, OnBeginFrame(args)).Times(0);
+ external_source2->TestOnBeginFrame(args);
+ testing::Mock::VerifyAndClearExpectations(&obs);
+ }
+
+ // Ticking |external_source1| does propagate to |begin_frame_source| and
+ // |obs|.
+ {
+ EXPECT_CALL(obs, OnBeginFrame(testing::_)).Times(1);
+ external_source1->TestOnBeginFrame(args);
+ testing::Mock::VerifyAndClearExpectations(&obs);
+ }
+
+ // Getting rid of |external_source1| means those BeginFrames will not
+ // propagate. Instead, |external_source2|'s BeginFrames will propagate
+ // to |begin_frame_source|.
+ {
+ manager_.UnregisterBeginFrameSource(external_source1.get());
+ EXPECT_CALL(obs, OnBeginFrame(testing::_)).Times(0);
+ external_source1->TestOnBeginFrame(args);
+ testing::Mock::VerifyAndClearExpectations(&obs);
+
+ EXPECT_CALL(obs, OnBeginFrame(testing::_)).Times(1);
+ external_source2->TestOnBeginFrame(args);
+ testing::Mock::VerifyAndClearExpectations(&obs);
+ }
+
+ // Tear down
+ manager_.UnregisterBeginFrameSource(external_source2.get());
+ begin_frame_source->RemoveObserver(&obs);
+}
+
+TEST_F(FrameSinkManagerTest, MultipleDisplays) {
+ cc::StubBeginFrameSource root1_source;
+ cc::StubBeginFrameSource root2_source;
+
+ // root1 -> A -> B
+ // root2 -> C
+ FakeFrameSinkManagerClient root1(FrameSinkId(1, 1), &manager_);
+ FakeFrameSinkManagerClient root2(FrameSinkId(2, 2), &manager_);
+ FakeFrameSinkManagerClient client_a(FrameSinkId(3, 3), &manager_);
+ FakeFrameSinkManagerClient client_b(FrameSinkId(4, 4), &manager_);
+ FakeFrameSinkManagerClient client_c(FrameSinkId(5, 5), &manager_);
+
+ manager_.RegisterBeginFrameSource(&root1_source, root1.frame_sink_id());
+ manager_.RegisterBeginFrameSource(&root2_source, root2.frame_sink_id());
+ EXPECT_EQ(root1.source(), &root1_source);
+ EXPECT_EQ(root2.source(), &root2_source);
+
+ // Set up initial hierarchy.
+ manager_.RegisterFrameSinkHierarchy(root1.frame_sink_id(),
+ client_a.frame_sink_id());
+ EXPECT_EQ(client_a.source(), root1.source());
+ manager_.RegisterFrameSinkHierarchy(client_a.frame_sink_id(),
+ client_b.frame_sink_id());
+ EXPECT_EQ(client_b.source(), root1.source());
+ manager_.RegisterFrameSinkHierarchy(root2.frame_sink_id(),
+ client_c.frame_sink_id());
+ EXPECT_EQ(client_c.source(), root2.source());
+
+ // Attach A into root2's subtree, like a window moving across displays.
+ // root1 -> A -> B
+ // root2 -> C -> A -> B
+ manager_.RegisterFrameSinkHierarchy(client_c.frame_sink_id(),
+ client_a.frame_sink_id());
+ // With the heuristic of just keeping existing BFS in the face of multiple,
+ // no client sources should change.
+ EXPECT_EQ(client_a.source(), root1.source());
+ EXPECT_EQ(client_b.source(), root1.source());
+ EXPECT_EQ(client_c.source(), root2.source());
+
+ // Detach A from root1. A and B should now be updated to root2.
+ manager_.UnregisterFrameSinkHierarchy(root1.frame_sink_id(),
+ client_a.frame_sink_id());
+ EXPECT_EQ(client_a.source(), root2.source());
+ EXPECT_EQ(client_b.source(), root2.source());
+ EXPECT_EQ(client_c.source(), root2.source());
+
+ // Detach root1 from BFS. root1 should now have no source.
+ manager_.UnregisterBeginFrameSource(&root1_source);
+ EXPECT_EQ(nullptr, root1.source());
+ EXPECT_NE(nullptr, root2.source());
+
+ // Detatch root2 from BFS.
+ manager_.UnregisterBeginFrameSource(&root2_source);
+ EXPECT_EQ(nullptr, client_a.source());
+ EXPECT_EQ(nullptr, client_b.source());
+ EXPECT_EQ(nullptr, client_c.source());
+ EXPECT_EQ(nullptr, root2.source());
+
+ // Cleanup hierarchy.
+ manager_.UnregisterFrameSinkHierarchy(root2.frame_sink_id(),
+ client_c.frame_sink_id());
+ manager_.UnregisterFrameSinkHierarchy(client_c.frame_sink_id(),
+ client_a.frame_sink_id());
+ manager_.UnregisterFrameSinkHierarchy(client_a.frame_sink_id(),
+ client_b.frame_sink_id());
+}
+
+// This test verifies that a BeginFrameSource path to the root from a
+// FrameSinkId is preserved even if that FrameSinkId has no children
+// and does not have a corresponding FrameSinkManagerClient.
+TEST_F(FrameSinkManagerTest, ParentWithoutClientRetained) {
+ cc::StubBeginFrameSource root_source;
+
+ constexpr FrameSinkId kFrameSinkIdRoot(1, 1);
+ constexpr FrameSinkId kFrameSinkIdA(2, 2);
+ constexpr FrameSinkId kFrameSinkIdB(3, 3);
+ constexpr FrameSinkId kFrameSinkIdC(4, 4);
+
+ FakeFrameSinkManagerClient root(kFrameSinkIdRoot, &manager_);
+ FakeFrameSinkManagerClient client_b(kFrameSinkIdB, &manager_);
+ FakeFrameSinkManagerClient client_c(kFrameSinkIdC, &manager_);
+
+ manager_.RegisterBeginFrameSource(&root_source, root.frame_sink_id());
+ EXPECT_EQ(&root_source, root.source());
+
+ // Set up initial hierarchy: root -> A -> B.
+ // Note that A does not have a FrameSinkManagerClient.
+ manager_.RegisterFrameSinkHierarchy(kFrameSinkIdRoot, kFrameSinkIdA);
+ manager_.RegisterFrameSinkHierarchy(kFrameSinkIdA, kFrameSinkIdB);
+ // The root's BeginFrameSource should propagate to B.
+ EXPECT_EQ(root.source(), client_b.source());
+
+ // Unregister B, and attach C to A: root -> A -> C
+ manager_.UnregisterFrameSinkHierarchy(kFrameSinkIdA, kFrameSinkIdB);
+ EXPECT_EQ(nullptr, client_b.source());
+ manager_.RegisterFrameSinkHierarchy(kFrameSinkIdA, kFrameSinkIdC);
+ // The root's BeginFrameSource should propagate to C.
+ EXPECT_EQ(root.source(), client_c.source());
+
+ manager_.UnregisterBeginFrameSource(&root_source);
+ EXPECT_EQ(nullptr, root.source());
+ EXPECT_EQ(nullptr, client_c.source());
+}
+
+// This test sets up the same hierarchy as ParentWithoutClientRetained.
+// However, this unit test registers the BeginFrameSource AFTER C
+// has been attached to A. This test verifies that the BeginFrameSource
+// propagates all the way to C.
+TEST_F(FrameSinkManagerTest,
+ ParentWithoutClientRetained_LateBeginFrameRegistration) {
+ cc::StubBeginFrameSource root_source;
+
+ constexpr FrameSinkId kFrameSinkIdRoot(1, 1);
+ constexpr FrameSinkId kFrameSinkIdA(2, 2);
+ constexpr FrameSinkId kFrameSinkIdB(3, 3);
+ constexpr FrameSinkId kFrameSinkIdC(4, 4);
+
+ FakeFrameSinkManagerClient root(kFrameSinkIdRoot, &manager_);
+ FakeFrameSinkManagerClient client_b(kFrameSinkIdB, &manager_);
+ FakeFrameSinkManagerClient client_c(kFrameSinkIdC, &manager_);
+
+ // Set up initial hierarchy: root -> A -> B.
+ // Note that A does not have a FrameSinkManagerClient.
+ manager_.RegisterFrameSinkHierarchy(kFrameSinkIdRoot, kFrameSinkIdA);
+ manager_.RegisterFrameSinkHierarchy(kFrameSinkIdA, kFrameSinkIdB);
+ // The root does not yet have a BeginFrameSource so client B should not have
+ // one either.
+ EXPECT_EQ(nullptr, client_b.source());
+
+ // Unregister B, and attach C to A: root -> A -> C
+ manager_.UnregisterFrameSinkHierarchy(kFrameSinkIdA, kFrameSinkIdB);
+ manager_.RegisterFrameSinkHierarchy(kFrameSinkIdA, kFrameSinkIdC);
+
+ // Registering a BeginFrameSource at the root should propagate it to C.
+ manager_.RegisterBeginFrameSource(&root_source, root.frame_sink_id());
+ // The root's BeginFrameSource should propagate to C.
+ EXPECT_EQ(&root_source, root.source());
+ EXPECT_EQ(root.source(), client_c.source());
+
+ manager_.UnregisterBeginFrameSource(&root_source);
+ EXPECT_EQ(nullptr, root.source());
+ EXPECT_EQ(nullptr, client_c.source());
+}
+
+// In practice, registering and unregistering both parent/child relationships
+// and FrameSinkManagerClients can happen in any ordering with respect to
+// each other. These following tests verify that all the data structures
+// are properly set up and cleaned up under the four permutations of orderings
+// of this nesting.
+
+class SurfaceManagerOrderingTest : public FrameSinkManagerTest {
+ public:
+ SurfaceManagerOrderingTest()
+ : client_a_(FrameSinkId(1, 1)),
+ client_b_(FrameSinkId(2, 2)),
+ client_c_(FrameSinkId(3, 3)),
+ hierarchy_registered_(false),
+ clients_registered_(false),
+ bfs_registered_(false) {
+ AssertCorrectBFSState();
+ }
+
+ ~SurfaceManagerOrderingTest() override {
+ EXPECT_FALSE(hierarchy_registered_);
+ EXPECT_FALSE(clients_registered_);
+ EXPECT_FALSE(bfs_registered_);
+ AssertCorrectBFSState();
+ }
+
+ void RegisterHierarchy() {
+ DCHECK(!hierarchy_registered_);
+ hierarchy_registered_ = true;
+ manager_.RegisterFrameSinkHierarchy(client_a_.frame_sink_id(),
+ client_b_.frame_sink_id());
+ manager_.RegisterFrameSinkHierarchy(client_b_.frame_sink_id(),
+ client_c_.frame_sink_id());
+ AssertCorrectBFSState();
+ }
+ void UnregisterHierarchy() {
+ DCHECK(hierarchy_registered_);
+ hierarchy_registered_ = false;
+ manager_.UnregisterFrameSinkHierarchy(client_a_.frame_sink_id(),
+ client_b_.frame_sink_id());
+ manager_.UnregisterFrameSinkHierarchy(client_b_.frame_sink_id(),
+ client_c_.frame_sink_id());
+ AssertCorrectBFSState();
+ }
+
+ void RegisterClients() {
+ DCHECK(!clients_registered_);
+ clients_registered_ = true;
+ client_a_.Register(&manager_);
+ client_b_.Register(&manager_);
+ client_c_.Register(&manager_);
+ AssertCorrectBFSState();
+ }
+
+ void UnregisterClients() {
+ DCHECK(clients_registered_);
+ clients_registered_ = false;
+ client_a_.Unregister();
+ client_b_.Unregister();
+ client_c_.Unregister();
+ AssertCorrectBFSState();
+ }
+
+ void RegisterBFS() {
+ DCHECK(!bfs_registered_);
+ bfs_registered_ = true;
+ manager_.RegisterBeginFrameSource(&source_, client_a_.frame_sink_id());
+ AssertCorrectBFSState();
+ }
+ void UnregisterBFS() {
+ DCHECK(bfs_registered_);
+ bfs_registered_ = false;
+ manager_.UnregisterBeginFrameSource(&source_);
+ AssertCorrectBFSState();
+ }
+
+ void AssertEmptyBFS() {
+ EXPECT_EQ(nullptr, client_a_.source());
+ EXPECT_EQ(nullptr, client_b_.source());
+ EXPECT_EQ(nullptr, client_c_.source());
+ }
+
+ void AssertAllValidBFS() {
+ EXPECT_EQ(&source_, client_a_.source());
+ EXPECT_EQ(&source_, client_b_.source());
+ EXPECT_EQ(&source_, client_c_.source());
+ }
+
+ protected:
+ void AssertCorrectBFSState() {
+ if (!clients_registered_ || !bfs_registered_) {
+ AssertEmptyBFS();
+ return;
+ }
+ if (!hierarchy_registered_) {
+ // A valid but not attached to anything.
+ EXPECT_EQ(&source_, client_a_.source());
+ EXPECT_EQ(nullptr, client_b_.source());
+ EXPECT_EQ(nullptr, client_c_.source());
+ return;
+ }
+
+ AssertAllValidBFS();
+ }
+
+ cc::StubBeginFrameSource source_;
+ // A -> B -> C hierarchy, with A always having the BFS.
+ FakeFrameSinkManagerClient client_a_;
+ FakeFrameSinkManagerClient client_b_;
+ FakeFrameSinkManagerClient client_c_;
+
+ bool hierarchy_registered_;
+ bool clients_registered_;
+ bool bfs_registered_;
+};
+
+enum RegisterOrder { REGISTER_HIERARCHY_FIRST, REGISTER_CLIENTS_FIRST };
+enum UnregisterOrder { UNREGISTER_HIERARCHY_FIRST, UNREGISTER_CLIENTS_FIRST };
+enum BFSOrder { BFS_FIRST, BFS_SECOND, BFS_THIRD };
+
+static const RegisterOrder kRegisterOrderList[] = {REGISTER_HIERARCHY_FIRST,
+ REGISTER_CLIENTS_FIRST};
+static const UnregisterOrder kUnregisterOrderList[] = {
+ UNREGISTER_HIERARCHY_FIRST, UNREGISTER_CLIENTS_FIRST};
+static const BFSOrder kBFSOrderList[] = {BFS_FIRST, BFS_SECOND, BFS_THIRD};
+
+class SurfaceManagerOrderingParamTest
+ : public SurfaceManagerOrderingTest,
+ public ::testing::WithParamInterface<
+ std::tr1::tuple<RegisterOrder, UnregisterOrder, BFSOrder>> {};
+
+TEST_P(SurfaceManagerOrderingParamTest, Ordering) {
+ // Test the four permutations of client/hierarchy setting/unsetting and test
+ // each place the BFS can be added and removed. The BFS and the
+ // client/hierarchy are less related, so BFS is tested independently instead
+ // of every permutation of BFS setting and unsetting.
+ // The register/unregister functions themselves test most of the state.
+ RegisterOrder register_order = std::tr1::get<0>(GetParam());
+ UnregisterOrder unregister_order = std::tr1::get<1>(GetParam());
+ BFSOrder bfs_order = std::tr1::get<2>(GetParam());
+
+ // Attach everything up in the specified order.
+ if (bfs_order == BFS_FIRST)
+ RegisterBFS();
+
+ if (register_order == REGISTER_HIERARCHY_FIRST)
+ RegisterHierarchy();
+ else
+ RegisterClients();
+
+ if (bfs_order == BFS_SECOND)
+ RegisterBFS();
+
+ if (register_order == REGISTER_HIERARCHY_FIRST)
+ RegisterClients();
+ else
+ RegisterHierarchy();
+
+ if (bfs_order == BFS_THIRD)
+ RegisterBFS();
+
+ // Everything hooked up, so should be valid.
+ AssertAllValidBFS();
+
+ // Detach everything in the specified order.
+ if (bfs_order == BFS_THIRD)
+ UnregisterBFS();
+
+ if (unregister_order == UNREGISTER_HIERARCHY_FIRST)
+ UnregisterHierarchy();
+ else
+ UnregisterClients();
+
+ if (bfs_order == BFS_SECOND)
+ UnregisterBFS();
+
+ if (unregister_order == UNREGISTER_HIERARCHY_FIRST)
+ UnregisterClients();
+ else
+ UnregisterHierarchy();
+
+ if (bfs_order == BFS_FIRST)
+ UnregisterBFS();
+}
+
+INSTANTIATE_TEST_CASE_P(
+ SurfaceManagerOrderingParamTestInstantiation,
+ SurfaceManagerOrderingParamTest,
+ ::testing::Combine(::testing::ValuesIn(kRegisterOrderList),
+ ::testing::ValuesIn(kUnregisterOrderList),
+ ::testing::ValuesIn(kBFSOrderList)));
+
+} // namespace viz