AUnit  1.5.5
Unit testing framework for Arduino platforms inspired by ArduinoUnit and Google Test.
TestRunner.h
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 #ifndef AUNIT_TEST_RUNNER_H
26 #define AUNIT_TEST_RUNNER_H
27 
28 #if defined(EPOXY_DUINO)
29 #include <stdlib.h> // exit()
30 #endif
31 #include <stdint.h>
32 #include <Arduino.h> // SERIAL_PORT_MONITOR, F(), Print
33 #include "Test.h"
34 
35 // ESP32 does not defined SERIAL_PORT_MONITOR
36 #ifndef SERIAL_PORT_MONITOR
37 #define SERIAL_PORT_MONITOR Serial
38 #endif
39 
40 namespace aunit {
41 
48 class TestRunner {
49  public:
54  typedef uint16_t TimeoutType;
55 
57  static void run() {
58  getRunner()->runTest();
59  }
60 
62  static void list() {
63  getRunner()->listTests();
64  }
65 
70  static void exclude(const char* pattern) {
71  getRunner()->setLifeCycleMatchingPattern(
72  pattern, Test::kLifeCycleExcluded);
73  }
74 
81  static void exclude(const char* testClass, const char* pattern) {
82  getRunner()->setLifeCycleMatchingPattern(testClass, pattern,
84  }
85 
90  static void include(const char* pattern) {
91  getRunner()->setLifeCycleMatchingPattern(pattern, Test::kLifeCycleNew);
92  }
93 
100  static void include(const char* testClass, const char* pattern) {
101  getRunner()->setLifeCycleMatchingPattern(testClass, pattern,
103  }
104 
106  static void setVerbosity(uint8_t verbosity) {
107  getRunner()->setVerbosityFlag(verbosity);
108  }
109 
111  static bool isVerbosity(uint8_t verbosity) {
112  return getRunner()->isVerbosityFlag(verbosity);
113  }
114 
116  static void setPrinter(Print* printer);
117 
124  static void setTimeout(TimeoutType seconds) {
125  getRunner()->setRunnerTimeout(seconds);
126  }
127 
128  private:
130  static const TimeoutType kTimeoutDefault = 10;
131 
133  static const uint8_t kMaxPatternLength = 63 + 1;
134 
136  static TestRunner* getRunner();
137 
139  static uint16_t countTests();
140 
141  // Disable copy-constructor and assignment operator
142  TestRunner(const TestRunner&) = delete;
143  TestRunner& operator=(const TestRunner&) = delete;
144 
146  TestRunner() {}
147 
156  void runTest() {
157  setupRunner();
158 
159  // Print initial header if this is the first run.
160  if (!mIsRunning) {
161  printStartRunner();
162  mIsRunning = true;
163  }
164 
165  // If no more test cases, then print out summary of run.
166  if (*Test::getRoot() == nullptr) {
167  if (!mIsResolved) {
168  mEndTime = millis();
169  resolveRun();
170  mIsResolved = true;
171  #if EPOXY_DUINO
172  exit((mFailedCount || mExpiredCount) ? 1 : 0);
173  #endif
174  }
175  return;
176  }
177 
178  // If reached the end and there are still test cases left, start from the
179  // beginning again.
180  if (*mCurrent == nullptr) {
181  mCurrent = Test::getRoot();
182  }
183 
184  // Implement a finite state machine that calls the (*mCurrent)->setup() or
185  // (*mCurrent)->loop(), then changes the test case's mStatus.
186  switch ((*mCurrent)->getLifeCycle()) {
187  case Test::kLifeCycleNew:
188  // Transfer the verbosity of the TestRunner to the Test.
189  (*mCurrent)->enableVerbosity(mVerbosity);
190  (*mCurrent)->setup();
191 
192  // Support assertXxx() statements inside the setup() method by
193  // moving to the next lifeCycle state if an assertXxx() did not fail
194  // inside the setup().
195  if ((*mCurrent)->getLifeCycle() == Test::kLifeCycleNew) {
196  (*mCurrent)->setLifeCycle(Test::kLifeCycleSetup);
197  }
198  break;
200  // If a test is excluded, go directly to LifeCycleFinished, without
201  // calling setup() or teardown().
202  (*mCurrent)->enableVerbosity(mVerbosity);
203  (*mCurrent)->setStatus(Test::kStatusSkipped);
204  mSkippedCount++;
205  (*mCurrent)->setLifeCycle(Test::kLifeCycleFinished);
206  break;
208  {
209  // Check for timeout. mTimeout == 0 means infinite timeout. NOTE: It
210  // feels like this code should go into the Test::loop() method (like
211  // the extra bit of code in TestOnce::loop()) because it seems like
212  // we could want the timeout to be configurable on a case by case
213  // basis. This would cause the testing() code to move down into a
214  // new again() virtual method dispatched from Test::loop(),
215  // analogous to once(). But let's keep the code here for now.
216  unsigned long now = millis();
217  if (mTimeout > 0 && now >= mStartTime + 1000L * mTimeout) {
218  (*mCurrent)->expire();
219  } else {
220  (*mCurrent)->loop();
221 
222  // If test status is unresolved (i.e. still in kLifeCycleNew
223  // state) after loop(), then this is a continuous testing() test
224  // case, so skip to the next test. Otherwise, stay on the current
225  // test so that the next iteration of runTest() can resolve the
226  // current test.
227  if ((*mCurrent)->getLifeCycle() == Test::kLifeCycleSetup) {
228  // skip to the next one, but keep current test in the list
229  mCurrent = (*mCurrent)->getNext();
230  }
231  }
232  }
233  break;
235  switch ((*mCurrent)->getStatus()) {
237  mSkippedCount++;
238  break;
239  case Test::kStatusPassed:
240  mPassedCount++;
241  break;
242  case Test::kStatusFailed:
243  mFailedCount++;
244  break;
246  mExpiredCount++;
247  break;
248  default:
249  // should never get here
250  mStatusErrorCount++;
251  break;
252  }
253  (*mCurrent)->teardown();
254  (*mCurrent)->setLifeCycle(Test::kLifeCycleFinished);
255  break;
257  (*mCurrent)->resolve();
258  // skip to the next one by taking current test out of the list
259  *mCurrent = *(*mCurrent)->getNext();
260  break;
261  }
262  }
263 
272  void listTests() {
273  setupRunner();
274 
275  Print* printer = Printer::getPrinter();
276  printer->print(F("TestRunner test count: "));
277  printer->println(mCount);
278  for (Test** p = Test::getRoot(); (*p) != nullptr; p = (*p)->getNext()) {
279  printer->print(F("Test "));
280  (*p)->getName().print(printer);
281  printer->print(F("; lifeCycle: "));
282  printer->println((*p)->getLifeCycle());
283  }
284  }
285 
287  void printStartRunner() const;
288 
290  void resolveRun() const;
291 
347  void setupRunner() {
348  if (mIsSetup) return;
349 
350  if (! Printer::getPrinter()) {
351  Printer::setPrinter(&SERIAL_PORT_MONITOR);
352  }
353 
354  mIsSetup = true;
355  mCount = countTests();
356  mCurrent = Test::getRoot();
357  mStartTime = millis();
358  }
359 
361  void setVerbosityFlag(uint8_t verbosity) { mVerbosity = verbosity; }
362 
364  bool isVerbosityFlag(uint8_t verbosity) const {
365  return mVerbosity & verbosity;
366  }
367 
369  void setLifeCycleMatchingPattern(const char* pattern, uint8_t lifeCycle);
370 
375  void setLifeCycleMatchingPattern(const char* testClass, const char* pattern,
376  uint8_t lifeCycle);
377 
379  void setRunnerTimeout(TimeoutType seconds);
380 
381  private:
382  // The current test case is represented by a pointer to a pointer. This
383  // allows treating the root node the same as all the other nodes, and
384  // simplifies the code traversing the singly-linked list significantly.
385  Test** mCurrent = nullptr;
386 
387  bool mIsResolved = false;
388  bool mIsSetup = false;
389  bool mIsRunning = false;
390  uint8_t mVerbosity = Verbosity::kDefault;
391  uint16_t mCount = 0;
392  uint16_t mPassedCount = 0;
393  uint16_t mFailedCount = 0;
394  uint16_t mSkippedCount = 0;
395  uint16_t mExpiredCount = 0;
396  uint16_t mStatusErrorCount = 0;
397  TimeoutType mTimeout = kTimeoutDefault;
398  unsigned long mStartTime;
399  unsigned long mEndTime;
400 };
401 
402 }
403 
404 #endif
aunit::Test::kLifeCycleFinished
static const uint8_t kLifeCycleFinished
The test has completed its life cycle.
Definition: Test.h:88
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::TestRunner::list
static void list()
Print out the known tests.
Definition: TestRunner.h:62
aunit::Printer::getPrinter
static Print * getPrinter()
Get the output printer used by the various assertion() methods and the TestRunner.
Definition: Printer.h:48
aunit::TestRunner::include
static void include(const char *testClass, const char *pattern)
Include the tests which match the pattern given by (testClass + "_" + pattern), the same concatenatio...
Definition: TestRunner.h:100
aunit::TestRunner::exclude
static void exclude(const char *testClass, const char *pattern)
Exclude the tests which match the pattern given by (testClass + "_" + pattern), the same concatenatio...
Definition: TestRunner.h:81
aunit::Verbosity::kDefault
static const uint8_t kDefault
The default verbosity.
Definition: Verbosity.h:69
aunit::TestRunner::setVerbosity
static void setVerbosity(uint8_t verbosity)
Set the verbosity flag.
Definition: TestRunner.h:106
aunit::TestRunner::TimeoutType
uint16_t TimeoutType
Integer type of the timeout parameter.
Definition: TestRunner.h:54
aunit::Test::kStatusPassed
static const uint8_t kStatusPassed
Test has passed, or pass() was called.
Definition: Test.h:99
aunit::TestRunner
The class that runs the various test cases defined by the test() and testing() macros.
Definition: TestRunner.h:48
aunit::TestRunner::run
static void run()
Run all tests using the current runner.
Definition: TestRunner.h:57
aunit::Test::kStatusExpired
static const uint8_t kStatusExpired
Test has timed out, or expire() called.
Definition: Test.h:108
aunit::TestRunner::setTimeout
static void setTimeout(TimeoutType seconds)
Set test runner timeout across all tests, in seconds.
Definition: TestRunner.h:124
aunit::TestRunner::isVerbosity
static bool isVerbosity(uint8_t verbosity)
Returns true if ANY of the bit flags of 'verbosity' is set.
Definition: TestRunner.h:111
aunit::Test::kStatusSkipped
static const uint8_t kStatusSkipped
Test is skipped through the exclude() method or skip() was called.
Definition: Test.h:105
aunit::TestRunner::exclude
static void exclude(const char *pattern)
Exclude the tests which match the pattern.
Definition: TestRunner.h:70
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::TestRunner::include
static void include(const char *pattern)
Include the tests which match the pattern.
Definition: TestRunner.h:90
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