// 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();
}