AUnit  1.5.4
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 
86 void TestRunner::runTest() {
87  setupRunner();
88 
89  // Print initial header if this is the first run.
90  if (!mIsRunning) {
91  printStartRunner();
92  mIsRunning = true;
93  }
94 
95  // If no more test cases, then print out summary of run.
96  if (*Test::getRoot() == nullptr) {
97  if (!mIsResolved) {
98  mEndTime = millis();
99  resolveRun();
100  mIsResolved = true;
101  #if EPOXY_DUINO
102  exit((mFailedCount || mExpiredCount) ? 1 : 0);
103  #endif
104  }
105  return;
106  }
107 
108  // If reached the end and there are still test cases left, start from the
109  // beginning again.
110  if (*mCurrent == nullptr) {
111  mCurrent = Test::getRoot();
112  }
113 
114  // Implement a finite state machine that calls the (*mCurrent)->setup() or
115  // (*mCurrent)->loop(), then changes the test case's mStatus.
116  switch ((*mCurrent)->getLifeCycle()) {
117  case Test::kLifeCycleNew:
118  // Transfer the verbosity of the TestRunner to the Test.
119  (*mCurrent)->enableVerbosity(mVerbosity);
120  (*mCurrent)->setup();
121 
122  // Support assertXxx() statements inside the setup() method by
123  // moving to the next lifeCycle state if an assertXxx() did not fail
124  // inside the setup().
125  if ((*mCurrent)->getLifeCycle() == Test::kLifeCycleNew) {
126  (*mCurrent)->setLifeCycle(Test::kLifeCycleSetup);
127  }
128  break;
130  // If a test is excluded, go directly to LifeCycleFinished, without
131  // calling setup() or teardown().
132  (*mCurrent)->enableVerbosity(mVerbosity);
133  (*mCurrent)->setStatus(Test::kStatusSkipped);
134  mSkippedCount++;
135  (*mCurrent)->setLifeCycle(Test::kLifeCycleFinished);
136  break;
138  {
139  // Check for timeout. mTimeout == 0 means infinite timeout.
140  // NOTE: It feels like this code should go into the Test::loop() method
141  // (like the extra bit of code in TestOnce::loop()) because it seems
142  // like we could want the timeout to be configurable on a case by case
143  // basis. This would cause the testing() code to move down into a new
144  // again() virtual method dispatched from Test::loop(), analogous to
145  // once(). But let's keep the code here for now.
146  unsigned long now = millis();
147  if (mTimeout > 0 && now >= mStartTime + 1000L * mTimeout) {
148  (*mCurrent)->expire();
149  } else {
150  (*mCurrent)->loop();
151 
152  // If test status is unresolved (i.e. still in kLifeCycleNew state)
153  // after loop(), then this is a continuous testing() test case, so
154  // skip to the next test. Otherwise, stay on the current test so that
155  // the next iteration of runTest() can resolve the current test.
156  if ((*mCurrent)->getLifeCycle() == Test::kLifeCycleSetup) {
157  // skip to the next one, but keep current test in the list
158  mCurrent = (*mCurrent)->getNext();
159  }
160  }
161  }
162  break;
164  switch ((*mCurrent)->getStatus()) {
166  mSkippedCount++;
167  break;
168  case Test::kStatusPassed:
169  mPassedCount++;
170  break;
171  case Test::kStatusFailed:
172  mFailedCount++;
173  break;
175  mExpiredCount++;
176  break;
177  default:
178  // should never get here
179  mStatusErrorCount++;
180  break;
181  }
182  (*mCurrent)->teardown();
183  (*mCurrent)->setLifeCycle(Test::kLifeCycleFinished);
184  break;
186  (*mCurrent)->resolve();
187  // skip to the next one by taking current test out of the list
188  *mCurrent = *(*mCurrent)->getNext();
189  break;
190  }
191 }
192 
193 void TestRunner::setupRunner() {
194  if (!mIsSetup) {
195  mIsSetup = true;
196  mCount = countTests();
197  mCurrent = Test::getRoot();
198  mStartTime = millis();
199  }
200 }
201 
202 // Count the number of tests in TestRunner instead of Test::insert() to avoid
203 // another C++ static initialization ordering problem.
204 uint16_t TestRunner::countTests() {
205  uint16_t count = 0;
206  for (Test** p = Test::getRoot(); *p != nullptr; p = (*p)->getNext()) {
207  count++;
208  }
209  return count;
210 }
211 
212 namespace {
213 
219 void printSeconds(Print* printer, unsigned long timeMillis) {
220  int s = timeMillis / 1000;
221  int ms = timeMillis % 1000;
222  printer->print(s);
223  printer->print('.');
224  if (ms < 100) printer->print('0');
225  if (ms < 10) printer->print('0');
226  printer->print(ms);
227 }
228 
229 }
230 
231 void TestRunner::printStartRunner() const {
233 
234  Print* printer = Printer::getPrinter();
235  printer->print(F("TestRunner started on "));
236  printer->print(mCount);
237  printer->println(F(" test(s)."));
238 }
239 
240 void TestRunner::resolveRun() const {
242  Print* printer = Printer::getPrinter();
243 
244  unsigned long elapsedTime = mEndTime - mStartTime;
245  printer->print(F("TestRunner duration: "));
246  printSeconds(printer, elapsedTime);
247  printer->println(" seconds.");
248 
249  printer->print(F("TestRunner summary: "));
250  printer->print(mPassedCount);
251  printer->print(F(" passed, "));
252  printer->print(mFailedCount);
253  printer->print(F(" failed, "));
254  printer->print(mSkippedCount);
255  printer->print(F(" skipped, "));
256  printer->print(mExpiredCount);
257  printer->print(F(" timed out, out of "));
258  printer->print(mCount);
259  printer->println(F(" test(s)."));
260 }
261 
262 void TestRunner::listTests() {
263  setupRunner();
264 
265  Print* printer = Printer::getPrinter();
266  printer->print(F("TestRunner test count: "));
267  printer->println(mCount);
268  for (Test** p = Test::getRoot(); (*p) != nullptr; p = (*p)->getNext()) {
269  printer->print(F("Test "));
270  (*p)->getName().print(printer);
271  printer->print(F("; lifeCycle: "));
272  printer->println((*p)->getLifeCycle());
273  }
274 }
275 
276 void TestRunner::setRunnerTimeout(TimeoutType timeout) {
277  mTimeout = timeout;
278 }
279 
280 }
aunit::Test::kLifeCycleFinished
static const uint8_t kLifeCycleFinished
The test has completed its life cycle.
Definition: Test.h:88
Compare.h
aunit::Test::kStatusFailed
static const uint8_t kStatusFailed
Test has failed, or fail() was called.
Definition: Test.h:102
aunit::TestRunner::setPrinter
static void setPrinter(Print *printer)
Set the output printer.
Definition: TestRunner.cpp:47
aunit::Test::kLifeCycleAsserted
static const uint8_t kLifeCycleAsserted
Test is asserted (using pass(), fail(), expired() or skipped()) and the getStatus() has been determin...
Definition: Test.h:80
aunit::Test::kLifeCycleExcluded
static const uint8_t kLifeCycleExcluded
Test is Excluded by an exclude() method.
Definition: Test.h:65
aunit::Printer::getPrinter
static Print * getPrinter()
Get the output printer used by the various assertion() methods and the TestRunner.
Definition: Printer.h:48
aunit::Test::kStatusPassed
static const uint8_t kStatusPassed
Test has passed, or pass() was called.
Definition: Test.h:99
aunit::Test::kStatusExpired
static const uint8_t kStatusExpired
Test has timed out, or expire() called.
Definition: Test.h:108
aunit::Verbosity::kTestRunSummary
static const uint8_t kTestRunSummary
Print TestRunner summary message.
Definition: Verbosity.h:58
aunit::TestRunner::isVerbosity
static bool isVerbosity(uint8_t verbosity)
Returns true if ANY of the bit flags of 'verbosity' is set.
Definition: TestRunner.h:100
aunit::Test::kStatusSkipped
static const uint8_t kStatusSkipped
Test is skipped through the exclude() method or skip() was called.
Definition: Test.h:105
aunit::Test::kLifeCycleNew
static const uint8_t kLifeCycleNew
Test is new, needs to be setup.
Definition: Test.h:57
aunit::Test::getNext
Test ** getNext()
Return the next pointer as a pointer to the pointer, similar to getRoot().
Definition: Test.h:188
aunit::Test::getRoot
static Test ** getRoot()
Get the pointer to the root pointer.
Definition: Test.cpp:36
aunit::Printer::setPrinter
static void setPrinter(Print *printer)
Set the printer.
Definition: Printer.h:51
aunit::Test::kLifeCycleSetup
static const uint8_t kLifeCycleSetup
Test has been set up by calling setup() and ready to execute the test code.
Definition: Test.h:74