Skip to content

Commit

Permalink
src/Assert{,Verbose}Macros.h: Implement assertNoFatalFailure(statemen…
Browse files Browse the repository at this point in the history
…t) macro; fixes #11
  • Loading branch information
bxparks committed Feb 26, 2021
1 parent eb39946 commit 91d93c1
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 51 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# Changelog

* Unreleased
* Implement `assertNoFatalFailure(statement)` macro to prevent continued
execution if `statement` contains assertion failures. Fixes
[Issue #11](https://github.com/bxparks/AUnit/issues/11).
* 1.5.3 (2021-02-23)
* I botched the 1.5.2 release. Try again as 1.5.3.
* 1.5.2 (2021-02-23)
Expand Down
53 changes: 21 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -862,57 +862,46 @@ method only. The statement after the `assertCustomStuff()` will continue to
execute.

In other words, in the following example, if the `assertCustomStuff()` fails,
then `doStuff()` inside `testF()` will execute:
then `assertMoreStuff()` inside `testF()` will execute:

```C++
class CustomTestOnce: public TestOnce {
protected:
// optional
void setup() override {
TestOnce::setup();
...setup code...
}

// optional
void teardown() override {
...teardown code...
TestOnce::teardown();
}

void assertCustomStuff() {
assertEqual(sharedValue, 3);

// This will not execute if the assertEqual() failed.
// This will not execute if the assertEqual() above fails.
assertLess(...);
}

void assertMoreStuff() {
assertEqual(...);
}

int sharedValue;
};

testF(CustomTestOnce, calculate) {
// DON'T DO THIS
testF(CustomTestOnce, dontDoThis) {
assertCustomStuff();

// This will execute even if assertCustomStuff() failed.
doStuff();

// This will immediately exit this method if assertCustomStuff() failed.
assertTrue(true);
// This will execute even if assertCustomStuff() fails.
assertMoreStuff();
}

// This will NOT execute if assertCustomStuff() failed.
doMoreStuff();
// DO THIS INSTEAD
testF(CustomTestOnce, doThis) {
assertNoFatalFailure(assertCustomStuff());
assertNoFatalFailure(assertMoreStuff());
}
```
AUnit tries to mitigate this problem by having every `assertXxx()` macro
perform a check to see if a previous assert statement raise an error condition
for the test. If so, then the assert macro immediately exits. In the code above,
`doMoreStuff()` will not execute, because the `assertNotEqual()` will immidately
exit upon detecting the failure of `assertCustomStuff()`.
Google Test has a
[ASSERT_NO_FATAL_FAILURE( statement)](https://github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md)
macro that can guard against this possibility. AUnit does not have that macro,
but we get the equivalent effect by doing a `assertTrue(true)` shown above.
The solution is to use the `assertNoFatalFailure(statement)` macro which checks
whether the inner `statement` returned with a fatal assertion. If so, then it
returns immediately, preventing execution from continuing to the code that
follows. This macro is modeled after the
[ASSERT_NO_FATAL_FAILURE(statement)](https://github.com/google/googletest/blob/master/docs/advanced.md)
macro in Google Test that provides the same functionality.
<a name="MetaAssertions"></a>
### Meta Assertions
Expand Down
14 changes: 7 additions & 7 deletions examples/fixture/fixture.ino
Original file line number Diff line number Diff line change
Expand Up @@ -77,18 +77,18 @@ testF(LogTest, insert) {
// Very useful for debugging assertRecords()
// enableVerbosity(Verbosity::kAssertionPassed);

assertRecords(2, "bike", 1, "car", 2);
// The assertNoFatalFailure() macro prevents execution from continuing if
// assertRecords() contains a failure.
assertNoFatalFailure(assertRecords(2, "bike", 1, "car", 2));

// Warning: This statement will execute even if assertRecords() fails. See
// README.md about "early returns" from assert statements.
// The assertNoFatalFailure() above prevents this statement from executing if
// assertRecords() fails.
container.insert("train", 3);

// This assert will return immediately if assertRecords() fails. See
// README.md about delayed status verification.
// This assert will return immediately upon failure.
assertEqual(3, container.numRecords);

// This statement will not execute if assertRecords() fails because the
// above assertEqual() will return.
// This statement will not execute if the above assertEqual() fails.
container.insert("plane", 4);
}

Expand Down
1 change: 1 addition & 0 deletions keywords.txt
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ assertTrue KEYWORD2
assertFalse KEYWORD2
assertNear KEYWORD2
assertNotNear KEYWORD2
assertNoFatalFailure KEYWORD2

# Public macros from MetaAssertMacros.h
checkTestDone KEYWORD2
Expand Down
11 changes: 11 additions & 0 deletions src/aunit/AssertMacros.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,15 @@ SOFTWARE.
return;\
} while (false)

/**
* Assert that the inner 'statement' returns with no fatal assertions. This is
* required because AUnit does not use exceptions, so we have to check the
* assertion state after calling an inner function. This macro is similar to
* the `ASSERT_NO_FATAL_FAILURE(statement)` in GoogleTest.
*/
#define assertNoFatalFailure(statement) do { \
statement; \
if (isDone()) return; \
} while (false)

#endif
11 changes: 11 additions & 0 deletions src/aunit/AssertVerboseMacros.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,15 @@ SOFTWARE.
return;\
} while (false)

