AUnit  0.5.3
Unit testing framework for Arduino platforms inspired by ArduinoUnit and Google Test.
TestRunner.cpp
1 /*
2 MIT License
3 
4 Copyright (c) 2018 Brian T. Park
5 
6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to deal
8 in the Software without restriction, including without limitation the rights
9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 copies of the Software, and to permit persons to whom the Software is
11 furnished to do so, subject to the following conditions:
12 
13 The above copyright notice and this permission notice shall be included in all
14 copies or substantial portions of the Software.
15 
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 SOFTWARE.
23 */
24 
25 #include <Arduino.h> // definition of 'Serial'
26 #include <string.h>
27 #include <stdint.h>
28 #include "FCString.h"
29 #include "Compare.h"
30 #include "Printer.h"
31 #include "Verbosity.h"
32 #include "Test.h"
33 #include "TestRunner.h"
34 
35 namespace aunit {
36 
37 // Use a function static singleton to avoid the static initialization ordering
38 // problem. It's probably not an issue right now, since TestRunner is expected
39 // to be called only after all static initialization, but future refactoring
40 // could change that so this is defensive programming.
41 TestRunner* TestRunner::getRunner() {
42  static TestRunner singletonRunner;
43  return &singletonRunner;
44 }
45 
46 void TestRunner::setPrinter(Print* printer) {
47  Printer::setPrinter(printer);
48 }
49 
50 void TestRunner::setLifeCycleMatchingPattern(const char* pattern,
51  uint8_t lifeCycle) {
52  size_t length = strlen(pattern);
53  if (length > 0 && pattern[length - 1] == '*') {
54  // prefix match
55  length--;
56  } else {
57  // exact match
58  length++;
59  }
60 
61  for (Test** p = Test::getRoot(); *p != nullptr; p = (*p)->getNext()) {
62  if (compareStringN((*p)->getName(), pattern, length) == 0) {
63  (*p)->setLifeCycle(lifeCycle);
64  }
65  }
66 }
67 
68 void TestRunner::setLifeCycleMatchingPattern(const char* testClass,
69  const char* pattern, uint8_t lifeCycle) {
70 
71  // Form the effective pattern by concatenating the two. This must match the
72  // algorithm used by testF() and testingF().
73  String fullPattern(testClass);
74  fullPattern.concat('_');
75  fullPattern.concat(pattern);
76 
77  setLifeCycleMatchingPattern(fullPattern.c_str(), lifeCycle);
78 }
79 
80 TestRunner::TestRunner():
81  mCurrent(nullptr),
82  mIsResolved(false),
83  mIsSetup(false),
84  mIsRunning(false),
85  mVerbosity(Verbosity::kDefault),
86  mCount(0),
87  mPassedCount(0),
88  mFailedCount(0),
89  mSkippedCount(0),
90  mStatusErrorCount(0),
91  mTimeout(kTimeoutDefault) {}
92 
93 void TestRunner::runTest() {
94  setupRunner();
95 
96  // Print initial header if this is the first run.
97  if (!mIsRunning) {
98  printStartRunner();
99  mIsRunning = true;
100  }
101 
102  // If no more test cases, then print out summary of run.
103  if (*Test::getRoot() == nullptr) {
104  if (!mIsResolved) {
105  mEndTime = millis();
106  resolveRun();
107  mIsResolved = true;
108  }
109  return;
110  }
111 
112  // If reached the end and there are still test cases left, start from the
113  // beginning again.
114  if (*mCurrent == nullptr) {
115  mCurrent = Test::getRoot();
116  }
117 
118  // Implement a finite state machine that calls the (*mCurrent)->setup() or
119  // (*mCurrent)->loop(), then changes the test case's mStatus.
120  switch ((*mCurrent)->getLifeCycle()) {
121  case Test::kLifeCycleNew:
122  // Transfer the verbosity of the TestRunner to the Test.
123  (*mCurrent)->enableVerbosity(mVerbosity);
124  (*mCurrent)->setup();
125 
126  // Support assertXxx() statements inside the setup() method by
127  // moving to the next lifeCycle state if an assertXxx() did not fail
128  // inside the setup().
129  if ((*mCurrent)->getLifeCycle() == Test::kLifeCycleNew) {
130  (*mCurrent)->setLifeCycle(Test::kLifeCycleSetup);
131  }
132  break;
134  // If a test is excluded, go directly to LifeCycleFinished, without
135  // calling setup() or teardown().
136  (*mCurrent)->enableVerbosity(mVerbosity);
137  (*mCurrent)->setStatus(Test::kStatusSkipped);
138  mSkippedCount++;
139  (*mCurrent)->setLifeCycle(Test::kLifeCycleFinished);
140  break;
142  {
143  // Check for timeout. mTimeout == 0 means infinite timeout.
144  // NOTE: It feels like this code should go into the Test::loop() method
145  // (like the extra bit of code in TestOnce::loop()) because it seems
146  // like we could want the timeout to be configurable on a case by case
147  // basis. This would cause the testing() code to move down into a new
148  // again() virtual method dispatched from Test::loop(), analogous to
149  // once(). But let's keep the code here for now.
150  unsigned long now = millis();
151  if (mTimeout > 0 && now >= mStartTime + 1000L * mTimeout) {
152  (*mCurrent)->expire();
153  } else {
154  (*mCurrent)->loop();
155 
156  // If test status is unresolved (i.e. still in kLifeCycleNew state)
157  // after loop(), then this is a continuous testing() test case, so
158  // skip to the next test. Otherwise, stay on the current test so that
159  // the next iteration of runTest() can resolve the current test.
160  if ((*mCurrent)->getLifeCycle() == Test::kLifeCycleSetup) {
161  // skip to the next one, but keep current test in the list
162  mCurrent = (*mCurrent)->getNext();
163  }
164  }
165  }
166  break;
168  switch ((*mCurrent)->getStatus()) {
170  mSkippedCount++;
171  break;
172  case Test::kStatusPassed:
173  mPassedCount++;
174  break;
175  case Test::kStatusFailed:
176  mFailedCount++;
177  break;
179  mExpiredCount++;
180  break;
181  default:
182  // should never get here
183  mStatusErrorCount++;
184  break;
185  }
186  (*mCurrent)->teardown();
187  (*mCurrent)->setLifeCycle(Test::kLifeCycleFinished);
188  break;
190  (*mCurrent)->resolve();
191  // skip to the next one by taking current test out of the list
192  *mCurrent = *(*mCurrent)->getNext();
193  break;
194  }
195 }
196 
197 void TestRunner::setupRunner() {
198  if (!mIsSetup) {
199  mIsSetup = true;
200  mCount = countTests();
201  mCurrent = Test::getRoot();
202  mStartTime = millis();
203  }
204 }
205 
206 // Count the number of tests in TestRunner instead of Test::insert() to avoid
207 // another C++ static initialization ordering problem.
208 uint16_t TestRunner::countTests() {
209  uint16_t count = 0;
210  for (Test** p = Test::getRoot(); *p != nullptr; p = (*p)->getNext()) {
211  count++;
212  }
213  return count;
214 }
215 
216 namespace {
217 
223 void printSeconds(Print* printer, unsigned long timeMillis) {
224  int s = timeMillis / 1000;
225  int ms = timeMillis % 1000;
226  printer->print(s);
227  printer->print('.');
228  if (ms < 100) printer->print('0');
229  if (ms < 10) printer->print('0');
230  printer->print(ms);
231 }
232 
233 }
234 
235 void TestRunner::printStartRunner() {
237 
238  Print* printer = Printer::getPrinter();
239  printer->print(F("TestRunner started on "));
240  printer->print(mCount);
241  printer->println(F(" test(s)."));
242 }
243 
244 void TestRunner::resolveRun() {
246  Print* printer = Printer::getPrinter();
247 
248  unsigned long elapsedTime = mEndTime - mStartTime;
249  printer->print(F("TestRunner duration: "));
250  printSeconds(printer, elapsedTime);
251  printer->println(" seconds.");
252 
253  printer->print(F("TestRunner summary: "));
254  printer->print(mPassedCount);
255  printer->print(F(" passed, "));
256  printer->print(mFailedCount);
257  printer->print(F(" failed, "));
258  printer->print(mSkippedCount);
259  printer->print(F(" skipped, "));
260  printer->print(mExpiredCount);
261  printer->print(F(" timed out, out of "));
262  printer->print(mCount);
263  printer->println(F(" test(s)."));
264 }
265 
266 void TestRunner::listTests() {
267  setupRunner();
268 
269  Print* printer = Printer::getPrinter();
270  printer->print(F("TestRunner test count: "));
271  printer->println(mCount);
272  for (Test** p = Test::getRoot(); (*p) != nullptr; p = (*p)->getNext()) {
273  printer->print(F("Test "));
274  (*p)->getName().print(printer);
275  printer->print(F("; lifeCycle: "));
276  printer->println((*p)->getLifeCycle());
277  }
278 }
279 
280 void TestRunner::setRunnerTimeout(TimeoutType timeout) {
281  mTimeout = timeout;
282 }
283 
284 }
Base class of all test cases.
Definition: Test.h:43
static const uint8_t kStatusFailed
Test has failed, or fail() was called.
Definition: Test.h:102
static const uint8_t kLifeCycleAsserted
Test is asserted (using pass(), fail(), expired() or skipped()) and the getStatus() has been determin...
Definition: Test.h:80
static void setPrinter(Print *printer)
Set the printer.
Definition: Printer.h:51
static const uint8_t kStatusPassed
Test has passed, or pass() was called.
Definition: Test.h:99
uint8_t TimeoutType
Integer type of the timeout parameter.
Definition: TestRunner.h:44
Test ** getNext()
Return the next pointer as a pointer to the pointer, similar to getRoot().
Definition: Test.h:188
static const uint8_t kDefault
The default verbosity.
Definition: Verbosity.h:69
static const uint8_t kStatusSkipped
Test is skipped through the exclude() method or skip() was called.
Definition: Test.h:105
This file provides overloaded compareXxx(a, b) functions which are used by the various assertXxx(a...
static const uint8_t kLifeCycleExcluded
Test is Excluded by an exclude() method.
Definition: Test.h:65
static Test ** getRoot()
Get the pointer to the root pointer.
Definition: Test.cpp:36
static const uint8_t kLifeCycleFinished
The test has completed its life cycle.
Definition: Test.h:88
static void setPrinter(Print *printer)
Set the output printer.
Definition: TestRunner.cpp:46
static const uint8_t kLifeCycleNew
Test is new, needs to be setup.
Definition: Test.h:57
static Print * getPrinter()
Get the output printer used by the various assertion() methods and the TestRunner.
Definition: Printer.h:48
static const uint8_t kLifeCycleSetup
Test has been set up by calling setup() and ready to execute the test code.
Definition: Test.h:74
static const uint8_t kTestRunSummary
Print TestRunner summary message.
Definition: Verbosity.h:58
static const uint8_t kStatusExpired
Test has timed out, or expire() called.
Definition: Test.h:108
static bool isVerbosity(uint8_t verbosity)
Returns true if ANY of the bit flags of &#39;verbosity&#39; is set.
Definition: TestRunner.h:97