Skip to content

Commit

Permalink
fixes #166 and fixes #167 - TODO: update benchmarks page
Browse files Browse the repository at this point in the history
  • Loading branch information
onqtam committed Dec 5, 2018
1 parent abb864e commit c494843
Show file tree
Hide file tree
Showing 18 changed files with 237 additions and 659 deletions.
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -37,7 +37,7 @@
</table>
</b>

**doctest** is a new C++ testing framework but is by far the fastest both in compile times (by [**orders of magnitude**](doc/markdown/benchmarks.md)) and runtime compared to other feature-rich alternatives. It brings the ability of compiled languages such as [**D**](https://dlang.org/spec/unittest.html) / [**Rust**](https://doc.rust-lang.org/book/testing.html) / [**Nim**](https://nim-lang.org/docs/unittest.html) to have tests written directly in the production code by providing a fast, transparent and flexible test runner with a clean interface.
**doctest** is a new C++ testing framework but is by far the fastest both in compile times (by [**orders of magnitude**](doc/markdown/benchmarks.md)) and runtime compared to other feature-rich alternatives. It brings the ability of compiled languages such as [**D**](https://dlang.org/spec/unittest.html) / [**Rust**](https://doc.rust-lang.org/book/second-edition/ch11-00-testing.html) / [**Nim**](https://nim-lang.org/docs/unittest.html) to have tests written directly in the production code by providing a fast, transparent and flexible test runner with a clean interface.

[<img src="https://cloud.githubusercontent.com/assets/8225057/5990484/70413560-a9ab-11e4-8942-1a63607c0b00.png" align="right">](http://www.patreon.com/onqtam)

Expand Down
28 changes: 6 additions & 22 deletions doc/markdown/assertions.md
Expand Up @@ -34,7 +34,7 @@ CHECK(thisReturnsTrue());
REQUIRE(i < 42);
```
Negating asserts - ```<LEVEL>_FALSE(expression)``` - evaluates the expression and records the _logical NOT_ of the result.
- Negating asserts - ```<LEVEL>_FALSE(expression)``` - evaluates the expression and records the _logical NOT_ of the result.
These forms exist as a workaround for the fact that ```!``` prefixed expressions cannot be decomposed properly.
Expand All @@ -44,7 +44,8 @@ Example:
REQUIRE_FALSE(thisReturnsFalse());
```

Note that these asserts also have a ```_MESSAGE``` form - like ```CHECK_MESSAGE(expression, message)``` which is basically a code block ```{}``` with a scoped [**```INFO()```**](logging.md#info) logging macro together with the ```CHECK``` macro - that way the message will be relevant only to that assert. All the other binary/unary/fast asserts don't have this variation.
- Using the [**```DOCTEST_CONFIG_SUPER_FAST_ASSERTS```**](configuration.md#doctest_config_super_fast_asserts) config option can make compilation of asserts lightning fast - by up to [**XX-XX%**](benchmarks.md#cost-of-an-assertion-macro)!
- These asserts also have a ```_MESSAGE``` form - like ```CHECK_MESSAGE(expression, message)``` which is basically a code block ```{}``` with a scoped [**```INFO()```**](logging.md#info) logging macro together with the ```CHECK``` macro - that way the message will be relevant only to that assert. The binary/unary asserts don't have this variation yet.

Examples:

Expand All @@ -62,7 +63,7 @@ For more information about the ```INFO()``` macro and logging with the streaming
These asserts don't use templates to decompose the comparison expressions for the left and right parts.
These have the same guarantees as the expression decomposing ones - just less templates - [**25%-45% faster**](benchmarks.md#cost-of-an-assertion-macro) for compile times.
These have the same guarantees as the expression decomposing ones but [**XX%-XX% faster**](benchmarks.md#cost-of-an-assertion-macro) for compilation.
```<LEVEL>``` is one of 3 possible: ```REQUIRE```/```CHECK```/```WARN```.
Expand All @@ -75,24 +76,7 @@ These have the same guarantees as the expression decomposing ones - just less te
- ```<LEVEL>_UNARY(expr)``` - same as ```<LEVEL>(expr)```
- ```<LEVEL>_UNARY_FALSE(expr)``` - same as ```<LEVEL>_FALSE(expr)```

## Fast asserts

These are the faster versions of the binary and unary asserts - by [**60-80%**](benchmarks.md#cost-of-an-assertion-macro) of compile time.

The difference is they don't evaluate the expression in a ```try/catch``` block - if the expression throws the whole test case ends.

There is also the [**```DOCTEST_CONFIG_SUPER_FAST_ASSERTS```**](configuration.md#doctest_config_super_fast_asserts) config identifier that makes them even faster by another [**50-80%**](benchmarks.md#cost-of-an-assertion-macro)!

```<LEVEL>``` is one of 3 possible: ```REQUIRE```/```CHECK```/```WARN```.

- ```FAST_<LEVEL>_EQ(left, right)``` - almost the same as ```<LEVEL>(left == right)```
- ```FAST_<LEVEL>_NE(left, right)``` - almost the same as ```<LEVEL>(left != right)```
- ```FAST_<LEVEL>_GT(left, right)``` - almost the same as ```<LEVEL>(left > right)```
- ```FAST_<LEVEL>_LT(left, right)``` - almost the same as ```<LEVEL>(left < right)```
- ```FAST_<LEVEL>_GE(left, right)``` - almost the same as ```<LEVEL>(left >= right)```
- ```FAST_<LEVEL>_LE(left, right)``` - almost the same as ```<LEVEL>(left <= right)```
- ```FAST_<LEVEL>_UNARY(expr)``` - almost the same as ```<LEVEL>(expr)```
- ```FAST_<LEVEL>_UNARY_FALSE(expr)``` - almost the same as ```<LEVEL>_FALSE(expr)```
- Using the [**```DOCTEST_CONFIG_SUPER_FAST_ASSERTS```**](configuration.md#doctest_config_super_fast_asserts) config option can make compilation of asserts lightning fast - by up to [**XX-XX%**](benchmarks.md#cost-of-an-assertion-macro)!

## Exceptions

Expand Down Expand Up @@ -133,7 +117,7 @@ Asserts can be used outside of a testing context (in code not called from a ```T

A ```doctest::Context``` object still has to be created somewhere and set as the default one using the ```setAsDefaultForAssertsOutOfTestCases()``` method - and then asserts will work. A handler can be registered by calling the ```setAssertHandler()``` method on the context object. If no handler is set then ```std::abort()``` is called on failure.

The results would be best when using the [**fast asserts**](assertions.md#fast-asserts) coupled with the [**```DOCTEST_CONFIG_SUPER_FAST_ASSERTS```**](configuration.md#doctest_config_super_fast_asserts) config identifier and by defining your own macro aliases - like shown [**here**](../../examples/all_features/doctest_proxy.h).
The results would be best when using the [**```DOCTEST_CONFIG_SUPER_FAST_ASSERTS```**](configuration.md#doctest_config_super_fast_asserts) config identifier.

Checkout the [**example**](../../examples/all_features/asserts_used_outside_of_tests.cpp) showcasing how that is done. For more information see the [**issue for the feature request**](https://github.com/onqtam/doctest/issues/114).

Expand Down
6 changes: 4 additions & 2 deletions doc/markdown/configuration.md
Expand Up @@ -93,9 +93,11 @@ This should be defined globally.

### **```DOCTEST_CONFIG_SUPER_FAST_ASSERTS```**

This makes the fast assert macros (```FAST_CHECK_EQ(a,b)``` - with a ```FAST_``` prefix) compile [**even faster**](benchmarks.md#cost-of-an-assertion-macro)! (50-80%)
This config option makes the assert macros (except for those dealing with exceptions) compile [**much faster**](benchmarks.md#cost-of-an-assertion-macro)! (XX-XX%)

Each fast assert is turned into a single function call - the only downside of this is: if an assert fails and a debugger is attached - when it breaks it will be in an internal function - the user will have to go 1 level up in the callstack to see the actual assert.
Each assert is turned into a single function call - the only downside of this is: if an assert fails and a debugger is attached - when it breaks it will be in an internal function - the user will have to go 1 level up in the callstack to see the actual assert.

It also implies [**```DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS```**](#doctest_config_no_try_catch_in_asserts) (so exceptions thrown during the evaluation of an assert are not caught by the assert itself but by the testing framework - meaning that the test case is immediately aborted).

This can be defined both globally and in specific source files only.

Expand Down
21 changes: 11 additions & 10 deletions doc/markdown/faq.md
Expand Up @@ -47,25 +47,26 @@ A quick and easy way to migrate most of your Catch tests to doctest is to change
```c++
#include "path/to/doctest.h"

#undef TEST_CASE
#define TEST_CASE(name, tags) DOCTEST_TEST_CASE(tags " " name) // will concatenate the tags and test name string literals to one
#define SECTION(name) DOCTEST_SUBCASE(name)
using doctest::Approx; // catch exposes this by default outside of its namespace

// only if tags are used: will concatenate them to the test name string literal
#undef TEST_CASE
#define TEST_CASE(name, tags) DOCTEST_TEST_CASE(tags " " name)

// catch exposes this by default outside of its namespace
using doctest::Approx;
```

### How to get the best compile-time performance with the framework?

Using the [**fast**](assertions.md#fast-asserts) asserts in combination with [**```DOCTEST_CONFIG_SUPER_FAST_ASSERTS```**](configuration.md#doctest_config_super_fast_asserts) yelds the [**fastest**](benchmarks.md#cost-of-an-assertion-macro) compile times.

There are only 2 drawbacks of this approach:
The [**```DOCTEST_CONFIG_SUPER_FAST_ASSERTS```**](configuration.md#doctest_config_super_fast_asserts) config option yelds the [**fastest possible**](benchmarks.md#cost-of-an-assertion-macro) compile times (up to XX-XX%). Also the expression-decomposing template machinery can be skipped by using the [**binary**](assertions.md#binary-and-unary-asserts) asserts.

- using fast asserts (60-90% [**faster**](benchmarks.md#cost-of-an-assertion-macro) than ```CHECK(a==b)```) means that there is no ```try/catch``` block in each assert so if an expression throws the whole test case ends.
- defining the [**```DOCTEST_CONFIG_SUPER_FAST_ASSERTS```**](configuration.md#doctest_config_super_fast_asserts) config identifier will result in even [**faster**](benchmarks.md#cost-of-an-assertion-macro) fast asserts (50-80%) at the cost of only one thing: when an assert fails and a debugger is present - the framework will break inside a doctest function so the user will have to go 1 level up in the callstack to see where the actual assert is in the source code.
There are only 2 tiny drawbacks of using this config option:

These 2 things can be considered negligible if you are dealing mainly with arithmetic (expressions are unlikely to throw exceptions) and all the tests usually pass (you don't need to often navigate to a failing assert with a debugger attached)
- there is no ```try/catch``` block in each assert so if an expression is thrown the whole test case ends (but is still caught and reported).
- when an assert fails and a debugger is present - the framework will break inside a doctest function so the user will have to go 1 level up in the callstack to see where the actual assert is in the source code.

If you want better aliases for the asserts instead of the long ones you could use [**```DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES```**](configuration.md#doctest_config_no_short_macro_names) and then define your aliases like this: ```#define CHECK_EQ DOCTEST_FAST_CHECK_EQ``` (example in [**here**](../../examples/all_features/alternative_macros.cpp) and [**here**](../../examples/all_features/doctest_proxy.h)).
These 2 things can be considered negligible and totally worth it if you are dealing mainly with expressions unlikely to throw exceptions and all the tests usually pass (you don't need to navigate often to a failing assert with a debugger attached).

### Is doctest thread-aware?

Expand Down
10 changes: 5 additions & 5 deletions doc/markdown/roadmap.md
Expand Up @@ -7,7 +7,7 @@ This library is free, and will stay free but needs your support to sustain its d

Planned features for future releases - order changes constantly...

### For 2.1:
### For 2.3:

- finish the reporter system
- what is done:
Expand All @@ -31,7 +31,7 @@ Planned features for future releases - order changes constantly...
- documentation
- matchers - should investigate what they are - look at google test/mock and Catch (also predicates and boost test)

### For 2.2:
### For 2.4:

- header with extensions
- demangling with the use of the cxxabi header
Expand Down Expand Up @@ -61,7 +61,7 @@ Planned features for future releases - order changes constantly...
- https://www.jetbrains.com/clion/features/unit-testing.html
- https://blog.jetbrains.com/clion/2017/03/clion-2017-1-released/#catch

### For 2.3:
### For 2.5:

- log levels - like in [boost test](http://www.boost.org/doc/libs/1_63_0/libs/test/doc/html/boost_test/utf_reference/rt_param_reference/log_level.html)
- running tests a [few times](https://github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md#repeating-the-tests)
Expand All @@ -88,6 +88,7 @@ Planned features for future releases - order changes constantly...

- use modules - use ```std::string``` and whatever else comes from the standard - no more hand rolled traits and classes
- minimize the use of the preprocessor
- remove backwards-compatible macros for the fast asserts

### Things that are being considered but not part of the roadmap yet:

Expand Down Expand Up @@ -143,7 +144,7 @@ Planned features for future releases - order changes constantly...
- http://preshing.com/20111124/always-use-a-lightweight-mutex/
- http://preshing.com/20120226/roll-your-own-lightweight-mutex/
- ability to provide a temp folder that is cleared between each test case
- make the _MESSAGE assert macros work with variadic arguments - and maybe write the ones for binary/unary/fast asserts as well
- make the _MESSAGE assert macros work with variadic arguments - and maybe write the ones for binary/unary asserts as well
- move from operator "<<" to "<=" for capturing the left operand when decomposing binary expressions with templates
- think about silencing warnings about unused variables when DOCTEST_CONFIG_DISABLE is used - see commit 6b61e8aa3818c5ea100cedc1bb48a60ea10df6e8 or issue #61
- also this: ```(void)(true ? (void)0 : ((void)(expression)))```
Expand All @@ -156,7 +157,6 @@ Planned features for future releases - order changes constantly...
### Things that are very unlikely to enter the roadmap:

- rethink static code analysis suppressions - users shouldn't have to use the same flags for code which uses doctest macros/types
- think about removing the binary asserts (leaving only the fast binary asserts) because normal asserts + no try/catch in asserts are almost the same
- move the "react()" part (the one that throws for REQUIRE asserts - or for when "abort-after=<int>" is reached) to a function call in the while() part of the asserts
- stop using underscores for the begining of identifiers - the anonymous variables - against the standard...
- templated fixture test cases
Expand Down
2 changes: 1 addition & 1 deletion doc/markdown/tutorial.md
Expand Up @@ -72,7 +72,7 @@ Although this was a simple test it's been enough to demonstrate a few things abo
1. All we did was ```#define``` one identifier and ```#include``` one header and we got everything - even an implementation of ```main()``` that will respond to command line arguments. You can only use that ```#define``` in one source file for (hopefully) obvious reasons. Once you have more than one file with unit tests in you'll just ```#include "doctest.h"``` and go. Usually it's a good idea to have a dedicated implementation file that just has ```#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN``` and ```#include "doctest.h"```. You can also provide your own implementation of main and drive **doctest** yourself - see [**supplying your own ```main()```**](main.md).
2. We introduce test cases with the ```TEST_CASE``` macro. It takes one argument - a free form test name (for more see [**Test cases and subcases**](testcases.md)). The test name doesn't have to be unique. You can run sets of tests by specifying a wildcarded test name or a tag expression. See the [**command line**](commandline.md) docs for more information on running tests.
3. The name is just a string. We haven't had to declare a function or method - or explicitly register the test case anywhere. Behind the scenes a function with a generated name is defined for you and automatically registered using static registry classes. By abstracting the function name away we can name our tests without the constraints of identifier names.
4. We write our individual test assertions using the ```CHECK()``` macro. Rather than a separate macro for each type of condition (equal, less than, greater than, etc.) we express the condition naturally using C++ syntax. Behind the scenes a simple expression template captures the left-hand-side and right-hand-side of the expression so we can display the values in our test report. There are other [**assertion macros**](assertions.md) not covered in this tutorial - but because of this technique the number of them is drastically reduced.
4. We write our individual test assertions using the ```CHECK()``` macro. Rather than a separate macro for each type of condition (equal, less than, greater than, etc.) we express the condition naturally using C++ syntax. Behind the scenes a simple expression template captures the left-hand-side and right-hand-side of the expression so we can display the values in our test report. There are other [**assertion macros**](assertions.md) not covered in this tutorial - but because of this technique the number of them is drastically reduced.

## Test cases and subcases

Expand Down

0 comments on commit c494843

Please sign in to comment.