-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #42 from bxparks/develop
1.2 - overloaded 2-argument versions of test() and testing(); new gtest.h macros
- Loading branch information
Showing
122 changed files
with
1,536 additions
and
562 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,3 +30,6 @@ | |
*.exe | ||
*.out | ||
*.app | ||
|
||
# Microsoft Visual Studio | ||
/.vs |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,7 +9,7 @@ AVR, ESP8266, ESP32 and Teensy platforms. The sister AUniter project provides | |
command line tools to verify, upload and validate the unit tests. The AUniter | ||
tools can be used in a continuous integration system like Jenkins. | ||
|
||
Version: 1.1.1 (2018-10-18) | ||
Version: 1.2 (2018-12-01) | ||
|
||
[![AUniter Jenkins Badge](https://us-central1-xparks2018.cloudfunctions.net/badge?project=AUnit)](https://github.com/bxparks/AUniter) | ||
|
||
|
@@ -99,6 +99,9 @@ Here are the features in AUnit which are not available in ArduinoUnit 2.2: | |
* Approximate comparisons: | ||
* `assertNear()` | ||
* `asssertNotNear()` | ||
* `test()` and `testing()` macros support both 1 and 2 arguments | ||
* `test(testName)` and `test(suiteName, testName)` | ||
* `testing(testName)` and `testing(suiteName, testName)` | ||
* Test fixtures using the "F" variations of existing macros: | ||
* `testF()` | ||
* `testingF()` | ||
|
@@ -117,8 +120,8 @@ Here are the features in AUnit which are not available in ArduinoUnit 2.2: | |
* `TestRunner::include(testClass, name)` | ||
* `TestRunner::exclude(testClass, name)` | ||
* Terse and verbose modes: | ||
* `#include <AUnit.h>` - terse messages uses less flash memory | ||
* `#include <AUnitVerbose.h>` - verbose messages uses more flash memory | ||
* `#include <AUnit.h>` - terse messages use less flash memory | ||
* `#include <AUnitVerbose.h>` - verbose messages use more flash memory | ||
* Tested on the following Arduino platforms: | ||
* AVR (8-bit) | ||
* Teensy ARM (32-bit) | ||
|
@@ -239,21 +242,23 @@ macros are used to create a test: | |
|
||
* `test(name) {...}` - creates a subclass of `TestOnce` | ||
* `testing(name) {...}` - creates a subclass of `TestAgain` | ||
* `test(suiteName, name) {...}` - creates a subclass of `TestOnce` | ||
* `testing(suiteName, name) {...}` - creates a subclass of `TestAgain` | ||
* `testF(classname, name) {...}` - creates a subclass of `classname` | ||
* `testingF(classname, name) {...}` - creates a subclass of `classname` | ||
|
||
The code in `{ }` following these macros becomes the body of a method in a | ||
subclass derived from the base class indicated above. The `test()` and `testF()` | ||
macros place the code body into the `TestOnce::once()` method. The `testing()` | ||
and `testingF()` macros place the code body into the `TestAgain::again()` | ||
method. The name of the subclass is a concatenation of the string `"test_"` and | ||
the `name` for `test()` and `testing()`, or the concatenation of | ||
`classname` + `"_"` + `name` for `testF()` and `testing()`. | ||
method. | ||
|
||
The argument to these macros are the name of the test case, and is used to | ||
generate a name for the subclass. (The name is available within the test code | ||
using the `Test::getName()` method). The macros also generate code to create an | ||
global instance of the subclass, which are static initialized by C++. | ||
The `test()` and `testing()` macros support 1 or 2 arguments. The one-argument | ||
version is inherited from ArduinoUnit. The two-argument version is | ||
analogous to the `TEST()` macro in GoogleTest, where the `suiteName` can | ||
be used to organize multiple tests into a collection of similar tests. The | ||
grouping is purely in the naming scheme of the generated code, there is no | ||
functional relationship between these tests. | ||
|
||
During static initialization, the constructor of the object adds itself to an | ||
internal list. The root of that list is given by `Test::getRoot()`. The | ||
|
@@ -268,11 +273,17 @@ Here is a rough outline of an AUnit unit test sketch: | |
#include <AUnit.h> | ||
using namespace aunit; | ||
|
||
test(example_test) { | ||
...assertXxx()... | ||
test(example) { | ||
... | ||
assertXxx(...) | ||
... | ||
} | ||
|
||
test(ExampleTest, example) { | ||
... | ||
} | ||
|
||
testing(looping_test) { | ||
testing(looping) { | ||
...code... | ||
if (...) { | ||
pass(); | ||
|
@@ -283,6 +294,10 @@ testing(looping_test) { | |
} | ||
} | ||
|
||
testing(LoopingTest, looping) { | ||
... | ||
} | ||
|
||
class CustomTestOnce: public TestOnce { | ||
protected: | ||
// optional | ||
|
@@ -348,10 +363,39 @@ void loop() { | |
``` | ||
***ArduinoUnit Compatibility***: _The basic structure of the unit test is | ||
identical to ArduinoUnit. AUnit adds the `testF()` and `testingF`() macros which | ||
identical to ArduinoUnit. AUnit adds the `testF()` and `testingF`() macros, | ||
and the two-argument versions of `test()` and `testing()` which | ||
are not available in ArduinoUnit. The `Test` class in ArduinoUnit has been | ||
replaced with the `TestAgain` class in AUnit._ | ||
### Generated Class and Instance Names | ||
The arguments to the various `test*()` macros are used to generate the name for | ||
the subclasses of `TestOnce` or `TestAgain`, and generate the names of the | ||
instances of those classes. For reference, here are the rules: | ||
* `test(name)` | ||
* class: `"test_"` + name | ||
* instance: `"test_"` + name + `"_instance"` | ||
* `testing(name)` | ||
* class: `"test_"` + name | ||
* instance: `"test_"` + name + `"_instance"` | ||
* `test(suiteName, name)` | ||
* class: `suiteName` + `"_"` + name | ||
* instance: `suiteName` + `"_"` + name + `"_instance"` | ||
* `testing(suiteName, name)` | ||
* class: `suiteName` + `"_"` + name | ||
* instance: `suiteName` + `"_"` + name + `"_instance"` | ||
* `testF(className, name)` | ||
* class: `className` + `"_"` + name | ||
* instance: `className` + `"_"` + name + `"_instance"` | ||
* `testingF(className, name)` | ||
* class: `className` + `"_"` + name | ||
* instance: `className` + `"_"` + name + `"_instance"` | ||
The instance name is available within the test code using the `Test::getName()` | ||
method. | ||
### Binary Assertions | ||
Inside the `test()` and `testing()` macros, the following assertions | ||
|
@@ -1152,6 +1196,41 @@ framework, but let me know if you truly need a timeout of greater than 4m15s). | |
|
||
***ArduinoUnit Compatibility***: _Only available in AUnit._ | ||
|
||
## GoogleTest Adapter | ||
|
||
It may be possible to run simple unit tests written using | ||
[Google Test](https://github.com/google/googletest/) API on an Arduino platform | ||
by using the | ||
[aunit/contrib/gtest.h](src/aunit/contrib/gtest.h) adapter. This | ||
adapter layer provides a number of macros Google Test macros which map to | ||
their equivalent macros in AUnit: | ||
|
||
* `ASSERT_EQ(e, a)` - `assertEqual()` | ||
* `ASSERT_NE(e, a)` - `assertNotEqual()` | ||
* `ASSERT_LT(e, a)` - `assertLess()` | ||
* `ASSERT_GT(e, a)` - `assertMore()` | ||
* `ASSERT_LE(e, a)` - `assertLessOrEqual()` | ||
* `ASSERT_GE(e, a)` - `assertMoreOrEqual()` | ||
* `ASSERT_STREQ(e, a)` - `assertEqual()` | ||
* `ASSERT_STRNE(e, a)` - `assertNotEqual()` | ||
* `ASSERT_STRCASEEQ(e, a)` - `assertStringCaseEqual()` | ||
* `ASSERT_STRCASENE(e, a)` - `assertStringCaseNotEqual()` | ||
* `ASSERT_TRUE(x)` - `assertTrue()` | ||
* `ASSERT_FALSE(x)` - `assertFalse()` | ||
|
||
To use the `gtest.h` adapter, include the following headers: | ||
```C++ | ||
#include <AUnit.h> | ||
#include <aunit/contrib/gtest.h> | ||
``` | ||
|
||
or | ||
|
||
```C++ | ||
#include <AUnitVerbose.h> | ||
#include <aunit/contrib/gtest.h> | ||
``` | ||
|
||
## Commandline Tools and Continuous Integration | ||
|
||
### AUniter | ||
|
@@ -1161,29 +1240,33 @@ The command line tools have been moved into the | |
The `auniter.sh` script can compile, upload and validate multiple AUnit tests on | ||
multiple Arduino boards. The script can monitor the serial port and determine if | ||
the unit test passed or failed, and it will print out a summary of all unit | ||
tests at the end. | ||
|
||
Full details are given in the AUniter project, but here are some quick examples | ||
of these tools using the [AceSegment](https://github.com/bxparks/AceSegment) | ||
project. | ||
|
||
The following compiles and verifies the given sketches: | ||
``` | ||
$ AUniter/auniter.sh --verify \ | ||
--boards nano,leonardo,esp8266,esp32 AceSegment/tests/*Test | ||
``` | ||
|
||
The following uploads to and runs all the unit tests on an Arduino Nano | ||
(`/dev/ttyUSB0`), then an Arduion Leonardo (`/dev/ttyACM0`): | ||
``` | ||
$ AUniter/auniter.sh --test \ | ||
--boards nano:/dev/ttyUSB1,leonardo:/dev/ttyACM0 AceSegment/tests/*Test | ||
``` | ||
|
||
The list of available ports can be found by: | ||
``` | ||
$ AUniter/auniter.sh --list_ports | ||
``` | ||
tests at the end. Full details are given in the AUniter project, but here are | ||
some quick examples copied from the `AUniter/README.md` file: | ||
|
||
* `$ auniter envs` | ||
* list the environments configured in the `auniter.ini` config file | ||
* `$ auniter ports` | ||
* list the available serial ports and devices | ||
* `$ auniter verify nano Blink.ino` | ||
* verify (compile) `Blink.ino` using the `env:nano` environment | ||
* `$ auniter verify nano,esp8266,esp32 Blink.ino` | ||
* verify `Blink.ino` on 3 target environments (`env:nano`, `env:esp8266`, | ||
`env:esp32`) | ||
* `$ auniter upload nano:/dev/ttyUSB0 Blink.ino` | ||
* upload `Blink.ino` to the `env:nano` target environment connected to | ||
`/dev/ttyUSB0` | ||
* `$ auniter test nano:USB0 BlinkTest.ino` | ||
* compile and upload `BlinkTest.ino` using the `env:nano` environment, | ||
upload it to the board at `/dev/ttyUSB0`, then validate the output of the | ||
[AUnit](https://github.com/bxparks/AUnit) unit test | ||
* `$ auniter test nano:USB0,esp8266:USB1,esp32:USB2 BlinkTest/ ClockTest/` | ||
* upload and verify the 2 unit tests (`BlinkTest/BlinkTest.ino`, | ||
`ClockTest/ClockTest.ino`) on 3 target environments (`env:nano`, | ||
`env:esp8266`, `env:esp32`) located at the 3 respective ports | ||
(`/dev/ttyUSB0`, `/dev/ttyUSB1`, `/dev/ttyUSB2`) | ||
* `$ auniter upmon nano:USB0 Blink.ino` | ||
* upload the `Blink.ino` sketch and monitor the serial port using a | ||
user-configurable terminal program (e.g. `picocom`) on `/dev/ttyUSB0` | ||
|
||
### Continuous Integration | ||
|
||
|
@@ -1269,20 +1352,103 @@ TestAgain TestOnce | |
::again() ::once() | ||
``` | ||
Placing the `Assertion` and `MetaAssertion` classes inside the `Test` hierarchy | ||
allows those assertion statements to have access to the internal states of the | ||
`Test` instance, which makes certain functions (like the early return upon | ||
delayed failure) slightly easier to implement. | ||
Normally, deep inheritance hierarchies like this should be avoided. However, | ||
placing the `Assertion` and `MetaAssertion` classes inside the `Test` hierarchy | ||
allowed those assertion statements to have access to the internal states of the | ||
`Test` instance. This made certain features (like the early return upon delayed | ||
failure) slightly easier to implement. For the most part, the end-users can | ||
ignore the existence of the `Assertion` and `MetaAssertion` classes and think of | ||
this as a simple 2-level inheritance tree. | ||
### Comparing Pointers | ||
Currently the `assertEqual()` and other `assertXxx()` methods do not support | ||
comparing arbitrary pointers (i.e. `(void*)`. This could change if | ||
comparing arbitrary pointers (i.e. `(void*)`. This could change if | ||
[Issue #34](https://github.com/bxparks/AUnit/issues/34) is | ||
resolved. In the meantime, a workaround is to cast the pointer to a `uintptr_t` | ||
integer type from `#include <stdint.h>` and then calling `assertEqual()` on the | ||
integer type. | ||
### Testing Private Helper Methods | ||
There is a school of throught which says that unit tests should test only the | ||
publically exposed methods of a class or library. I agree mostly with that | ||
sentiment, but not rigidly. I think it is sometimes useful to write unit tests | ||
for `protected` or `private` methods. For example, when creating a chain of | ||
small helper methods, which build up to larger publically exposed methods, it is | ||
extremely useful to write unit tests for the helper methods in isolation. | ||
Normally those helper methods would be `private` because they are used | ||
only within that class, and we don't want to expose them to the public API. One | ||
option is to make them `public` but add a comment in the function to say that it | ||
is exposed only for testing purposes. This does not seem satisfactory because | ||
users will tend to ignore such comments if the helper functions are useful. | ||
I think a better way is to keep the helper functions `private` but make | ||
the unit tests a `friend class` of the target class. The syntax for doing this | ||
can be tricky, it took me a number of attempts to get this right, especially if | ||
you are also using namespaces for your target class: | ||
```C++ | ||
//------------------- Target.h ------------- | ||
// Auto-generated test class names. | ||
class Test_helper; | ||
class TargetSuite_helper; | ||
class TargetTest_helper; | ||
namespace mylib { | ||
class Target { | ||
public: | ||
void publicMethod() { | ||
... | ||
int a = helper(); | ||
... | ||
} | ||
private: | ||
// Must have the global scope operator '::' | ||
friend class ::Test_helper; | ||
friend class ::TargetSuite_helper; | ||
friend class ::TargetTest_helper; | ||
static int helper() {...} | ||
}; | ||
} | ||
//------------------- TargetTest.ino ------------- | ||
#include <AUnit.h> | ||
#include "Target.h" | ||
using namespace aunit; | ||
using namespace mylib; | ||
test(helper) { | ||
assertEqual(1, Target::helper(...)); | ||
} | ||
test(TargetSuite, helper) { | ||
assertEqual(1, Target::helper(...)); | ||
} | ||
class TargetTest: public TestOnce { | ||
... | ||
}; | ||
testF(TargetTest, helper) { | ||
assertEqual(1, Target::helper(...)); | ||
} | ||
``` | ||
|
||
The tricky part is that in `Target.h` you need a forward declaration of the | ||
various auto-generated AUnit test classes, and within the `Target` class itsef, | ||
the `friend` declaration needs to have a global scope `::` specifier before the | ||
name of the test class. | ||
|
||
## Benchmarks | ||
|
||
AUnit consumes as much as 65% less flash memory than ArduinoUnit 2.2 on an AVR | ||
|
@@ -1365,6 +1531,8 @@ will incorporate everything, but I will give your ideas serious consideration. | |
## Authors | ||
|
||
* Created by Brian T. Park ([email protected]). | ||
* The Google Test adapter (`gtest.h`) was created by Chris Johnson | ||
([email protected]). | ||
* The design and syntax of many macros (e.g. `test()`, `assertXxx()`) were | ||
borrowed from the [ArduinoUnit](https://github.com/mmurdoch/arduinounit) | ||
project to allow AUnit to be almost a drop-in replacement. Many thanks to | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.