// Copyright (c) 2006-2008 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/at_exit.h" #include "base/file_util.h" #include "base/path_service.h" #include "base/singleton.h" #include "testing/gtest/include/gtest/gtest.h" namespace { COMPILE_ASSERT(DefaultSingletonTraits<int>::kRegisterAtExit == true, a); template<typename Type> struct LockTrait : public DefaultSingletonTraits<Type> { }; struct Init5Trait : public DefaultSingletonTraits<int> { static int* New() { return new int(5); } }; typedef void (*CallbackFunc)(); struct CallbackTrait : public DefaultSingletonTraits<CallbackFunc> { static void Delete(CallbackFunc* p) { if (*p) (*p)(); DefaultSingletonTraits<CallbackFunc>::Delete(p); } }; struct NoLeakTrait : public CallbackTrait { }; struct LeakTrait : public CallbackTrait { static const bool kRegisterAtExit = false; }; int* SingletonInt1() { return Singleton<int>::get(); } int* SingletonInt2() { // Force to use a different singleton than SingletonInt1. return Singleton<int, DefaultSingletonTraits<int> >::get(); } class DummyDifferentiatingClass { }; int* SingletonInt3() { // Force to use a different singleton than SingletonInt1 and SingletonInt2. // Note that any type can be used; int, float, std::wstring... return Singleton<int, DefaultSingletonTraits<int>, DummyDifferentiatingClass>::get(); } int* SingletonInt4() { return Singleton<int, LockTrait<int> >::get(); } int* SingletonInt5() { return Singleton<int, Init5Trait>::get(); } void SingletonNoLeak(CallbackFunc CallOnQuit) { *Singleton<CallbackFunc, NoLeakTrait>::get() = CallOnQuit; } void SingletonLeak(CallbackFunc CallOnQuit) { *Singleton<CallbackFunc, LeakTrait>::get() = CallOnQuit; } CallbackFunc* GetLeakySingleton() { return Singleton<CallbackFunc, LeakTrait>::get(); } } // namespace class SingletonTest : public testing::Test { public: SingletonTest() { } virtual void SetUp() { non_leak_called_ = false; leaky_called_ = false; } protected: void VerifiesCallbacks() { EXPECT_TRUE(non_leak_called_); EXPECT_FALSE(leaky_called_); non_leak_called_ = false; leaky_called_ = false; } void VerifiesCallbacksNotCalled() { EXPECT_FALSE(non_leak_called_); EXPECT_FALSE(leaky_called_); non_leak_called_ = false; leaky_called_ = false; } static void CallbackNoLeak() { non_leak_called_ = true; } static void CallbackLeak() { leaky_called_ = true; } private: static bool non_leak_called_; static bool leaky_called_; }; bool SingletonTest::non_leak_called_ = false; bool SingletonTest::leaky_called_ = false; TEST_F(SingletonTest, Basic) { int* singleton_int_1; int* singleton_int_2; int* singleton_int_3; int* singleton_int_4; int* singleton_int_5; CallbackFunc* leaky_singleton; { base::ShadowingAtExitManager sem; { singleton_int_1 = SingletonInt1(); } // Ensure POD type initialization. EXPECT_EQ(*singleton_int_1, 0); *singleton_int_1 = 1; EXPECT_EQ(singleton_int_1, SingletonInt1()); EXPECT_EQ(*singleton_int_1, 1); { singleton_int_2 = SingletonInt2(); } // Same instance that 1. EXPECT_EQ(*singleton_int_2, 1); EXPECT_EQ(singleton_int_1, singleton_int_2); { singleton_int_3 = SingletonInt3(); } // Different instance than 1 and 2. EXPECT_EQ(*singleton_int_3, 0); EXPECT_NE(singleton_int_1, singleton_int_3); *singleton_int_3 = 3; EXPECT_EQ(*singleton_int_1, 1); EXPECT_EQ(*singleton_int_2, 1); { singleton_int_4 = SingletonInt4(); } // Use a lock for creation. Not really tested at length. EXPECT_EQ(*singleton_int_4, 0); *singleton_int_4 = 4; EXPECT_NE(singleton_int_1, singleton_int_4); EXPECT_NE(singleton_int_3, singleton_int_4); { singleton_int_5 = SingletonInt5(); } // Is default initialized to 5. EXPECT_EQ(*singleton_int_5, 5); EXPECT_NE(singleton_int_1, singleton_int_5); EXPECT_NE(singleton_int_3, singleton_int_5); EXPECT_NE(singleton_int_4, singleton_int_5); SingletonNoLeak(&CallbackNoLeak); SingletonLeak(&CallbackLeak); leaky_singleton = GetLeakySingleton(); EXPECT_TRUE(leaky_singleton); } // Verify that only the expected callback has been called. VerifiesCallbacks(); // Delete the leaky singleton. It is interesting to note that Purify does // *not* detect the leak when this call is commented out. :( DefaultSingletonTraits<CallbackFunc>::Delete(leaky_singleton); { base::ShadowingAtExitManager sem; // Verifiy that the variables were reset. { singleton_int_1 = SingletonInt1(); EXPECT_EQ(*singleton_int_1, 0); } { singleton_int_5 = SingletonInt5(); EXPECT_EQ(*singleton_int_5, 5); } } // The leaky singleton shouldn't leak since SingletonLeak has not been called. VerifiesCallbacksNotCalled(); }