/**
* Assert that the inner 'statement' returns with no fatal assertions. This is
* required because AUnit does not use exceptions, so we have to check the
* assertion state after calling an inner function. This macro is similar to
* the `ASSERT_NO_FATAL_FAILURE(statement)` in GoogleTest.
*/
#define assertNoFatalFailure(statement) do { \
statement; \
if (isDone()) return; \
} while (false)

#endif
22 changes: 10 additions & 12 deletions tests/AUnitTest/AUnitTest.ino
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,9 @@ SOFTWARE.

#endif

// Defined in ESP8266, not defined in AVR or Teensy, broken in ESP32.
#if !defined(ESP8266)
#undef FPSTR
#define FPSTR(pstr_pointer) \
(reinterpret_cast<const __FlashStringHelper *>(pstr_pointer))
// Defined in ESP8266 and ESP32, not defined in AVR or Teensy.
#ifndef FPSTR
#define FPSTR(pstr) (reinterpret_cast<const __FlashStringHelper *>(pstr))
#endif

signed char sc = 4;
Expand Down Expand Up @@ -603,7 +601,7 @@ class CustomOnceFixture: public TestOnce {
TestOnce::teardown();
}

void assertCommon(int m) {
void testCommon(int m) {
assertLess(m, subject);
}

Expand All @@ -615,7 +613,7 @@ class CustomOnceFixture: public TestOnce {
};

testF(CustomOnceFixture, common) {
assertCommon(5);
assertNoFatalFailure(testCommon(5));
assertEqual(6, subject);
}

Expand All @@ -630,15 +628,15 @@ class CustomAgainFixture: public TestAgain {
TestAgain::teardown();
}

void assertCommon(int m) {
void testCommon(int m) {
assertLess(m, subject);
}

int subject;
};

testingF(CustomAgainFixture, common) {
assertCommon(5);
assertNoFatalFailure(testCommon(5));
assertEqual(6, subject);
pass();
}
Expand All @@ -651,15 +649,15 @@ testingF(CustomAgainFixture, common) {
// because testingF() overrides an again() method which doesn't exist in
// TestOnce.
testingF(CustomOnceFixture, crossedOnce) {
assertCommon();
testCommon();
}
#endif

#if 0
// Test a testF() macro with a TestAgain class. Should get compiler error
// because testF() overrides a once() method which doesn't exist in TestAgain.
testF(CustomAgainFixture, crossedAgain) {
assertCommon();
testCommon();
}
#endif

Expand Down Expand Up @@ -692,7 +690,7 @@ class CustomTestOnce: public TestOnce {
CustomTestOnce myTestOnce1("customTestOnce1");
CustomTestOnce myTestOnce2("customTestOnce2");

#endif
#endif // USE_AUNIT

// ------------------------------------------------------
// The main body.
Expand Down

0 comments on commit 91d93c1

Please sign in to comment.