AUnit  0.3.1
Unit testing framework for Arduino platforms inspired by ArduinoUnit.
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::setStatusMatchingPattern(const char* pattern, uint8_t status) {
51  size_t length = strlen(pattern);
52  if (length > 0 && pattern[length - 1] == '*') {
53  // prefix match
54  length--;
55  } else {
56  // exact match
57  length++;
58  }
59 
60  for (Test** p = Test::getRoot(); *p != nullptr; p = (*p)->getNext()) {
61  if (compareStringN((*p)->getName(), pattern, length) == 0) {
62  (*p)->setStatus(status);
63  }
64  }
65 }
66 
67 TestRunner::TestRunner():
68  mCurrent(nullptr),
69  mIsResolved(false),
70  mIsSetup(false),
71  mIsRunning(false),
72  mVerbosity(Verbosity::kDefault),
73  mCount(0),
74  mPassedCount(0),
75  mFailedCount(0),
76  mSkippedCount(0),
77  mTimeout(kTimeoutDefault) {}
78 
79 void TestRunner::runTest() {
80  setupRunner();
81 
82  // Print initial header if this is the first run.
83  if (!mIsRunning) {
84  printStartRunner();
85  mIsRunning = true;
86  }
87 
88  // If no more test cases, then print out summary of run.
89  if (*Test::getRoot() == nullptr) {
90  if (!mIsResolved) {
91  resolveRun();
92  }
93  return;
94  }
95 
96  // If reached the end and there are still test cases left, start from the
97  // beginning again.
98  if (*mCurrent == nullptr) {
99  mCurrent = Test::getRoot();
100  }
101 
102  // Implement a finite state machine that calls the (*mCurrent)->setup() or
103  // (*mCurrent)->loop(), then changes the test case's mStatus.
104  switch ((*mCurrent)->getStatus()) {
105  case Test::kStatusNew:
106  (*mCurrent)->setup();
107 
108  // support assertXxx() statements inside the setup() method
109  if ((*mCurrent)->getStatus() == Test::kStatusNew) {
110  (*mCurrent)->setStatus(Test::kStatusSetup);
111  }
112  break;
113  case Test::kStatusSetup:
114  {
115  // Check for timeout. mTimeout == 0 means infinite timeout.
116  // NOTE: It feels like this code should go into the Test::loop() method
117  // (like the extra bit of code in TestOnce::loop()) because it seems
118  // like we could want the timeout to be configurable on a case by case
119  // basis. This would cause the testing() code to move down into a new
120  // again() virtual method dispatched from Test::loop(), analogous to
121  // once(). But let's keep the code here for now.
122  unsigned long now = millis();
123  if (mTimeout > 0 && now >= mStartTime + mTimeout) {
124  (*mCurrent)->expire();
125  } else {
126  (*mCurrent)->loop();
127 
128  // If test status is unresolved (i.e. still in kStatusSetup state)
129  // after loop(), then this is a continuous testing() test case, so
130  // skip to the next test. Otherwise, stay on the current test so that
131  // the next iteration of runTest() can resolve the current test.
132  if ((*mCurrent)->getStatus() == Test::kStatusSetup) {
133  // skip to the next one, but keep current test in the list
134  mCurrent = (*mCurrent)->getNext();
135  }
136  }
137  }
138  break;
140  mSkippedCount++;
141  resolveTest((*mCurrent));
142  // skip to the next one by taking current test out of the list
143  *mCurrent = *(*mCurrent)->getNext();
144  break;
145  case Test::kStatusPassed:
146  mPassedCount++;
147  resolveTest((*mCurrent));
148  // skip to the next one by taking current test out of the list
149  *mCurrent = *(*mCurrent)->getNext();
150  break;
151  case Test::kStatusFailed:
152  mFailedCount++;
153  resolveTest((*mCurrent));
154  // skip to the next one by taking current test out of the list
155  *mCurrent = *(*mCurrent)->getNext();
156  break;
158  mExpiredCount++;
159  resolveTest((*mCurrent));
160  // skip to the next one by taking current test out of the list
161  *mCurrent = *(*mCurrent)->getNext();
162  break;
163  }
164 }
165 
166 void TestRunner::setupRunner() {
167  if (!mIsSetup) {
168  mIsSetup = true;
169  mCount = countTests();
170  mCurrent = Test::getRoot();
171  mStartTime = millis();
172  }
173 }
174 
175 // Count the number of tests in TestRunner instead of Test::insert() to avoid
176 // another C++ static initialization ordering problem.
177 uint16_t TestRunner::countTests() {
178  uint16_t count = 0;
179  for (Test** p = Test::getRoot(); *p != nullptr; p = (*p)->getNext()) {
180  count++;
181  }
182  return count;
183 }
184 
185 void TestRunner::printStartRunner() {
187 
188  Print* printer = Printer::getPrinter();
189  printer->print(F("TestRunner started on "));
190  printer->print(mCount);
191  printer->println(F(" test(s)."));
192 }
193 
194 void TestRunner::resolveTest(Test* testCase) {
195  if (!isVerbosity(Verbosity::kTestAll)) return;
196 
197  Print* printer = Printer::getPrinter();
198  printer->print(F("Test "));
199  Printer::print(testCase->getName());
200  if (testCase->getStatus() == Test::kStatusSkipped) {
201  printer->println(F(" skipped."));
202  } else if (testCase->getStatus() == Test::kStatusFailed) {
203  printer->println(F(" failed."));
204  } else if (testCase->getStatus() == Test::kStatusPassed) {
205  printer->println(F(" passed."));
206  } else if (testCase->getStatus() == Test::kStatusExpired) {
207  printer->println(F(" timed out."));
208  }
209 }
210 
211 void TestRunner::resolveRun() {
213 
214  Print* printer = Printer::getPrinter();
215  printer->print(F("TestRunner summary: "));
216  printer->print(mPassedCount);
217  printer->print(F(" passed, "));
218  printer->print(mFailedCount);
219  printer->print(F(" failed, "));
220  printer->print(mSkippedCount);
221  printer->print(F(" skipped, "));
222  printer->print(mExpiredCount);
223  printer->print(F(" timed out, out of "));
224  printer->print(mCount);
225  printer->println(F(" test(s)."));
226 
227  mIsResolved = true;
228 }
229 
230 void TestRunner::listTests() {
231  setupRunner();
232 
233  Print* printer = Printer::getPrinter();
234  printer->print(F("TestRunner test count: "));
235  printer->print(mCount);
236  printer->println('.');
237  for (Test** p = Test::getRoot(); (*p) != nullptr; p = (*p)->getNext()) {
238  printer->print(F("Test "));
239  Printer::print((*p)->getName());
240  printer->println(F(" found."));
241  }
242 }
243 
244 void TestRunner::setRunnerTimeout(unsigned long timeout) {
245  mTimeout = timeout;
246 }
247 
248 }
static const uint8_t kStatusFailed
Test has failed, or failed() was called.
Definition: Test.h:115
static const uint8_t kStatusNew
Test is new, needs to be setup.
Definition: Test.h:106
static void print(const FCString &s)
Convenience method for printing an FCString.
Definition: Printer.cpp:33
Various macros (test(), testing(), externTest(), externTesting()) are defined in this header...
static void setPrinter(Print *printer)
Set the printer.
Definition: Printer.h:53
static const uint8_t kStatusPassed
Test has passed, or pass() was called.
Definition: Test.h:112
Test ** getNext()
Return the next pointer as a pointer to the pointer, similar to getRoot().
Definition: Test.h:174
static const uint8_t kStatusSkipped
Test is skipped, through the exclude() method or skip() was called.
Definition: Test.h:118
static Test ** getRoot()
Get the pointer to the root pointer.
Definition: Test.cpp:33
static void setPrinter(Print *printer)
Set the output printer.
Definition: TestRunner.cpp:46
static Print * getPrinter()
Get the output printer used by the various assertion() methods and the TestRunner.
Definition: Printer.h:50
static const uint8_t kTestRunSummary
Print TestRunner summary message.
Definition: Verbosity.h:58
static const uint8_t kStatusSetup
Test is set up.
Definition: Test.h:109
static const uint8_t kStatusExpired
Test has timed out, or expire() called.
Definition: Test.h:121
static const uint8_t kTestAll
Print all test status messages.
Definition: Verbosity.h:65
static bool isVerbosity(uint8_t verbosity)
Returns true if ANY of the bit flags of &#39;verbosity&#39; is set.
Definition: TestRunner.h:71