AUnit  0.4.0
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 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  // Transfer the verbosity of the TestRunner to the Test.
107  (*mCurrent)->enableVerbosity(mVerbosity);
108 
109  (*mCurrent)->setup();
110 
111  // support assertXxx() statements inside the setup() method
112  if ((*mCurrent)->getStatus() == Test::kStatusNew) {
113  (*mCurrent)->setStatus(Test::kStatusSetup);
114  }
115  break;
116  case Test::kStatusSetup:
117  {
118  // Check for timeout. mTimeout == 0 means infinite timeout.
119  // NOTE: It feels like this code should go into the Test::loop() method
120  // (like the extra bit of code in TestOnce::loop()) because it seems
121  // like we could want the timeout to be configurable on a case by case
122  // basis. This would cause the testing() code to move down into a new
123  // again() virtual method dispatched from Test::loop(), analogous to
124  // once(). But let's keep the code here for now.
125  unsigned long now = millis();
126  if (mTimeout > 0 && now >= mStartTime + 1000L * mTimeout) {
127  (*mCurrent)->expire();
128  } else {
129  (*mCurrent)->loop();
130 
131  // If test status is unresolved (i.e. still in kStatusSetup state)
132  // after loop(), then this is a continuous testing() test case, so
133  // skip to the next test. Otherwise, stay on the current test so that
134  // the next iteration of runTest() can resolve the current test.
135  if ((*mCurrent)->getStatus() == Test::kStatusSetup) {
136  // skip to the next one, but keep current test in the list
137  mCurrent = (*mCurrent)->getNext();
138  }
139  }
140  }
141  break;
143  mSkippedCount++;
144  (*mCurrent)->resolve();
145  // skip to the next one by taking current test out of the list
146  *mCurrent = *(*mCurrent)->getNext();
147  break;
148  case Test::kStatusPassed:
149  mPassedCount++;
150  (*mCurrent)->resolve();
151  // skip to the next one by taking current test out of the list
152  *mCurrent = *(*mCurrent)->getNext();
153  break;
154  case Test::kStatusFailed:
155  mFailedCount++;
156  (*mCurrent)->resolve();
157  // skip to the next one by taking current test out of the list
158  *mCurrent = *(*mCurrent)->getNext();
159  break;
161  mExpiredCount++;
162  (*mCurrent)->resolve();
163  // skip to the next one by taking current test out of the list
164  *mCurrent = *(*mCurrent)->getNext();
165  break;
166  }
167 }
168 
169 void TestRunner::setupRunner() {
170  if (!mIsSetup) {
171  mIsSetup = true;
172  mCount = countTests();
173  mCurrent = Test::getRoot();
174  mStartTime = millis();
175  }
176 }
177 
178 // Count the number of tests in TestRunner instead of Test::insert() to avoid
179 // another C++ static initialization ordering problem.
180 uint16_t TestRunner::countTests() {
181  uint16_t count = 0;
182  for (Test** p = Test::getRoot(); *p != nullptr; p = (*p)->getNext()) {
183  count++;
184  }
185  return count;
186 }
187 
188 void TestRunner::printStartRunner() {
190 
191  Print* printer = Printer::getPrinter();
192  printer->print(F("TestRunner started on "));
193  printer->print(mCount);
194  printer->println(F(" test(s)."));
195 }
196 
197 void TestRunner::resolveRun() {
199 
200  Print* printer = Printer::getPrinter();
201  printer->print(F("TestRunner summary: "));
202  printer->print(mPassedCount);
203  printer->print(F(" passed, "));
204  printer->print(mFailedCount);
205  printer->print(F(" failed, "));
206  printer->print(mSkippedCount);
207  printer->print(F(" skipped, "));
208  printer->print(mExpiredCount);
209  printer->print(F(" timed out, out of "));
210  printer->print(mCount);
211  printer->println(F(" test(s)."));
212 
213  mIsResolved = true;
214 }
215 
216 void TestRunner::listTests() {
217  setupRunner();
218 
219  Print* printer = Printer::getPrinter();
220  printer->print(F("TestRunner test count: "));
221  printer->println(mCount);
222  for (Test** p = Test::getRoot(); (*p) != nullptr; p = (*p)->getNext()) {
223  printer->print(F("Test "));
224  Printer::print((*p)->getName());
225  printer->print(F("; status: "));
226  printer->println((*p)->getStatus());
227  }
228 }
229 
230 void TestRunner::setRunnerTimeout(TimeoutType timeout) {
231  mTimeout = timeout;
232 }
233 
234 }
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:115
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:74