|  | // Copyright (c) 2012 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 "base/memory/scoped_vector.h" | 
|  |  | 
|  | #include "base/bind.h" | 
|  | #include "base/callback.h" | 
|  | #include "base/memory/scoped_ptr.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // The LifeCycleObject notifies its Observer upon construction & destruction. | 
|  | class LifeCycleObject { | 
|  | public: | 
|  | class Observer { | 
|  | public: | 
|  | virtual void OnLifeCycleConstruct(LifeCycleObject* o) = 0; | 
|  | virtual void OnLifeCycleDestroy(LifeCycleObject* o) = 0; | 
|  |  | 
|  | protected: | 
|  | virtual ~Observer() {} | 
|  | }; | 
|  |  | 
|  | ~LifeCycleObject() { | 
|  | if (observer_) | 
|  | observer_->OnLifeCycleDestroy(this); | 
|  | } | 
|  |  | 
|  | private: | 
|  | friend class LifeCycleWatcher; | 
|  |  | 
|  | explicit LifeCycleObject(Observer* observer) | 
|  | : observer_(observer) { | 
|  | observer_->OnLifeCycleConstruct(this); | 
|  | } | 
|  |  | 
|  | void DisconnectObserver() { | 
|  | observer_ = nullptr; | 
|  | } | 
|  |  | 
|  | Observer* observer_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(LifeCycleObject); | 
|  | }; | 
|  |  | 
|  | // The life cycle states we care about for the purposes of testing ScopedVector | 
|  | // against objects. | 
|  | enum LifeCycleState { | 
|  | LC_INITIAL, | 
|  | LC_CONSTRUCTED, | 
|  | LC_DESTROYED, | 
|  | }; | 
|  |  | 
|  | // Because we wish to watch the life cycle of an object being constructed and | 
|  | // destroyed, and further wish to test expectations against the state of that | 
|  | // object, we cannot save state in that object itself. Instead, we use this | 
|  | // pairing of the watcher, which observes the object and notifies of | 
|  | // construction & destruction. Since we also may be testing assumptions about | 
|  | // things not getting freed, this class also acts like a scoping object and | 
|  | // deletes the |constructed_life_cycle_object_|, if any when the | 
|  | // LifeCycleWatcher is destroyed. To keep this simple, the only expected state | 
|  | // changes are: | 
|  | //   INITIAL -> CONSTRUCTED -> DESTROYED. | 
|  | // Anything more complicated than that should start another test. | 
|  | class LifeCycleWatcher : public LifeCycleObject::Observer { | 
|  | public: | 
|  | LifeCycleWatcher() : life_cycle_state_(LC_INITIAL) {} | 
|  | ~LifeCycleWatcher() override { | 
|  | // Stop watching the watched object. Without this, the object's destructor | 
|  | // will call into OnLifeCycleDestroy when destructed, which happens after | 
|  | // this destructor has finished running. | 
|  | if (constructed_life_cycle_object_) | 
|  | constructed_life_cycle_object_->DisconnectObserver(); | 
|  | } | 
|  |  | 
|  | // Assert INITIAL -> CONSTRUCTED and no LifeCycleObject associated with this | 
|  | // LifeCycleWatcher. | 
|  | void OnLifeCycleConstruct(LifeCycleObject* object) override { | 
|  | ASSERT_EQ(LC_INITIAL, life_cycle_state_); | 
|  | ASSERT_EQ(NULL, constructed_life_cycle_object_.get()); | 
|  | life_cycle_state_ = LC_CONSTRUCTED; | 
|  | constructed_life_cycle_object_.reset(object); | 
|  | } | 
|  |  | 
|  | // Assert CONSTRUCTED -> DESTROYED and the |object| being destroyed is the | 
|  | // same one we saw constructed. | 
|  | void OnLifeCycleDestroy(LifeCycleObject* object) override { | 
|  | ASSERT_EQ(LC_CONSTRUCTED, life_cycle_state_); | 
|  | LifeCycleObject* constructed_life_cycle_object = | 
|  | constructed_life_cycle_object_.release(); | 
|  | ASSERT_EQ(constructed_life_cycle_object, object); | 
|  | life_cycle_state_ = LC_DESTROYED; | 
|  | } | 
|  |  | 
|  | LifeCycleState life_cycle_state() const { return life_cycle_state_; } | 
|  |  | 
|  | // Factory method for creating a new LifeCycleObject tied to this | 
|  | // LifeCycleWatcher. | 
|  | LifeCycleObject* NewLifeCycleObject() { | 
|  | return new LifeCycleObject(this); | 
|  | } | 
|  |  | 
|  | // Returns true iff |object| is the same object that this watcher is tracking. | 
|  | bool IsWatching(LifeCycleObject* object) const { | 
|  | return object == constructed_life_cycle_object_.get(); | 
|  | } | 
|  |  | 
|  | private: | 
|  | LifeCycleState life_cycle_state_; | 
|  | scoped_ptr<LifeCycleObject> constructed_life_cycle_object_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(LifeCycleWatcher); | 
|  | }; | 
|  |  | 
|  | TEST(ScopedVectorTest, LifeCycleWatcher) { | 
|  | LifeCycleWatcher watcher; | 
|  | EXPECT_EQ(LC_INITIAL, watcher.life_cycle_state()); | 
|  | LifeCycleObject* object = watcher.NewLifeCycleObject(); | 
|  | EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state()); | 
|  | delete object; | 
|  | EXPECT_EQ(LC_DESTROYED, watcher.life_cycle_state()); | 
|  | } | 
|  |  | 
|  | TEST(ScopedVectorTest, PopBack) { | 
|  | LifeCycleWatcher watcher; | 
|  | EXPECT_EQ(LC_INITIAL, watcher.life_cycle_state()); | 
|  | ScopedVector<LifeCycleObject> scoped_vector; | 
|  | scoped_vector.push_back(watcher.NewLifeCycleObject()); | 
|  | EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state()); | 
|  | EXPECT_TRUE(watcher.IsWatching(scoped_vector.back())); | 
|  | scoped_vector.pop_back(); | 
|  | EXPECT_EQ(LC_DESTROYED, watcher.life_cycle_state()); | 
|  | EXPECT_TRUE(scoped_vector.empty()); | 
|  | } | 
|  |  | 
|  | TEST(ScopedVectorTest, Clear) { | 
|  | LifeCycleWatcher watcher; | 
|  | EXPECT_EQ(LC_INITIAL, watcher.life_cycle_state()); | 
|  | ScopedVector<LifeCycleObject> scoped_vector; | 
|  | scoped_vector.push_back(watcher.NewLifeCycleObject()); | 
|  | EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state()); | 
|  | EXPECT_TRUE(watcher.IsWatching(scoped_vector.back())); | 
|  | scoped_vector.clear(); | 
|  | EXPECT_EQ(LC_DESTROYED, watcher.life_cycle_state()); | 
|  | EXPECT_TRUE(scoped_vector.empty()); | 
|  | } | 
|  |  | 
|  | TEST(ScopedVectorTest, WeakClear) { | 
|  | LifeCycleWatcher watcher; | 
|  | EXPECT_EQ(LC_INITIAL, watcher.life_cycle_state()); | 
|  | ScopedVector<LifeCycleObject> scoped_vector; | 
|  | scoped_vector.push_back(watcher.NewLifeCycleObject()); | 
|  | EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state()); | 
|  | EXPECT_TRUE(watcher.IsWatching(scoped_vector.back())); | 
|  | scoped_vector.weak_clear(); | 
|  | EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state()); | 
|  | EXPECT_TRUE(scoped_vector.empty()); | 
|  | } | 
|  |  | 
|  | TEST(ScopedVectorTest, ResizeShrink) { | 
|  | LifeCycleWatcher first_watcher; | 
|  | EXPECT_EQ(LC_INITIAL, first_watcher.life_cycle_state()); | 
|  | LifeCycleWatcher second_watcher; | 
|  | EXPECT_EQ(LC_INITIAL, second_watcher.life_cycle_state()); | 
|  | ScopedVector<LifeCycleObject> scoped_vector; | 
|  |  | 
|  | scoped_vector.push_back(first_watcher.NewLifeCycleObject()); | 
|  | EXPECT_EQ(LC_CONSTRUCTED, first_watcher.life_cycle_state()); | 
|  | EXPECT_EQ(LC_INITIAL, second_watcher.life_cycle_state()); | 
|  | EXPECT_TRUE(first_watcher.IsWatching(scoped_vector[0])); | 
|  | EXPECT_FALSE(second_watcher.IsWatching(scoped_vector[0])); | 
|  |  | 
|  | scoped_vector.push_back(second_watcher.NewLifeCycleObject()); | 
|  | EXPECT_EQ(LC_CONSTRUCTED, first_watcher.life_cycle_state()); | 
|  | EXPECT_EQ(LC_CONSTRUCTED, second_watcher.life_cycle_state()); | 
|  | EXPECT_FALSE(first_watcher.IsWatching(scoped_vector[1])); | 
|  | EXPECT_TRUE(second_watcher.IsWatching(scoped_vector[1])); | 
|  |  | 
|  | // Test that shrinking a vector deletes elements in the disappearing range. | 
|  | scoped_vector.resize(1); | 
|  | EXPECT_EQ(LC_CONSTRUCTED, first_watcher.life_cycle_state()); | 
|  | EXPECT_EQ(LC_DESTROYED, second_watcher.life_cycle_state()); | 
|  | EXPECT_EQ(1u, scoped_vector.size()); | 
|  | EXPECT_TRUE(first_watcher.IsWatching(scoped_vector[0])); | 
|  | } | 
|  |  | 
|  | TEST(ScopedVectorTest, ResizeGrow) { | 
|  | LifeCycleWatcher watcher; | 
|  | EXPECT_EQ(LC_INITIAL, watcher.life_cycle_state()); | 
|  | ScopedVector<LifeCycleObject> scoped_vector; | 
|  | scoped_vector.push_back(watcher.NewLifeCycleObject()); | 
|  | EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state()); | 
|  | EXPECT_TRUE(watcher.IsWatching(scoped_vector.back())); | 
|  |  | 
|  | scoped_vector.resize(5); | 
|  | EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state()); | 
|  | ASSERT_EQ(5u, scoped_vector.size()); | 
|  | EXPECT_TRUE(watcher.IsWatching(scoped_vector[0])); | 
|  | EXPECT_FALSE(watcher.IsWatching(scoped_vector[1])); | 
|  | EXPECT_FALSE(watcher.IsWatching(scoped_vector[2])); | 
|  | EXPECT_FALSE(watcher.IsWatching(scoped_vector[3])); | 
|  | EXPECT_FALSE(watcher.IsWatching(scoped_vector[4])); | 
|  | } | 
|  |  | 
|  | TEST(ScopedVectorTest, Scope) { | 
|  | LifeCycleWatcher watcher; | 
|  | EXPECT_EQ(LC_INITIAL, watcher.life_cycle_state()); | 
|  | { | 
|  | ScopedVector<LifeCycleObject> scoped_vector; | 
|  | scoped_vector.push_back(watcher.NewLifeCycleObject()); | 
|  | EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state()); | 
|  | EXPECT_TRUE(watcher.IsWatching(scoped_vector.back())); | 
|  | } | 
|  | EXPECT_EQ(LC_DESTROYED, watcher.life_cycle_state()); | 
|  | } | 
|  |  | 
|  | TEST(ScopedVectorTest, MoveConstruct) { | 
|  | LifeCycleWatcher watcher; | 
|  | EXPECT_EQ(LC_INITIAL, watcher.life_cycle_state()); | 
|  | { | 
|  | ScopedVector<LifeCycleObject> scoped_vector; | 
|  | scoped_vector.push_back(watcher.NewLifeCycleObject()); | 
|  | EXPECT_FALSE(scoped_vector.empty()); | 
|  | EXPECT_TRUE(watcher.IsWatching(scoped_vector.back())); | 
|  |  | 
|  | ScopedVector<LifeCycleObject> scoped_vector_copy(scoped_vector.Pass()); | 
|  | EXPECT_TRUE(scoped_vector.empty()); | 
|  | EXPECT_FALSE(scoped_vector_copy.empty()); | 
|  | EXPECT_TRUE(watcher.IsWatching(scoped_vector_copy.back())); | 
|  |  | 
|  | EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state()); | 
|  | } | 
|  | EXPECT_EQ(LC_DESTROYED, watcher.life_cycle_state()); | 
|  | } | 
|  |  | 
|  | TEST(ScopedVectorTest, MoveAssign) { | 
|  | LifeCycleWatcher watcher; | 
|  | EXPECT_EQ(LC_INITIAL, watcher.life_cycle_state()); | 
|  | { | 
|  | ScopedVector<LifeCycleObject> scoped_vector; | 
|  | scoped_vector.push_back(watcher.NewLifeCycleObject()); | 
|  | ScopedVector<LifeCycleObject> scoped_vector_assign; | 
|  | EXPECT_FALSE(scoped_vector.empty()); | 
|  | EXPECT_TRUE(watcher.IsWatching(scoped_vector.back())); | 
|  |  | 
|  | scoped_vector_assign = scoped_vector.Pass(); | 
|  | EXPECT_TRUE(scoped_vector.empty()); | 
|  | EXPECT_FALSE(scoped_vector_assign.empty()); | 
|  | EXPECT_TRUE(watcher.IsWatching(scoped_vector_assign.back())); | 
|  |  | 
|  | EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state()); | 
|  | } | 
|  | EXPECT_EQ(LC_DESTROYED, watcher.life_cycle_state()); | 
|  | } | 
|  |  | 
|  | class DeleteCounter { | 
|  | public: | 
|  | explicit DeleteCounter(int* deletes) | 
|  | : deletes_(deletes) { | 
|  | } | 
|  |  | 
|  | ~DeleteCounter() { | 
|  | (*deletes_)++; | 
|  | } | 
|  |  | 
|  | void VoidMethod0() {} | 
|  |  | 
|  | private: | 
|  | int* const deletes_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(DeleteCounter); | 
|  | }; | 
|  |  | 
|  | template <typename T> | 
|  | ScopedVector<T> PassThru(ScopedVector<T> scoper) { | 
|  | return scoper.Pass(); | 
|  | } | 
|  |  | 
|  | TEST(ScopedVectorTest, Passed) { | 
|  | int deletes = 0; | 
|  | ScopedVector<DeleteCounter> deleter_vector; | 
|  | deleter_vector.push_back(new DeleteCounter(&deletes)); | 
|  | EXPECT_EQ(0, deletes); | 
|  | base::Callback<ScopedVector<DeleteCounter>(void)> callback = | 
|  | base::Bind(&PassThru<DeleteCounter>, base::Passed(&deleter_vector)); | 
|  | EXPECT_EQ(0, deletes); | 
|  | ScopedVector<DeleteCounter> result = callback.Run(); | 
|  | EXPECT_EQ(0, deletes); | 
|  | result.clear(); | 
|  | EXPECT_EQ(1, deletes); | 
|  | }; | 
|  |  | 
|  | TEST(ScopedVectorTest, InsertRange) { | 
|  | LifeCycleWatcher watchers[5]; | 
|  |  | 
|  | std::vector<LifeCycleObject*> vec; | 
|  | for(LifeCycleWatcher* it = watchers; it != watchers + arraysize(watchers); | 
|  | ++it) { | 
|  | EXPECT_EQ(LC_INITIAL, it->life_cycle_state()); | 
|  | vec.push_back(it->NewLifeCycleObject()); | 
|  | EXPECT_EQ(LC_CONSTRUCTED, it->life_cycle_state()); | 
|  | } | 
|  | // Start scope for ScopedVector. | 
|  | { | 
|  | ScopedVector<LifeCycleObject> scoped_vector; | 
|  | scoped_vector.insert(scoped_vector.end(), vec.begin() + 1, vec.begin() + 3); | 
|  | for(LifeCycleWatcher* it = watchers; it != watchers + arraysize(watchers); | 
|  | ++it) | 
|  | EXPECT_EQ(LC_CONSTRUCTED, it->life_cycle_state()); | 
|  | } | 
|  | for(LifeCycleWatcher* it = watchers; it != watchers + 1; ++it) | 
|  | EXPECT_EQ(LC_CONSTRUCTED, it->life_cycle_state()); | 
|  | for(LifeCycleWatcher* it = watchers + 1; it != watchers + 3; ++it) | 
|  | EXPECT_EQ(LC_DESTROYED, it->life_cycle_state()); | 
|  | for(LifeCycleWatcher* it = watchers + 3; it != watchers + arraysize(watchers); | 
|  | ++it) | 
|  | EXPECT_EQ(LC_CONSTRUCTED, it->life_cycle_state()); | 
|  | } | 
|  |  | 
|  | // Assertions for push_back(scoped_ptr). | 
|  | TEST(ScopedVectorTest, PushBackScopedPtr) { | 
|  | int delete_counter = 0; | 
|  | scoped_ptr<DeleteCounter> elem(new DeleteCounter(&delete_counter)); | 
|  | EXPECT_EQ(0, delete_counter); | 
|  | { | 
|  | ScopedVector<DeleteCounter> v; | 
|  | v.push_back(elem.Pass()); | 
|  | EXPECT_EQ(0, delete_counter); | 
|  | } | 
|  | EXPECT_EQ(1, delete_counter); | 
|  | } | 
|  |  | 
|  | }  // namespace |