Index: cc/scheduler/begin_frame_observer_map_unittest.cc |
diff --git a/cc/scheduler/begin_frame_observer_map_unittest.cc b/cc/scheduler/begin_frame_observer_map_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..45a4c9cb564c7703a99e27315efe15ecda6e8e1d |
--- /dev/null |
+++ b/cc/scheduler/begin_frame_observer_map_unittest.cc |
@@ -0,0 +1,431 @@ |
+// Copyright 2011 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 "cc/scheduler/begin_frame_observer_map.h" |
+ |
+#include <algorithm> |
+ |
+#include "base/basictypes.h" |
+#include "base/gtest_prod_util.h" |
+#include "testing/gmock/include/gmock/gmock.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+// We use a macro here so that we get good line number information from gtest. |
+#define EXPECT_EMPTY(obmap) \ |
+ EXPECT_FALSE(obmap.HasObservers()); \ |
+ for (auto it = obmap.begin(); it != obmap.end(); it++) \ |
+ NOTREACHED(); \ |
+ for (auto it = obmap.cbegin(); it != obmap.cend(); it++) \ |
+ NOTREACHED(); \ |
+ EXPECT_EQ(GetConstIterators(&obmap), 0U); \ |
+ EXPECT_EQ(GetIterators(&obmap), 0U); |
+ |
+namespace cc { |
+ |
+// This has to be in the same name space as BeginFrameObserverMap so it is |
+// correctly |
+// friended. |
+class BeginFrameObserverMapTest : public ::testing::Test { |
+ public: |
+ template <class ObserverType, class ObserverDataType> |
+ size_t GetIterators( |
+ BeginFrameObserverMap<ObserverType, ObserverDataType>* obmap) { |
+ return obmap->iterators_; |
+ } |
+ |
+ template <class ObserverType, class ObserverDataType> |
+ size_t GetConstIterators( |
+ BeginFrameObserverMap<ObserverType, ObserverDataType>* obmap) { |
+ return obmap->const_iterators_; |
+ } |
+}; |
+ |
+namespace { |
+ |
+class TestObserver { |
+ public: |
+ int observe_; |
+ |
+ TestObserver() : observe_(0) {} |
+ void Observe() { observe_++; } |
+ int GetObserve() const { return observe_; } |
+}; |
+ |
+struct TestObserverData { |
+ TestObserverData() : data(0) {} |
+ |
+ size_t data; |
+}; |
+ |
+TEST_F(BeginFrameObserverMapTest, Types) { |
+ ::testing::StaticAssertTypeEq< |
+ BeginFrameObserverMap<TestObserver, |
+ TestObserverData>::const_iterator::value_type, |
+ std::pair<TestObserver* const, TestObserverData*>>(); |
+} |
+ |
+TEST_F(BeginFrameObserverMapTest, MapInitiallyEmpty) { |
+ BeginFrameObserverMap<TestObserver, TestObserverData> obmap; |
+ EXPECT_EMPTY(obmap); |
+} |
+ |
+TEST_F(BeginFrameObserverMapTest, SingleObserver) { |
+ size_t data_value1 = __LINE__; |
+ size_t data_value2 = __LINE__; |
+ BeginFrameObserverMap<TestObserver, TestObserverData> obmap; |
+ TestObserver obs1; |
+ |
+ // Observer not added yet |
+ EXPECT_FALSE(obmap.HasObserver(&obs1)); |
+ EXPECT_EQ(obmap[&obs1], nullptr); |
+ |
+ // Add the observer |
+ obmap.AddObserver(&obs1); |
+ EXPECT_TRUE(obmap.HasObserver(&obs1)); |
+ EXPECT_TRUE(obmap.HasObservers()); |
+ EXPECT_EQ(obmap[&obs1]->data, 0U); |
+ |
+ // Normal iterator |
+ for (auto it = obmap.begin(); it != obmap.end(); it++) { |
+ EXPECT_EQ(GetIterators(&obmap), 1U); |
+ EXPECT_EQ(GetConstIterators(&obmap), 0U); |
+ EXPECT_EQ(&obs1, it->first); |
+ it->second->data = data_value1; |
+ } |
+ EXPECT_EQ(GetIterators(&obmap), 0U); |
+ EXPECT_EQ(obmap[&obs1]->data, data_value1); |
+ // const iterator |
+ for (auto it = obmap.cbegin(); it != obmap.cend(); it++) { |
+ EXPECT_EQ(GetIterators(&obmap), 0U); |
+ EXPECT_EQ(GetConstIterators(&obmap), 1U); |
+ EXPECT_EQ(&obs1, it->first); |
+ EXPECT_EQ(it->second->data, data_value1); |
+ } |
+ EXPECT_EQ(GetConstIterators(&obmap), 0U); |
+ // Nested iterators |
+ for (auto it = obmap.begin(); it != obmap.end(); it++) { |
+ for (auto jt = obmap.begin(); jt != obmap.end(); jt++) { |
+ EXPECT_EQ(GetIterators(&obmap), 2U); |
+ EXPECT_EQ(GetConstIterators(&obmap), 0U); |
+ EXPECT_EQ(&obs1, it->first); |
+ it->second->data = data_value2; |
+ } |
+ EXPECT_EQ(GetIterators(&obmap), 1U); |
+ } |
+ EXPECT_EQ(GetIterators(&obmap), 0U); |
+ EXPECT_EQ(obmap[&obs1]->data, data_value2); |
+ // Nested const iterator |
+ for (auto it = obmap.cbegin(); it != obmap.cend(); it++) { |
+ for (auto jt = obmap.cbegin(); jt != obmap.cend(); jt++) { |
+ EXPECT_EQ(GetIterators(&obmap), 0U); |
+ EXPECT_EQ(GetConstIterators(&obmap), 2U); |
+ EXPECT_EQ(&obs1, jt->first); |
+ EXPECT_EQ(jt->second->data, data_value2); |
+ } |
+ EXPECT_EQ(GetConstIterators(&obmap), 1U); |
+ } |
+ EXPECT_EQ(GetConstIterators(&obmap), 0U); |
+ |
+ // Remove the observer |
+ obmap.RemoveObserver(&obs1); |
+ EXPECT_EMPTY(obmap); |
+} |
+ |
+TEST_F(BeginFrameObserverMapTest, TwoObservers) { |
+ size_t data_value1 = __LINE__; |
+ size_t data_value2 = __LINE__; |
+ size_t data_value3 = __LINE__; |
+ BeginFrameObserverMap<TestObserver, TestObserverData> obmap; |
+ TestObserver obs1; |
+ TestObserver obs2; |
+ |
+ obmap.AddObserver(&obs1); |
+ EXPECT_TRUE(obmap.HasObserver(&obs1)); |
+ obmap.AddObserver(&obs2); |
+ EXPECT_TRUE(obmap.HasObserver(&obs2)); |
+ |
+ EXPECT_TRUE(obmap.HasObservers()); |
+ // Normal iterator |
+ for (auto it = obmap.begin(); it != obmap.end(); it++) { |
+ EXPECT_EQ(GetIterators(&obmap), 1U); |
+ EXPECT_EQ(GetConstIterators(&obmap), 0U); |
+ EXPECT_EQ(it->second->data, 0U); |
+ it->second->data = data_value1; |
+ } |
+ EXPECT_EQ(GetIterators(&obmap), 0U); |
+ // const iterator |
+ for (auto it = obmap.cbegin(); it != obmap.cend(); it++) { |
+ EXPECT_EQ(GetIterators(&obmap), 0U); |
+ EXPECT_EQ(GetConstIterators(&obmap), 1U); |
+ EXPECT_EQ(it->second->data, data_value1); |
+ } |
+ EXPECT_EQ(GetConstIterators(&obmap), 0U); |
+ // operator[] |
+ EXPECT_EQ(obmap[&obs1]->data, data_value1); |
+ EXPECT_EQ(obmap[&obs2]->data, data_value1); |
+ |
+ // Nested iterators |
+ for (auto it = obmap.begin(); it != obmap.end(); it++) { |
+ for (auto jt = obmap.begin(); jt != obmap.end(); jt++) { |
+ EXPECT_EQ(GetIterators(&obmap), 2U); |
+ EXPECT_EQ(GetConstIterators(&obmap), 0U); |
+ it->second->data = data_value2; |
+ } |
+ EXPECT_EQ(GetIterators(&obmap), 1U); |
+ } |
+ EXPECT_EQ(GetIterators(&obmap), 0U); |
+ // Nested const iterator |
+ for (auto it = obmap.cbegin(); it != obmap.cend(); it++) { |
+ for (auto jt = obmap.cbegin(); jt != obmap.cend(); jt++) { |
+ EXPECT_EQ(GetIterators(&obmap), 0U); |
+ EXPECT_EQ(GetConstIterators(&obmap), 2U); |
+ EXPECT_EQ(jt->second->data, data_value2); |
+ } |
+ EXPECT_EQ(GetConstIterators(&obmap), 1U); |
+ } |
+ EXPECT_EQ(GetConstIterators(&obmap), 0U); |
+ |
+ // Remove a single observer |
+ obmap.RemoveObserver(&obs2); |
+ |
+ // Repeat the above |
+ EXPECT_TRUE(obmap.HasObservers()); |
+ // Normal iterator |
+ for (auto it = obmap.begin(); it != obmap.end(); it++) { |
+ EXPECT_EQ(it->second->data, data_value2); |
+ it->second->data = data_value3; |
+ } |
+ // const iterator |
+ for (auto it = obmap.begin(); it != obmap.end(); it++) { |
+ EXPECT_EQ(it->second->data, data_value3); |
+ } |
+ // operator[] |
+ EXPECT_EQ(obmap[&obs1]->data, data_value3); |
+ EXPECT_EQ(obmap[&obs2], nullptr); |
+ |
+ // Remove the other observer |
+ obmap.RemoveObserver(&obs1); |
+ EXPECT_EMPTY(obmap); |
+} |
+ |
+TEST_F(BeginFrameObserverMapTest, AddWhileIterating) { |
+ size_t data_value1 = __LINE__; |
+ BeginFrameObserverMap<TestObserver, TestObserverData> obmap; |
+ TestObserver obs1; |
+ TestObserver obs2; |
+ TestObserver obs3; |
+ obmap.AddObserver(&obs1); |
+ obmap.AddObserver(&obs3); |
+ |
+ obmap[&obs1]->data = data_value1; |
+ obmap[&obs3]->data = data_value1; |
+ |
+ int i = 1; |
+ for (auto it = obmap.begin(); it != obmap.end(); it++, i++) { |
+ // Calling add twice while iterating should be safe |
+ obmap.AddObserver(&obs2); |
+ // Should be able to access the data right after adding. |
+ EXPECT_TRUE(obmap.HasObserver(&obs2)); |
+ obmap[&obs2]->data = i; |
+ } |
+ EXPECT_TRUE(obmap.HasObservers()); |
+ |
+ EXPECT_EQ(obmap[&obs1]->data, data_value1); |
+ EXPECT_EQ(obmap[&obs2]->data, 2U); |
+ EXPECT_EQ(obmap[&obs3]->data, data_value1); |
+} |
+ |
+TEST_F(BeginFrameObserverMapTest, RemoveOneWhileIterating) { |
+ size_t data_value1 = __LINE__; |
+ BeginFrameObserverMap<TestObserver, TestObserverData> obmap; |
+ TestObserver obs1; |
+ TestObserver obs2; |
+ TestObserver obs3; |
+ obmap.AddObserver(&obs1); |
+ obmap.AddObserver(&obs2); |
+ obmap.AddObserver(&obs3); |
+ |
+ obmap[&obs1]->data = data_value1; |
+ obmap[&obs2]->data = data_value1; |
+ obmap[&obs3]->data = data_value1; |
+ |
+ for (auto it = obmap.begin(); it != obmap.end(); it++) { |
+ obmap.RemoveObserver(&obs2); |
+ // Observer doesn't go away until the iteration is finished... |
+ EXPECT_TRUE(obmap.HasObserver(&obs2)); |
+ } |
+ EXPECT_FALSE(obmap.HasObserver(&obs2)); |
+ EXPECT_TRUE(obmap.HasObservers()); |
+ |
+ EXPECT_EQ(obmap[&obs1]->data, data_value1); |
+ EXPECT_EQ(obmap[&obs2], nullptr); |
+ EXPECT_EQ(obmap[&obs3]->data, data_value1); |
+} |
+ |
+TEST_F(BeginFrameObserverMapTest, NestedRemoveOneWhileIterating) { |
+ size_t data_value1 = __LINE__; |
+ BeginFrameObserverMap<TestObserver, TestObserverData> obmap; |
+ TestObserver obs1; |
+ TestObserver obs2; |
+ TestObserver obs3; |
+ obmap.AddObserver(&obs1); |
+ obmap.AddObserver(&obs2); |
+ obmap.AddObserver(&obs3); |
+ |
+ obmap[&obs1]->data = data_value1; |
+ obmap[&obs2]->data = data_value1; |
+ obmap[&obs3]->data = data_value1; |
+ |
+ for (auto it = obmap.begin(); it != obmap.end(); it++) { |
+ for (auto jt = obmap.begin(); jt != obmap.end(); jt++) { |
+ obmap.RemoveObserver(&obs2); |
+ } |
+ } |
+ EXPECT_TRUE(obmap.HasObservers()); |
+ |
+ EXPECT_EQ(obmap[&obs1]->data, data_value1); |
+ EXPECT_EQ(obmap[&obs2], nullptr); |
+ EXPECT_EQ(obmap[&obs3]->data, data_value1); |
+} |
+ |
+TEST_F(BeginFrameObserverMapTest, RemoveAllWhileIterating) { |
+ BeginFrameObserverMap<TestObserver, TestObserverData> obmap; |
+ TestObserver obs1; |
+ TestObserver obs2; |
+ TestObserver obs3; |
+ obmap.AddObserver(&obs1); |
+ obmap.AddObserver(&obs2); |
+ |
+ for (auto it = obmap.begin(); it != obmap.end(); it++) { |
+ obmap.RemoveObserver(it->first); |
+ } |
+ EXPECT_EMPTY(obmap); |
+} |
+ |
+TEST_F(BeginFrameObserverMapTest, NestedRemoveAllWhileIterating) { |
+ BeginFrameObserverMap<TestObserver, TestObserverData> obmap; |
+ TestObserver obs1; |
+ TestObserver obs2; |
+ TestObserver obs3; |
+ obmap.AddObserver(&obs1); |
+ obmap.AddObserver(&obs2); |
+ |
+ for (auto it = obmap.begin(); it != obmap.end(); it++) { |
+ for (auto jt = obmap.begin(); jt != obmap.end(); jt++) { |
+ obmap.RemoveObserver(jt->first); |
+ } |
+ } |
+ EXPECT_EMPTY(obmap); |
+} |
+ |
+TEST_F(BeginFrameObserverMapTest, AttemptToRemoveWhileConstIterating) { |
+ BeginFrameObserverMap<TestObserver, TestObserverData> obmap; |
+ TestObserver obs1; |
+ TestObserver obs2; |
+ obmap.AddObserver(&obs1); |
+ obmap.AddObserver(&obs2); |
+ |
+ EXPECT_DEATH({ |
+ for (auto it = obmap.cbegin(); it != obmap.cend(); it++) { |
+ obmap.RemoveObserver(const_cast<TestObserver*>(it->first)); |
+ } |
+ }, ""); |
+ |
+ const BeginFrameObserverMap<TestObserver, TestObserverData>* cobmap = &obmap; |
+ EXPECT_DEATH({ |
+ for (auto it = cobmap->begin(); it != cobmap->end(); it++) { |
+ obmap.RemoveObserver(const_cast<TestObserver*>(it->first)); |
+ } |
+ }, ""); |
+} |
+ |
+TEST_F(BeginFrameObserverMapTest, AttemptToAddWhileConstIterating) { |
+ BeginFrameObserverMap<TestObserver, TestObserverData> obmap; |
+ TestObserver obs1; |
+ TestObserver obs2; |
+ TestObserver obs3; |
+ obmap.AddObserver(&obs1); |
+ obmap.AddObserver(&obs2); |
+ |
+ EXPECT_DEATH({ |
+ for (auto it = obmap.cbegin(); it != obmap.cend(); it++) { |
+ obmap.AddObserver(&obs3); |
+ } |
+ }, ""); |
+ |
+ const BeginFrameObserverMap<TestObserver, TestObserverData>* cobmap = &obmap; |
+ EXPECT_DEATH({ |
+ for (auto it = cobmap->begin(); it != cobmap->end(); it++) { |
+ obmap.AddObserver(&obs3); |
+ } |
+ }, ""); |
+} |
+ |
+struct TestObserverOwner { |
+ TestObserverOwner() : ob1(), ob2(), obmap() {} |
+ |
+ TestObserver ob1; |
+ TestObserver ob2; |
+ BeginFrameObserverMap<TestObserver, TestObserverData> obmap; |
+ |
+ void Notify() { |
+ for (auto it = obmap.begin(); it != obmap.end(); it++) { |
+ it->first->Observe(); |
+ } |
+ } |
+ |
+ int LargestObserver() const { |
+ int largest = 0; |
+ for (auto it = obmap.begin(); it != obmap.end(); it++) { |
+ largest = std::max(largest, it->first->GetObserve()); |
+ } |
+ return largest; |
+ } |
+ size_t LargestData() const { |
+ size_t largest1 = 0; |
+ size_t largest2 = 0; |
+ for (auto it = obmap.begin(); it != obmap.end(); it++) { |
+ largest1 = std::max(largest1, it->second->data); |
+ largest2 = std::max(largest2, obmap[it->first]->data); |
+ } |
+ DCHECK_EQ(largest1, largest2); |
+ return largest1; |
+ } |
+}; |
+ |
+TEST_F(BeginFrameObserverMapTest, ObserverOwner) { |
+ TestObserverOwner owner; |
+ |
+ owner.Notify(); |
+ EXPECT_EQ(owner.LargestObserver(), 0); |
+ EXPECT_EQ(owner.LargestData(), 0U); |
+ |
+ owner.obmap.AddObserver(&owner.ob1); |
+ owner.Notify(); |
+ EXPECT_EQ(owner.LargestObserver(), 1); |
+ EXPECT_EQ(owner.LargestData(), 0U); |
+ |
+ owner.obmap[&owner.ob1]->data = 2; |
+ EXPECT_EQ(owner.LargestObserver(), 1); |
+ EXPECT_EQ(owner.LargestData(), 2U); |
+ |
+ owner.obmap.AddObserver(&owner.ob2); |
+ EXPECT_EQ(owner.LargestObserver(), 1); |
+ EXPECT_EQ(owner.LargestData(), 2U); |
+ owner.Notify(); |
+ owner.Notify(); |
+ EXPECT_EQ(owner.LargestObserver(), 3); |
+ EXPECT_EQ(owner.LargestData(), 2U); |
+ |
+ owner.obmap[&owner.ob2]->data = 5; |
+ EXPECT_EQ(owner.LargestObserver(), 3); |
+ EXPECT_EQ(owner.LargestData(), 5U); |
+ |
+ owner.obmap.RemoveObserver(&owner.ob1); |
+ EXPECT_EQ(owner.LargestObserver(), 2); |
+ EXPECT_EQ(owner.LargestData(), 5U); |
+} |
+ |
+} // namespace |
+} // namespace cc |