AUnit  0.4.1
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::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 void TestRunner::setStatusMatchingPattern(const char* testClass,
68  const char* pattern, uint8_t status) {
69 
70  // Form the effective pattern by concatenating the two. This must match the
71  // algorithm used by testF() and testingF().
72  String fullPattern(testClass);
73  fullPattern.concat('_');
74  fullPattern.concat(pattern);
75 
76  setStatusMatchingPattern(fullPattern.c_str(), status);
77 }
78 
79 TestRunner::TestRunner():
80  mCurrent(nullptr),
81  mIsResolved(false),
82  mIsSetup(false),
83  mIsRunning(false),
84  mVerbosity(Verbosity::kDefault),
85  mCount(0),
86  mPassedCount(0),
87  mFailedCount(0),
88  mSkippedCount(0),
89  mTimeout(kTimeoutDefault) {}
90 
91 void TestRunner::runTest() {
92  setupRunner();
93 
94  // Print initial header if this is the first run.
95  if (!mIsRunning) {
96  printStartRunner();
97  mIsRunning = true;
98  }
99 
100  // If no more test cases, then print out summary of run.
101  if (*Test::getRoot() == nullptr) {
102  if (!mIsResolved) {
103  resolveRun();
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)->getStatus()) {
117  case Test::kStatusNew:
118  // Transfer the verbosity of the TestRunner to the Test.
119  (*mCurrent)->enableVerbosity(mVerbosity);
120 
121  (*mCurrent)->setup();
122 
123  // support assertXxx() statements inside the setup() method
124  if ((*mCurrent)->getStatus() == Test::kStatusNew) {
125  (*mCurrent)->setStatus(Test::kStatusSetup);
126  }
127  break;
128  case Test::kStatusSetup:
129  {
130  // Check for timeout. mTimeout == 0 means infinite timeout.
131  // NOTE: It feels like this code should go into the Test::loop() method
132  // (like the extra bit of code in TestOnce::loop()) because it seems
133  // like we could want the timeout to be configurable on a case by case
134  // basis. This would cause the testing() code to move down into a new
135  // again() virtual method dispatched from Test::loop(), analogous to
136  // once(). But let's keep the code here for now.
137  unsigned long now = millis();
138  if (mTimeout > 0 && now >= mStartTime + 1000L * mTimeout) {
139  (*mCurrent)->expire();
140  } else {
141  (*mCurrent)->loop();
142 
143  // If test status is unresolved (i.e. still in kStatusSetup state)
144  // after loop(), then this is a continuous testing() test case, so
145  // skip to the next test. Otherwise, stay on the current test so that
146  // the next iteration of runTest() can resolve the current test.
147  if ((*mCurrent)->getStatus() == Test::kStatusSetup) {
148  // skip to the next one, but keep current test in the list
149  mCurrent = (*mCurrent)->getNext();
150  }
151  }
152  }
153  break;
155  mSkippedCount++;
156  (*mCurrent)->teardown();
157  (*mCurrent)->resolve();
158  // skip to the next one by taking current test out of the list
159  *mCurrent = *(*mCurrent)->getNext();
160  break;
161  case Test::kStatusPassed:
162  mPassedCount++;
163  (*mCurrent)->teardown();
164  (*mCurrent)->resolve();
165  // skip to the next one by taking current test out of the list
166  *mCurrent = *(*mCurrent)->getNext();
167  break;
168  case Test::kStatusFailed:
169  mFailedCount++;
170  (*mCurrent)->teardown();
171  (*mCurrent)->resolve();
172  // skip to the next one by taking current test out of the list
173  *mCurrent = *(*mCurrent)->getNext();
174  break;
176  mExpiredCount++;
177  (*mCurrent)->teardown();
178  (*mCurrent)->resolve();
179  // skip to the next one by taking current test out of the list
180  *mCurrent = *(*mCurrent)->getNext();
181  break;
182  }
183 }
184 
185 void TestRunner::setupRunner() {
186  if (!mIsSetup) {
187  mIsSetup = true;
188  mCount = countTests();
189  mCurrent = Test::getRoot();
190  mStartTime = millis();
191  }
192 }
193 
194 // Count the number of tests in TestRunner instead of Test::insert() to avoid
195 // another C++ static initialization ordering problem.
196 uint16_t TestRunner::countTests() {
197  uint16_t count = 0;
198  for (Test** p = Test::getRoot(); *p != nullptr; p = (*p)->getNext()) {
199  count++;
200  }
201  return count;
202 }
203 
204 void TestRunner::printStartRunner() {
206 
207  Print* printer = Printer::getPrinter();
208  printer->print(F("TestRunner started on "));
209  printer->print(mCount);
210  printer->println(F(" test(s)."));
211 }
212 
213 void TestRunner::resolveRun() {
215 
216  Print* printer = Printer::getPrinter();
217  printer->print(F("TestRunner summary: "));
218  printer->print(mPassedCount);
219  printer->print(F(" passed, "));
220  printer->print(mFailedCount);
221  printer->print(F(" failed, "));
222  printer->print(mSkippedCount);
223  printer->print(F(" skipped, "));
224  printer->print(mExpiredCount);
225  printer->print(F(" timed out, out of "));
226  printer->print(mCount);
227  printer->println(F(" test(s)."));
228 
229  mIsResolved = true;
230 }
231 
232 void TestRunner::listTests() {
233  setupRunner();
234 
235  Print* printer = Printer::getPrinter();
236  printer->print(F("TestRunner test count: "));
237  printer->println(mCount);
238  for (Test** p = Test::getRoot(); (*p) != nullptr; p = (*p)->getNext()) {
239  printer->print(F("Test "));
240  Printer::print((*p)->getName());
241  printer->print(F("; status: "));
242  printer->println((*p)->getStatus());
243  }
244 }
245 
246 void TestRunner::setRunnerTimeout(TimeoutType timeout) {
247  mTimeout = timeout;
248 }
249 
250 }
static const uint8_t kStatusFailed
Test has failed, or failed() was called.
Definition: Test.h:64
static const uint8_t kStatusNew
Test is new, needs to be setup.
Definition: Test.h:55
static void print(const FCString &s)
Convenience method for printing an FCString.
Definition: Printer.cpp:33
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:61
Test ** getNext()
Return the next pointer as a pointer to the pointer, similar to getRoot().
Definition: Test.h:127
static const uint8_t kStatusSkipped
Test is skipped, through the exclude() method or skip() was called.
Definition: Test.h:67
static Test ** getRoot()
Get the pointer to the root pointer.
Definition: Test.cpp:44
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:58
static const uint8_t kStatusExpired
Test has timed out, or expire() called.
Definition: Test.h:70
static bool isVerbosity(uint8_t verbosity)
Returns true if ANY of the bit flags of &#39;verbosity&#39; is set.
Definition: TestRunner.h:96