1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2020 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 The code included in this file is provided under the terms of the ISC license
11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12 To use, copy, modify, and/or distribute this software for any purpose with or
13 without fee is hereby granted provided that the above copyright notice and
14 this permission notice appear in all copies.
15
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18 DISCLAIMED.
19
20 ==============================================================================
21 */
22
23 namespace juce
24 {
25
UnitTest(const String & nm,const String & ctg)26 UnitTest::UnitTest (const String& nm, const String& ctg)
27 : name (nm), category (ctg)
28 {
29 getAllTests().add (this);
30 }
31
~UnitTest()32 UnitTest::~UnitTest()
33 {
34 getAllTests().removeFirstMatchingValue (this);
35 }
36
getAllTests()37 Array<UnitTest*>& UnitTest::getAllTests()
38 {
39 static Array<UnitTest*> tests;
40 return tests;
41 }
42
getTestsInCategory(const String & category)43 Array<UnitTest*> UnitTest::getTestsInCategory (const String& category)
44 {
45 if (category.isEmpty())
46 return getAllTests();
47
48 Array<UnitTest*> unitTests;
49
50 for (auto* test : getAllTests())
51 if (test->getCategory() == category)
52 unitTests.add (test);
53
54 return unitTests;
55 }
56
getAllCategories()57 StringArray UnitTest::getAllCategories()
58 {
59 StringArray categories;
60
61 for (auto* test : getAllTests())
62 if (test->getCategory().isNotEmpty())
63 categories.addIfNotAlreadyThere (test->getCategory());
64
65 return categories;
66 }
67
initialise()68 void UnitTest::initialise() {}
shutdown()69 void UnitTest::shutdown() {}
70
performTest(UnitTestRunner * const newRunner)71 void UnitTest::performTest (UnitTestRunner* const newRunner)
72 {
73 jassert (newRunner != nullptr);
74 runner = newRunner;
75
76 initialise();
77 runTest();
78 shutdown();
79 }
80
logMessage(const String & message)81 void UnitTest::logMessage (const String& message)
82 {
83 // This method's only valid while the test is being run!
84 jassert (runner != nullptr);
85
86 runner->logMessage (message);
87 }
88
beginTest(const String & testName)89 void UnitTest::beginTest (const String& testName)
90 {
91 // This method's only valid while the test is being run!
92 jassert (runner != nullptr);
93
94 runner->beginNewTest (this, testName);
95 }
96
expect(const bool result,const String & failureMessage)97 void UnitTest::expect (const bool result, const String& failureMessage)
98 {
99 // This method's only valid while the test is being run!
100 jassert (runner != nullptr);
101
102 if (result)
103 runner->addPass();
104 else
105 runner->addFail (failureMessage);
106 }
107
getRandom() const108 Random UnitTest::getRandom() const
109 {
110 // This method's only valid while the test is being run!
111 jassert (runner != nullptr);
112
113 return runner->randomForTest;
114 }
115
116 //==============================================================================
UnitTestRunner()117 UnitTestRunner::UnitTestRunner() {}
~UnitTestRunner()118 UnitTestRunner::~UnitTestRunner() {}
119
setAssertOnFailure(bool shouldAssert)120 void UnitTestRunner::setAssertOnFailure (bool shouldAssert) noexcept
121 {
122 assertOnFailure = shouldAssert;
123 }
124
setPassesAreLogged(bool shouldDisplayPasses)125 void UnitTestRunner::setPassesAreLogged (bool shouldDisplayPasses) noexcept
126 {
127 logPasses = shouldDisplayPasses;
128 }
129
getNumResults() const130 int UnitTestRunner::getNumResults() const noexcept
131 {
132 return results.size();
133 }
134
getResult(int index) const135 const UnitTestRunner::TestResult* UnitTestRunner::getResult (int index) const noexcept
136 {
137 return results [index];
138 }
139
resultsUpdated()140 void UnitTestRunner::resultsUpdated()
141 {
142 }
143
runTests(const Array<UnitTest * > & tests,int64 randomSeed)144 void UnitTestRunner::runTests (const Array<UnitTest*>& tests, int64 randomSeed)
145 {
146 results.clear();
147 resultsUpdated();
148
149 if (randomSeed == 0)
150 randomSeed = Random().nextInt (0x7ffffff);
151
152 randomForTest = Random (randomSeed);
153 logMessage ("Random seed: 0x" + String::toHexString (randomSeed));
154
155 for (auto* t : tests)
156 {
157 if (shouldAbortTests())
158 break;
159
160 #if JUCE_EXCEPTIONS_DISABLED
161 t->performTest (this);
162 #else
163 try
164 {
165 t->performTest (this);
166 }
167 catch (...)
168 {
169 addFail ("An unhandled exception was thrown!");
170 }
171 #endif
172 }
173
174 endTest();
175 }
176
runAllTests(int64 randomSeed)177 void UnitTestRunner::runAllTests (int64 randomSeed)
178 {
179 runTests (UnitTest::getAllTests(), randomSeed);
180 }
181
runTestsInCategory(const String & category,int64 randomSeed)182 void UnitTestRunner::runTestsInCategory (const String& category, int64 randomSeed)
183 {
184 runTests (UnitTest::getTestsInCategory (category), randomSeed);
185 }
186
logMessage(const String & message)187 void UnitTestRunner::logMessage (const String& message)
188 {
189 Logger::writeToLog (message);
190 }
191
shouldAbortTests()192 bool UnitTestRunner::shouldAbortTests()
193 {
194 return false;
195 }
196
beginNewTest(UnitTest * const test,const String & subCategory)197 void UnitTestRunner::beginNewTest (UnitTest* const test, const String& subCategory)
198 {
199 endTest();
200 currentTest = test;
201
202 auto testName = test->getName();
203 results.add (new TestResult (testName, subCategory));
204
205 logMessage ("-----------------------------------------------------------------");
206 logMessage ("Starting test: " + testName + " / " + subCategory + "...");
207
208 resultsUpdated();
209 }
210
endTest()211 void UnitTestRunner::endTest()
212 {
213 if (auto* r = results.getLast())
214 {
215 r->endTime = Time::getCurrentTime();
216
217 if (r->failures > 0)
218 {
219 String m ("FAILED!! ");
220 m << r->failures << (r->failures == 1 ? " test" : " tests")
221 << " failed, out of a total of " << (r->passes + r->failures);
222
223 logMessage (String());
224 logMessage (m);
225 logMessage (String());
226 }
227 else
228 {
229 logMessage ("All tests completed successfully");
230 }
231 }
232 }
233
addPass()234 void UnitTestRunner::addPass()
235 {
236 {
237 const ScopedLock sl (results.getLock());
238
239 auto* r = results.getLast();
240 jassert (r != nullptr); // You need to call UnitTest::beginTest() before performing any tests!
241
242 r->passes++;
243
244 if (logPasses)
245 {
246 String message ("Test ");
247 message << (r->failures + r->passes) << " passed";
248 logMessage (message);
249 }
250 }
251
252 resultsUpdated();
253 }
254
addFail(const String & failureMessage)255 void UnitTestRunner::addFail (const String& failureMessage)
256 {
257 {
258 const ScopedLock sl (results.getLock());
259
260 auto* r = results.getLast();
261 jassert (r != nullptr); // You need to call UnitTest::beginTest() before performing any tests!
262
263 r->failures++;
264
265 String message ("!!! Test ");
266 message << (r->failures + r->passes) << " failed";
267
268 if (failureMessage.isNotEmpty())
269 message << ": " << failureMessage;
270
271 r->messages.add (message);
272
273 logMessage (message);
274 }
275
276 resultsUpdated();
277
278 if (assertOnFailure) { jassertfalse; }
279 }
280
281 } // namespace juce
282