AUnit  1.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 #include "string_util.h"
35 
36 namespace aunit {
37 
38 // Use a function static singleton to avoid the static initialization ordering
39 // problem. It's probably not an issue right now, since TestRunner is expected
40 // to be called only after all static initialization, but future refactoring
41 // could change that so this is defensive programming.
42 TestRunner* TestRunner::getRunner() {
43  static TestRunner singletonRunner;
44  return &singletonRunner;
45 }
46 
47 void TestRunner::setPrinter(Print* printer) {
48  Printer::setPrinter(printer);
49 }
50 
51 void TestRunner::setLifeCycleMatchingPattern(const char* pattern,
52  uint8_t lifeCycle) {
53  size_t length = strlen(pattern);
54  if (length > 0 && pattern[length - 1] == '*') {
55  // prefix match
56  length--;
57  } else {
58  // exact match
59  length++;
60  }
61 
62  for (Test** p = Test::getRoot(); *p != nullptr; p = (*p)->getNext()) {
63  if ((*p)->getName().compareToN(pattern, length) == 0) {
64  (*p)->setLifeCycle(lifeCycle);
65  }
66  }
67 }
68 
69 void TestRunner::setLifeCycleMatchingPattern(const char* testClass,
70  const char* pattern, uint8_t lifeCycle) {
71  // The effective pattern is the join of testClass and pattern with a '_'
72  // delimiter. This must match the algorithm used by testF() and testingF().
73  // We use string_join() instead of String so that AUnit can avoid a direct
74  // dependency on the String class. AUnit thus avoids allocating any memory on
75  // the heap.
76  char fullPattern[kMaxPatternLength];
77  bool status = internal::string_join(fullPattern, kMaxPatternLength, '_',
78  testClass, pattern);
79  if (!status) return;
80 
81  setLifeCycleMatchingPattern(fullPattern, lifeCycle);
82 }
83 
84 TestRunner::TestRunner():
85  mCurrent(nullptr),
86  mIsResolved(false),
87  mIsSetup(false),
88  mIsRunning(false),
89  mVerbosity(Verbosity::kDefault),
90  mCount(0),
91  mPassedCount(0),
92  mFailedCount(0),
93  mSkippedCount(0),
94  mStatusErrorCount(0),
95  mTimeout(kTimeoutDefault) {}
96 
97 void TestRunner::runTest() {
98  setupRunner();
99 
100  // Print initial header if this is the first run.
101  if (!mIsRunning) {
102  printStartRunner();
103  mIsRunning = true;
104  }
105 
106  // If no more test cases, then print out summary of run.
107  if (*Test::getRoot() == nullptr) {
108  if (!mIsResolved) {
109  mEndTime = millis();
110  resolveRun();
111  mIsResolved = true;
112  #ifndef ARDUINO
113  exit(0);
114  #endif
115  }
116  return;
117  }
118 
119  // If reached the end and there are still test cases left, start from the
120  // beginning again.
121  if (*mCurrent == nullptr) {
122  mCurrent = Test::getRoot();
123  }
124 
125  // Implement a finite state machine that calls the (*mCurrent)->setup() or
126  // (*mCurrent)->loop(), then changes the test case's mStatus.
127  switch ((*mCurrent)->getLifeCycle()) {
128  case Test::kLifeCycleNew:
129  // Transfer the verbosity of the TestRunner to the Test.
130  (*mCurrent)->enableVerbosity(mVerbosity);
131  (*mCurrent)->setup();
132 
133  // Support assertXxx() statements inside the setup() method by
134  // moving to the next lifeCycle state if an assertXxx() did not fail
135  // inside the setup().
136  if ((*mCurrent)->getLifeCycle() == Test::kLifeCycleNew) {
137  (*mCurrent)->setLifeCycle(Test::kLifeCycleSetup);
138  }
139  break;
141  // If a test is excluded, go directly to LifeCycleFinished, without
142  // calling setup() or teardown().
143  (*mCurrent)->enableVerbosity(mVerbosity);
144  (*mCurrent)->setStatus(Test::kStatusSkipped);
145  mSkippedCount++;
146  (*mCurrent)->setLifeCycle(Test::kLifeCycleFinished);
147  break;
149  {
150  // Check for timeout. mTimeout == 0 means infinite timeout.
151  // NOTE: It feels like this code should go into the Test::loop() method
152  // (like the extra bit of code in TestOnce::loop()) because it seems
153  // like we could want the timeout to be configurable on a case by case
154  // basis. This would cause the testing() code to move down into a new
155  // again() virtual method dispatched from Test::loop(), analogous to
156  // once(). But let's keep the code here for now.
157  unsigned long now = millis();
158  if (mTimeout > 0 && now >= mStartTime + 1000L * mTimeout) {
159  (*mCurrent)->expire();
160  } else {
161  (*mCurrent)->loop();
162 
163  // If test status is unresolved (i.e. still in kLifeCycleNew state)
164  // after loop(), then this is a continuous testing() test case, so
165  // skip to the next test. Otherwise, stay on the current test so that
166  // the next iteration of runTest() can resolve the current test.
167  if ((*mCurrent)->getLifeCycle() == Test::kLifeCycleSetup) {
168  // skip to the next one, but keep current test in the list
169  mCurrent = (*mCurrent)->getNext();
170  }
171  }
172  }
173  break;
175  switch ((*mCurrent)->getStatus()) {
177  mSkippedCount++;
178  break;
179  case Test::kStatusPassed:
180  mPassedCount++;
181  break;
182  case Test::kStatusFailed:
183  mFailedCount++;
184  break;
186  mExpiredCount++;
187  break;
188  default:
189  // should never get here
190  mStatusErrorCount++;
191  break;
192  }
193  (*mCurrent)->teardown();
194  (*mCurrent)->setLifeCycle(Test::kLifeCycleFinished);
195  break;
197  (*mCurrent)->resolve();
198  // skip to the next one by taking current test out of the list
199  *mCurrent = *(*mCurrent)->getNext();
200  break;
201  }
202 }
203 
204 void TestRunner::setupRunner() {
205  if (!mIsSetup) {
206  mIsSetup = true;
207  mCount = countTests();
208  mCurrent = Test::getRoot();
209  mStartTime = millis();
210  }
211 }
212 
213 // Count the number of tests in TestRunner instead of Test::insert() to avoid
214 // another C++ static initialization ordering problem.
215 uint16_t TestRunner::countTests() {
216  uint16_t count = 0;
217  for (Test** p = Test::getRoot(); *p != nullptr; p = (*p)->getNext()) {
218  count++;
219  }
220  return count;
221 }
222 
223 namespace {
224 
230 void printSeconds(Print* printer, unsigned long timeMillis) {
231  int s = timeMillis / 1000;
232  int ms = timeMillis % 1000;
233  printer->print(s);
234  printer->print('.');
235  if (ms < 100) printer->print('0');
236  if (ms < 10) printer->print('0');
237  printer->print(ms);
238 }
239 
240 }
241 
242 void TestRunner::printStartRunner() const {
244 
245  Print* printer = Printer::getPrinter();
246  printer->print(F("TestRunner started on "));
247  printer->print(mCount);
248  printer->println(F(" test(s)."));
249 }
250 
251 void TestRunner::resolveRun() const {
253  Print* printer = Printer::getPrinter();
254 
255  unsigned long elapsedTime = mEndTime - mStartTime;
256  printer->print(F("TestRunner duration: "));
257  printSeconds(printer, elapsedTime);
258  printer->println(" seconds.");
259 
260  printer->print(F("TestRunner summary: "));
261  printer->print(mPassedCount);
262  printer->print(F(" passed, "));
263  printer->print(mFailedCount);
264  printer->print(F(" failed, "));
265  printer->print(mSkippedCount);
266  printer->print(F(" skipped, "));
267  printer->print(mExpiredCount);
268  printer->print(F(" timed out, out of "));
269  printer->print(mCount);
270  printer->println(F(" test(s)."));
271 }
272 
273 void TestRunner::listTests() {
274  setupRunner();
275 
276  Print* printer = Printer::getPrinter();
277  printer->print(F("TestRunner test count: "));
278  printer->println(mCount);
279  for (Test** p = Test::getRoot(); (*p) != nullptr; p = (*p)->getNext()) {
280  printer->print(F("Test "));
281  (*p)->getName().print(printer);
282  printer->print(F("; lifeCycle: "));
283  printer->println((*p)->getLifeCycle());
284  }
285 }
286 
287 void TestRunner::setRunnerTimeout(TimeoutType timeout) {
288  mTimeout = timeout;
289 }
290 
291 }
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:47
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