Skip to content

Commit 4d44bd8

Browse files
committed
Improved the example snippets and added them to tests
This should ensure that the example snippets remain stable and building at all times
1 parent f771b26 commit 4d44bd8

5 files changed

Lines changed: 148 additions & 12 deletions

File tree

CMakeLists.txt

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,7 @@ if (SINGLETON_BUILD_TESTING)
6969
# If parent did not include CTest, ensure testing is enabled locally.
7070
enable_testing()
7171

72-
add_executable(
73-
singleton_test
74-
test/unit/singleton_test.cpp
75-
)
72+
add_executable(singleton_test test/unit/singleton_test.cpp)
7673
set_target_properties(
7774
singleton_test PROPERTIES
7875
CXX_STANDARD 11 CXX_STANDARD_REQUIRED TRUE
@@ -88,4 +85,15 @@ if (SINGLETON_BUILD_TESTING)
8885

8986
# Register in ctest
9087
add_test(NAME singleton_test COMMAND "$<TARGET_FILE:singleton_test>")
88+
89+
# Build examples, and register as tests in ctest
90+
add_executable(example_base_usage doc/example_base_usage.cpp)
91+
target_link_libraries(example_base_usage singleton)
92+
add_test(NAME example_base_usage COMMAND "$<TARGET_FILE:example_base_usage>")
93+
add_executable(example_constructor doc/example_constructor.cpp)
94+
target_link_libraries(example_constructor singleton)
95+
add_test(NAME example_constructor COMMAND "$<TARGET_FILE:example_constructor>")
96+
add_executable(example_full_usage doc/example_full_usage.cpp)
97+
target_link_libraries(example_full_usage singleton)
98+
add_test(NAME example_full_usage COMMAND "$<TARGET_FILE:example_full_usage>")
9199
endif()

README.md

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ A great issue with singletons normally is that they make unit testing very hard.
1010
To start using this library, include the `singleton.hpp` file into your code, and inherit from the `Singleton` class in your code and get its instance as follows:
1111

1212
```cpp
13+
#include <singleton.hpp>
14+
1315
class MySingleton : public Singleton<MySingleton>
1416
{
1517
private:
@@ -87,40 +89,55 @@ protected:
8789
// A protected allows a mock to subclass this.
8890
MySingleton()
8991
{
92+
++m_allocCounter;
9093
}
9194
friend BaseType;
92-
93-
virtual int MyFunction();
95+
static int m_allocCounter;
96+
public:
97+
virtual int GetAllocCount()
98+
{
99+
return m_allocCounter;
100+
}
94101
};
102+
int MySingleton::m_allocCounter = 0;
95103

96104
int main()
97105
{
98106
// This returns the constructed singleton.
99107
auto& instance = MySingleton::Get();
100108

101109
// This returns the real implementation's result.
102-
auto realResult1 = instance.MyFunction();
110+
if (instance.GetAllocCount() != 1)
111+
return 1;
103112

104113
// This reconstructs the singleton instance and returns the same instance as earlier.
105-
auto& instance2 = SingletonTestApi<MySingleton>::Reconstruct()
114+
auto& instance2 = testing::SingletonTestApi<MySingleton>::Reconstruct();
106115

107116
// This returns the real implementation's result from the fresh instance.
108-
auto realResult2 = instance.MyFunction();
117+
if (instance.GetAllocCount() != 2 || &instance != &instance2)
118+
return 2;
109119

110120
struct MyMockSingleton : public MySingleton
111121
{
112-
// Override the MyFunction behaviour in the mock.
113-
virtual int MyFunction() override;
122+
// Override the GetAllocCount behaviour in the mock.
123+
virtual int GetAllocCount() override
124+
{
125+
return -1;
126+
}
114127
};
115128
MyMockSingleton mock;
116129

117130
// This injects the mock implementation.
118131
// The real instance is destroyed, the `instance` and `instance2` variables are invalidated.
119-
SingletonTestApi<MySingleton>::Inject(&mock);
132+
testing::SingletonTestApi<MySingleton>::Inject(&mock);
120133

121134
// This returns the mock implementation.
122135
auto& instance3 = MySingleton::Get();
123136

137+
// This calls the mocked GetAllocCount() instead of the normal one.
138+
if (instance3.GetAllocCount() != -1)
139+
return 3;
140+
124141
return 0;
125142
}
126143
```

doc/example_base_usage.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#include <singleton.hpp>
2+
3+
class MySingleton : public Singleton<MySingleton>
4+
{
5+
private:
6+
MySingleton() = default;
7+
friend BaseType;
8+
};
9+
10+
int main()
11+
{
12+
// This returns the constructed singleton.
13+
auto& instance = MySingleton::Get();
14+
15+
// Call functions on `instance` here.
16+
17+
return 0;
18+
}

doc/example_constructor.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#include <singleton.hpp>
2+
3+
class MySingleton : public Singleton<MySingleton>
4+
{
5+
private:
6+
MySingleton(int customArg1, double customArg2)
7+
{
8+
}
9+
friend BaseType;
10+
};
11+
12+
int main()
13+
{
14+
{
15+
// This returns a nullptr, because the singleton is uninitialized.
16+
auto instancePtr = MySingleton::TryGet();
17+
}
18+
{
19+
// This returns the constructed singleton.
20+
auto& instance = MySingleton::Get(42, 3.1415);
21+
}
22+
{
23+
// This returns a pointer to the initialized singleton.
24+
auto instancePtr = MySingleton::TryGet();
25+
}
26+
{
27+
// This returns the same singleton.
28+
// The difference in arguments is ignored.
29+
auto& instance = MySingleton::Get(42000, -3.1415);
30+
}
31+
32+
return 0;
33+
}

doc/example_full_usage.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#include <singleton.hpp>
2+
#include <singleton_test.hpp>
3+
4+
class MySingleton : public Singleton<MySingleton>
5+
{
6+
protected:
7+
// A protected allows a mock to subclass this.
8+
MySingleton()
9+
{
10+
++m_allocCounter;
11+
}
12+
friend BaseType;
13+
static int m_allocCounter;
14+
public:
15+
virtual int GetAllocCount()
16+
{
17+
return m_allocCounter;
18+
}
19+
};
20+
int MySingleton::m_allocCounter = 0;
21+
22+
int main()
23+
{
24+
// This returns the constructed singleton.
25+
auto& instance = MySingleton::Get();
26+
27+
// This returns the real implementation's result.
28+
if (instance.GetAllocCount() != 1)
29+
return 1;
30+
31+
// This reconstructs the singleton instance and returns the same instance as earlier.
32+
auto& instance2 = testing::SingletonTestApi<MySingleton>::Reconstruct();
33+
34+
// This returns the real implementation's result from the fresh instance.
35+
if (instance.GetAllocCount() != 2 || &instance != &instance2)
36+
return 2;
37+
38+
struct MyMockSingleton : public MySingleton
39+
{
40+
// Override the GetAllocCount behaviour in the mock.
41+
virtual int GetAllocCount() override
42+
{
43+
return -1;
44+
}
45+
};
46+
MyMockSingleton mock;
47+
48+
// This injects the mock implementation.
49+
// The real instance is destroyed, the `instance` and `instance2` variables are invalidated.
50+
testing::SingletonTestApi<MySingleton>::Inject(&mock);
51+
52+
// This returns the mock implementation.
53+
auto& instance3 = MySingleton::Get();
54+
55+
// This calls the mocked GetAllocCount() instead of the normal one.
56+
if (instance3.GetAllocCount() != -1)
57+
return 3;
58+
59+
return 0;
60+
}

0 commit comments

Comments
 (0)