| // 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/at_exit.h" |
| #include "base/file_util.h" |
| #include "base/memory/singleton.h" |
| #include "base/path_service.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace { |
| |
| COMPILE_ASSERT(DefaultSingletonTraits<int>::kRegisterAtExit == true, a); |
| |
| typedef void (*CallbackFunc)(); |
| |
| class IntSingleton { |
| public: |
| static IntSingleton* GetInstance() { |
| return Singleton<IntSingleton>::get(); |
| } |
| |
| int value_; |
| }; |
| |
| class Init5Singleton { |
| public: |
| struct Trait; |
| |
| static Init5Singleton* GetInstance() { |
| return Singleton<Init5Singleton, Trait>::get(); |
| } |
| |
| int value_; |
| }; |
| |
| struct Init5Singleton::Trait : public DefaultSingletonTraits<Init5Singleton> { |
| static Init5Singleton* New() { |
| Init5Singleton* instance = new Init5Singleton(); |
| instance->value_ = 5; |
| return instance; |
| } |
| }; |
| |
| int* SingletonInt() { |
| return &IntSingleton::GetInstance()->value_; |
| } |
| |
| int* SingletonInt5() { |
| return &Init5Singleton::GetInstance()->value_; |
| } |
| |
| template <typename Type> |
| struct CallbackTrait : public DefaultSingletonTraits<Type> { |
| static void Delete(Type* instance) { |
| if (instance->callback_) |
| (instance->callback_)(); |
| DefaultSingletonTraits<Type>::Delete(instance); |
| } |
| }; |
| |
| class CallbackSingleton { |
| public: |
| CallbackSingleton() : callback_(NULL) { } |
| CallbackFunc callback_; |
| }; |
| |
| class CallbackSingletonWithNoLeakTrait : public CallbackSingleton { |
| public: |
| struct Trait : public CallbackTrait<CallbackSingletonWithNoLeakTrait> { }; |
| |
| CallbackSingletonWithNoLeakTrait() : CallbackSingleton() { } |
| |
| static CallbackSingletonWithNoLeakTrait* GetInstance() { |
| return Singleton<CallbackSingletonWithNoLeakTrait, Trait>::get(); |
| } |
| }; |
| |
| class CallbackSingletonWithLeakTrait : public CallbackSingleton { |
| public: |
| struct Trait : public CallbackTrait<CallbackSingletonWithLeakTrait> { |
| static const bool kRegisterAtExit = false; |
| }; |
| |
| CallbackSingletonWithLeakTrait() : CallbackSingleton() { } |
| |
| static CallbackSingletonWithLeakTrait* GetInstance() { |
| return Singleton<CallbackSingletonWithLeakTrait, Trait>::get(); |
| } |
| }; |
| |
| class CallbackSingletonWithStaticTrait : public CallbackSingleton { |
| public: |
| struct Trait; |
| |
| CallbackSingletonWithStaticTrait() : CallbackSingleton() { } |
| |
| static CallbackSingletonWithStaticTrait* GetInstance() { |
| return Singleton<CallbackSingletonWithStaticTrait, Trait>::get(); |
| } |
| }; |
| |
| struct CallbackSingletonWithStaticTrait::Trait |
| : public StaticMemorySingletonTraits<CallbackSingletonWithStaticTrait> { |
| static void Delete(CallbackSingletonWithStaticTrait* instance) { |
| if (instance->callback_) |
| (instance->callback_)(); |
| StaticMemorySingletonTraits<CallbackSingletonWithStaticTrait>::Delete( |
| instance); |
| } |
| }; |
| |
| template <class Type> |
| class AlignedTestSingleton { |
| public: |
| AlignedTestSingleton() {} |
| ~AlignedTestSingleton() {} |
| static AlignedTestSingleton* GetInstance() { |
| return Singleton<AlignedTestSingleton, |
| StaticMemorySingletonTraits<AlignedTestSingleton> >::get(); |
| } |
| |
| Type type_; |
| }; |
| |
| |
| void SingletonNoLeak(CallbackFunc CallOnQuit) { |
| CallbackSingletonWithNoLeakTrait::GetInstance()->callback_ = CallOnQuit; |
| } |
| |
| void SingletonLeak(CallbackFunc CallOnQuit) { |
| CallbackSingletonWithLeakTrait::GetInstance()->callback_ = CallOnQuit; |
| } |
| |
| CallbackFunc* GetLeakySingleton() { |
| return &CallbackSingletonWithLeakTrait::GetInstance()->callback_; |
| } |
| |
| void DeleteLeakySingleton() { |
| DefaultSingletonTraits<CallbackSingletonWithLeakTrait>::Delete( |
| CallbackSingletonWithLeakTrait::GetInstance()); |
| } |
| |
| void SingletonStatic(CallbackFunc CallOnQuit) { |
| CallbackSingletonWithStaticTrait::GetInstance()->callback_ = CallOnQuit; |
| } |
| |
| CallbackFunc* GetStaticSingleton() { |
| return &CallbackSingletonWithStaticTrait::GetInstance()->callback_; |
| } |
| |
| } // namespace |
| |
| class SingletonTest : public testing::Test { |
| public: |
| SingletonTest() {} |
| |
| virtual void SetUp() override { |
| non_leak_called_ = false; |
| leaky_called_ = false; |
| static_called_ = false; |
| } |
| |
| protected: |
| void VerifiesCallbacks() { |
| EXPECT_TRUE(non_leak_called_); |
| EXPECT_FALSE(leaky_called_); |
| EXPECT_TRUE(static_called_); |
| non_leak_called_ = false; |
| leaky_called_ = false; |
| static_called_ = false; |
| } |
| |
| void VerifiesCallbacksNotCalled() { |
| EXPECT_FALSE(non_leak_called_); |
| EXPECT_FALSE(leaky_called_); |
| EXPECT_FALSE(static_called_); |
| non_leak_called_ = false; |
| leaky_called_ = false; |
| static_called_ = false; |
| } |
| |
| static void CallbackNoLeak() { |
| non_leak_called_ = true; |
| } |
| |
| static void CallbackLeak() { |
| leaky_called_ = true; |
| } |
| |
| static void CallbackStatic() { |
| static_called_ = true; |
| } |
| |
| private: |
| static bool non_leak_called_; |
| static bool leaky_called_; |
| static bool static_called_; |
| }; |
| |
| bool SingletonTest::non_leak_called_ = false; |
| bool SingletonTest::leaky_called_ = false; |
| bool SingletonTest::static_called_ = false; |
| |
| TEST_F(SingletonTest, Basic) { |
| int* singleton_int; |
| int* singleton_int_5; |
| CallbackFunc* leaky_singleton; |
| CallbackFunc* static_singleton; |
| |
| { |
| base::ShadowingAtExitManager sem; |
| { |
| singleton_int = SingletonInt(); |
| } |
| // Ensure POD type initialization. |
| EXPECT_EQ(*singleton_int, 0); |
| *singleton_int = 1; |
| |
| EXPECT_EQ(singleton_int, SingletonInt()); |
| EXPECT_EQ(*singleton_int, 1); |
| |
| { |
| singleton_int_5 = SingletonInt5(); |
| } |
| // Is default initialized to 5. |
| EXPECT_EQ(*singleton_int_5, 5); |
| |
| SingletonNoLeak(&CallbackNoLeak); |
| SingletonLeak(&CallbackLeak); |
| SingletonStatic(&CallbackStatic); |
| static_singleton = GetStaticSingleton(); |
| leaky_singleton = GetLeakySingleton(); |
| EXPECT_TRUE(leaky_singleton); |
| } |
| |
| // Verify that only the expected callback has been called. |
| VerifiesCallbacks(); |
| // Delete the leaky singleton. |
| DeleteLeakySingleton(); |
| |
| // The static singleton can't be acquired post-atexit. |
| EXPECT_EQ(NULL, GetStaticSingleton()); |
| |
| { |
| base::ShadowingAtExitManager sem; |
| // Verifiy that the variables were reset. |
| { |
| singleton_int = SingletonInt(); |
| EXPECT_EQ(*singleton_int, 0); |
| } |
| { |
| singleton_int_5 = SingletonInt5(); |
| EXPECT_EQ(*singleton_int_5, 5); |
| } |
| { |
| // Resurrect the static singleton, and assert that it |
| // still points to the same (static) memory. |
| CallbackSingletonWithStaticTrait::Trait::Resurrect(); |
| EXPECT_EQ(GetStaticSingleton(), static_singleton); |
| } |
| } |
| // The leaky singleton shouldn't leak since SingletonLeak has not been called. |
| VerifiesCallbacksNotCalled(); |
| } |
| |
| #define EXPECT_ALIGNED(ptr, align) \ |
| EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(ptr) & (align - 1)) |
| |
| TEST_F(SingletonTest, Alignment) { |
| using base::AlignedMemory; |
| |
| // Create some static singletons with increasing sizes and alignment |
| // requirements. By ordering this way, the linker will need to do some work to |
| // ensure proper alignment of the static data. |
| AlignedTestSingleton<int32>* align4 = |
| AlignedTestSingleton<int32>::GetInstance(); |
| AlignedTestSingleton<AlignedMemory<32, 32> >* align32 = |
| AlignedTestSingleton<AlignedMemory<32, 32> >::GetInstance(); |
| AlignedTestSingleton<AlignedMemory<128, 128> >* align128 = |
| AlignedTestSingleton<AlignedMemory<128, 128> >::GetInstance(); |
| AlignedTestSingleton<AlignedMemory<4096, 4096> >* align4096 = |
| AlignedTestSingleton<AlignedMemory<4096, 4096> >::GetInstance(); |
| |
| EXPECT_ALIGNED(align4, 4); |
| EXPECT_ALIGNED(align32, 32); |
| EXPECT_ALIGNED(align128, 128); |
| EXPECT_ALIGNED(align4096, 4096); |
| } |