// 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 "sync/internal_api/public/util/weak_handle.h" #include "base/bind.h" #include "base/compiler_specific.h" #include "base/location.h" #include "base/memory/weak_ptr.h" #include "base/message_loop.h" #include "base/threading/thread.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" namespace syncer { using ::testing::_; using ::testing::SaveArg; using ::testing::StrictMock; class Base { public: Base() : weak_ptr_factory_(this) {} WeakHandle AsWeakHandle() { return MakeWeakHandle(weak_ptr_factory_.GetWeakPtr()); } void Kill() { weak_ptr_factory_.InvalidateWeakPtrs(); } MOCK_METHOD0(Test, void()); MOCK_METHOD1(Test1, void(const int&)); MOCK_METHOD2(Test2, void(const int&, Base*)); MOCK_METHOD3(Test3, void(const int&, Base*, float)); MOCK_METHOD4(Test4, void(const int&, Base*, float, const char*)); MOCK_METHOD1(TestWithSelf, void(const WeakHandle&)); private: base::WeakPtrFactory weak_ptr_factory_; }; class Derived : public Base, public base::SupportsWeakPtr {}; class WeakHandleTest : public ::testing::Test { protected: virtual void TearDown() { // Process any last-minute posted tasks. PumpLoop(); } void PumpLoop() { message_loop_.RunUntilIdle(); } static void CallTestFromOtherThread(tracked_objects::Location from_here, const WeakHandle& h) { base::Thread t("Test thread"); ASSERT_TRUE(t.Start()); t.message_loop()->PostTask( from_here, base::Bind(&WeakHandleTest::CallTest, from_here, h)); } private: static void CallTest(tracked_objects::Location from_here, const WeakHandle& h) { h.Call(from_here, &Base::Test); } base::MessageLoop message_loop_; }; TEST_F(WeakHandleTest, Uninitialized) { // Default. WeakHandle h; EXPECT_FALSE(h.IsInitialized()); // Copy. { WeakHandle h2(h); EXPECT_FALSE(h2.IsInitialized()); } // Assign. { WeakHandle h2; h2 = h; EXPECT_FALSE(h.IsInitialized()); } } TEST_F(WeakHandleTest, InitializedAfterDestroy) { WeakHandle h; { StrictMock b; h = b.AsWeakHandle(); } EXPECT_TRUE(h.IsInitialized()); EXPECT_FALSE(h.Get()); } TEST_F(WeakHandleTest, InitializedAfterInvalidate) { StrictMock b; WeakHandle h = b.AsWeakHandle(); b.Kill(); EXPECT_TRUE(h.IsInitialized()); EXPECT_FALSE(h.Get()); } TEST_F(WeakHandleTest, Call) { StrictMock b; const char test_str[] = "test"; EXPECT_CALL(b, Test()); EXPECT_CALL(b, Test1(5)); EXPECT_CALL(b, Test2(5, &b)); EXPECT_CALL(b, Test3(5, &b, 5)); EXPECT_CALL(b, Test4(5, &b, 5, test_str)); WeakHandle h = b.AsWeakHandle(); EXPECT_TRUE(h.IsInitialized()); // Should run. h.Call(FROM_HERE, &Base::Test); h.Call(FROM_HERE, &Base::Test1, 5); h.Call(FROM_HERE, &Base::Test2, 5, &b); h.Call(FROM_HERE, &Base::Test3, 5, &b, 5); h.Call(FROM_HERE, &Base::Test4, 5, &b, 5, test_str); PumpLoop(); } TEST_F(WeakHandleTest, CallAfterDestroy) { { StrictMock b; EXPECT_CALL(b, Test()).Times(0); WeakHandle h = b.AsWeakHandle(); EXPECT_TRUE(h.IsInitialized()); // Should not run. h.Call(FROM_HERE, &Base::Test); } PumpLoop(); } TEST_F(WeakHandleTest, CallAfterInvalidate) { StrictMock b; EXPECT_CALL(b, Test()).Times(0); WeakHandle h = b.AsWeakHandle(); EXPECT_TRUE(h.IsInitialized()); // Should not run. h.Call(FROM_HERE, &Base::Test); b.Kill(); PumpLoop(); } TEST_F(WeakHandleTest, CallThreaded) { StrictMock b; EXPECT_CALL(b, Test()); WeakHandle h = b.AsWeakHandle(); // Should run. CallTestFromOtherThread(FROM_HERE, h); PumpLoop(); } TEST_F(WeakHandleTest, CallAfterDestroyThreaded) { WeakHandle h; { StrictMock b; EXPECT_CALL(b, Test()).Times(0); h = b.AsWeakHandle(); } // Should not run. CallTestFromOtherThread(FROM_HERE, h); PumpLoop(); } TEST_F(WeakHandleTest, CallAfterInvalidateThreaded) { StrictMock b; EXPECT_CALL(b, Test()).Times(0); WeakHandle h = b.AsWeakHandle(); b.Kill(); // Should not run. CallTestFromOtherThread(FROM_HERE, h); PumpLoop(); } TEST_F(WeakHandleTest, DeleteOnOtherThread) { StrictMock b; EXPECT_CALL(b, Test()).Times(0); WeakHandle* h = new WeakHandle(b.AsWeakHandle()); { base::Thread t("Test thread"); ASSERT_TRUE(t.Start()); t.message_loop()->DeleteSoon(FROM_HERE, h); } PumpLoop(); } void CallTestWithSelf(const WeakHandle& b1) { StrictMock b2; b1.Call(FROM_HERE, &Base::TestWithSelf, b2.AsWeakHandle()); } TEST_F(WeakHandleTest, WithDestroyedThread) { StrictMock b1; WeakHandle b2; EXPECT_CALL(b1, TestWithSelf(_)).WillOnce(SaveArg<0>(&b2)); { base::Thread t("Test thread"); ASSERT_TRUE(t.Start()); t.message_loop()->PostTask(FROM_HERE, base::Bind(&CallTestWithSelf, b1.AsWeakHandle())); } // Calls b1.TestWithSelf(). PumpLoop(); // Shouldn't do anything, since the thread is gone. b2.Call(FROM_HERE, &Base::Test); // |b2| shouldn't leak when it's destroyed, even if the original // thread is gone. } TEST_F(WeakHandleTest, InitializedAcrossCopyAssign) { StrictMock b; EXPECT_CALL(b, Test()).Times(3); EXPECT_TRUE(b.AsWeakHandle().IsInitialized()); b.AsWeakHandle().Call(FROM_HERE, &Base::Test); { WeakHandle h(b.AsWeakHandle()); EXPECT_TRUE(h.IsInitialized()); h.Call(FROM_HERE, &Base::Test); h.Reset(); EXPECT_FALSE(h.IsInitialized()); } { WeakHandle h; h = b.AsWeakHandle(); EXPECT_TRUE(h.IsInitialized()); h.Call(FROM_HERE, &Base::Test); h.Reset(); EXPECT_FALSE(h.IsInitialized()); } PumpLoop(); } TEST_F(WeakHandleTest, TypeConversionConstructor) { StrictMock d; EXPECT_CALL(d, Test()).Times(2); const WeakHandle weak_handle = MakeWeakHandle(d.AsWeakPtr()); // Should trigger type conversion constructor. const WeakHandle base_weak_handle(weak_handle); // Should trigger regular copy constructor. const WeakHandle derived_weak_handle(weak_handle); EXPECT_TRUE(base_weak_handle.IsInitialized()); base_weak_handle.Call(FROM_HERE, &Base::Test); EXPECT_TRUE(derived_weak_handle.IsInitialized()); // Copy constructor shouldn't construct a new |core_|. EXPECT_EQ(weak_handle.core_.get(), derived_weak_handle.core_.get()); derived_weak_handle.Call(FROM_HERE, &Base::Test); PumpLoop(); } TEST_F(WeakHandleTest, TypeConversionConstructorMakeWeakHandle) { const base::WeakPtr weak_ptr; // Should trigger type conversion constructor after MakeWeakHandle. WeakHandle base_weak_handle(MakeWeakHandle(weak_ptr)); // Should trigger regular copy constructor after MakeWeakHandle. const WeakHandle derived_weak_handle(MakeWeakHandle(weak_ptr)); EXPECT_TRUE(base_weak_handle.IsInitialized()); EXPECT_TRUE(derived_weak_handle.IsInitialized()); } TEST_F(WeakHandleTest, TypeConversionConstructorAssignment) { const WeakHandle weak_handle = MakeWeakHandle(Derived().AsWeakPtr()); // Should trigger type conversion constructor before the assignment. WeakHandle base_weak_handle; base_weak_handle = weak_handle; // Should trigger regular copy constructor before the assignment. WeakHandle derived_weak_handle; derived_weak_handle = weak_handle; EXPECT_TRUE(base_weak_handle.IsInitialized()); EXPECT_TRUE(derived_weak_handle.IsInitialized()); // Copy constructor shouldn't construct a new |core_|. EXPECT_EQ(weak_handle.core_.get(), derived_weak_handle.core_.get()); } TEST_F(WeakHandleTest, TypeConversionConstructorUninitialized) { const WeakHandle base_weak_handle = WeakHandle(); EXPECT_FALSE(base_weak_handle.IsInitialized()); } TEST_F(WeakHandleTest, TypeConversionConstructorUninitializedAssignment) { WeakHandle base_weak_handle; base_weak_handle = WeakHandle(); EXPECT_FALSE(base_weak_handle.IsInitialized()); } } // namespace syncer