Compare commits

...

198 Commits

Author SHA1 Message Date
Martin Hořeňovský
cd76f5730c v2.1.0 2018-01-10 13:53:04 +01:00
Martin Hořeňovský
f5910f38ef Copy reporters to single_include during releases 2018-01-10 13:44:08 +01:00
Phil Nash
421ab16062 Exclude string-literal arrays from automatic range serialisation
These have specialised serialisers already anyway, and were causing ambiguities in VS 2015 & 2017
2018-01-08 15:15:44 +00:00
Phil Nash
161dd4ed24 Merge commit '2c43620d9baed1fdcaa9146af1d3eb90520cbe92' 2018-01-08 11:13:29 +00:00
Aivars Kalvans
13ea4225e7 Add fuxedo.io to open source users 2018-01-06 14:02:01 +01:00
Phil Nash
2c43620d9b Exclude benchmark dir 2018-01-05 14:39:59 +00:00
Phil Nash
8be1df243e Added test for stringifying std::arrays 2018-01-04 10:52:55 +00:00
Phil Nash
32eb90b9bd Fix stringifying of unknown enums 2018-01-04 10:21:52 +00:00
Phil Nash
702cfdaf6e Added special handling for vector<bool> when stringifying 2018-01-04 10:05:02 +00:00
Phil Nash
e41e8e8384 Added tests for stringifying map and set 2018-01-04 10:03:08 +00:00
Phil Nash
af3f2499bc Added generic container detection in StringMaker.
Removed vector specialisation as this is now covered generically - as are any containers that can be called via (freestanding) begin/ end
2018-01-04 09:30:06 +00:00
philsquared
c3a1143d23 Cleanly override warning level for SelfTest in MSVC
Eliminates warning about warning level override (by removing the default /W3)
2018-01-02 10:18:35 +00:00
Martin Hořeňovský
f580591bf8 Test different way of excluding system headers 2017-12-25 19:38:51 +01:00
Phil Nash
fc88313d45 Added DtCraft to Open Source users 2017-12-12 17:22:40 +00:00
Martin Hořeňovský
3979845d5f Add coverage badge 2017-12-11 13:06:27 +01:00
Dan Nissenbaum
88d2bac624 Trivial typo fix. (#1119) 2017-12-09 21:29:39 +01:00
Pfiffikus
ed33e9787e Update MessageTests.cpp
typo corrected
2017-12-09 20:51:56 +01:00
Martin Hořeňovský
f466d9a1ed Fix a7a9ee5 2017-12-09 20:49:06 +01:00
Martin Hořeňovský
a7a9ee5552 Manual cherry-pick from #1111
This hsould merge still-valid piece of #1111 into master.
2017-12-09 20:17:47 +01:00
Martin Hořeňovský
0cf05d54a6 Force Travis badge to use status of master branch 2017-12-08 21:47:42 +01:00
Martin Hořeňovský
11887fbbab Point AppVeyor status badge at status for this repo
Previously it pointed to AppVeyor's test repo status
2017-12-08 21:36:43 +01:00
Phil Nash
347be87126 Removed debug code accidentally left in previous commit 2017-12-08 16:30:16 +00:00
Phil Nash
4da655c1b0 Increased int size for timers to avoid truncations 2017-12-08 15:59:00 +00:00
Martin Hořeňovský
c4d1aa9033 Fix std::uncaught_exception deprecation warning in ~ScopedMessage
Closes #1124
2017-12-07 19:10:28 +01:00
Martin Hořeňovský
495d2458e0 Add UnorderedEqualsMatcher for vectors
Closes #1093
2017-12-07 19:05:00 +01:00
Phil Nash
3035120dc7 Some bits of tidy up 2017-12-07 00:02:32 +00:00
Martin Hořeňovský
584e04d480 Add compact reporter baseline 2017-12-06 21:47:14 +01:00
Martin Hořeňovský
673dcc16a9 Make approval tests also check compact reporter 2017-12-06 15:48:46 +01:00
Martin Hořeňovský
0c122c135d Add constructor arg checking to WithinAbsMatcher
Also tests :-)
2017-12-06 15:42:03 +01:00
Phil Nash
d19b7292b3 xml reporter reports WARN message when not used with -s 2017-12-06 14:30:17 +00:00
Phil Nash
5e063616df Moved runner helpers into Catch namespace
not sure they weren't there to start with
2017-12-05 23:26:21 +00:00
Phil Nash
aa9d635014 Refactored StreamRedirect classes 2017-12-05 23:19:28 +00:00
Phil Nash
7c5a21fb7d Added clog test 2017-12-05 17:48:15 +00:00
Phil Nash
533cdc6bc1 Revirtualised IResultCapture methods
Didn't really impact runtime anyway, but will need to use interface for threading support.
2017-12-05 16:23:10 +00:00
Martin Hořeňovský
51e281a684 Simplify code coverage CMake toggle 2017-12-03 14:53:23 +01:00
Martin Hořeňovský
24851dff99 Add release notes from the EOL of Catch Classic 2017-12-03 14:19:02 +01:00
Martin Hořeňovský
a4fd96fbaa Remove debug prints from batch scripts 2017-12-03 14:14:58 +01:00
Martin Hořeňovský
12c57cedda No longer rename AppVeyor builds 2017-12-03 13:06:21 +01:00
Martin Hořeňovský
45a465713e Add codecov.io coverage collection from AppVeyor
Also had to add new project to redirect CTest output, add
separate batch scripts for AppVeyor because it doesn't handle
multi-line batch scripts in yaml properly, and other helper
scripts.
2017-12-03 13:03:52 +01:00
Phil Nash
dfa817ae73 Just track whether last assertion passed directly, rather than deduce it from counts 2017-12-02 18:44:23 +00:00
Phil Nash
57c346a46d Removed assertionRun() and rolled its logic into assertionPassed() and assertionEnded() 2017-12-02 18:44:23 +00:00
Martin Hořeňovský
67f734c799 Remove system headers when preprocessing coverage report 2017-12-02 14:17:42 +01:00
Phil Nash
b76e80ed3d Small clean-ups 2017-11-30 17:54:44 +03:00
Martin Hořeňovský
a3632facf3 Fix teamcity reporter compilation with single header 2017-11-30 13:48:24 +01:00
Phil Nash
7d0db6b8e9 Moved -Wparentheses suppression before the push for GCC
Because of bugs in GCC 4 & 5 that prevent _Pragma from working :-(
2017-11-29 20:19:50 +03:00
Phil Nash
8a7493cd88 Globally suppress Wunused-variable again, for now 2017-11-29 20:01:00 +03:00
Phil Nash
b5a5d9a6f8 Stop leaking some warning suppressions to user code 2017-11-29 19:14:33 +03:00
Martin Hořeňovský
8c32d0b644 Add more weird chrono::duration stringification tests 2017-11-28 21:47:06 +01:00
Martin Hořeňovský
28d1955ea8 Also test Approx template constructor 2017-11-28 21:29:34 +01:00
Martin Hořeňovský
20211a33e6 Stop using brew if not needed -- fix build on XCode9 image 2017-11-27 22:34:44 +01:00
Phil Nash
e3941a9ad2 De-virtualised isBinaryExpression() and getResult() on ITransientExpression 2017-11-27 22:49:26 +03:00
Phil Nash
da86ddc620 Fixed accidental const & formating 2017-11-27 22:28:45 +03:00
Phil Nash
4b614ee1d1 Moved all AssertionHandler logic into RunContext and de-virtualised interface
This makes the assertion handling much less "chatty". AssertionHandler is now just a thin shim over RunContext
2017-11-27 22:23:15 +03:00
Phil Nash
5461242ffe Renamed last usge specific handle method (and made the low level ones private) 2017-11-27 22:23:15 +03:00
Martin Hořeňovský
e344984a1b Add codecov.io coverage tracking
* Every Linux build tracks coverage when running Debug mode
* OS X not supported yet (Future WIP)
* Our own unit tests, non-default reporters and Clara are ignored
2017-11-27 20:13:47 +01:00
Phil Nash
db44964e27 Refactored most handle() calls to more specific/ descriptive calls 2017-11-26 21:28:43 +00:00
Phil Nash
2800adba25 Qualified handleExceptionMatchExpr in Catch::
(was picking it up by ADL before - no need to rely on that!)
2017-11-26 21:28:43 +00:00
Martin Hořeňovský
ae1547e202 Add extra tests to ctest 2017-11-26 21:33:09 +01:00
Martin Hořeňovský
73a1623eaf Re-enable 2 string tests 2017-11-25 18:38:18 +01:00
Phil Nash
c411c131cb Move crtdbg.h include outside namespace 2017-11-24 10:36:54 +00:00
Phil Nash
091595780e Clean-up re-usable string streams 2017-11-24 08:46:17 +00:00
Phil Nash
f417995afc Cache IResultCapture in AssertionHandler to avoid repeated lookups 2017-11-23 19:21:09 +00:00
Phil Nash
9329d97a43 Always debug-break non-inline 2017-11-23 19:14:26 +00:00
Phil Nash
8141a7836f Inline shouldDebugBreak() 2017-11-23 16:58:43 +00:00
Phil Nash
5323202652 Bake exception guard into assertion handler flow 2017-11-23 16:52:46 +00:00
Martin Hořeňovský
f052762c11 Reduce amount of CI output on success 2017-11-22 18:29:58 +01:00
Martin Hořeňovský
401ad7a189 Remove isTrue, alwaysTrue, alwaysFalse
isTrue and alwaysFalse were replaced by (void)0, 0 inspired by doctest
alwaysTrue was replaced by warning suppression
2017-11-22 16:03:45 +01:00
Martin Hořeňovský
63c097a077 Remove superfluous TravisCI build entry 2017-11-21 18:56:29 +01:00
Martin Hořeňovský
87c125ecb8 Enable Werror for dev builds 2017-11-21 18:55:28 +01:00
Phil Nash
3b965aa501 (re)Inlined isTrue() 2017-11-21 13:12:22 +00:00
Phil Nash
e54dcdac8b Added space in StringRef literal operator 2017-11-21 12:09:04 +00:00
Phil Nash
e4a898eaaa Removed templated StringRef ctor and added StringRef literal 2017-11-21 11:08:39 +00:00
Phil Nash
c39109dce3 Ignore all cmake-build-* folders 2017-11-21 11:08:39 +00:00
Martin Hořeňovský
a8a1c379c0 Introduce a way to intentionally expose interface for use in tests
Fixes #1076
2017-11-21 11:10:07 +01:00
Phil Nash
e08a4ed99e Added missing <cstring> include to stringref.cpp 2017-11-21 09:26:56 +00:00
Phil Nash
fcba30569c Refactored to resetAssertionInfo() 2017-11-20 16:33:06 +00:00
Phil Nash
4353614df7 Added StringRef constructor that captures string literal size at compile time 2017-11-20 16:33:05 +00:00
Martin Hořeňovský
f36817ef83 Check single-header using test examples
This means that examples build for all matrix entries
2017-11-19 22:03:24 +01:00
Martin Hořeňovský
812bf21740 Move imports close to point of usage in Python scripts
This means that you no longer need urllib2 to regenerate
single header file, etc
2017-11-19 22:02:22 +01:00
Martin Hořeňovský
baf3d2f360 Split out ratio_string::symbol bodies 2017-11-19 14:54:52 +01:00
Martin Hořeňovský
b083b04126 Fix compilation when using g++ with libc++
Fixes #1110
2017-11-19 14:47:18 +01:00
Phil Nash
505d2f8977 Merge pull request #1107 from coombez/contrib
performance improvements
2017-11-17 23:32:04 +00:00
Neal Coombes
f18366150e performance improvement - getCurrentContext
inlined getCurrentContext and getMutableContext
Further work on #1086.
Brings test from 0m37.913 to 0m25.584s
Catch2 is now faster than Catch 1.x!!
2017-11-17 14:55:30 -06:00
Neal Coombes
fe725648a7 performance improvement - StringRef::operator=
inlined and reduced data copy in half.
Further work on #1086.
Brings test from 0m44.942s to 0m37.913.
2017-11-17 14:15:26 -06:00
Phil Nash
b0c379f621 Inlined StringRef ctors/ dtor and size() and empty() 2017-11-17 18:38:54 +00:00
Phil Nash
c443afcca0 Merge pull request #1104 from coombez/contrib
Performance improvement
2017-11-17 18:38:30 +00:00
Phil Nash
502da4b38d Added files for multiply inclusions of test cases 2017-11-17 15:46:57 +00:00
Phil Nash
8da845810d Rebased due to whitespace changes 2017-11-17 15:46:57 +00:00
Phil Nash
61e838edf2 Reorganised (some) usage tests so they can be included multiple times 2017-11-17 15:46:57 +00:00
lbersch
516dbc83bc Add inja to open source users (#1106)
* Add Inja to open-source users
* Capitalize first letter in user description
* Fix url
2017-11-17 11:49:39 +01:00
Neal Coombes
b9339333df Performance improvement
Begin to address #1086
Brings test from 2m51.072s to 1m15.661s
2017-11-15 14:43:43 -06:00
Neal Coombes
61e29b5630 Fix AssertionPrinter name conflict in console and compact reporters 2017-11-15 21:26:31 +01:00
Martin Hořeňovský
54fb6f2d23 Provide WandBox link for online testing
It used to be provided for Catch Classic, it was lost during
transition of Catch2 to master.

Closes #1101
2017-11-15 20:23:05 +01:00
Martin Hořeňovský
a077ebae4c Use svg build status icon for AppVeyor
Closes #1100
2017-11-15 18:54:04 +01:00
Martin Moene
2bbba4f544 Refer to example code from the Tutorial 2017-11-15 15:37:39 +01:00
Martin Moene
29cdd6c526 Add link to event listener example to documentation 2017-11-15 15:37:39 +01:00
Martin Moene
dfb7217613 Add list of examples to documentation 2017-11-15 15:37:39 +01:00
Martin Moene
f6ae45122b Add matrix element for examples to AppVeyor 2017-11-15 15:37:39 +01:00
Martin Moene
d5d2bee4c5 Add matrix element for examples to Travis configuration 2017-11-15 15:37:39 +01:00
Martin Moene
85de0727d4 Add examples subdirectory to CMake build; included if BUILD_EXAMPLES is true 2017-11-15 15:37:39 +01:00
Martin Moene
4ecb2e112e Add examples folder with initial examples 2017-11-15 15:37:39 +01:00
Martin Hořeňovský
97a8640cbf Update 3rd party bugs documentation
Removes bugs from no longer supported compilers
Adds a confirmed 3rd party bug in VS 2015

Closes #881
2017-11-14 22:05:30 +01:00
Martin Hořeňovský
033e078320 Fix typo in build system docs 2017-11-14 21:42:28 +01:00
Martin Hořeňovský
9796a77a37 Initial prototype of PCH support
Related to #1061
2017-11-14 21:41:36 +01:00
Martin Hořeňovský
98d4c49d1c Provide ConsoleReporter declaration with EXTERNAL_INTERFACES
Related to #991
2017-11-14 20:42:58 +01:00
Martin Hořeňovský
a096e4b3f2 Provide XmlReporter declaration with EXTERNAL_INTERFACES
Related to #991
2017-11-14 17:56:27 +01:00
Martin Hořeňovský
4b3730de8a Provide JunitReporter declaration with EXTERNAL_INTERFACES
Related to #991
2017-11-14 17:15:13 +01:00
Martin Hořeňovský
6acdacfde0 Provide CompactReporter declaration with EXTERNAL_INTERFACES
Related to #991
2017-11-14 16:13:35 +01:00
Martin Hořeňovský
a3cba7a0d5 Conditionally compile problematic tests under old libstdc++ 2017-11-13 21:23:52 +01:00
Martin Hořeňovský
9796846ad0 Workaround libstdc++-4.8 regex issue in approval tests 2017-11-13 18:19:40 +01:00
Phil Nash
74d3dfd4cc All tests files have .tests.cpp suffix. Also moved tests out of TestMain.cpp and moved up a level 2017-11-13 16:03:27 +00:00
Phil Nash
e34754e433 Split SelfTest test files into Usage and Introspective varieties
Usage: just exercises Catch. The tests are over arbitrary date/ types
Introspective: Tests parts of Catch itself.
2017-11-13 15:38:52 +00:00
Martin Hořeňovský
55b71bebf1 Add tests for case insensitive string matching 2017-11-13 15:46:33 +01:00
Martin Hořeňovský
b0857e846f Provide a regex matcher against std::string
Related to #1040
2017-11-13 15:35:31 +01:00
Martin Hořeňovský
a06b6dc3ea Remove pointless StringRef -> std::string conversions 2017-11-13 13:08:59 +01:00
Martin Hořeňovský
0adb04807a Change how non-nullness is enforce in StringRef constructor 2017-11-13 13:04:45 +01:00
Martin Hořeňovský
f80f28e09a Fix pointless type mismatch between StringRef and std::string 2017-11-13 12:49:13 +01:00
Martin Hořeňovský
484eee973c Move StringRef's impl details to anonymous namespace 2017-11-13 12:41:04 +01:00
Martin Hořeňovský
d09fe4459d Stop recounting constant string's length on each passed assertion 2017-11-13 12:27:02 +01:00
Martin Hořeňovský
e484236825 Don't invoke UB when nullptr is passed to StringRef constructor 2017-11-13 12:09:19 +01:00
Martin Hořeňovský
e7c23b73da Don't call strlen in StringRef when the length was already passed 2017-11-13 12:03:45 +01:00
Phil Nash
3537b7858f Removed vestigal ComparatorT template arg to MatcherBase 2017-11-13 10:08:48 +00:00
Martin Hořeňovský
b74d4ca96d Add compilation test for #1027 2017-11-13 10:07:06 +01:00
Martin Hořeňovský
8dbaac61ff Final set of fixes for floating matchers approvals 2017-11-12 11:49:36 +01:00
Martin Hořeňovský
a0dbc62955 Fix OS X compilation error 2017-11-10 19:56:39 +01:00
Martin Hořeňovský
cecee3459a Add another MSVC NAN macro 2017-11-10 19:52:43 +01:00
Martin Hořeňovský
030321e3e0 Add NAN test for Approx 2017-11-10 18:48:45 +01:00
Martin Hořeňovský
5f961af70e Remove leftover commented out lines 2017-11-10 18:37:58 +01:00
Martin Hořeňovský
0b1f1b1003 Add ULP and margin matcher
Closes #1074
2017-11-10 18:33:00 +01:00
Phil Nash
24e6d5fa33 Fixed release notes mention of *_THROWS_MATCHES
As reported in #1088
2017-11-09 11:09:17 +01:00
Martin Hořeňovský
13370bddf2 Revert "Move <ctime> include out of line"
This reverts commit 36f02d76d6.
2017-11-08 08:31:48 +01:00
Martin Hořeňovský
36f02d76d6 Move <ctime> include out of line 2017-11-07 21:59:52 +01:00
Phil Nash
07ac9b92e4 Updated badges (again) for Catch2 2017-11-07 18:50:55 +00:00
Phil Nash
0d3fc59f6d Added missing <memory> include 2017-11-07 18:48:57 +00:00
Phil Nash
56e1075613 Introduced ReusableStringStream and removed all uses of std::ostringstream from the main path
ReusableStringStream holds a std::ostringstream internally, but only exposes the ostream interface.
It caches a pool of ostringstreams in a vector which is currently global, but will be made thread-local.

Altogether this should enable both runtime and compile-time benefits. although more work is needed to realise the compile time opportunities.
2017-11-07 18:01:10 +00:00
Phil Nash
868e125d49 Moved a lot of stream related stuff out of the public headers and replaced more ostream dependencies with iosfwd 2017-11-07 15:55:09 +00:00
Phil Nash
c9cdb9a48f Tweaked logo 2017-11-07 11:41:49 +00:00
Phil Nash
5fd1d7174c Added link to Catch2 blog post 2017-11-07 11:27:01 +00:00
Phil Nash
3a4c765030 Refreshed artwork for Catch2 2017-11-07 11:25:13 +00:00
Martin Hořeňovský
a20b286999 Improve travis.yml
- Added new compilers and OS X images
- Option to run SelfTest under Valgrind
- Merge "Debug" and "Release" configurations into one run
-- This saves apt setup and cmake download step per compiler, 60-90s
- Fix C++14 compilation under Clang 3.8 and up
2017-11-07 11:24:18 +01:00
Daniel Doubleday
e28763ad05 Fix platform detection for iOS 2017-11-06 10:08:22 +01:00
Martin Hořeňovský
b2dd48f0c0 Cleanup travis.yml: remove duplicate compilers, remove unused flag 2017-11-05 15:58:12 +01:00
Martin Hořeňovský
7a562d39b2 Cleanup CMakelists.txt 2017-11-05 14:15:03 +01:00
Martin Moene
fa9c4207f1 Replace include_directories() with target_include_directories()
to prevent inheritance of include directories that possibly lead to a clash.

A clash occurs when a folder is included, e.g. examples, that wants to use the single-include directory instead of the normal include directory as used by the SelfTest in the next higher level.
2017-11-04 22:08:56 +01:00
Martin Hořeňovský
4f9123dc20 Remove self-include in header 2017-11-03 22:34:49 +01:00
Phil Nash
19ab2117c5 Remove spurious test following merge 2017-11-03 18:09:55 +00:00
Phil Nash
4acf112c19 Removed zombie files
These files were removed from the Catch2 branch, and crept back in when Catch2 merged with master
2017-11-03 16:56:11 +00:00
Phil Nash
53f6d3fc8e Locked release notes reference to v2.0.1 release 2017-11-03 13:31:59 +00:00
Phil Nash
cf76a795cc Added note about Catch2 to readme 2017-11-03 13:18:26 +00:00
Phil Nash
811f4d13d7 Updated links in readme 2017-11-03 13:15:22 +00:00
Phil Nash
7423a481eb Updated some Catch references to Catch2 2017-11-03 13:05:09 +00:00
Phil Nash
46c7c9d3a0 Merge branch 'catch2' 2017-11-03 12:05:38 +00:00
Phil Nash
b119ebdde1 v2.0.1 release 2017-11-03 12:01:52 +00:00
Phil Nash
1c43fb64c1 Added docs for extending command line with Clara 2017-11-02 18:01:24 +00:00
Phil Nash
8b40c26434 Removed handling of start-up exceptions from custom main docs 2017-11-02 18:01:24 +00:00
Phil Nash
fe05062f9e Print any start-up exceptions in Session's constructor, so custom main's don't need to worry about them 2017-11-02 17:58:07 +00:00
Martin Hořeňovský
31cc62e6b7 Updated release notes with Approx changes 2017-11-01 22:25:17 +01:00
Martin Hořeňovský
a49e6fdc27 Update Approx documentation 2017-11-01 13:45:21 +01:00
Pfiffikus
2d91035404 Update assertions.md
scale more detailed explained; have to be adapted to PR #1068 if necessary
2017-11-01 13:32:08 +01:00
Martin Hořeňovský
accf9859b4 Add OSX specific INFINITE macro parsing in approval tests 2017-11-01 08:46:49 +01:00
Martin Hořeňovský
22ac9d2184 Approx cleanup: More tests, INFINITY handling, etc 2017-11-01 07:30:11 +01:00
Pfiffikus
00af677577 Approx rework: default scale == 0, epsilon applies to Approx::value
Also adds check to Approx::epsilon that the new epsilon has a valid
(ie one between 0 and 1)

Based on
http://realtimecollisiondetection.net/blog/?p=89
https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
https://en.wikipedia.org/wiki/Approximation_error#Formal_Definition

The given epsilon should refer to the target value, otherwise
the result would be unexpected, e.g. 101.02 == Approx(100).epsilon(0.01)
is true.
The default scale should be invisible, thus,
e.g. 101.01 == Approx(100).epsilon(0.01) gets false.
Finally even 101.000001 == Approx(100).epsilon(0.01) is false
2017-10-31 15:43:42 +01:00
Martin Hořeňovský
2a3606f8e3 v1.11.0 2017-10-31 13:55:48 +01:00
Martin Hořeňovský
a6cf19abff Make Approx::margin inclusive
Fixes #952, related to #980
2017-10-30 21:33:29 +01:00
Pfiffikus
06586b7180 Update test-cases-and-sections.md
some clarification and typo correction
2017-10-26 13:57:18 +02:00
Clare Macrae
93b3d2cb8f Fix very minor typo
it's -> its
2017-10-24 20:00:27 +02:00
Pfiffikus
a90473df28 Update build-systems.md
typo correction
2017-10-24 19:59:59 +02:00
Sebastian Grottel
c9d9699ca8 adds flushes to the output stream of teamcity reporter, making the test output more responsive. 2017-10-17 16:42:05 +02:00
Sebastian Grottel
296955c437 RandomNumberGenerator::result_type should be unsigned (#1050)
`result_type` must be unsigned:
http://en.cppreference.com/w/cpp/concept/UniformRandomBitGenerator

Using a signed type causes an infinite loop working with MS Visual Studio 2017, targetting: v140, WindowsTargetPlatformVersion 10.0.15063.0, Debug, x64
2017-10-15 18:30:40 +02:00
dvirtz
664cbf702c added PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS option 2017-10-15 17:58:39 +02:00
Martin Hořeňovský
fb6700df54 Fix documentation crosslink in configuration.md 2017-10-14 08:36:44 +02:00
Phil Nash
da6c2a6914 Fixed expansion of _FALSE binary expression
- see #1051
2017-10-13 19:44:20 +01:00
Dmitry Kozhevnikov
9c07718b5f Update handling of __JETBRAINS_IDE__ macro
1. Use it to conditionally define CATCH_INTERNAL_CONFIG_COUNTER, not
   CATCH_CONFIG_COUNTER, as __JETBRAINS_IDE__ is similar to
   compiler-provided macros, not to user-provided ones.

2. Since __COUNTER__ will work starting with CLion 2017.3, use it
   when possible (and hopefully remove this check altogether
   at some point).
2017-09-07 18:00:04 +02:00
solvingj
5ca44b6872 Minor - added header-only flag in conan
See header-only guidelines: 
http://conanio.readthedocs.io/en/latest/howtos/header_only.html?highlight=header%20only
Its borderline cosmetic, but it does have a purpose.
2017-08-28 12:18:54 +02:00
Sam Bristow
a04bd6d436 Remove duplicate CLI option
The "use-colour" option was accidentally duplicated as part of commit
feaf355 (Implemented libidentify support).
2017-08-28 12:16:23 +02:00
Martin Hořeňovský
784f6dfb34 Fix updateVcpkgPackage 2017-08-27 11:43:55 +02:00
Martin Hořeňovský
7818e2666d v1.10.0 2017-08-26 15:34:18 +02:00
Martin Hořeňovský
cd30dd1a70 Workaround raw string literal bug in VS2017 2017-08-26 15:14:27 +02:00
Phil Nash
8e8c0c1675 Tweaked how failedButOk assertions are recorded
- fixes issue where sections in !shouldfail or !mayfail test cases that have failing assertions where marked as failed instead of failedButOk
2017-08-25 11:37:49 +01:00
Phil Nash
b6e7c9bd7a Specialise removeConst for nullptr 2017-08-24 23:07:44 +02:00
Phil Nash
180d9242f5 Suppress more signed/ unsigned mismatches during Evaluator calls on MSVC 2017-08-24 23:07:03 +02:00
Phil Nash
b7bd52cc98 Cherry-picked "evaluate" refactoring from dev-modernize branch
- fixed up NULL comparisons to allow for NULL being a long
- should address #981
2017-08-24 23:07:03 +02:00
Martin Hořeňovský
b07a2bdf87 Refactor release scripts, automatically update Wandbox on release 2017-08-24 21:59:06 +02:00
Martin Hořeňovský
c03e8fce92 Explicitly ignore return value of getchar
This silences MSVC warning about ignored return value
2017-08-22 22:06:37 +02:00
Phil Nash
27640a5a96 Added Clara and TextFlowCpp to open source users 2017-08-17 10:49:56 +01:00
Phil Nash
dd3867bbcd Create CODE_OF_CONDUCT.md 2017-08-17 07:45:12 +01:00
Phil Nash
387f8d254d Removed unnecessary single quotes 2017-08-15 19:41:46 +01:00
Phil Nash
c65eccd68e Added --libidentify and --wait-for-keypress to docs 2017-08-15 19:39:38 +01:00
Phil Nash
61c5675c11 Removed inadvertent use of auto merged from dev-modernise 2017-08-15 19:34:10 +01:00
Phil Nash
70e4af9d44 Implemented wait-for-keypress option 2017-08-15 14:12:11 +01:00
Monocasual
8f41bdb92d Add open-source user 2017-08-13 17:55:50 +02:00
Phil Nash
7fa5d9ca94 Removed redundant processName argument from libIdentify call 2017-08-11 22:03:09 +01:00
Phil Nash
feaf355489 Implemented libidentify support
- see https://github.com/janwilmans/LibIdentify
2017-08-11 19:55:55 +01:00
Martin Hořeňovský
2ce6c74f8f v1.9.7 2017-08-11 00:01:20 +02:00
Phil Nash
9688891868 Fix issue with fatal errors and non-failing assertions
Fixes #990
2017-08-10 21:44:54 +02:00
Martin Hořeňovský
4f21bb72ff Add tests for #961 2017-08-10 21:38:07 +02:00
Martin Hořeňovský
b435e0d7c7 Make default reporter configurable at compile time
Closes #978
2017-08-10 16:45:38 +02:00
Martin Hořeňovský
ba0a09fd9e Update documentation with changes from 7e4038d 2017-08-10 16:43:17 +02:00
165 changed files with 15853 additions and 8672 deletions

6
.gitignore vendored
View File

@@ -24,6 +24,6 @@ DerivedData
*.xccheckout
Build
.idea
cmake-build-debug
cmake-build-release
.vs
.vs
cmake-build-*
benchmark-dir

View File

@@ -1,207 +1,187 @@
language: cpp
sudo: false
common_sources: &all_sources
- ubuntu-toolchain-r-test
- llvm-toolchain-trusty
- llvm-toolchain-trusty-3.9
- llvm-toolchain-trusty-4.0
- llvm-toolchain-trusty-5.0
matrix:
include:
# 1/ Linux Clang Builds
- os: linux
compiler: clang
addons: &clang35
addons:
apt:
sources: ['llvm-toolchain-precise-3.5', 'ubuntu-toolchain-r-test']
packages: ['clang-3.5']
env: COMPILER='clang++-3.5' BUILD_TYPE='Release'
sources: *all_sources
packages: ['valgrind', 'lcov', 'clang-3.5']
env: COMPILER='clang++-3.5' VALGRIND=1
- os: linux
compiler: clang
addons: *clang35
env: COMPILER='clang++-3.5' BUILD_TYPE='Debug'
- os: linux
compiler: clang
addons: &clang36
addons:
apt:
sources: ['llvm-toolchain-precise-3.6', 'ubuntu-toolchain-r-test']
packages: ['clang-3.6']
env: COMPILER='clang++-3.6' BUILD_TYPE='Release'
sources: *all_sources
packages: ['valgrind', 'lcov', 'clang-3.6']
env: COMPILER='clang++-3.6' VALGRIND=1
# Travis's containers do not seem to have Clang 3.7 in apt, no matter what sources I add.
# - os: linux
# compiler: clang
# addons:
# apt:
# sources: *all_sources
# packages: ['valgrind', 'clang-3.7']
# env: COMPILER='clang++-3.7' VALGRIND=1
- os: linux
compiler: clang
addons: *clang36
env: COMPILER='clang++-3.6' BUILD_TYPE='Debug'
- os: linux
compiler: clang
addons: &clang37
addons:
apt:
sources: ['llvm-toolchain-precise-3.7', 'ubuntu-toolchain-r-test']
packages: ['clang-3.7']
env: COMPILER='clang++-3.7' BUILD_TYPE='Release'
sources: *all_sources
packages: ['valgrind', 'lcov', 'clang-3.8']
env: COMPILER='clang++-3.8' VALGRIND=1
- os: linux
compiler: clang
addons: *clang37
env: COMPILER='clang++-3.7' BUILD_TYPE='Debug'
addons:
apt:
sources: *all_sources
packages: ['clang-3.9', 'valgrind', 'lcov']
env: COMPILER='clang++-3.9' VALGRIND=1
- os: linux
compiler: clang
addons: &clang38
apt:
sources: ['llvm-toolchain-precise-3.8', 'ubuntu-toolchain-r-test']
packages: ['clang-3.8']
env: COMPILER='clang++-3.8' BUILD_TYPE='Release'
addons:
apt:
sources: *all_sources
packages: ['clang-4.0', 'valgrind', 'lcov']
env: COMPILER='clang++-4.0' VALGRIND=1
- os: linux
compiler: clang
addons: *clang38
env: COMPILER='clang++-3.8' BUILD_TYPE='Debug'
addons:
apt:
sources: *all_sources
packages: ['clang-5.0', 'valgrind', 'lcov']
env: COMPILER='clang++-5.0' VALGRIND=1
# 2/ Linux GCC Builds
- os: linux
compiler: gcc
addons: &gcc48
addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['g++-4.8']
env: COMPILER='g++-4.8' BUILD_TYPE='Release'
packages: ['valgrind', 'lcov', 'g++-4.8']
env: COMPILER='g++-4.8' VALGRIND=1
- os: linux
compiler: gcc
addons: *gcc48
env: COMPILER='g++-4.8' BUILD_TYPE='Debug'
- os: linux
compiler: gcc
addons: &gcc49
addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['g++-4.9']
env: COMPILER='g++-4.9' BUILD_TYPE='Release'
sources: *all_sources
packages: ['valgrind', 'lcov', 'g++-4.9']
env: COMPILER='g++-4.9' VALGRIND=1
- os: linux
compiler: gcc
addons: *gcc49
env: COMPILER='g++-4.9' BUILD_TYPE='Debug'
- os: linux
compiler: gcc
addons: &gcc5
addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['g++-5']
env: COMPILER='g++-5' BUILD_TYPE='Release'
- os: linux
compiler: gcc
addons: *gcc5
env: COMPILER='g++-5' BUILD_TYPE='Debug'
sources: *all_sources
packages: ['valgrind', 'lcov', 'g++-5']
env: COMPILER='g++-5' VALGRIND=1
- os: linux
compiler: gcc
addons: &gcc6
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['g++-6']
env: COMPILER='g++-6' BUILD_TYPE='Release'
sources: *all_sources
packages: ['valgrind', 'lcov', 'g++-6']
env: COMPILER='g++-6' VALGRIND=1
- os: linux
compiler: gcc
addons: *gcc6
env: COMPILER='g++-6' BUILD_TYPE='Debug'
addons: &gcc7
apt:
sources: *all_sources
packages: ['valgrind', 'lcov', 'g++-7']
env: COMPILER='g++-7' VALGRIND=1
# 3a/ Linux C++11 GCC builds
- os: linux
compiler: gcc
addons: *gcc48
env: COMPILER='g++-4.8' BUILD_TYPE='Release' CPP11=1
- os: linux
compiler: gcc
addons: *gcc48
env: COMPILER='g++-4.8' BUILD_TYPE='Debug' CPP11=1
# 3b/ Linux C++11 Clang builds
# 3b/ Linux C++14 Clang builds
- os: linux
compiler: clang
addons: *clang38
env: COMPILER='clang++-3.8' BUILD_TYPE='Release' CPP11=1
addons:
apt:
packages: ['clang-3.8', 'valgrind', 'lcov', 'libstdc++-6-dev']
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-trusty
env: COMPILER='clang++-3.8' CPP14=1 VALGRIND=1
- os: linux
compiler: clang
addons: *clang38
env: COMPILER='clang++-3.8' BUILD_TYPE='Debug' CPP11=1
addons:
apt:
sources: *all_sources
packages: ['clang-3.9', 'valgrind', 'lcov', 'libstdc++-6-dev']
env: COMPILER='clang++-3.9' CPP14=1 VALGRIND=1
- os: linux
compiler: clang
addons:
apt:
sources: *all_sources
packages: ['clang-4.0', 'valgrind', 'lcov', 'libstdc++-6-dev']
env: COMPILER='clang++-4.0' CPP14=1 VALGRIND=1
- os: linux
compiler: clang
addons:
apt:
sources: *all_sources
packages: ['clang-5.0', 'valgrind', 'lcov', 'libstdc++-6-dev']
env: COMPILER='clang++-5.0' CPP14=1 VALGRIND=1
# 4a/ Linux C++14 GCC builds
- os: linux
compiler: gcc
addons: *gcc6
env: COMPILER='g++-6' BUILD_TYPE='Release' CPP14=1
env: COMPILER='g++-6' CPP14=1 VALGRIND=1
- os: linux
compiler: gcc
addons: *gcc6
env: COMPILER='g++-6' BUILD_TYPE='Debug' CPP14=1
# # 4b/ Linux C++14 Clang builds
# - os: linux
# compiler: clang
# addons: *clang38
# env: COMPILER='clang++-3.8' BUILD_TYPE='Release' CPP14=1
#
# - os: linux
# compiler: clang
# addons: *clang38
# env: COMPILER='clang++-3.8' BUILD_TYPE='Debug' CPP14=1
addons: *gcc7
env: COMPILER='g++-7' CPP14=1 VALGRIND=1
# 5/ OSX Clang Builds
- os: osx
osx_image: xcode7.3
compiler: clang
env: COMPILER='clang++' BUILD_TYPE='Debug'
- os: osx
osx_image: xcode7.3
compiler: clang
env: COMPILER='clang++' BUILD_TYPE='Release'
env: COMPILER='clang++'
- os: osx
osx_image: xcode8
compiler: clang
env: COMPILER='clang++' BUILD_TYPE='Debug'
env: COMPILER='clang++'
- os: osx
osx_image: xcode8
osx_image: xcode9
compiler: clang
env: COMPILER='clang++' BUILD_TYPE='Release'
env: COMPILER='clang++'
- os: osx
osx_image: xcode8
osx_image: xcode9.1
compiler: clang
env: COMPILER='clang++' BUILD_TYPE='Debug' USE_CPP11=1
env: COMPILER='clang++'
- os: osx
osx_image: xcode8
osx_image: xcode9.1
compiler: clang
env: COMPILER='clang++' BUILD_TYPE='Release' USE_CPP11=1
- os: osx
osx_image: xcode8
compiler: clang
env: COMPILER='clang++' BUILD_TYPE='Debug' USE_CPP14=1
- os: osx
osx_image: xcode8
compiler: clang
env: COMPILER='clang++' BUILD_TYPE='Release' USE_CPP14=1
env: COMPILER='clang++' USE_CPP14=1
install:
@@ -213,15 +193,34 @@ install:
mkdir cmake && travis_retry wget --no-check-certificate --quiet -O - ${CMAKE_URL} | tar --strip-components=1 -xz -C cmake
export PATH=${DEPS_DIR}/cmake/bin:${PATH}
elif [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then
which cmake || brew install cmake
which cmake || brew install cmake;
fi
before_script:
- export CXX=${COMPILER}
- cd ${TRAVIS_BUILD_DIR}
- cmake -H. -BBuild -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -Wdev -DUSE_CPP11=${CPP11} -DUSE_CPP14=${CPP14}
- cd Build
# Regenerate single header file, so it is tested in the examples...
- python scripts/generateSingleHeader.py
- |
# Use Debug builds for running Valgrind and building examples
cmake -H. -BBuild-Debug -DCMAKE_BUILD_TYPE=Debug -Wdev -DUSE_CPP14=${CPP14} -DUSE_VALGRIND=${VALGRIND} -DBUILD_EXAMPLES=ON -DENABLE_COVERAGE=ON
# Don't bother with release build for coverage build
cmake -H. -BBuild-Release -DCMAKE_BUILD_TYPE=Release -Wdev -DUSE_CPP14=${CPP14}
script:
- make -j 2
- ctest -V -j 2
- |
cd Build-Debug
make -j 2
CTEST_OUTPUT_ON_FAILURE=1 ctest -j 2
# Coverage collection does not work for OS X atm
if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then
make gcov
make lcov
bash <(curl -s https://codecov.io/bash) -X gcov || echo "Codecov did not collect coverage reports"
fi
# Go to release build
cd ../Build-Release
make -j 2
CTEST_OUTPUT_ON_FAILURE=1 ctest -j 2

157
CMake/FindGcov.cmake Normal file
View File

@@ -0,0 +1,157 @@
# This file is part of CMake-codecov.
#
# Copyright (c)
# 2015-2017 RWTH Aachen University, Federal Republic of Germany
#
# See the LICENSE file in the package base directory for details
#
# Written by Alexander Haase, alexander.haase@rwth-aachen.de
#
# include required Modules
include(FindPackageHandleStandardArgs)
# Search for gcov binary.
set(CMAKE_REQUIRED_QUIET_SAVE ${CMAKE_REQUIRED_QUIET})
set(CMAKE_REQUIRED_QUIET ${codecov_FIND_QUIETLY})
get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
foreach (LANG ${ENABLED_LANGUAGES})
# Gcov evaluation is dependend on the used compiler. Check gcov support for
# each compiler that is used. If gcov binary was already found for this
# compiler, do not try to find it again.
if (NOT GCOV_${CMAKE_${LANG}_COMPILER_ID}_BIN)
get_filename_component(COMPILER_PATH "${CMAKE_${LANG}_COMPILER}" PATH)
if ("${CMAKE_${LANG}_COMPILER_ID}" STREQUAL "GNU")
# Some distributions like OSX (homebrew) ship gcov with the compiler
# version appended as gcov-x. To find this binary we'll build the
# suggested binary name with the compiler version.
string(REGEX MATCH "^[0-9]+" GCC_VERSION
"${CMAKE_${LANG}_COMPILER_VERSION}")
find_program(GCOV_BIN NAMES gcov-${GCC_VERSION} gcov
HINTS ${COMPILER_PATH})
elseif ("${CMAKE_${LANG}_COMPILER_ID}" STREQUAL "Clang")
# Some distributions like Debian ship llvm-cov with the compiler
# version appended as llvm-cov-x.y. To find this binary we'll build
# the suggested binary name with the compiler version.
string(REGEX MATCH "^[0-9]+.[0-9]+" LLVM_VERSION
"${CMAKE_${LANG}_COMPILER_VERSION}")
# llvm-cov prior version 3.5 seems to be not working with coverage
# evaluation tools, but these versions are compatible with the gcc
# gcov tool.
if(LLVM_VERSION VERSION_GREATER 3.4)
find_program(LLVM_COV_BIN NAMES "llvm-cov-${LLVM_VERSION}"
"llvm-cov" HINTS ${COMPILER_PATH})
mark_as_advanced(LLVM_COV_BIN)
if (LLVM_COV_BIN)
find_program(LLVM_COV_WRAPPER "llvm-cov-wrapper" PATHS
${CMAKE_MODULE_PATH})
if (LLVM_COV_WRAPPER)
set(GCOV_BIN "${LLVM_COV_WRAPPER}" CACHE FILEPATH "")
# set additional parameters
set(GCOV_${CMAKE_${LANG}_COMPILER_ID}_ENV
"LLVM_COV_BIN=${LLVM_COV_BIN}" CACHE STRING
"Environment variables for llvm-cov-wrapper.")
mark_as_advanced(GCOV_${CMAKE_${LANG}_COMPILER_ID}_ENV)
endif ()
endif ()
endif ()
if (NOT GCOV_BIN)
# Fall back to gcov binary if llvm-cov was not found or is
# incompatible. This is the default on OSX, but may crash on
# recent Linux versions.
find_program(GCOV_BIN gcov HINTS ${COMPILER_PATH})
endif ()
endif ()
if (GCOV_BIN)
set(GCOV_${CMAKE_${LANG}_COMPILER_ID}_BIN "${GCOV_BIN}" CACHE STRING
"${LANG} gcov binary.")
if (NOT CMAKE_REQUIRED_QUIET)
message("-- Found gcov evaluation for "
"${CMAKE_${LANG}_COMPILER_ID}: ${GCOV_BIN}")
endif()
unset(GCOV_BIN CACHE)
endif ()
endif ()
endforeach ()
# Add a new global target for all gcov targets. This target could be used to
# generate the gcov files for the whole project instead of calling <TARGET>-gcov
# for each target.
if (NOT TARGET gcov)
add_custom_target(gcov)
endif (NOT TARGET gcov)
# This function will add gcov evaluation for target <TNAME>. Only sources of
# this target will be evaluated and no dependencies will be added. It will call
# Gcov on any source file of <TNAME> once and store the gcov file in the same
# directory.
function (add_gcov_target TNAME)
set(TDIR ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${TNAME}.dir)
# We don't have to check, if the target has support for coverage, thus this
# will be checked by add_coverage_target in Findcoverage.cmake. Instead we
# have to determine which gcov binary to use.
get_target_property(TSOURCES ${TNAME} SOURCES)
set(SOURCES "")
set(TCOMPILER "")
foreach (FILE ${TSOURCES})
codecov_path_of_source(${FILE} FILE)
if (NOT "${FILE}" STREQUAL "")
codecov_lang_of_source(${FILE} LANG)
if (NOT "${LANG}" STREQUAL "")
list(APPEND SOURCES "${FILE}")
set(TCOMPILER ${CMAKE_${LANG}_COMPILER_ID})
endif ()
endif ()
endforeach ()
# If no gcov binary was found, coverage data can't be evaluated.
if (NOT GCOV_${TCOMPILER}_BIN)
message(WARNING "No coverage evaluation binary found for ${TCOMPILER}.")
return()
endif ()
set(GCOV_BIN "${GCOV_${TCOMPILER}_BIN}")
set(GCOV_ENV "${GCOV_${TCOMPILER}_ENV}")
set(BUFFER "")
foreach(FILE ${SOURCES})
get_filename_component(FILE_PATH "${TDIR}/${FILE}" PATH)
# call gcov
add_custom_command(OUTPUT ${TDIR}/${FILE}.gcov
COMMAND ${GCOV_ENV} ${GCOV_BIN} ${TDIR}/${FILE}.gcno > /dev/null
DEPENDS ${TNAME} ${TDIR}/${FILE}.gcno
WORKING_DIRECTORY ${FILE_PATH}
)
list(APPEND BUFFER ${TDIR}/${FILE}.gcov)
endforeach()
# add target for gcov evaluation of <TNAME>
add_custom_target(${TNAME}-gcov DEPENDS ${BUFFER})
# add evaluation target to the global gcov target.
add_dependencies(gcov ${TNAME}-gcov)
endfunction (add_gcov_target)

354
CMake/FindLcov.cmake Normal file
View File

@@ -0,0 +1,354 @@
# This file is part of CMake-codecov.
#
# Copyright (c)
# 2015-2017 RWTH Aachen University, Federal Republic of Germany
#
# See the LICENSE file in the package base directory for details
#
# Written by Alexander Haase, alexander.haase@rwth-aachen.de
#
# configuration
set(LCOV_DATA_PATH "${CMAKE_BINARY_DIR}/lcov/data")
set(LCOV_DATA_PATH_INIT "${LCOV_DATA_PATH}/init")
set(LCOV_DATA_PATH_CAPTURE "${LCOV_DATA_PATH}/capture")
set(LCOV_HTML_PATH "${CMAKE_BINARY_DIR}/lcov/html")
# Search for Gcov which is used by Lcov.
find_package(Gcov)
# This function will add lcov evaluation for target <TNAME>. Only sources of
# this target will be evaluated and no dependencies will be added. It will call
# geninfo on any source file of <TNAME> once and store the info file in the same
# directory.
#
# Note: This function is only a wrapper to define this function always, even if
# coverage is not supported by the compiler or disabled. This function must
# be defined here, because the module will be exited, if there is no coverage
# support by the compiler or it is disabled by the user.
function (add_lcov_target TNAME)
if (LCOV_FOUND)
# capture initial coverage data
lcov_capture_initial_tgt(${TNAME})
# capture coverage data after execution
lcov_capture_tgt(${TNAME})
endif ()
endfunction (add_lcov_target)
# include required Modules
include(FindPackageHandleStandardArgs)
# Search for required lcov binaries.
find_program(LCOV_BIN lcov)
find_program(GENINFO_BIN geninfo)
find_program(GENHTML_BIN genhtml)
find_package_handle_standard_args(lcov
REQUIRED_VARS LCOV_BIN GENINFO_BIN GENHTML_BIN
)
# enable genhtml C++ demangeling, if c++filt is found.
set(GENHTML_CPPFILT_FLAG "")
find_program(CPPFILT_BIN c++filt)
if (NOT CPPFILT_BIN STREQUAL "")
set(GENHTML_CPPFILT_FLAG "--demangle-cpp")
endif (NOT CPPFILT_BIN STREQUAL "")
# enable no-external flag for lcov, if available.
if (GENINFO_BIN AND NOT DEFINED GENINFO_EXTERN_FLAG)
set(FLAG "")
execute_process(COMMAND ${GENINFO_BIN} --help OUTPUT_VARIABLE GENINFO_HELP)
string(REGEX MATCH "external" GENINFO_RES "${GENINFO_HELP}")
if (GENINFO_RES)
set(FLAG "--no-external")
endif ()
set(GENINFO_EXTERN_FLAG "${FLAG}"
CACHE STRING "Geninfo flag to exclude system sources.")
endif ()
# If Lcov was not found, exit module now.
if (NOT LCOV_FOUND)
return()
endif (NOT LCOV_FOUND)
# Create directories to be used.
file(MAKE_DIRECTORY ${LCOV_DATA_PATH_INIT})
file(MAKE_DIRECTORY ${LCOV_DATA_PATH_CAPTURE})
set(LCOV_REMOVE_PATTERNS "")
# This function will merge lcov files to a single target file. Additional lcov
# flags may be set with setting LCOV_EXTRA_FLAGS before calling this function.
function (lcov_merge_files OUTFILE ...)
# Remove ${OUTFILE} from ${ARGV} and generate lcov parameters with files.
list(REMOVE_AT ARGV 0)
# Generate merged file.
string(REPLACE "${CMAKE_BINARY_DIR}/" "" FILE_REL "${OUTFILE}")
add_custom_command(OUTPUT "${OUTFILE}.raw"
COMMAND cat ${ARGV} > ${OUTFILE}.raw
DEPENDS ${ARGV}
COMMENT "Generating ${FILE_REL}"
)
add_custom_command(OUTPUT "${OUTFILE}"
COMMAND ${LCOV_BIN} --quiet -a ${OUTFILE}.raw --output-file ${OUTFILE}
--base-directory ${PROJECT_SOURCE_DIR} ${LCOV_EXTRA_FLAGS}
COMMAND ${LCOV_BIN} --quiet -r ${OUTFILE} ${LCOV_REMOVE_PATTERNS}
--output-file ${OUTFILE} ${LCOV_EXTRA_FLAGS}
DEPENDS ${OUTFILE}.raw
COMMENT "Post-processing ${FILE_REL}"
)
endfunction ()
# Add a new global target to generate initial coverage reports for all targets.
# This target will be used to generate the global initial info file, which is
# used to gather even empty report data.
if (NOT TARGET lcov-capture-init)
add_custom_target(lcov-capture-init)
set(LCOV_CAPTURE_INIT_FILES "" CACHE INTERNAL "")
endif (NOT TARGET lcov-capture-init)
# This function will add initial capture of coverage data for target <TNAME>,
# which is needed to get also data for objects, which were not loaded at
# execution time. It will call geninfo for every source file of <TNAME> once and
# store the info file in the same directory.
function (lcov_capture_initial_tgt TNAME)
# We don't have to check, if the target has support for coverage, thus this
# will be checked by add_coverage_target in Findcoverage.cmake. Instead we
# have to determine which gcov binary to use.
get_target_property(TSOURCES ${TNAME} SOURCES)
set(SOURCES "")
set(TCOMPILER "")
foreach (FILE ${TSOURCES})
codecov_path_of_source(${FILE} FILE)
if (NOT "${FILE}" STREQUAL "")
codecov_lang_of_source(${FILE} LANG)
if (NOT "${LANG}" STREQUAL "")
list(APPEND SOURCES "${FILE}")
set(TCOMPILER ${CMAKE_${LANG}_COMPILER_ID})
endif ()
endif ()
endforeach ()
# If no gcov binary was found, coverage data can't be evaluated.
if (NOT GCOV_${TCOMPILER}_BIN)
message(WARNING "No coverage evaluation binary found for ${TCOMPILER}.")
return()
endif ()
set(GCOV_BIN "${GCOV_${TCOMPILER}_BIN}")
set(GCOV_ENV "${GCOV_${TCOMPILER}_ENV}")
set(TDIR ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${TNAME}.dir)
set(GENINFO_FILES "")
foreach(FILE ${SOURCES})
# generate empty coverage files
set(OUTFILE "${TDIR}/${FILE}.info.init")
list(APPEND GENINFO_FILES ${OUTFILE})
add_custom_command(OUTPUT ${OUTFILE} COMMAND ${GCOV_ENV} ${GENINFO_BIN}
--quiet --base-directory ${PROJECT_SOURCE_DIR} --initial
--gcov-tool ${GCOV_BIN} --output-filename ${OUTFILE}
${GENINFO_EXTERN_FLAG} ${TDIR}/${FILE}.gcno
DEPENDS ${TNAME}
COMMENT "Capturing initial coverage data for ${FILE}"
)
endforeach()
# Concatenate all files generated by geninfo to a single file per target.
set(OUTFILE "${LCOV_DATA_PATH_INIT}/${TNAME}.info")
set(LCOV_EXTRA_FLAGS "--initial")
lcov_merge_files("${OUTFILE}" ${GENINFO_FILES})
add_custom_target(${TNAME}-capture-init ALL DEPENDS ${OUTFILE})
# add geninfo file generation to global lcov-geninfo target
add_dependencies(lcov-capture-init ${TNAME}-capture-init)
set(LCOV_CAPTURE_INIT_FILES "${LCOV_CAPTURE_INIT_FILES}"
"${OUTFILE}" CACHE INTERNAL ""
)
endfunction (lcov_capture_initial_tgt)
# This function will generate the global info file for all targets. It has to be
# called after all other CMake functions in the root CMakeLists.txt file, to get
# a full list of all targets that generate coverage data.
function (lcov_capture_initial)
# Skip this function (and do not create the following targets), if there are
# no input files.
if ("${LCOV_CAPTURE_INIT_FILES}" STREQUAL "")
return()
endif ()
# Add a new target to merge the files of all targets.
set(OUTFILE "${LCOV_DATA_PATH_INIT}/all_targets.info")
lcov_merge_files("${OUTFILE}" ${LCOV_CAPTURE_INIT_FILES})
add_custom_target(lcov-geninfo-init ALL DEPENDS ${OUTFILE}
lcov-capture-init
)
endfunction (lcov_capture_initial)
# Add a new global target to generate coverage reports for all targets. This
# target will be used to generate the global info file.
if (NOT TARGET lcov-capture)
add_custom_target(lcov-capture)
set(LCOV_CAPTURE_FILES "" CACHE INTERNAL "")
endif (NOT TARGET lcov-capture)
# This function will add capture of coverage data for target <TNAME>, which is
# needed to get also data for objects, which were not loaded at execution time.
# It will call geninfo for every source file of <TNAME> once and store the info
# file in the same directory.
function (lcov_capture_tgt TNAME)
# We don't have to check, if the target has support for coverage, thus this
# will be checked by add_coverage_target in Findcoverage.cmake. Instead we
# have to determine which gcov binary to use.
get_target_property(TSOURCES ${TNAME} SOURCES)
set(SOURCES "")
set(TCOMPILER "")
foreach (FILE ${TSOURCES})
codecov_path_of_source(${FILE} FILE)
if (NOT "${FILE}" STREQUAL "")
codecov_lang_of_source(${FILE} LANG)
if (NOT "${LANG}" STREQUAL "")
list(APPEND SOURCES "${FILE}")
set(TCOMPILER ${CMAKE_${LANG}_COMPILER_ID})
endif ()
endif ()
endforeach ()
# If no gcov binary was found, coverage data can't be evaluated.
if (NOT GCOV_${TCOMPILER}_BIN)
message(WARNING "No coverage evaluation binary found for ${TCOMPILER}.")
return()
endif ()
set(GCOV_BIN "${GCOV_${TCOMPILER}_BIN}")
set(GCOV_ENV "${GCOV_${TCOMPILER}_ENV}")
set(TDIR ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${TNAME}.dir)
set(GENINFO_FILES "")
foreach(FILE ${SOURCES})
# Generate coverage files. If no .gcda file was generated during
# execution, the empty coverage file will be used instead.
set(OUTFILE "${TDIR}/${FILE}.info")
list(APPEND GENINFO_FILES ${OUTFILE})
add_custom_command(OUTPUT ${OUTFILE}
COMMAND test -f "${TDIR}/${FILE}.gcda"
&& ${GCOV_ENV} ${GENINFO_BIN} --quiet --base-directory
${PROJECT_SOURCE_DIR} --gcov-tool ${GCOV_BIN}
--output-filename ${OUTFILE} ${GENINFO_EXTERN_FLAG}
${TDIR}/${FILE}.gcda
|| cp ${OUTFILE}.init ${OUTFILE}
DEPENDS ${TNAME} ${TNAME}-capture-init
COMMENT "Capturing coverage data for ${FILE}"
)
endforeach()
# Concatenate all files generated by geninfo to a single file per target.
set(OUTFILE "${LCOV_DATA_PATH_CAPTURE}/${TNAME}.info")
lcov_merge_files("${OUTFILE}" ${GENINFO_FILES})
add_custom_target(${TNAME}-geninfo DEPENDS ${OUTFILE})
# add geninfo file generation to global lcov-capture target
add_dependencies(lcov-capture ${TNAME}-geninfo)
set(LCOV_CAPTURE_FILES "${LCOV_CAPTURE_FILES}" "${OUTFILE}" CACHE INTERNAL
""
)
# Add target for generating html output for this target only.
file(MAKE_DIRECTORY ${LCOV_HTML_PATH}/${TNAME})
add_custom_target(${TNAME}-genhtml
COMMAND ${GENHTML_BIN} --quiet --sort --prefix ${PROJECT_SOURCE_DIR}
--baseline-file ${LCOV_DATA_PATH_INIT}/${TNAME}.info
--output-directory ${LCOV_HTML_PATH}/${TNAME}
--title "${CMAKE_PROJECT_NAME} - target ${TNAME}"
${GENHTML_CPPFILT_FLAG} ${OUTFILE}
DEPENDS ${TNAME}-geninfo ${TNAME}-capture-init
)
endfunction (lcov_capture_tgt)
# This function will generate the global info file for all targets. It has to be
# called after all other CMake functions in the root CMakeLists.txt file, to get
# a full list of all targets that generate coverage data.
function (lcov_capture)
# Skip this function (and do not create the following targets), if there are
# no input files.
if ("${LCOV_CAPTURE_FILES}" STREQUAL "")
return()
endif ()
# Add a new target to merge the files of all targets.
set(OUTFILE "${LCOV_DATA_PATH_CAPTURE}/all_targets.info")
lcov_merge_files("${OUTFILE}" ${LCOV_CAPTURE_FILES})
add_custom_target(lcov-geninfo DEPENDS ${OUTFILE} lcov-capture)
# Add a new global target for all lcov targets. This target could be used to
# generate the lcov html output for the whole project instead of calling
# <TARGET>-geninfo and <TARGET>-genhtml for each target. It will also be
# used to generate a html site for all project data together instead of one
# for each target.
if (NOT TARGET lcov)
file(MAKE_DIRECTORY ${LCOV_HTML_PATH}/all_targets)
add_custom_target(lcov
COMMAND ${GENHTML_BIN} --quiet --sort
--baseline-file ${LCOV_DATA_PATH_INIT}/all_targets.info
--output-directory ${LCOV_HTML_PATH}/all_targets
--title "${CMAKE_PROJECT_NAME}" --prefix "${PROJECT_SOURCE_DIR}"
${GENHTML_CPPFILT_FLAG} ${OUTFILE}
DEPENDS lcov-geninfo-init lcov-geninfo
)
endif ()
endfunction (lcov_capture)
# Add a new global target to generate the lcov html report for the whole project
# instead of calling <TARGET>-genhtml for each target (to create an own report
# for each target). Instead of the lcov target it does not require geninfo for
# all targets, so you have to call <TARGET>-geninfo to generate the info files
# the targets you'd like to have in your report or lcov-geninfo for generating
# info files for all targets before calling lcov-genhtml.
file(MAKE_DIRECTORY ${LCOV_HTML_PATH}/selected_targets)
if (NOT TARGET lcov-genhtml)
add_custom_target(lcov-genhtml
COMMAND ${GENHTML_BIN}
--quiet
--output-directory ${LCOV_HTML_PATH}/selected_targets
--title \"${CMAKE_PROJECT_NAME} - targets `find
${LCOV_DATA_PATH_CAPTURE} -name \"*.info\" ! -name
\"all_targets.info\" -exec basename {} .info \\\;`\"
--prefix ${PROJECT_SOURCE_DIR}
--sort
${GENHTML_CPPFILT_FLAG}
`find ${LCOV_DATA_PATH_CAPTURE} -name \"*.info\" ! -name
\"all_targets.info\"`
)
endif (NOT TARGET lcov-genhtml)

258
CMake/Findcodecov.cmake Normal file
View File

@@ -0,0 +1,258 @@
# This file is part of CMake-codecov.
#
# Copyright (c)
# 2015-2017 RWTH Aachen University, Federal Republic of Germany
#
# See the LICENSE file in the package base directory for details
#
# Written by Alexander Haase, alexander.haase@rwth-aachen.de
#
# Add an option to choose, if coverage should be enabled or not. If enabled
# marked targets will be build with coverage support and appropriate targets
# will be added. If disabled coverage will be ignored for *ALL* targets.
option(ENABLE_COVERAGE "Enable coverage build." OFF)
set(COVERAGE_FLAG_CANDIDATES
# gcc and clang
"-O0 -g -fprofile-arcs -ftest-coverage"
# gcc and clang fallback
"-O0 -g --coverage"
)
# Add coverage support for target ${TNAME} and register target for coverage
# evaluation. If coverage is disabled or not supported, this function will
# simply do nothing.
#
# Note: This function is only a wrapper to define this function always, even if
# coverage is not supported by the compiler or disabled. This function must
# be defined here, because the module will be exited, if there is no coverage
# support by the compiler or it is disabled by the user.
function (add_coverage TNAME)
# only add coverage for target, if coverage is support and enabled.
if (ENABLE_COVERAGE)
foreach (TNAME ${ARGV})
add_coverage_target(${TNAME})
endforeach ()
endif ()
endfunction (add_coverage)
# Add global target to gather coverage information after all targets have been
# added. Other evaluation functions could be added here, after checks for the
# specific module have been passed.
#
# Note: This function is only a wrapper to define this function always, even if
# coverage is not supported by the compiler or disabled. This function must
# be defined here, because the module will be exited, if there is no coverage
# support by the compiler or it is disabled by the user.
function (coverage_evaluate)
# add lcov evaluation
if (LCOV_FOUND)
lcov_capture_initial()
lcov_capture()
endif (LCOV_FOUND)
endfunction ()
# Exit this module, if coverage is disabled. add_coverage is defined before this
# return, so this module can be exited now safely without breaking any build-
# scripts.
if (NOT ENABLE_COVERAGE)
return()
endif ()
# Find the reuired flags foreach language.
set(CMAKE_REQUIRED_QUIET_SAVE ${CMAKE_REQUIRED_QUIET})
set(CMAKE_REQUIRED_QUIET ${codecov_FIND_QUIETLY})
get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
foreach (LANG ${ENABLED_LANGUAGES})
# Coverage flags are not dependend on language, but the used compiler. So
# instead of searching flags foreach language, search flags foreach compiler
# used.
set(COMPILER ${CMAKE_${LANG}_COMPILER_ID})
if (NOT COVERAGE_${COMPILER}_FLAGS)
foreach (FLAG ${COVERAGE_FLAG_CANDIDATES})
if(NOT CMAKE_REQUIRED_QUIET)
message(STATUS "Try ${COMPILER} code coverage flag = [${FLAG}]")
endif()
set(CMAKE_REQUIRED_FLAGS "${FLAG}")
unset(COVERAGE_FLAG_DETECTED CACHE)
if (${LANG} STREQUAL "C")
include(CheckCCompilerFlag)
check_c_compiler_flag("${FLAG}" COVERAGE_FLAG_DETECTED)
elseif (${LANG} STREQUAL "CXX")
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag("${FLAG}" COVERAGE_FLAG_DETECTED)
elseif (${LANG} STREQUAL "Fortran")
# CheckFortranCompilerFlag was introduced in CMake 3.x. To be
# compatible with older Cmake versions, we will check if this
# module is present before we use it. Otherwise we will define
# Fortran coverage support as not available.
include(CheckFortranCompilerFlag OPTIONAL
RESULT_VARIABLE INCLUDED)
if (INCLUDED)
check_fortran_compiler_flag("${FLAG}"
COVERAGE_FLAG_DETECTED)
elseif (NOT CMAKE_REQUIRED_QUIET)
message("-- Performing Test COVERAGE_FLAG_DETECTED")
message("-- Performing Test COVERAGE_FLAG_DETECTED - Failed"
" (Check not supported)")
endif ()
endif()
if (COVERAGE_FLAG_DETECTED)
set(COVERAGE_${COMPILER}_FLAGS "${FLAG}"
CACHE STRING "${COMPILER} flags for code coverage.")
mark_as_advanced(COVERAGE_${COMPILER}_FLAGS)
break()
else ()
message(WARNING "Code coverage is not available for ${COMPILER}"
" compiler. Targets using this compiler will be "
"compiled without it.")
endif ()
endforeach ()
endif ()
endforeach ()
set(CMAKE_REQUIRED_QUIET ${CMAKE_REQUIRED_QUIET_SAVE})
# Helper function to get the language of a source file.
function (codecov_lang_of_source FILE RETURN_VAR)
get_filename_component(FILE_EXT "${FILE}" EXT)
string(TOLOWER "${FILE_EXT}" FILE_EXT)
string(SUBSTRING "${FILE_EXT}" 1 -1 FILE_EXT)
get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
foreach (LANG ${ENABLED_LANGUAGES})
list(FIND CMAKE_${LANG}_SOURCE_FILE_EXTENSIONS "${FILE_EXT}" TEMP)
if (NOT ${TEMP} EQUAL -1)
set(${RETURN_VAR} "${LANG}" PARENT_SCOPE)
return()
endif ()
endforeach()
set(${RETURN_VAR} "" PARENT_SCOPE)
endfunction ()
# Helper function to get the relative path of the source file destination path.
# This path is needed by FindGcov and FindLcov cmake files to locate the
# captured data.
function (codecov_path_of_source FILE RETURN_VAR)
string(REGEX MATCH "TARGET_OBJECTS:([^ >]+)" _source ${FILE})
# If expression was found, SOURCEFILE is a generator-expression for an
# object library. Currently we found no way to call this function automatic
# for the referenced target, so it must be called in the directoryso of the
# object library definition.
if (NOT "${_source}" STREQUAL "")
set(${RETURN_VAR} "" PARENT_SCOPE)
return()
endif ()
string(REPLACE "${CMAKE_CURRENT_BINARY_DIR}/" "" FILE "${FILE}")
if(IS_ABSOLUTE ${FILE})
file(RELATIVE_PATH FILE ${CMAKE_CURRENT_SOURCE_DIR} ${FILE})
endif()
# get the right path for file
string(REPLACE ".." "__" PATH "${FILE}")
set(${RETURN_VAR} "${PATH}" PARENT_SCOPE)
endfunction()
# Add coverage support for target ${TNAME} and register target for coverage
# evaluation.
function(add_coverage_target TNAME)
# Check if all sources for target use the same compiler. If a target uses
# e.g. C and Fortran mixed and uses different compilers (e.g. clang and
# gfortran) this can trigger huge problems, because different compilers may
# use different implementations for code coverage.
get_target_property(TSOURCES ${TNAME} SOURCES)
set(TARGET_COMPILER "")
set(ADDITIONAL_FILES "")
foreach (FILE ${TSOURCES})
# If expression was found, FILE is a generator-expression for an object
# library. Object libraries will be ignored.
string(REGEX MATCH "TARGET_OBJECTS:([^ >]+)" _file ${FILE})
if ("${_file}" STREQUAL "")
codecov_lang_of_source(${FILE} LANG)
if (LANG)
list(APPEND TARGET_COMPILER ${CMAKE_${LANG}_COMPILER_ID})
list(APPEND ADDITIONAL_FILES "${FILE}.gcno")
list(APPEND ADDITIONAL_FILES "${FILE}.gcda")
endif ()
endif ()
endforeach ()
list(REMOVE_DUPLICATES TARGET_COMPILER)
list(LENGTH TARGET_COMPILER NUM_COMPILERS)
if (NUM_COMPILERS GREATER 1)
message(WARNING "Can't use code coverage for target ${TNAME}, because "
"it will be compiled by incompatible compilers. Target will be "
"compiled without code coverage.")
return()
elseif (NUM_COMPILERS EQUAL 0)
message(WARNING "Can't use code coverage for target ${TNAME}, because "
"it uses an unknown compiler. Target will be compiled without "
"code coverage.")
return()
elseif (NOT DEFINED "COVERAGE_${TARGET_COMPILER}_FLAGS")
# A warning has been printed before, so just return if flags for this
# compiler aren't available.
return()
endif()
# enable coverage for target
set_property(TARGET ${TNAME} APPEND_STRING
PROPERTY COMPILE_FLAGS " ${COVERAGE_${TARGET_COMPILER}_FLAGS}")
set_property(TARGET ${TNAME} APPEND_STRING
PROPERTY LINK_FLAGS " ${COVERAGE_${TARGET_COMPILER}_FLAGS}")
# Add gcov files generated by compiler to clean target.
set(CLEAN_FILES "")
foreach (FILE ${ADDITIONAL_FILES})
codecov_path_of_source(${FILE} FILE)
list(APPEND CLEAN_FILES "CMakeFiles/${TNAME}.dir/${FILE}")
endforeach()
set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES
"${CLEAN_FILES}")
add_gcov_target(${TNAME})
add_lcov_target(${TNAME})
endfunction(add_coverage_target)
# Include modules for parsing the collected data and output it in a readable
# format (like gcov and lcov).
find_package(Gcov)
find_package(Lcov)

56
CMake/llvm-cov-wrapper Executable file
View File

@@ -0,0 +1,56 @@
#!/bin/sh
# This file is part of CMake-codecov.
#
# Copyright (c)
# 2015-2017 RWTH Aachen University, Federal Republic of Germany
#
# See the LICENSE file in the package base directory for details
#
# Written by Alexander Haase, alexander.haase@rwth-aachen.de
#
if [ -z "$LLVM_COV_BIN" ]
then
echo "LLVM_COV_BIN not set!" >& 2
exit 1
fi
# Get LLVM version to find out.
LLVM_VERSION=$($LLVM_COV_BIN -version | grep -i "LLVM version" \
| sed "s/^\([A-Za-z ]*\)\([0-9]\).\([0-9]\).*$/\2.\3/g")
if [ "$1" = "-v" ]
then
echo "llvm-cov-wrapper $LLVM_VERSION"
exit 0
fi
if [ -n "$LLVM_VERSION" ]
then
MAJOR=$(echo $LLVM_VERSION | cut -d'.' -f1)
MINOR=$(echo $LLVM_VERSION | cut -d'.' -f2)
if [ $MAJOR -eq 3 ] && [ $MINOR -le 4 ]
then
if [ -f "$1" ]
then
filename=$(basename "$1")
extension="${filename##*.}"
case "$extension" in
"gcno") exec $LLVM_COV_BIN --gcno="$1" ;;
"gcda") exec $LLVM_COV_BIN --gcda="$1" ;;
esac
fi
fi
if [ $MAJOR -eq 3 ] && [ $MINOR -le 5 ]
then
exec $LLVM_COV_BIN $@
fi
fi
exec $LLVM_COV_BIN gcov $@

View File

@@ -2,6 +2,10 @@ cmake_minimum_required(VERSION 3.0)
project(CatchSelfTest)
option(USE_VALGRIND "Perform SelfTests with Valgrind" OFF)
option(BUILD_EXAMPLES "Build documentation examples" OFF)
option(ENABLE_COVERAGE "Generate coverage for codecov.io" OFF)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
# define some folders
@@ -9,16 +13,7 @@ set(CATCH_DIR ${CMAKE_CURRENT_SOURCE_DIR})
set(SELF_TEST_DIR ${CATCH_DIR}/projects/SelfTest)
set(BENCHMARK_DIR ${CATCH_DIR}/projects/Benchmark)
set(HEADER_DIR ${CATCH_DIR}/include)
set(CATCH_VERSION_NUMBER 2.0.0-develop.6)
if(USE_CPP14)
message(STATUS "Enabling C++14")
set(CMAKE_CXX_FLAGS "-std=c++14 ${CMAKE_CXX_FLAGS}")
else()
message(STATUS "Enabling C++11")
set(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}")
endif()
set(CATCH_VERSION_NUMBER 2.1.0)
if(USE_WMAIN)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ENTRY:wmainCRTStartup")
@@ -54,31 +49,32 @@ endfunction()
# define the sources of the self test
# Please keep these ordered alphabetically
set(TEST_SOURCES
${SELF_TEST_DIR}/ApproxTests.cpp
${SELF_TEST_DIR}/BDDTests.cpp
${SELF_TEST_DIR}/Benchmark.tests.cpp
${SELF_TEST_DIR}/ClassTests.cpp
${SELF_TEST_DIR}/CmdLineTests.cpp
${SELF_TEST_DIR}/CompilationTests.cpp
${SELF_TEST_DIR}/ConditionTests.cpp
${SELF_TEST_DIR}/DecompositionTests.cpp
${SELF_TEST_DIR}/EnumToString.cpp
${SELF_TEST_DIR}/ExceptionTests.cpp
${SELF_TEST_DIR}/MessageTests.cpp
${SELF_TEST_DIR}/MiscTests.cpp
${SELF_TEST_DIR}/PartTrackerTests.cpp
${SELF_TEST_DIR}/TagAliasTests.cpp
${SELF_TEST_DIR}/TestMain.cpp
${SELF_TEST_DIR}/ToStringChrono.cpp
${SELF_TEST_DIR}/ToStringGeneralTests.cpp
${SELF_TEST_DIR}/ToStringPair.cpp
${SELF_TEST_DIR}/ToStringTuple.cpp
${SELF_TEST_DIR}/ToStringVector.cpp
${SELF_TEST_DIR}/ToStringWhich.cpp
${SELF_TEST_DIR}/TrickyTests.cpp
${SELF_TEST_DIR}/VariadicMacrosTests.cpp
${SELF_TEST_DIR}/MatchersTests.cpp
${SELF_TEST_DIR}/StringRef.tests.cpp
${SELF_TEST_DIR}/IntrospectiveTests/CmdLine.tests.cpp
${SELF_TEST_DIR}/IntrospectiveTests/PartTracker.tests.cpp
${SELF_TEST_DIR}/IntrospectiveTests/TagAlias.tests.cpp
${SELF_TEST_DIR}/IntrospectiveTests/String.tests.cpp
${SELF_TEST_DIR}/IntrospectiveTests/Xml.tests.cpp
${SELF_TEST_DIR}/UsageTests/Approx.tests.cpp
${SELF_TEST_DIR}/UsageTests/BDD.tests.cpp
${SELF_TEST_DIR}/UsageTests/Benchmark.tests.cpp
${SELF_TEST_DIR}/UsageTests/Class.tests.cpp
${SELF_TEST_DIR}/UsageTests/Compilation.tests.cpp
${SELF_TEST_DIR}/UsageTests/Condition.tests.cpp
${SELF_TEST_DIR}/UsageTests/Decomposition.tests.cpp
${SELF_TEST_DIR}/UsageTests/EnumToString.tests.cpp
${SELF_TEST_DIR}/UsageTests/Exception.tests.cpp
${SELF_TEST_DIR}/UsageTests/Message.tests.cpp
${SELF_TEST_DIR}/UsageTests/Misc.tests.cpp
${SELF_TEST_DIR}/UsageTests/ToStringChrono.tests.cpp
${SELF_TEST_DIR}/UsageTests/ToStringGeneral.tests.cpp
${SELF_TEST_DIR}/UsageTests/ToStringPair.tests.cpp
${SELF_TEST_DIR}/UsageTests/ToStringTuple.tests.cpp
${SELF_TEST_DIR}/UsageTests/ToStringVector.tests.cpp
${SELF_TEST_DIR}/UsageTests/ToStringWhich.tests.cpp
${SELF_TEST_DIR}/UsageTests/Tricky.tests.cpp
${SELF_TEST_DIR}/UsageTests/VariadicMacros.tests.cpp
${SELF_TEST_DIR}/UsageTests/Matchers.tests.cpp
)
CheckFileList(TEST_SOURCES ${SELF_TEST_DIR})
@@ -147,6 +143,7 @@ set(INTERNAL_HEADERS
${HEADER_DIR}/internal/catch_leak_detector.h
${HEADER_DIR}/internal/catch_list.h
${HEADER_DIR}/internal/catch_matchers.h
${HEADER_DIR}/internal/catch_matchers_floating.h
${HEADER_DIR}/internal/catch_matchers_string.h
${HEADER_DIR}/internal/catch_matchers_vector.h
${HEADER_DIR}/internal/catch_message.h
@@ -166,7 +163,6 @@ set(INTERNAL_HEADERS
${HEADER_DIR}/internal/catch_session.h
${HEADER_DIR}/internal/catch_startup_exception_registry.h
${HEADER_DIR}/internal/catch_stream.h
${HEADER_DIR}/internal/catch_streambuf.h
${HEADER_DIR}/internal/catch_stringref.h
${HEADER_DIR}/internal/catch_string_manip.h
${HEADER_DIR}/internal/catch_suppress_warnings.h
@@ -183,6 +179,7 @@ set(INTERNAL_HEADERS
${HEADER_DIR}/internal/catch_timer.h
${HEADER_DIR}/internal/catch_tostring.h
${HEADER_DIR}/internal/catch_totals.h
${HEADER_DIR}/internal/catch_user_interfaces.h
${HEADER_DIR}/internal/catch_version.h
${HEADER_DIR}/internal/catch_wildcard_pattern.h
${HEADER_DIR}/internal/catch_windows_h_proxy.h
@@ -214,6 +211,7 @@ set(IMPL_SOURCES
${HEADER_DIR}/internal/catch_list.cpp
${HEADER_DIR}/internal/catch_leak_detector.cpp
${HEADER_DIR}/internal/catch_matchers.cpp
${HEADER_DIR}/internal/catch_matchers_floating.cpp
${HEADER_DIR}/internal/catch_matchers_string.cpp
${HEADER_DIR}/internal/catch_message.cpp
${HEADER_DIR}/internal/catch_registry_hub.cpp
@@ -227,7 +225,6 @@ set(IMPL_SOURCES
${HEADER_DIR}/internal/catch_session.cpp
${HEADER_DIR}/internal/catch_startup_exception_registry.cpp
${HEADER_DIR}/internal/catch_stream.cpp
${HEADER_DIR}/internal/catch_streambuf.cpp
${HEADER_DIR}/internal/catch_stringref.cpp
${HEADER_DIR}/internal/catch_string_manip.cpp
${HEADER_DIR}/internal/catch_tag_alias.cpp
@@ -253,9 +250,13 @@ CheckFileList(INTERNAL_FILES ${HEADER_DIR}/internal)
set(REPORTER_HEADERS
${HEADER_DIR}/reporters/catch_reporter_automake.hpp
${HEADER_DIR}/reporters/catch_reporter_bases.hpp
${HEADER_DIR}/reporters/catch_reporter_compact.h
${HEADER_DIR}/reporters/catch_reporter_console.h
${HEADER_DIR}/reporters/catch_reporter_junit.h
${HEADER_DIR}/reporters/catch_reporter_multi.h
${HEADER_DIR}/reporters/catch_reporter_tap.hpp
${HEADER_DIR}/reporters/catch_reporter_teamcity.hpp
${HEADER_DIR}/reporters/catch_reporter_xml.h
)
set(REPORTER_SOURCES
${HEADER_DIR}/reporters/catch_reporter_bases.cpp
@@ -280,17 +281,35 @@ set(HEADERS
SOURCE_GROUP("Tests" FILES ${TEST_SOURCES})
SOURCE_GROUP("Surrogates" FILES ${SURROGATE_SOURCES})
# configure the executable
include_directories(${HEADER_DIR})
# Projects consuming Catch via ExternalProject_Add might want to use install step
# without building all of our selftests.
if (NOT NO_SELFTEST)
add_executable(SelfTest ${TEST_SOURCES} ${IMPL_SOURCES} ${REPORTER_SOURCES} ${SURROGATE_SOURCES} ${HEADERS})
target_include_directories(SelfTest PRIVATE ${HEADER_DIR})
if(USE_CPP14)
message(STATUS "Enabling C++14")
set_property(TARGET SelfTest PROPERTY CXX_STANDARD 14)
else()
message(STATUS "Enabling C++11")
set_property(TARGET SelfTest PROPERTY CXX_STANDARD 11)
endif()
set_property(TARGET SelfTest PROPERTY CXX_STANDARD_REQUIRED ON)
set_property(TARGET SelfTest PROPERTY CXX_EXTENSIONS OFF)
if (ENABLE_COVERAGE)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/CMake")
find_package(codecov)
add_coverage(SelfTest)
list(APPEND LCOV_REMOVE_PATTERNS "'/usr/*'")
coverage_evaluate()
endif()
# Add desired warnings
if ( CMAKE_CXX_COMPILER_ID MATCHES "Clang|AppleClang|GNU" )
target_compile_options( SelfTest PRIVATE -Wall -Wextra -Wunreachable-code )
target_compile_options( SelfTest PRIVATE -Wall -Wextra -Wunreachable-code -Werror )
endif()
# Clang specific warning go here
if ( CMAKE_CXX_COMPILER_ID MATCHES "Clang" )
@@ -298,26 +317,48 @@ if (NOT NO_SELFTEST)
target_compile_options( SelfTest PRIVATE -Wweak-vtables -Wexit-time-destructors -Wglobal-constructors -Wmissing-noreturn )
endif()
if ( CMAKE_CXX_COMPILER_ID MATCHES "MSVC" )
target_compile_options( SelfTest PRIVATE /W4 /w44265 /WX /w44061 /w44062 )
STRING(REGEX REPLACE "/W[0-9]" "/W4" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) # override default warning level
target_compile_options( SelfTest PRIVATE /w44265 /WX /w44061 /w44062 )
endif()
# configure unit tests via CTest
enable_testing()
add_test(NAME RunTests COMMAND SelfTest)
include(CTest)
add_test(NAME RunTests COMMAND $<TARGET_FILE:SelfTest>)
add_test(NAME ListTests COMMAND SelfTest --list-tests)
add_test(NAME ListTests COMMAND $<TARGET_FILE:SelfTest> --list-tests --verbosity high)
set_tests_properties(ListTests PROPERTIES PASS_REGULAR_EXPRESSION "[0-9]+ test cases")
add_test(NAME ListTags COMMAND SelfTest --list-tags)
add_test(NAME ListTags COMMAND $<TARGET_FILE:SelfTest> --list-tags)
set_tests_properties(ListTags PROPERTIES PASS_REGULAR_EXPRESSION "[0-9]+ tags")
add_test(NAME ListReporters COMMAND $<TARGET_FILE:SelfTest> --list-reporters)
set_tests_properties(ListReporters PROPERTIES PASS_REGULAR_EXPRESSION "Available reporters:")
add_test(NAME ListTestNamesOnly COMMAND $<TARGET_FILE:SelfTest> --list-test-names-only)
set_tests_properties(ListTestNamesOnly PROPERTIES PASS_REGULAR_EXPRESSION "Regex string matcher")
# AppVeyor has a Python 2.7 in path, but doesn't have .py files as autorunnable
add_test(NAME ApprovalTests COMMAND python ${CMAKE_CURRENT_SOURCE_DIR}/scripts/approvalTests.py $<TARGET_FILE:SelfTest>)
set_tests_properties(ApprovalTests PROPERTIES FAIL_REGULAR_EXPRESSION "Results differed")
if (USE_VALGRIND)
add_test(NAME ValgrindRunTests COMMAND valgrind --leak-check=full --error-exitcode=1 $<TARGET_FILE:SelfTest>)
add_test(NAME ValgrindListTests COMMAND valgrind --leak-check=full --error-exitcode=1 $<TARGET_FILE:SelfTest> --list-tests --verbosity high)
set_tests_properties(ValgrindListTests PROPERTIES PASS_REGULAR_EXPRESSION "definitely lost: 0 bytes in 0 blocks")
add_test(NAME ValgrindListTags COMMAND valgrind --leak-check=full --error-exitcode=1 $<TARGET_FILE:SelfTest> --list-tags)
set_tests_properties(ValgrindListTags PROPERTIES PASS_REGULAR_EXPRESSION "definitely lost: 0 bytes in 0 blocks")
endif()
endif() # !NO_SELFTEST
if(BUILD_EXAMPLES)
add_subdirectory(examples)
endif()
install(DIRECTORY "single_include/" DESTINATION "include/catch")
## Provide some pkg-config integration

View File

@@ -1,17 +1,25 @@
<a id="top"></a>
![catch logo](catch-logo-small.png)
![catch logo](artwork/catch2-logo-small.png)
[![Github Releases](https://img.shields.io/github/release/philsquared/catch.svg)](https://github.com/philsquared/catch/releases)
[![Build Status](https://travis-ci.org/philsquared/Catch.svg?branch=catch2)](https://travis-ci.org/philsquared/Catch?branch=catch2)
[![Build status](https://ci.appveyor.com/api/projects/status/hrtk60hv6tw6fght/branch/catch2?svg=true)](https://ci.appveyor.com/project/philsquared/catch/branch/catch2)
[![Github Releases](https://img.shields.io/github/release/catchorg/catch2.svg)](https://github.com/catchorg/catch2/releases)
[![Build Status](https://travis-ci.org/catchorg/Catch2.svg?branch=master)](https://travis-ci.org/catchorg/Catch2)
[![Build status](https://ci.appveyor.com/api/projects/status/github/catchorg/Catch2?svg=true)](https://ci.appveyor.com/project/catchorg/catch2)
[![codecov](https://codecov.io/gh/catchorg/Catch2/branch/master/graph/badge.svg)](https://codecov.io/gh/catchorg/Catch2)
[![Try online](https://img.shields.io/badge/try-online-blue.svg)](https://wandbox.org/permlink/nerce2MiN6sVmfCi)
<a href="https://github.com/philsquared/Catch/releases/download/v2.0.0-develop.6/catch.hpp">The latest version of the single header can be downloaded directly using this link</a>
<a href="https://github.com/catchorg/Catch2/releases/download/v2.0.1/catch.hpp">The latest version of the single header can be downloaded directly using this link</a>
## Catch2 is released!
If you've been using an earlier version of Catch, please see the
Breaking Changes section of [the release notes](https://github.com/catchorg/Catch2/releases/tag/v2.0.1)
before moving to Catch2. You might also like to read [this blog post](http://www.levelofindirection.com/journal/2017/11/3/catch2-released.html) for more details.
## What's the Catch?
Catch stands for C++ Automated Test Cases in Headers and is a
Catch2 stands for C++ Automated Test Cases in a Header and is a
multi-paradigm test framework for C++. which also supports Objective-C
and, maybe, C.
(and maybe C).
It is primarily distributed as a single header file, although certain
extensions may require additional headers.
@@ -23,6 +31,6 @@ This documentation comprises these three parts:
* [Reference section](docs/Readme.md#top) - all the details
## More
* Issues and bugs can be raised on the [Issue tracker on GitHub](https://github.com/philsquared/Catch/issues)
* Issues and bugs can be raised on the [Issue tracker on GitHub](https://github.com/catchorg/Catch2/issues)
* For discussion or questions please use [the dedicated Google Groups forum](https://groups.google.com/forum/?fromgroups#!forum/catch-forum)
* See [who else is using Catch](docs/opensource-users.md#top)
* See [who else is using Catch2](docs/opensource-users.md#top)

View File

@@ -1,6 +1,10 @@
# version string format -- This will be overwritten later anyway
version: "{build}"
branches:
except:
- /dev-travis.+/
os:
- Visual Studio 2017
- Visual Studio 2015
@@ -18,20 +22,15 @@ environment:
matrix:
exclude:
-
additional_flags: "/permissive- /std:c++latest"
os: Visual Studio 2015
-
additional_flags: "/permissive- /std:c++latest"
os: Visual Studio 2013
- os: Visual Studio 2015
additional_flags: "/permissive- /std:c++latest"
init:
- git config --global core.autocrlf input
# Set build version to git commit-hash
- ps: Update-AppveyorBuild -Version "$($env:APPVEYOR_REPO_BRANCH) - $($env:APPVEYOR_REPO_COMMIT)"
# fetch repository as zip archive
shallow_clone: true
install:
- ps: if (($env:CONFIGURATION) -eq "Debug" ) { python -m pip install codecov }
- ps: if (($env:CONFIGURATION) -eq "Debug" ) { .\misc\installOpenCppCoverage.ps1 }
# Win32 and x64 are CMake-compatible solution platform names.
# This allows us to pass %PLATFORM% to CMake -A.
@@ -47,7 +46,11 @@ configuration:
#Cmake will autodetect the compiler, but we set the arch
before_build:
- set CXXFLAGS=%additional_flags%
- cmake -H. -BBuild -A%PLATFORM% -DUSE_WMAIN=%wmain%
# Indirection because appveyor doesn't handle multiline batch scripts properly
# https://stackoverflow.com/questions/37627248/how-to-split-a-command-over-multiple-lines-in-appveyor-yml/37647169#37647169
# https://help.appveyor.com/discussions/questions/3888-multi-line-cmd-or-powershell-warning-ignore
- cmd: .\misc\appveyorBuildConfigurationScript.bat
# build with MSBuild
build:
@@ -56,5 +59,5 @@ build:
verbosity: normal # MSBuild verbosity level {quiet|minimal|normal|detailed}
test_script:
- cd Build
- ctest -V -j 2 -C %CONFIGURATION%
- set CTEST_OUTPUT_ON_FAILURE=1
- cmd: .\misc\appveyorTestRunScript.bat

BIN
artwork/catch2-c-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

10
codecov.yml Normal file
View File

@@ -0,0 +1,10 @@
codecov:
branch: master
coverage:
ignore:
- "projects/SelfTest"
- "**/catch_reporter_tap.hpp"
- "**/catch_reporter_automake.hpp"
- "**/catch_reporter_teamcity.hpp"
- "**/external/clara.hpp"

View File

@@ -4,7 +4,7 @@ from conans import ConanFile
class CatchConan(ConanFile):
name = "Catch"
version = "2.0.0-develop.6"
version = "2.1.0"
description = "A modern, C++-native, header-only, framework for unit-tests, TDD and BDD"
author = "philsquared"
generators = "cmake"

View File

@@ -1,7 +1,7 @@
<a id="top"></a>
# Reference
To get the most out of Catch, start with the [tutorial](tutorial.md#top).
To get the most out of Catch2, start with the [tutorial](tutorial.md#top).
Once you're up and running consider the following reference material.
Writing tests:

View File

@@ -21,7 +21,7 @@ The ```CHECK``` family are equivalent but execution continues in the same test c
* **REQUIRE(** _expression_ **)** and
* **CHECK(** _expression_ **)**
Evaluates the expression and records the result. If an exception is thrown it is caught, reported, and counted as a failure. These are the macros you will use most of the time
Evaluates the expression and records the result. If an exception is thrown, it is caught, reported, and counted as a failure. These are the macros you will use most of the time.
Examples:
```
@@ -64,7 +64,7 @@ This way `Approx` is constructed with reasonable defaults, covering most simple
* __epsilon__ - epsilon serves to set the percentage by which a result can be erroneous, before it is rejected. By default set to `std::numeric_limits<float>::epsilon()*100`.
* __margin__ - margin serves to set the the absolute value by which a result can be erroneous before it is rejected. By default set to `0.0`.
* __scale__ - scale serves to adjust the base for comparison used by epsilon, can be used when By default set to `1.0`.
* __scale__ - scale serves to adjust the epsilon's multiplicator. By default set to `0.0`.
#### epsilon example
```cpp
@@ -84,7 +84,12 @@ Approx target = Approx(100).margin(5);
```
#### scale
Scale can be useful if the computation leading to the result worked on different scale, than is used by the results (and thus expected errors are on a different scale than would be expected based on the results alone).
Scale can be useful if the computation leading to the result worked
on different scale than is used by the results. Since allowed difference
between Approx's value and compared value is based primarily on Approx's value
(the allowed difference is computed as
`(Approx::scale + Approx::value) * epsilon`), the resulting comparison could
need rescaling to be correct.
## Exceptions

View File

@@ -55,6 +55,17 @@ Because of the incremental nature of Catch's test suites and ability to run spec
## Low-level tools
### Precompiled headers (PCHs)
Catch offers prototypal support for being included in precompiled headers, but because of its single-header nature it does need some actions by the user:
* The precompiled header needs to define `CATCH_CONFIG_ALL_PARTS`
* The implementation file needs to
* undefine `TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED`
* define `CATCH_CONFIG_IMPL_ONLY`
* define `CATCH_CONFIG_MAIN` or `CATCH_CONFIG_RUNNER`
* include "catch.hpp" again
### CMake
In general we recommend "vendoring" Catch's single-include releases inside your own repository. If you do this, the following example shows a minimal CMake project:
@@ -133,7 +144,7 @@ TEST_CASE("Test3", "[a][b][c]") {
REQUIRE(a == b);
}
```
would be registered as 3 tests, `Test1`, `Test2` and `Test3`, and ctest 4 labels would be created, `a`, `b`, `c` and `unit`.
would be registered as 3 tests, `Test1`, `Test2` and `Test3`, and 4 ctest labels would be created, `a`, `b`, `c` and `unit`.
### CodeCoverage module (GCOV, LCOV...)

View File

@@ -19,7 +19,7 @@ file that defines `CATCH_CONFIG_EXTERNAL_INTERFACES`.
Then register it using `CATCH_REGISTER_LISTENER`.
For example:
For example ([complete source code](../examples/210-Evt-EventListeners.cpp)):
```c++
#define CATCH_CONFIG_MAIN

View File

@@ -97,33 +97,11 @@ with expansion:
```
### Visual Studio 2013 -- do-while loop withing range based for fails to compile (C2059)
There is a known bug in Visual Studio 2013 (VC 12), that causes compilation error if range based for is followed by an assertion macro, without enclosing the block in braces. This snippet is sufficient to trigger the error
```cpp
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
TEST_CASE("Syntax error with VC12") {
for ( auto x : { 1 , 2, 3 } )
REQUIRE( x < 3.14 );
}
```
An easy workaround is possible, use braces:
```cpp
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
### Visual Studio 2015 -- Wrong line number reported in debug mode
VS 2015 has a known bug where `__LINE__` macro can be improperly expanded under certain circumstances, while compiling multi-file project in Debug mode.
TEST_CASE("No longer a syntax error with VC12") {
for ( auto x : { 1 , 2, 3 } ) {
REQUIRE( x < 3.14 );
}
}
```
### Visual Studio 2003 -- Syntax error caused by improperly expanded `__LINE__` macro
Older version of Visual Studio can have trouble compiling Catch, not expanding the `__LINE__` macro properly when recompiling the test binary. This is caused by Edit and Continue being on.
A workaround is to turn off Edit and Continue when compiling the test binary.
A workaround is to compile the binary in Release mode.
### Clang/G++ -- skipping leaf sections after an exception
Some versions of `libc++` and `libstdc++` (or their runtimes) have a bug with `std::uncaught_exception()` getting stuck returning `true` after rethrow, even if there are no active exceptions. One such case is this snippet, which skipped the sections "a" and "b", when compiled against `libcxxrt` from master
@@ -144,3 +122,8 @@ TEST_CASE("b") {
```
If you are seeing a problem like this, i.e. a weird test paths that trigger only under Clang with `libc++`, or only under very specific version of `libstdc++`, it is very likely you are seeing this. The only known workaround is to use a fixed version of your standard library.
### Clang/G++ -- `Matches` string matcher always returns false
This is a bug in `libstdc++-4.8`, where all matching methods from `<regex>` return false. Since `Matches` uses `<regex>` internally, if the underlying implementation does not work, it doesn't work either.
Workaround: Use newer version of `libstdc++`.

31
docs/list-of-examples.md Normal file
View File

@@ -0,0 +1,31 @@
<a id="top"></a>
# List of examples
- Test Case: [Single-file](../examples/010-TestCase.cpp)
- Test Case: [Multiple-file 1](../examples/020-TestCase-1.cpp), [2](../examples/020-TestCase-1.cpp)
- Assertion: [REQUIRE, CHECK](../examples/030-Asn-Require-Check.cpp)
- Assertion: [REQUIRE_THAT and Matchers](../examples/040-Asn-RequireThat.cpp)
- Assertion: [REQUIRE_NO_THROW](../examples/050-Asn-RequireNoThrow.cpp)
- Assertion: [REQUIRE_THROWS](../examples/050-Asn-RequireThrows.cpp)
- Assertion: [REQUIRE_THROWS_AS](../examples/070-Asn-RequireThrowsAs.cpp)
- Assertion: [REQUIRE_THROWS_WITH](../examples/080-Asn-RequireThrowsWith.cpp)
- Assertion: [REQUIRE_THROWS_MATCHES](../examples/090-Asn-RequireThrowsMatches.cpp)
- Fixture: [Sections](../examples/100-Fix-Section.cpp)
- Fixture: [Class-based fixtures](../examples/110-Fix-ClassFixture.cpp)
- BDD: [SCENARIO, GIVEN, WHEN, THEN](../examples/120-Bdd-ScenarioGivenWhenThen.cpp)
- Floating point: [Approx - Comparisons](../examples/130-Fpt-Approx.cpp)
- Logging: [CAPTURE - Capture expression](../examples/140-Log-Capture.cpp)
- Logging: [INFO - Provide information with failure](../examples/150-Log-Info.cpp)
- Logging: [WARN - Issue warning](../examples/160-Log-Warn.cpp)
- Logging: [FAIL, FAIL_CHECK - Issue message and force failure/continue](../examples/170-Log-Fail.cpp)
- Logging: [SUCCEED - Issue message and continue](../examples/180-Log-Succeed.cpp)
- Report: [User-defined type](../examples/190-Rpt-ReportUserDefinedType.cpp)
- Report: [Reporter](../examples/200-Rpt-UserDefinedReporter.cpp)
- Listener: [Listeners](../examples/210-Evt-EventListeners.cpp)
- Configuration: [Provide your own main()](../examples/220-Cfg-OwnMain.cpp)
- Configuration: [Compile-time configuration](../examples/230-Cfg-CompileTimeConfiguration.cpp)
- Configuration: [Run-time configuration](../examples/240-Cfg-RunTimeConfiguration.cpp)
---
[Home](Readme.md#top)

View File

@@ -36,10 +36,22 @@ REQUIRE_THAT( str,
```
## Built in matchers
Currently Catch has some string matchers and some vector matchers. They are in the `Catch::Matchers` and `Catch` namespaces.
The string matchers are `StartsWith`, `EndsWith`, `Contains` and `Equals`. Each of them also takes an optional second argument, that decides case sensitivity (by-default, they are case sensitive).
Catch currently provides some matchers, they are in the `Catch::Matchers` and `Catch` namespaces.
### String matchers
The string matchers are `StartsWith`, `EndsWith`, `Contains`, `Equals` and `Matches`. The first four match a literal (sub)string against a result, while `Matches` takes and matches an ECMAScript regex. Do note that `Matches` matches the string as a whole, meaning that "abc" will not match against "abcd", but "abc.*" will.
Each of the provided `std::string` matchers also takes an optional second argument, that decides case sensitivity (by-default, they are case sensitive).
### Vector matchers
The vector matchers are `Contains`, `VectorContains` and `Equals`. `VectorContains` looks for a single element in the matched vector, `Contains` looks for a set (vector) of elements inside the matched vector.
### Floating point matchers
The floating point matchers are `WithinULP` and `WithinAbs`. `WithinAbs` accepts floating point numbers that are within a certain margin of target. `WithinULP` performs an [ULP](https://en.wikipedia.org/wiki/Unit_in_the_last_place)-based comparison of two floating point numbers and accepts them if they are less than certain number of ULPs apart.
Do note that ULP-based checks only make sense when both compared numbers are of the same type and `WithinULP` will use type of its argument as the target type. This means that `WithinULP(1.f, 1)` will expect to compare `float`s, but `WithinULP(1., 1)` will expect to compare `double`s.
## Custom matchers
It's easy to provide your own matchers to extend Catch or just to work with your own types.

View File

@@ -30,13 +30,22 @@ A, header-only, embedded scripting language designed from the ground up to direc
A, single-header-only, type-safe, command line parser - which also prints formatted usage strings.
### [Couchbase-lite-core](https://github.com/couchbase/couchbase-lite-core)
The next-generation core storage and query engine for Couchbase Lite/
The next-generation core storage and query engine for Couchbase Lite
### [DtCraft](https://github.com/twhuang-uiuc/DtCraft)
A High-performance Cluster Computing Engine
### [Fuxedo](https://github.com/fuxedo/fuxedo)
Open source Oracle Tuxedo-like XATMI middleware for C and C++.
### [Inja](https://github.com/pantor/inja)
A header-only template engine for modern C++.
### [JSON for Modern C++](https://github.com/nlohmann/json)
A, single-header, JSON parsing library that takes advantage of what C++ has to offer.
### [MNMLSTC Core](https://github.com/mnmlstc/core)
a small and easy to use C++11 library that adds a functionality set that will be available in C++14 and later, as well as some useful additions
A small and easy to use C++11 library that adds a functionality set that will be available in C++14 and later, as well as some useful additions.
### [nanodbc](https://github.com/lexicalunit/nanodbc/)
A small C++ library wrapper for the native C ODBC API.

View File

@@ -39,34 +39,20 @@ If you still want Catch to process the command line, but you want to programatic
int main( int argc, char* argv[] )
{
Catch::Session session; // There must be exactly one instance
// writing to session.configData() here sets defaults
// this is the preferred way to set them
// Verify that all tests, aliases, etc registered properly
const auto& exceptions = getRegistryHub().getStartupExceptionRegistry().getExceptions();
if ( !exceptions.empty() ) {
// iterate over all exceptions and notify user
for ( const auto& ex_ptr : exceptions ) {
try {
std::rethrow_exception(ex_ptr);
} catch (std::exception const& ex) {
Catch::cerr() << ex.what();
}
}
// Indicate that an error occured before main
return 1;
}
int returnCode = session.applyCommandLine( argc, argv );
if( returnCode != 0 ) // Indicates a command line error
return returnCode;
return returnCode;
// writing to session.configData() or session.Config() here
// overrides command line args
// only do this if you know you need to
int numFailed = session.run();
// numFailed is clamped to 255 as some unices only use the lower 8 bits.
// This clamping has already been applied, so just return it here
// You can also do any post run clean-up here
@@ -80,7 +66,44 @@ To take full control of the config simply omit the call to ```applyCommandLine()
## Adding your own command line options
Catch embeds a powerful command line parser which you can also use to parse your own options out. This capability is still in active development but will be documented here when it is ready.
Catch embeds a powerful command line parser called [Clara](https://github.com/philsquared/Clara).
As of Catch2 (and Clara 1.0) Clara allows you to write _composable_ option and argument parsers,
so extending Catch's own command line options is now easy.
```c++
#define CATCH_CONFIG_RUNNER
#include "catch.hpp"
int main( int argc, char* argv[] )
{
Catch::Session session; // There must be exactly one instance
int height = 0; // Some user variable you want to be able to set
// Build a new parser on top of Catch's
auto cli
= session.cli() // Get Catch's composite command line parser
| Opt( height, "height" ) // bind variable to a new option, with a hint string
["-g"]["--height"] // the option names it will respond to
("how high?"); // description string for the help output
// Now pass the new composite back to Catch so it uses that
session.cli( cli );
// Let Catch (using Clara) parse the command line
int returnCode = session.applyCommandLine( argc, argv );
if( returnCode != 0 ) // Indicates a command line error
return returnCode;
// if set on the command line then 'height' is now set at this point
if( height > 0 )
std::cout << "height: " << height << std::endl;
return session.run();
}
```
See the [Clara documentation](https://github.com/philsquared/Clara/blob/master/README.md) for more details.
---

View File

@@ -1,5 +1,46 @@
<a id="top"></a>
# 2.0.0 (in progress)
# 2.1.0
## Improvements
* Various performance improvements
* On top of the performance regression fixes
* Experimental support for PCH was added (#1061)
* `CATCH_CONFIG_EXTERNAL_INTERFACES` now brings in declarations of Console, Compact, XML and JUnit reporters
* `MatcherBase` no longer has a pointless second template argument
* Reduced the number of warning suppressions that leak into user's code
* Bugs in g++ 4.x and 5.x mean that some of them have to be left in
## Fixes
* Fixed performance regression from Catch classic
* One of the performance improvement patches for Catch classic was not applied to Catch2
* Fixed platform detection for iOS (#1084)
* Fixed compilation when `g++` is used together with `libc++` (#1110)
* Fixed TeamCity reporter compilation with the single header version
* To fix the underlying issue we will be versioning reporters in single_include folder per release
* The XML reporter will now report `WARN` messages even when not used with `-s`
* Fixed compilation when `VectorContains` matcher was combined using `&&` (#1092)
* Fixed test duration overflowing after 10 seconds (#1125, #1129)
* Fixed `std::uncaught_exception` deprecation warning (#1124)
## New features
* New Matchers
* Regex matcher for strings, `Matches`.
* Set-equal matcher for vectors, `UnorderedEquals`
* Floating point matchers, `WithinAbs` and `WithinULP`.
* Stringification now attempts to decompose all containers (#606)
* Containers are objects that respond to ADL `begin(T)` and `end(T)`.
## Other changes
* Reporters will now be versioned in the `single_include` folder to ensure their compatibility with the last released version
# 2.0.1
## Breaking changes
* Removed C++98 support
@@ -25,13 +66,18 @@
* `std::pair` and `std::tuple` are no longer stringified by default
* This is done to avoid dragging in `<tuple>` and `<utility>` headers in common path
* Their stringification can be enabled per-file via new configuration macros
* `Approx` is subtly different and hopefully behaves more as users would expect
* `Approx::scale` defaults to `0.0`
* `Approx::epsilon` no longer applies to the larger of the two compared values, but only to the `Approx`'s value
* `INFINITY == Approx(INFINITY)` returns true
## Improvements
* Reporters and Listeners can be defined in files different from the main file
* The file has to define `CATCH_CONFIG_EXTERNAL_INTERFACES` before including catch.hpp.
* Errors that happen during set up before main are now caught and properly reported once main is entered
* If you are providing your own main, you can access and use these as well.
* New assertion macros, *_THROWS_WITH(expr, exception_type, matcher) are provided
* New assertion macros, *_THROWS_MATCHES(expr, exception_type, matcher) are provided
* As the arguments suggest, these allow you to assert that an expression throws desired type of exception and pass the exception to a matcher.
* JUnit reporter no longer has significantly different output for test cases with and without sections
* Most assertions now support expressions containing commas (ie `REQUIRE(foo() == std::vector<int>{1, 2, 3});`)
@@ -82,6 +128,41 @@
# Older versions
## 1.11.x
### 1.11.0
#### Fixes
* The original expression in `REQUIRE_FALSE( expr )` is now reporter properly as `!( expr )` (#1051)
* Previously the parentheses were missing and `x != y` would be expanded as `!x != x`
* `Approx::Margin` is now inclusive (#952)
* Previously it was meant and documented as inclusive, but the check itself wasn't
* This means that `REQUIRE( 0.25f == Approx( 0.0f ).margin( 0.25f ) )` passes, instead of fails
* `RandomNumberGenerator::result_type` is now unsigned (#1050)
#### Improvements
* `__JETBRAINS_IDE__` macro handling is now CLion version specific (#1017)
* When CLion 2017.3 or newer is detected, `__COUNTER__` is used instead of
* TeamCity reporter now explicitly flushes output stream after each report (#1057)
* On some platforms, output from redirected streams would show up only after the tests finished running
* `ParseAndAddCatchTests` now can add test files as dependency to CMake configuration
* This means you do not have to manually rerun CMake configuration step to detect new tests
## 1.10.x
### 1.10.0
#### Fixes
* Evaluation layer has been rewritten (backported from Catch 2)
* The new layer is much simpler and fixes some issues (#981)
* Implemented workaround for VS 2017 raw string literal stringification bug (#995)
* Fixed interaction between `[!shouldfail]` and `[!mayfail]` tags and sections
* Previously sections with failing assertions would be marked as failed, not failed-but-ok
#### Improvements
* Added [libidentify](https://github.com/janwilmans/LibIdentify) support
* Added "wait-for-keypress" option
## 1.9.x
### 1.9.6

View File

@@ -2,7 +2,7 @@
# Tutorial
**Contents**<br>
[Getting Catch](#getting-catch)<br>
[Getting Catch2](#getting-catch2)<br>
[Where to put it?](#where-to-put-it)<br>
[Writing tests](#writing-tests)<br>
[Test cases and sections](#test-cases-and-sections)<br>
@@ -10,22 +10,22 @@
[Scaling up](#scaling-up)<br>
[Next steps](#next-steps)<br>
## Getting Catch
## Getting Catch2
The simplest way to get Catch is to download the latest [single header version](https://raw.githubusercontent.com/philsquared/Catch/master/single_include/catch.hpp). The single header is generated by merging a set of individual headers but it is still just normal source code in a header file.
The simplest way to get Catch2 is to download the latest [single header version](https://raw.githubusercontent.com/CatchOrg/Catch2/master/single_include/catch.hpp). The single header is generated by merging a set of individual headers but it is still just normal source code in a header file.
The full source for Catch, including test projects, documentation, and other things, is hosted on GitHub. [http://catch-lib.net](http://catch-lib.net) will redirect you there.
The full source for Catch2, including test projects, documentation, and other things, is hosted on GitHub. [http://catch-lib.net](http://catch-lib.net) will redirect you there.
## Where to put it?
Catch is header only. All you need to do is drop the file(s) somewhere reachable from your project - either in some central location you can set your header search path to find, or directly into your project tree itself! This is a particularly good option for other Open-Source projects that want to use Catch for their test suite. See [this blog entry for more on that](http://www.levelofindirection.com/journal/2011/5/27/unit-testing-in-c-and-objective-c-just-got-ridiculously-easi.html).
Catch2 is header only. All you need to do is drop the file somewhere reachable from your project - either in some central location you can set your header search path to find, or directly into your project tree itself! This is a particularly good option for other Open-Source projects that want to use Catch for their test suite. See [this blog entry for more on that](http://www.levelofindirection.com/journal/2011/5/27/unit-testing-in-c-and-objective-c-just-got-ridiculously-easi.html).
The rest of this tutorial will assume that the Catch single-include header (or the include folder) is available unqualified - but you may need to prefix it with a folder name if necessary.
The rest of this tutorial will assume that the Catch2 single-include header (or the include folder) is available unqualified - but you may need to prefix it with a folder name if necessary.
## Writing tests
Let's start with a really simple example. Say you have written a function to calculate factorials and now you want to test it (let's leave aside TDD for now).
Let's start with a really simple example ([code](../examples/010-TestCase.cpp)). Say you have written a function to calculate factorials and now you want to test it (let's leave aside TDD for now).
```c++
unsigned int Factorial( unsigned int number ) {
@@ -33,7 +33,7 @@ unsigned int Factorial( unsigned int number ) {
}
```
To keep things simple we'll put everything in a single file (<a href="#scaling-up">see later for more on how to structure your test files</a>)
To keep things simple we'll put everything in a single file (<a href="#scaling-up">see later for more on how to structure your test files</a>).
```c++
#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file
@@ -110,7 +110,7 @@ Most test frameworks have a class-based fixture mechanism. That is, test cases m
While Catch fully supports this way of working there are a few problems with the approach. In particular the way your code must be split up, and the blunt granularity of it, may cause problems. You can only have one setup/ teardown pair across a set of methods, but sometimes you want slightly different setup in each method, or you may even want several levels of setup (a concept which we will clarify later on in this tutorial). It was <a href="http://jamesnewkirk.typepad.com/posts/2007/09/why-you-should-.html">problems like these</a> that led James Newkirk, who led the team that built NUnit, to start again from scratch and <a href="http://jamesnewkirk.typepad.com/posts/2007/09/announcing-xuni.html">build xUnit</a>).
Catch takes a different approach (to both NUnit and xUnit) that is a more natural fit for C++ and the C family of languages. This is best explained through an example:
Catch takes a different approach (to both NUnit and xUnit) that is a more natural fit for C++ and the C family of languages. This is best explained through an example ([code](../examples/100-Fix-Section.cpp)):
```c++
TEST_CASE( "vectors can be sized and resized", "[vector]" ) {
@@ -175,7 +175,7 @@ Sections can be nested to an arbitrary depth (limited only by your stack size).
If you name your test cases and sections appropriately you can achieve a BDD-style specification structure. This became such a useful way of working that first class support has been added to Catch. Scenarios can be specified using ```SCENARIO```, ```GIVEN```, ```WHEN``` and ```THEN``` macros, which map on to ```TEST_CASE```s and ```SECTION```s, respectively. For more details see [Test cases and sections](test-cases-and-sections.md#top).
The vector example can be adjusted to use these macros like so:
The vector example can be adjusted to use these macros like so ([example code](../examples/120-Bdd-ScenarioGivenWhenThen.cpp)):
```c++
SCENARIO( "vectors can be sized and resized", "[vector]" ) {
@@ -245,7 +245,7 @@ The requirement is that the following block of code ([or equivalent](own-main.md
appears in _exactly one_ source file. Use as many additional cpp files (or whatever you call your implementation files) as you need for your tests, partitioned however makes most sense for your way of working. Each additional file need only ```#include "catch.hpp"``` - do not repeat the ```#define```!
In fact it is usually a good idea to put the block with the ```#define``` [in its own source file](slow-compiles.md#top).
In fact it is usually a good idea to put the block with the ```#define``` [in its own source file](slow-compiles.md#top) (code example [main](../examples/020-TestCase-1.cpp), [tests](../examples/020-TestCase-2.cpp)).
Do not write your tests in header files!

View File

@@ -0,0 +1,15 @@
// 000-CatchMain.cpp
// In a Catch project with multiple files, dedicate one file to compile the
// source code of Catch itself and reuse the resulting object file for linking.
// Let Catch provide main():
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
// That's it
// Compile implementation of Catch for use with files that do contain tests:
// - g++ -std=c++11 -Wall -I$(CATCH_SINGLE_INCLUDE) -c 000-CatchMain.cpp
// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% -c 000-CatchMain.cpp

36
examples/010-TestCase.cpp Normal file
View File

@@ -0,0 +1,36 @@
// 010-TestCase.cpp
// Let Catch provide main():
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
int Factorial( int number ) {
return number <= 1 ? number : Factorial( number - 1 ) * number; // fail
// return number <= 1 ? 1 : Factorial( number - 1 ) * number; // pass
}
TEST_CASE( "Factorial of 0 is 1 (fail)", "[single-file]" ) {
REQUIRE( Factorial(0) == 1 );
}
TEST_CASE( "Factorials of 1 and higher are computed (pass)", "[single-file]" ) {
REQUIRE( Factorial(1) == 1 );
REQUIRE( Factorial(2) == 2 );
REQUIRE( Factorial(3) == 6 );
REQUIRE( Factorial(10) == 3628800 );
}
// Compile & run:
// - g++ -std=c++11 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 010-TestCase 010-TestCase.cpp && 010-TestCase --success
// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% 010-TestCase.cpp && 010-TestCase --success
// Expected compact output (all assertions):
//
// prompt> 010-TestCase --reporter compact --success
// 010-TestCase.cpp:14: failed: Factorial(0) == 1 for: 0 == 1
// 010-TestCase.cpp:18: passed: Factorial(1) == 1 for: 1 == 1
// 010-TestCase.cpp:19: passed: Factorial(2) == 2 for: 2 == 2
// 010-TestCase.cpp:20: passed: Factorial(3) == 6 for: 6 == 6
// 010-TestCase.cpp:21: passed: Factorial(10) == 3628800 for: 3628800 (0x375f00) == 3628800 (0x375f00)
// Failed 1 test case, failed 1 assertion.

View File

@@ -0,0 +1,35 @@
// 020-TestCase-1.cpp
// In a Catch project with multiple files, dedicate one file to compile the
// source code of Catch itself and reuse the resulting object file for linking.
// Let Catch provide main():
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
TEST_CASE( "1: All test cases reside in other .cpp files (empty)", "[multi-file:1]" ) {
}
// ^^^
// Normally no TEST_CASEs in this file.
// Here just to show there are two source files via option --list-tests.
// Compile & run:
// - g++ -std=c++11 -Wall -I$(CATCH_SINGLE_INCLUDE) -c 020-TestCase-1.cpp
// - g++ -std=c++11 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 020-TestCase TestCase-1.o 020-TestCase-2.cpp && 020-TestCase --success
//
// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% -c 020-TestCase-1.cpp
// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% -Fe020-TestCase.exe 020-TestCase-1.obj 020-TestCase-2.cpp && 020-TestCase --success
// Expected test case listing:
//
// prompt> 020-TestCase --list-tests *
// Matching test cases:
// 1: All test cases reside in other .cpp files (empty)
// [multi-file:1]
// 2: Factorial of 0 is computed (fail)
// [multi-file:2]
// 2: Factorials of 1 and higher are computed (pass)
// [multi-file:2]
// 3 matching test cases

View File

@@ -0,0 +1,33 @@
// 020-TestCase-2.cpp
// main() provided by Catch in file 020-TestCase-1.cpp.
#include "catch.hpp"
int Factorial( int number ) {
return number <= 1 ? number : Factorial( number - 1 ) * number; // fail
// return number <= 1 ? 1 : Factorial( number - 1 ) * number; // pass
}
TEST_CASE( "2: Factorial of 0 is 1 (fail)", "[multi-file:2]" ) {
REQUIRE( Factorial(0) == 1 );
}
TEST_CASE( "2: Factorials of 1 and higher are computed (pass)", "[multi-file:2]" ) {
REQUIRE( Factorial(1) == 1 );
REQUIRE( Factorial(2) == 2 );
REQUIRE( Factorial(3) == 6 );
REQUIRE( Factorial(10) == 3628800 );
}
// Compile: see 020-TestCase-1.cpp
// Expected compact output (all assertions):
//
// prompt> 020-TestCase --reporter compact --success
// 020-TestCase-2.cpp:13: failed: Factorial(0) == 1 for: 0 == 1
// 020-TestCase-2.cpp:17: passed: Factorial(1) == 1 for: 1 == 1
// 020-TestCase-2.cpp:18: passed: Factorial(2) == 2 for: 2 == 2
// 020-TestCase-2.cpp:19: passed: Factorial(3) == 6 for: 6 == 6
// 020-TestCase-2.cpp:20: passed: Factorial(10) == 3628800 for: 3628800 (0x375f00) == 3628800 (0x375f00)
// Failed 1 test case, failed 1 assertion.

View File

@@ -0,0 +1,74 @@
// 030-Asn-Require-Check.cpp
// Catch has two natural expression assertion macro's:
// - REQUIRE() stops at first failure.
// - CHECK() continues after failure.
// There are two variants to support decomposing negated expressions:
// - REQUIRE_FALSE() stops at first failure.
// - CHECK_FALSE() continues after failure.
// main() provided in 000-CatchMain.cpp
#include "catch.hpp"
std::string one() {
return "1";
}
TEST_CASE( "Assert that something is true (pass)", "[require]" ) {
REQUIRE( one() == "1" );
}
TEST_CASE( "Assert that something is true (fail)", "[require]" ) {
REQUIRE( one() == "x" );
}
TEST_CASE( "Assert that something is true (stop at first failure)", "[require]" ) {
WARN( "REQUIRE stops at first failure:" );
REQUIRE( one() == "x" );
REQUIRE( one() == "1" );
}
TEST_CASE( "Assert that something is true (continue after failure)", "[check]" ) {
WARN( "CHECK continues after failure:" );
CHECK( one() == "x" );
REQUIRE( one() == "1" );
}
TEST_CASE( "Assert that something is false (stops at first failure)", "[require-false]" ) {
WARN( "REQUIRE_FALSE stops at first failure:" );
REQUIRE_FALSE( one() == "1" );
REQUIRE_FALSE( one() != "1" );
}
TEST_CASE( "Assert that something is false (continue after failure)", "[check-false]" ) {
WARN( "CHECK_FALSE continues after failure:" );
CHECK_FALSE( one() == "1" );
REQUIRE_FALSE( one() != "1" );
}
// Compile & run:
// - g++ -std=c++11 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 030-Asn-Require-Check 030-Asn-Require-Check.cpp 000-CatchMain.o && 030-Asn-Require-Check --success
// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% 030-Asn-Require-Check.cpp 000-CatchMain.obj && 030-Asn-Require-Check --success
// Expected compact output (all assertions):
//
// prompt> 030-Asn-Require-Check.exe --reporter compact --success
// 030-Asn-Require-Check.cpp:20: passed: one() == "1" for: "1" == "1"
// 030-Asn-Require-Check.cpp:24: failed: one() == "x" for: "1" == "x"
// 030-Asn-Require-Check.cpp:28: warning: 'REQUIRE stops at first failure:'
// 030-Asn-Require-Check.cpp:30: failed: one() == "x" for: "1" == "x"
// 030-Asn-Require-Check.cpp:35: warning: 'CHECK continues after failure:'
// 030-Asn-Require-Check.cpp:37: failed: one() == "x" for: "1" == "x"
// 030-Asn-Require-Check.cpp:38: passed: one() == "1" for: "1" == "1"
// 030-Asn-Require-Check.cpp:42: warning: 'REQUIRE_FALSE stops at first failure:'
// 030-Asn-Require-Check.cpp:44: failed: !(one() == "1") for: !("1" == "1")
// 030-Asn-Require-Check.cpp:49: warning: 'CHECK_FALSE continues after failure:'
// 030-Asn-Require-Check.cpp:51: failed: !(one() == "1") for: !("1" == "1")
// 030-Asn-Require-Check.cpp:52: passed: !(one() != "1") for: !("1" != "1")
// Failed 5 test cases, failed 5 assertions.

View File

@@ -0,0 +1,69 @@
// 100-Fix-Section.cpp
// Catch has two ways to express fixtures:
// - Sections (this file)
// - Traditional class-based fixtures
// main() provided in 000-CatchMain.cpp
#include "catch.hpp"
TEST_CASE( "vectors can be sized and resized", "[vector]" ) {
// For each section, vector v is anew:
std::vector<int> v( 5 );
REQUIRE( v.size() == 5 );
REQUIRE( v.capacity() >= 5 );
SECTION( "resizing bigger changes size and capacity" ) {
v.resize( 10 );
REQUIRE( v.size() == 10 );
REQUIRE( v.capacity() >= 10 );
}
SECTION( "resizing smaller changes size but not capacity" ) {
v.resize( 0 );
REQUIRE( v.size() == 0 );
REQUIRE( v.capacity() >= 5 );
}
SECTION( "reserving bigger changes capacity but not size" ) {
v.reserve( 10 );
REQUIRE( v.size() == 5 );
REQUIRE( v.capacity() >= 10 );
}
SECTION( "reserving smaller does not change size or capacity" ) {
v.reserve( 0 );
REQUIRE( v.size() == 5 );
REQUIRE( v.capacity() >= 5 );
}
}
// Compile & run:
// - g++ -std=c++11 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 100-Fix-Section 100-Fix-Section.cpp 000-CatchMain.o && 100-Fix-Section --success
// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% 100-Fix-Section.cpp 000-CatchMain.obj && 100-Fix-Section --success
// Expected compact output (all assertions):
//
// prompt> 100-Fix-Section.exe --reporter compact --success
// 100-Fix-Section.cpp:17: passed: v.size() == 5 for: 5 == 5
// 100-Fix-Section.cpp:18: passed: v.capacity() >= 5 for: 5 >= 5
// 100-Fix-Section.cpp:23: passed: v.size() == 10 for: 10 == 10
// 100-Fix-Section.cpp:24: passed: v.capacity() >= 10 for: 10 >= 10
// 100-Fix-Section.cpp:17: passed: v.size() == 5 for: 5 == 5
// 100-Fix-Section.cpp:18: passed: v.capacity() >= 5 for: 5 >= 5
// 100-Fix-Section.cpp:29: passed: v.size() == 0 for: 0 == 0
// 100-Fix-Section.cpp:30: passed: v.capacity() >= 5 for: 5 >= 5
// 100-Fix-Section.cpp:17: passed: v.size() == 5 for: 5 == 5
// 100-Fix-Section.cpp:18: passed: v.capacity() >= 5 for: 5 >= 5
// 100-Fix-Section.cpp:35: passed: v.size() == 5 for: 5 == 5
// 100-Fix-Section.cpp:36: passed: v.capacity() >= 10 for: 10 >= 10
// 100-Fix-Section.cpp:17: passed: v.size() == 5 for: 5 == 5
// 100-Fix-Section.cpp:18: passed: v.capacity() >= 5 for: 5 >= 5
// 100-Fix-Section.cpp:41: passed: v.size() == 5 for: 5 == 5
// 100-Fix-Section.cpp:42: passed: v.capacity() >= 5 for: 5 >= 5
// Passed 1 test case with 16 assertions.

View File

@@ -0,0 +1,63 @@
// 110-Fix-ClassFixture.cpp
// Catch has two ways to express fixtures:
// - Sections
// - Traditional class-based fixtures (this file)
// main() provided in 000-CatchMain.cpp
#include "catch.hpp"
class DBConnection
{
public:
static DBConnection createConnection( std::string const & /*dbName*/ ) {
return DBConnection();
}
bool executeSQL( std::string const & /*query*/, int const /*id*/, std::string const & arg ) {
if ( arg.length() == 0 ) {
throw std::logic_error("empty SQL query argument");
}
return true; // ok
}
};
class UniqueTestsFixture
{
protected:
UniqueTestsFixture()
: conn( DBConnection::createConnection( "myDB" ) )
{}
int getID() {
return ++uniqueID;
}
protected:
DBConnection conn;
private:
static int uniqueID;
};
int UniqueTestsFixture::uniqueID = 0;
TEST_CASE_METHOD( UniqueTestsFixture, "Create Employee/No Name", "[create]" ) {
REQUIRE_THROWS( conn.executeSQL( "INSERT INTO employee (id, name) VALUES (?, ?)", getID(), "") );
}
TEST_CASE_METHOD( UniqueTestsFixture, "Create Employee/Normal", "[create]" ) {
REQUIRE( conn.executeSQL( "INSERT INTO employee (id, name) VALUES (?, ?)", getID(), "Joe Bloggs" ) );
}
// Compile & run:
// - g++ -std=c++11 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 110-Fix-ClassFixture 110-Fix-ClassFixture.cpp 000-CatchMain.o && 110-Fix-ClassFixture --success
// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% 110-Fix-ClassFixture.cpp 000-CatchMain.obj && 110-Fix-ClassFixture --success
// Expected compact output (all assertions):
//
// prompt> 110-Fix-ClassFixture.exe --reporter compact --success
// 110-Fix-ClassFixture.cpp:47: passed: conn.executeSQL( "INSERT INTO employee (id, name) VALUES (?, ?)", getID(), "")
// 110-Fix-ClassFixture.cpp:51: passed: conn.executeSQL( "INSERT INTO employee (id, name) VALUES (?, ?)", getID(), "Joe Bloggs" ) for: true
// Passed both 2 test cases with 2 assertions.

View File

@@ -0,0 +1,73 @@
// 120-Bdd-ScenarioGivenWhenThen.cpp
// main() provided in 000-CatchMain.cpp
#include "catch.hpp"
SCENARIO( "vectors can be sized and resized", "[vector]" ) {
GIVEN( "A vector with some items" ) {
std::vector<int> v( 5 );
REQUIRE( v.size() == 5 );
REQUIRE( v.capacity() >= 5 );
WHEN( "the size is increased" ) {
v.resize( 10 );
THEN( "the size and capacity change" ) {
REQUIRE( v.size() == 10 );
REQUIRE( v.capacity() >= 10 );
}
}
WHEN( "the size is reduced" ) {
v.resize( 0 );
THEN( "the size changes but not capacity" ) {
REQUIRE( v.size() == 0 );
REQUIRE( v.capacity() >= 5 );
}
}
WHEN( "more capacity is reserved" ) {
v.reserve( 10 );
THEN( "the capacity changes but not the size" ) {
REQUIRE( v.size() == 5 );
REQUIRE( v.capacity() >= 10 );
}
}
WHEN( "less capacity is reserved" ) {
v.reserve( 0 );
THEN( "neither size nor capacity are changed" ) {
REQUIRE( v.size() == 5 );
REQUIRE( v.capacity() >= 5 );
}
}
}
}
// Compile & run:
// - g++ -std=c++11 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 120-Bdd-ScenarioGivenWhenThen 120-Bdd-ScenarioGivenWhenThen.cpp 000-CatchMain.o && 120-Bdd-ScenarioGivenWhenThen --success
// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% 120-Bdd-ScenarioGivenWhenThen.cpp 000-CatchMain.obj && 120-Bdd-ScenarioGivenWhenThen --success
// Expected compact output (all assertions):
//
// prompt> 120-Bdd-ScenarioGivenWhenThen.exe --reporter compact --success
// 120-Bdd-ScenarioGivenWhenThen.cpp:12: passed: v.size() == 5 for: 5 == 5
// 120-Bdd-ScenarioGivenWhenThen.cpp:13: passed: v.capacity() >= 5 for: 5 >= 5
// 120-Bdd-ScenarioGivenWhenThen.cpp:19: passed: v.size() == 10 for: 10 == 10
// 120-Bdd-ScenarioGivenWhenThen.cpp:20: passed: v.capacity() >= 10 for: 10 >= 10
// 120-Bdd-ScenarioGivenWhenThen.cpp:12: passed: v.size() == 5 for: 5 == 5
// 120-Bdd-ScenarioGivenWhenThen.cpp:13: passed: v.capacity() >= 5 for: 5 >= 5
// 120-Bdd-ScenarioGivenWhenThen.cpp:27: passed: v.size() == 0 for: 0 == 0
// 120-Bdd-ScenarioGivenWhenThen.cpp:28: passed: v.capacity() >= 5 for: 5 >= 5
// 120-Bdd-ScenarioGivenWhenThen.cpp:12: passed: v.size() == 5 for: 5 == 5
// 120-Bdd-ScenarioGivenWhenThen.cpp:13: passed: v.capacity() >= 5 for: 5 >= 5
// 120-Bdd-ScenarioGivenWhenThen.cpp:35: passed: v.size() == 5 for: 5 == 5
// 120-Bdd-ScenarioGivenWhenThen.cpp:36: passed: v.capacity() >= 10 for: 10 >= 10
// 120-Bdd-ScenarioGivenWhenThen.cpp:12: passed: v.size() == 5 for: 5 == 5
// 120-Bdd-ScenarioGivenWhenThen.cpp:13: passed: v.capacity() >= 5 for: 5 >= 5
// 120-Bdd-ScenarioGivenWhenThen.cpp:43: passed: v.size() == 5 for: 5 == 5
// 120-Bdd-ScenarioGivenWhenThen.cpp:44: passed: v.capacity() >= 5 for: 5 >= 5
// Passed 1 test case with 16 assertions.

View File

@@ -0,0 +1,423 @@
// 210-Evt-EventListeners.cpp
// Contents:
// 1. Printing of listener data
// 2. My listener and registration
// 3. Test cases
// main() provided in 000-CatchMain.cpp
// Let Catch provide the required interfaces:
#define CATCH_CONFIG_EXTERNAL_INTERFACES
#include "catch.hpp"
#include <iostream>
// -----------------------------------------------------------------------
// 1. Printing of listener data:
//
std::string ws(int const level) {
return std::string( 2 * level, ' ' );
}
template< typename T >
std::ostream& operator<<( std::ostream& os, std::vector<T> const& v ) {
os << "{ ";
for ( auto x : v )
os << x << ", ";
return os << "}";
}
// struct SourceLineInfo {
// char const* file;
// std::size_t line;
// };
void print( std::ostream& os, int const level, std::string const& title, Catch::SourceLineInfo const& info ) {
os << ws(level ) << title << ":\n"
<< ws(level+1) << "- file: " << info.file << "\n"
<< ws(level+1) << "- line: " << info.line << "\n";
}
//struct MessageInfo {
// std::string macroName;
// std::string message;
// SourceLineInfo lineInfo;
// ResultWas::OfType type;
// unsigned int sequence;
//};
void print( std::ostream& os, int const level, Catch::MessageInfo const& info ) {
os << ws(level+1) << "- macroName: '" << info.macroName << "'\n"
<< ws(level+1) << "- message '" << info.message << "'\n";
print( os,level+1 , "- lineInfo", info.lineInfo );
os << ws(level+1) << "- sequence " << info.sequence << "\n";
}
void print( std::ostream& os, int const level, std::string const& title, std::vector<Catch::MessageInfo> const& v ) {
os << ws(level ) << title << ":\n";
for ( auto x : v )
{
os << ws(level+1) << "{\n";
print( os, level+2, x );
os << ws(level+1) << "}\n";
}
// os << ws(level+1) << "\n";
}
// struct TestRunInfo {
// std::string name;
// };
void print( std::ostream& os, int const level, std::string const& title, Catch::TestRunInfo const& info ) {
os << ws(level ) << title << ":\n"
<< ws(level+1) << "- name: " << info.name << "\n";
}
// struct Counts {
// std::size_t total() const;
// bool allPassed() const;
// bool allOk() const;
//
// std::size_t passed = 0;
// std::size_t failed = 0;
// std::size_t failedButOk = 0;
// };
void print( std::ostream& os, int const level, std::string const& title, Catch::Counts const& info ) {
os << ws(level ) << title << ":\n"
<< ws(level+1) << "- total(): " << info.total() << "\n"
<< ws(level+1) << "- allPassed(): " << info.allPassed() << "\n"
<< ws(level+1) << "- allOk(): " << info.allOk() << "\n"
<< ws(level+1) << "- passed: " << info.passed << "\n"
<< ws(level+1) << "- failed: " << info.failed << "\n"
<< ws(level+1) << "- failedButOk: " << info.failedButOk << "\n";
}
// struct Totals {
// Counts assertions;
// Counts testCases;
// };
void print( std::ostream& os, int const level, std::string const& title, Catch::Totals const& info ) {
os << ws(level) << title << ":\n";
print( os, level+1, "- assertions", info.assertions );
print( os, level+1, "- testCases" , info.testCases );
}
// struct TestRunStats {
// TestRunInfo runInfo;
// Totals totals;
// bool aborting;
// };
void print( std::ostream& os, int const level, std::string const& title, Catch::TestRunStats const& info ) {
os << ws(level) << title << ":\n";
print( os, level+1 , "- runInfo", info.runInfo );
print( os, level+1 , "- totals" , info.totals );
os << ws(level+1) << "- aborting: " << info.aborting << "\n";
}
// struct TestCaseInfo {
// enum SpecialProperties{
// None = 0,
// IsHidden = 1 << 1,
// ShouldFail = 1 << 2,
// MayFail = 1 << 3,
// Throws = 1 << 4,
// NonPortable = 1 << 5,
// Benchmark = 1 << 6
// };
//
// bool isHidden() const;
// bool throws() const;
// bool okToFail() const;
// bool expectedToFail() const;
//
// std::string tagsAsString() const;
//
// std::string name;
// std::string className;
// std::string description;
// std::vector<std::string> tags;
// std::vector<std::string> lcaseTags;
// SourceLineInfo lineInfo;
// SpecialProperties properties;
// };
void print( std::ostream& os, int const level, std::string const& title, Catch::TestCaseInfo const& info ) {
os << ws(level ) << title << ":\n"
<< ws(level+1) << "- isHidden(): " << info.isHidden() << "\n"
<< ws(level+1) << "- throws(): " << info.throws() << "\n"
<< ws(level+1) << "- okToFail(): " << info.okToFail() << "\n"
<< ws(level+1) << "- expectedToFail(): " << info.expectedToFail() << "\n"
<< ws(level+1) << "- tagsAsString(): '" << info.tagsAsString() << "'\n"
<< ws(level+1) << "- name: '" << info.name << "'\n"
<< ws(level+1) << "- className: '" << info.className << "'\n"
<< ws(level+1) << "- description: '" << info.description << "'\n"
<< ws(level+1) << "- tags: " << info.tags << "\n"
<< ws(level+1) << "- lcaseTags: " << info.lcaseTags << "\n";
print( os, level+1 , "- lineInfo", info.lineInfo );
os << ws(level+1) << "- properties (flags): 0x" << std::hex << info.properties << std::dec << "\n";
}
// struct TestCaseStats {
// TestCaseInfo testInfo;
// Totals totals;
// std::string stdOut;
// std::string stdErr;
// bool aborting;
// };
void print( std::ostream& os, int const level, std::string const& title, Catch::TestCaseStats const& info ) {
os << ws(level ) << title << ":\n";
print( os, level+1 , "- testInfo", info.testInfo );
print( os, level+1 , "- totals" , info.totals );
os << ws(level+1) << "- stdOut: " << info.stdOut << "\n"
<< ws(level+1) << "- stdErr: " << info.stdErr << "\n"
<< ws(level+1) << "- aborting: " << info.aborting << "\n";
}
// struct SectionInfo {
// std::string name;
// std::string description;
// SourceLineInfo lineInfo;
// };
void print( std::ostream& os, int const level, std::string const& title, Catch::SectionInfo const& info ) {
os << ws(level ) << title << ":\n"
<< ws(level+1) << "- name: " << info.name << "\n"
<< ws(level+1) << "- description: '" << info.description << "'\n";
print( os, level+1 , "- lineInfo", info.lineInfo );
}
// struct SectionStats {
// SectionInfo sectionInfo;
// Counts assertions;
// double durationInSeconds;
// bool missingAssertions;
// };
void print( std::ostream& os, int const level, std::string const& title, Catch::SectionStats const& info ) {
os << ws(level ) << title << ":\n";
print( os, level+1 , "- sectionInfo", info.sectionInfo );
print( os, level+1 , "- assertions" , info.assertions );
os << ws(level+1) << "- durationInSeconds: " << info.durationInSeconds << "\n"
<< ws(level+1) << "- missingAssertions: " << info.missingAssertions << "\n";
}
// struct AssertionInfo
// {
// StringRef macroName;
// SourceLineInfo lineInfo;
// StringRef capturedExpression;
// ResultDisposition::Flags resultDisposition;
// };
void print( std::ostream& os, int const level, std::string const& title, Catch::AssertionInfo const& info ) {
os << ws(level ) << title << ":\n"
<< ws(level+1) << "- macroName: '" << info.macroName << "'\n";
print( os, level+1 , "- lineInfo" , info.lineInfo );
os << ws(level+1) << "- capturedExpression: '" << info.capturedExpression << "'\n"
<< ws(level+1) << "- resultDisposition (flags): 0x" << std::hex << info.resultDisposition << std::dec << "\n";
}
//struct AssertionResultData
//{
// std::string reconstructExpression() const;
//
// std::string message;
// mutable std::string reconstructedExpression;
// LazyExpression lazyExpression;
// ResultWas::OfType resultType;
//};
void print( std::ostream& os, int const level, std::string const& title, Catch::AssertionResultData const& info ) {
os << ws(level ) << title << ":\n"
<< ws(level+1) << "- reconstructExpression(): '" << info.reconstructExpression() << "'\n"
<< ws(level+1) << "- message: '" << info.message << "'\n"
<< ws(level+1) << "- lazyExpression: '" << "(info.lazyExpression)" << "'\n"
<< ws(level+1) << "- resultType: '" << info.resultType << "'\n";
}
//class AssertionResult {
// bool isOk() const;
// bool succeeded() const;
// ResultWas::OfType getResultType() const;
// bool hasExpression() const;
// bool hasMessage() const;
// std::string getExpression() const;
// std::string getExpressionInMacro() const;
// bool hasExpandedExpression() const;
// std::string getExpandedExpression() const;
// std::string getMessage() const;
// SourceLineInfo getSourceInfo() const;
// std::string getTestMacroName() const;
//
// AssertionInfo m_info;
// AssertionResultData m_resultData;
//};
void print( std::ostream& os, int const level, std::string const& title, Catch::AssertionResult const& info ) {
os << ws(level ) << title << ":\n"
<< ws(level+1) << "- isOk(): " << info.isOk() << "\n"
<< ws(level+1) << "- succeeded(): " << info.succeeded() << "\n"
<< ws(level+1) << "- getResultType(): " << info.getResultType() << "\n"
<< ws(level+1) << "- hasExpression(): " << info.hasExpression() << "\n"
<< ws(level+1) << "- hasMessage(): " << info.hasMessage() << "\n"
<< ws(level+1) << "- getExpression(): '" << info.getExpression() << "'\n"
<< ws(level+1) << "- getExpressionInMacro(): '" << info.getExpressionInMacro() << "'\n"
<< ws(level+1) << "- hasExpandedExpression(): " << info.hasExpandedExpression() << "\n"
<< ws(level+1) << "- getExpandedExpression(): " << info.getExpandedExpression() << "'\n"
<< ws(level+1) << "- getMessage(): '" << info.getMessage() << "'\n";
print( os, level+1 , "- getSourceInfo(): ", info.getSourceInfo() );
os << ws(level+1) << "- getTestMacroName(): '" << info.getTestMacroName() << "'\n";
// print( os, level+1 , "- *** m_info (AssertionInfo)", info.m_info );
// print( os, level+1 , "- *** m_resultData (AssertionResultData)", info.m_resultData );
}
// struct AssertionStats {
// AssertionResult assertionResult;
// std::vector<MessageInfo> infoMessages;
// Totals totals;
// };
void print( std::ostream& os, int const level, std::string const& title, Catch::AssertionStats const& info ) {
os << ws(level ) << title << ":\n";
print( os, level+1 , "- assertionResult", info.assertionResult );
print( os, level+1 , "- infoMessages", info.infoMessages );
print( os, level+1 , "- totals", info.totals );
}
// -----------------------------------------------------------------------
// 2. My listener and registration:
//
char const * dashed_line =
"--------------------------------------------------------------------------";
struct MyListener : Catch::TestEventListenerBase {
using TestEventListenerBase::TestEventListenerBase; // inherit constructor
// Get rid of Wweak-tables
~MyListener();
// The whole test run starting
virtual void testRunStarting( Catch::TestRunInfo const& testRunInfo ) override {
std::cout
<< std::boolalpha
<< "\nEvent: testRunStarting:\n";
print( std::cout, 1, "- testRunInfo", testRunInfo );
}
// The whole test run ending
virtual void testRunEnded( Catch::TestRunStats const& testRunStats ) override {
std::cout
<< dashed_line
<< "\nEvent: testRunEnded:\n";
print( std::cout, 1, "- testRunStats", testRunStats );
}
// A test is being skipped (because it is "hidden")
virtual void skipTest( Catch::TestCaseInfo const& testInfo ) override {
std::cout
<< dashed_line
<< "\nEvent: skipTest:\n";
print( std::cout, 1, "- testInfo", testInfo );
}
// Test cases starting
virtual void testCaseStarting( Catch::TestCaseInfo const& testInfo ) override {
std::cout
<< dashed_line
<< "\nEvent: testCaseStarting:\n";
print( std::cout, 1, "- testInfo", testInfo );
}
// Test cases ending
virtual void testCaseEnded( Catch::TestCaseStats const& testCaseStats ) override {
std::cout << "\nEvent: testCaseEnded:\n";
print( std::cout, 1, "testCaseStats", testCaseStats );
}
// Sections starting
virtual void sectionStarting( Catch::SectionInfo const& sectionInfo ) override {
std::cout << "\nEvent: sectionStarting:\n";
print( std::cout, 1, "- sectionInfo", sectionInfo );
}
// Sections ending
virtual void sectionEnded( Catch::SectionStats const& sectionStats ) override {
std::cout << "\nEvent: sectionEnded:\n";
print( std::cout, 1, "- sectionStats", sectionStats );
}
// Assertions before/ after
virtual void assertionStarting( Catch::AssertionInfo const& assertionInfo ) override {
std::cout << "\nEvent: assertionStarting:\n";
print( std::cout, 1, "- assertionInfo", assertionInfo );
}
virtual bool assertionEnded( Catch::AssertionStats const& assertionStats ) override {
std::cout << "\nEvent: assertionEnded:\n";
print( std::cout, 1, "- assertionStats", assertionStats );
return true;
}
};
CATCH_REGISTER_LISTENER( MyListener )
// Get rid of Wweak-tables
MyListener::~MyListener() {}
// -----------------------------------------------------------------------
// 3. Test cases:
//
TEST_CASE( "1: Hidden testcase", "[.hidden]" ) {
}
TEST_CASE( "2: Testcase with sections", "[tag-A][tag-B]" ) {
int i = 42;
REQUIRE( i == 42 );
SECTION("Section 1") {
INFO("Section 1")
i = 7;
SECTION("Section 1.1") {
INFO("Section 1.1")
REQUIRE( i == 42 );
}
}
SECTION("Section 2") {
INFO("Section 2")
REQUIRE( i == 42 );
}
WARN("At end of test case");
}
struct Fixture {
int fortytwo() const {
return 42;
}
};
TEST_CASE_METHOD( Fixture, "3: Testcase with class-based fixture", "[tag-C][tag-D]" ) {
REQUIRE( fortytwo() == 42 );
}
// Compile & run:
// - g++ -std=c++11 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 210-Evt-EventListeners 210-Evt-EventListeners.cpp 000-CatchMain.o && 210-Evt-EventListeners --success
// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% 210-Evt-EventListeners.cpp 000-CatchMain.obj && 210-Evt-EventListeners --success
// Expected compact output (all assertions):
//
// prompt> 210-Evt-EventListeners --reporter compact --success
// result omitted for brevity.

100
examples/CMakeLists.txt Normal file
View File

@@ -0,0 +1,100 @@
#
# Build examples.
#
# Requires BUILD_EXAMPLES to be defined 'true', see ../CMakeLists.txt.
#
cmake_minimum_required( VERSION 3.0 )
project( CatchExamples CXX )
# define folders used:
set( EXAMPLES_DIR ${CATCH_DIR}/examples )
set( HEADER_DIR ${CATCH_DIR}/single_include )
# single-file sources:
set( SOURCES_SINGLE_FILE
010-TestCase.cpp
)
# multiple-file modules:
set( SOURCES_020
020-TestCase-1.cpp
020-TestCase-2.cpp
)
# main for idiomatic test sources:
set( SOURCES_IDIOMATIC_MAIN
000-CatchMain.cpp
)
# sources to combine with 000-CatchMain.cpp:
set( SOURCES_IDIOMATIC_TESTS
030-Asn-Require-Check.cpp
100-Fix-Section.cpp
110-Fix-ClassFixture.cpp
120-Bdd-ScenarioGivenWhenThen.cpp
210-Evt-EventListeners.cpp
)
# check if all sources are listed, warn if not:
set( SOURCES_ALL
${SOURCES_020}
${SOURCES_SINGLE_FILE}
${SOURCES_IDIOMATIC_MAIN}
${SOURCES_IDIOMATIC_TESTS}
)
foreach( name ${SOURCES_ALL} )
list( APPEND SOURCES_ALL_PATH ${EXAMPLES_DIR}/${name} )
endforeach()
CheckFileList( SOURCES_ALL_PATH ${EXAMPLES_DIR} )
# create target names:
string( REPLACE ".cpp" "" BASENAMES_SINGLE_FILE "${SOURCES_SINGLE_FILE}" )
string( REPLACE ".cpp" "" BASENAMES_IDIOMATIC_TESTS "${SOURCES_IDIOMATIC_TESTS}" )
set( TARGETS_SINGLE_FILE ${BASENAMES_SINGLE_FILE} )
set( TARGETS_IDIOMATIC_TESTS ${BASENAMES_IDIOMATIC_TESTS} )
set( TARGETS_ALL ${TARGETS_SINGLE_FILE} ${TARGETS_IDIOMATIC_TESTS} 020-TestCase CatchMain )
# define program targets:
add_library( CatchMain OBJECT ${EXAMPLES_DIR}/${SOURCES_IDIOMATIC_MAIN} ${HEADER_DIR}/catch.hpp )
add_executable( 020-TestCase ${EXAMPLES_DIR}/020-TestCase-1.cpp ${EXAMPLES_DIR}/020-TestCase-2.cpp ${HEADER_DIR}/catch.hpp )
foreach( name ${TARGETS_SINGLE_FILE} )
add_executable( ${name} ${EXAMPLES_DIR}/${name}.cpp ${HEADER_DIR}/catch.hpp )
endforeach()
foreach( name ${TARGETS_IDIOMATIC_TESTS} )
add_executable( ${name} ${EXAMPLES_DIR}/${name}.cpp $<TARGET_OBJECTS:CatchMain> ${HEADER_DIR}/catch.hpp )
endforeach()
foreach( name ${TARGETS_ALL} )
target_include_directories( ${name} PRIVATE ${HEADER_DIR} )
set_property(TARGET ${name} PROPERTY CXX_STANDARD 11)
# Add desired warnings
if ( CMAKE_CXX_COMPILER_ID MATCHES "Clang|AppleClang|GNU" )
target_compile_options( ${name} PRIVATE -Wall -Wextra -Wunreachable-code )
endif()
# Clang specific warning go here
if ( CMAKE_CXX_COMPILER_ID MATCHES "Clang" )
# Actually keep these
target_compile_options( ${name} PRIVATE -Wweak-vtables -Wexit-time-destructors -Wglobal-constructors -Wmissing-noreturn )
endif()
if ( CMAKE_CXX_COMPILER_ID MATCHES "MSVC" )
target_compile_options( ${name} PRIVATE /W4 /w44265 /WX )
endif()
endforeach()

View File

@@ -9,6 +9,7 @@
#ifndef TWOBLUECUBES_CATCH_HPP_INCLUDED
#define TWOBLUECUBES_CATCH_HPP_INCLUDED
#ifdef __clang__
# pragma clang system_header
#elif defined __GNUC__
@@ -19,12 +20,20 @@
#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER)
# define CATCH_IMPL
# define CATCH_CONFIG_ALL_PARTS
#endif
// In the impl file, we want to have access to all parts of the headers
// Can also be used to sanely support PCHs
#if defined(CATCH_CONFIG_ALL_PARTS)
# define CATCH_CONFIG_EXTERNAL_INTERFACES
# if defined(CATCH_CONFIG_DISABLE_MATCHERS)
# undef CATCH_CONFIG_DISABLE_MATCHERS
# endif
# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
#endif
#if !defined(CATCH_CONFIG_IMPL_ONLY)
#include "internal/catch_platform.h"
#ifdef CATCH_IMPL
@@ -34,6 +43,7 @@
# endif
#endif
#include "internal/catch_user_interfaces.h"
#include "internal/catch_tag_alias_autoregistrar.h"
#include "internal/catch_test_registry.h"
#include "internal/catch_capture.hpp"
@@ -61,6 +71,8 @@
#include "internal/catch_external_interfaces.h"
#endif
#endif // ! CATCH_CONFIG_IMPL_ONLY
#ifdef CATCH_IMPL
#include "internal/catch_impl.hpp"
#endif
@@ -69,6 +81,7 @@
#include "internal/catch_default_main.hpp"
#endif
#if !defined(CATCH_CONFIG_IMPL_ONLY)
#ifdef CLARA_CONFIG_MAIN_NOT_DEFINED
# undef CLARA_CONFIG_MAIN
@@ -327,6 +340,8 @@ using Catch::Detail::Approx;
#endif
#endif // ! CATCH_CONFIG_IMPL_ONLY
#include "internal/catch_reenable_warnings.h"
#endif // TWOBLUECUBES_CATCH_HPP_INCLUDED

View File

@@ -8,22 +8,26 @@
#include "catch_approx.h"
#include <cmath>
#include <limits>
namespace {
// Performs equivalent check of std::fabs(lhs - rhs) <= margin
// But without the subtraction to allow for INFINITY in comparison
bool marginComparison(double lhs, double rhs, double margin) {
return (lhs + margin >= rhs) && (rhs + margin >= lhs);
}
}
namespace Catch {
namespace Detail {
double dmax(double lhs, double rhs) {
if (lhs < rhs) {
return rhs;
}
return lhs;
}
Approx::Approx ( double value )
: m_epsilon( std::numeric_limits<float>::epsilon()*100 ),
m_margin( 0.0 ),
m_scale( 1.0 ),
m_scale( 0.0 ),
m_value( value )
{}
@@ -32,9 +36,15 @@ namespace Detail {
}
std::string Approx::toString() const {
std::ostringstream oss;
oss << "Approx( " << ::Catch::Detail::stringify( m_value ) << " )";
return oss.str();
ReusableStringStream rss;
rss << "Approx( " << ::Catch::Detail::stringify( m_value ) << " )";
return rss.str();
}
bool Approx::equalityComparisonImpl(const double other) const {
// First try with fixed margin, then compute margin based on epsilon, scale and Approx's value
// Thanks to Richard Harris for his help refining the scaled margin value
return marginComparison(m_value, other, m_margin) || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(m_value)));
}
} // end namespace Detail

View File

@@ -8,19 +8,18 @@
#ifndef TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED
#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED
#include "catch_enforce.h"
#include "catch_tostring.h"
#include <cmath>
#include <type_traits>
#include <stdexcept>
namespace Catch {
namespace Detail {
double dmax(double lhs, double rhs);
class Approx {
private:
bool equalityComparisonImpl(double other) const;
public:
explicit Approx ( double value );
@@ -42,14 +41,8 @@ namespace Detail {
template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
friend bool operator == ( const T& lhs, Approx const& rhs ) {
// Thanks to Richard Harris for his help refining this formula
auto lhs_v = static_cast<double>(lhs);
bool relativeOK = std::fabs(lhs_v - rhs.m_value) < rhs.m_epsilon * (rhs.m_scale +
dmax(std::fabs(lhs_v), std::fabs(rhs.m_value)));
if (relativeOK) {
return true;
}
return std::fabs(lhs_v - rhs.m_value) <= rhs.m_margin;
return rhs.equalityComparisonImpl(lhs_v);
}
template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
@@ -89,14 +82,28 @@ namespace Detail {
template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
Approx& epsilon( T const& newEpsilon ) {
m_epsilon = static_cast<double>(newEpsilon);
double epsilonAsDouble = static_cast<double>(newEpsilon);
if( epsilonAsDouble < 0 || epsilonAsDouble > 1.0 ) {
throw std::domain_error
( "Invalid Approx::epsilon: " +
Catch::Detail::stringify( epsilonAsDouble ) +
", Approx::epsilon has to be between 0 and 1" );
}
m_epsilon = epsilonAsDouble;
return *this;
}
template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
Approx& margin( T const& newMargin ) {
m_margin = static_cast<double>(newMargin);
CATCH_ENFORCE(m_margin >= 0, "Invalid Approx::margin: " << m_margin << ", Approx::Margin has to be non-negative.");
double marginAsDouble = static_cast<double>(newMargin);
if( marginAsDouble < 0 ) {
throw std::domain_error
( "Invalid Approx::margin: " +
Catch::Detail::stringify( marginAsDouble ) +
", Approx::Margin has to be non-negative." );
}
m_margin = marginAsDouble;
return *this;
}

View File

@@ -8,15 +8,13 @@
#include "catch_assertionhandler.h"
#include "catch_assertionresult.h"
#include "catch_interfaces_capture.h"
#include "catch_interfaces_runner.h"
#include "catch_interfaces_config.h"
#include "catch_context.h"
#include "catch_debugger.h"
#include "catch_interfaces_registry_hub.h"
#include "catch_capture_matchers.h"
#include <cassert>
#include "catch_run_context.h"
namespace Catch {
@@ -56,86 +54,55 @@ namespace Catch {
SourceLineInfo const& lineInfo,
StringRef capturedExpression,
ResultDisposition::Flags resultDisposition )
: m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition }
{
getCurrentContext().getResultCapture()->assertionStarting( m_assertionInfo );
: m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition },
m_resultCapture( getResultCapture() )
{}
void AssertionHandler::handleExpr( ITransientExpression const& expr ) {
m_resultCapture.handleExpr( m_assertionInfo, expr, m_reaction );
}
AssertionHandler::~AssertionHandler() {
if ( m_inExceptionGuard ) {
handle( ResultWas::ThrewException, "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE" );
getCurrentContext().getResultCapture()->exceptionEarlyReported();
}
}
void AssertionHandler::handle( ITransientExpression const& expr ) {
bool negated = isFalseTest( m_assertionInfo.resultDisposition );
bool result = expr.getResult() != negated;
handle( result ? ResultWas::Ok : ResultWas::ExpressionFailed, &expr, negated );
}
void AssertionHandler::handle( ResultWas::OfType resultType ) {
handle( resultType, nullptr, false );
}
void AssertionHandler::handle( ResultWas::OfType resultType, StringRef const& message ) {
AssertionResultData data( resultType, LazyExpression( false ) );
data.message = message;
handle( data, nullptr );
}
void AssertionHandler::handle( ResultWas::OfType resultType, ITransientExpression const* expr, bool negated ) {
AssertionResultData data( resultType, LazyExpression( negated ) );
handle( data, expr );
}
void AssertionHandler::handle( AssertionResultData const& resultData, ITransientExpression const* expr ) {
getResultCapture().assertionRun();
AssertionResult assertionResult{ m_assertionInfo, resultData };
assertionResult.m_resultData.lazyExpression.m_transientExpression = expr;
getResultCapture().assertionEnded( assertionResult );
if( !assertionResult.isOk() ) {
m_shouldDebugBreak = getCurrentContext().getConfig()->shouldDebugBreak();
m_shouldThrow =
getCurrentContext().getRunner()->aborting() ||
(m_assertionInfo.resultDisposition & ResultDisposition::Normal);
}
void AssertionHandler::handleMessage(ResultWas::OfType resultType, StringRef const& message) {
m_resultCapture.handleMessage( m_assertionInfo, resultType, message, m_reaction );
}
auto AssertionHandler::allowThrows() const -> bool {
return getCurrentContext().getConfig()->allowThrows();
}
auto AssertionHandler::shouldDebugBreak() const -> bool {
return m_shouldDebugBreak;
}
void AssertionHandler::reactWithDebugBreak() const {
if (m_shouldDebugBreak) {
///////////////////////////////////////////////////////////////////
// To inspect the state during test, you need to go one level up the callstack
// To go back to the test and change execution, jump over the reactWithoutDebugBreak() call
///////////////////////////////////////////////////////////////////
void AssertionHandler::complete() {
setCompleted();
if( m_reaction.shouldDebugBreak ) {
// If you find your debugger stopping you here then go one level up on the
// call-stack for the code that caused it (typically a failed assertion)
// (To go back to the test and change execution, jump over the throw, next)
CATCH_BREAK_INTO_DEBUGGER();
}
reactWithoutDebugBreak();
}
void AssertionHandler::reactWithoutDebugBreak() const {
if( m_shouldThrow )
if( m_reaction.shouldThrow )
throw Catch::TestFailureException();
}
void AssertionHandler::useActiveException() {
handle( ResultWas::ThrewException, Catch::translateActiveException() );
void AssertionHandler::setCompleted() {
m_completed = true;
}
void AssertionHandler::setExceptionGuard() {
assert( m_inExceptionGuard == false );
m_inExceptionGuard = true;
void AssertionHandler::handleUnexpectedInflightException() {
m_resultCapture.handleUnexpectedInflightException( m_assertionInfo, Catch::translateActiveException(), m_reaction );
}
void AssertionHandler::unsetExceptionGuard() {
assert( m_inExceptionGuard == true );
m_inExceptionGuard = false;
void AssertionHandler::handleExceptionThrownAsExpected() {
m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction);
}
void AssertionHandler::handleExceptionNotThrownAsExpected() {
m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction);
}
void AssertionHandler::handleUnexpectedExceptionNotThrown() {
m_resultCapture.handleUnexpectedExceptionNotThrown( m_assertionInfo, m_reaction );
}
void AssertionHandler::handleThrowingCallSkipped() {
m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction);
}
// This is the overload that takes a string and infers the Equals matcher from it

View File

@@ -8,17 +8,21 @@
#ifndef TWOBLUECUBES_CATCH_ASSERTIONHANDLER_H_INCLUDED
#define TWOBLUECUBES_CATCH_ASSERTIONHANDLER_H_INCLUDED
#include "catch_decomposer.h"
#include "catch_assertioninfo.h"
#include "catch_decomposer.h"
#include "catch_interfaces_capture.h"
namespace Catch {
struct TestFailureException{};
struct AssertionResultData;
struct IResultCapture;
class RunContext;
class LazyExpression {
friend class AssertionHandler;
friend struct AssertionStats;
friend class RunContext;
ITransientExpression const* m_transientExpression = nullptr;
bool m_isNegated;
@@ -32,11 +36,16 @@ namespace Catch {
friend auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream&;
};
struct AssertionReaction {
bool shouldDebugBreak = false;
bool shouldThrow = false;
};
class AssertionHandler {
AssertionInfo m_assertionInfo;
bool m_shouldDebugBreak = false;
bool m_shouldThrow = false;
bool m_inExceptionGuard = false;
AssertionReaction m_reaction;
bool m_completed = false;
IResultCapture& m_resultCapture;
public:
AssertionHandler
@@ -44,26 +53,32 @@ namespace Catch {
SourceLineInfo const& lineInfo,
StringRef capturedExpression,
ResultDisposition::Flags resultDisposition );
~AssertionHandler();
~AssertionHandler() {
if ( !m_completed ) {
m_resultCapture.handleIncomplete( m_assertionInfo );
}
}
void handle( ITransientExpression const& expr );
template<typename T>
void handle( ExprLhs<T> const& expr ) {
handle( expr.makeUnaryExpr() );
void handleExpr( ExprLhs<T> const& expr ) {
handleExpr( expr.makeUnaryExpr() );
}
void handle( ResultWas::OfType resultType );
void handle( ResultWas::OfType resultType, StringRef const& message );
void handle( ResultWas::OfType resultType, ITransientExpression const* expr, bool negated );
void handle( AssertionResultData const& resultData, ITransientExpression const* expr );
void handleExpr( ITransientExpression const& expr );
auto shouldDebugBreak() const -> bool;
void handleMessage(ResultWas::OfType resultType, StringRef const& message);
void handleExceptionThrownAsExpected();
void handleUnexpectedExceptionNotThrown();
void handleExceptionNotThrownAsExpected();
void handleThrowingCallSkipped();
void handleUnexpectedInflightException();
void complete();
void setCompleted();
// query
auto allowThrows() const -> bool;
void reactWithDebugBreak() const;
void reactWithoutDebugBreak() const;
void useActiveException();
void setExceptionGuard();
void unsetExceptionGuard();
};
void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef matcherString );

View File

@@ -17,10 +17,9 @@ namespace Catch {
if( reconstructedExpression.empty() ) {
if( lazyExpression ) {
// !TBD Use stringstream for now, but rework above to pass stream in
std::ostringstream oss;
oss << lazyExpression;
reconstructedExpression = oss.str();
ReusableStringStream rss;
rss << lazyExpression;
reconstructedExpression = rss.str();
}
}
return reconstructedExpression;
@@ -55,7 +54,7 @@ namespace Catch {
std::string AssertionResult::getExpression() const {
if( isFalseTest( m_info.resultDisposition ) )
return "!(" + std::string(m_info.capturedExpression) + ")";
return "!(" + m_info.capturedExpression + ")";
else
return m_info.capturedExpression;
}
@@ -66,9 +65,9 @@ namespace Catch {
expr = m_info.capturedExpression;
else {
expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 );
expr += m_info.macroName;
expr += m_info.macroName.c_str();
expr += "( ";
expr += m_info.capturedExpression;
expr += m_info.capturedExpression.c_str();
expr += " )";
}
return expr;
@@ -92,7 +91,7 @@ namespace Catch {
return m_info.lineInfo;
}
std::string AssertionResult::getTestMacroName() const {
StringRef AssertionResult::getTestMacroName() const {
return m_info.macroName;
}

View File

@@ -47,7 +47,7 @@ namespace Catch {
std::string getExpandedExpression() const;
std::string getMessage() const;
SourceLineInfo getSourceInfo() const;
std::string getTestMacroName() const;
StringRef getTestMacroName() const;
//protected:
AssertionInfo m_info;

View File

@@ -11,7 +11,6 @@
#include "catch_assertionhandler.h"
#include "catch_message.h"
#include "catch_interfaces_capture.h"
#include "catch_debugger.h"
#if !defined(CATCH_CONFIG_DISABLE)
@@ -22,48 +21,33 @@
#endif
#if defined(CATCH_CONFIG_FAST_COMPILE)
///////////////////////////////////////////////////////////////////////////////
// We can speedup compilation significantly by breaking into debugger lower in
// the callstack, because then we don't have to expand CATCH_BREAK_INTO_DEBUGGER
// macro in each assertion
#define INTERNAL_CATCH_REACT( handler ) \
handler.reactWithDebugBreak();
///////////////////////////////////////////////////////////////////////////////
// Another way to speed-up compilation is to omit local try-catch for REQUIRE*
// macros.
// This can potentially cause false negative, if the test code catches
// the exception before it propagates back up to the runner.
#define INTERNAL_CATCH_TRY( capturer ) capturer.setExceptionGuard();
#define INTERNAL_CATCH_CATCH( capturer ) capturer.unsetExceptionGuard();
#define INTERNAL_CATCH_TRY
#define INTERNAL_CATCH_CATCH( capturer )
#else // CATCH_CONFIG_FAST_COMPILE
///////////////////////////////////////////////////////////////////////////////
// In the event of a failure works out if the debugger needs to be invoked
// and/or an exception thrown and takes appropriate action.
// This needs to be done as a macro so the debugger will stop in the user
// source code rather than in Catch library code
#define INTERNAL_CATCH_REACT( handler ) \
if( handler.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \
handler.reactWithoutDebugBreak();
#define INTERNAL_CATCH_TRY( capturer ) try
#define INTERNAL_CATCH_CATCH( capturer ) catch(...) { capturer.useActiveException(); }
#define INTERNAL_CATCH_TRY try
#define INTERNAL_CATCH_CATCH( handler ) catch(...) { handler.handleUnexpectedInflightException(); }
#endif
#define INTERNAL_CATCH_REACT( handler ) handler.complete();
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \
do { \
Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \
INTERNAL_CATCH_TRY( catchAssertionHandler ) { \
INTERNAL_CATCH_TRY { \
CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
catchAssertionHandler.handle( Catch::Decomposer() <= __VA_ARGS__ ); \
catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); \
CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
} INTERNAL_CATCH_CATCH( catchAssertionHandler ) \
INTERNAL_CATCH_REACT( catchAssertionHandler ) \
} while( Catch::isTrue( false && static_cast<bool>( !!(__VA_ARGS__) ) ) ) // the expression here is never evaluated at runtime but it forces the compiler to give it a look
} while( (void)0, false && static_cast<bool>( !!(__VA_ARGS__) ) ) // the expression here is never evaluated at runtime but it forces the compiler to give it a look
// The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&.
///////////////////////////////////////////////////////////////////////////////
@@ -82,13 +66,13 @@
Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \
try { \
static_cast<void>(__VA_ARGS__); \
catchAssertionHandler.handle( Catch::ResultWas::Ok ); \
catchAssertionHandler.handleExceptionNotThrownAsExpected(); \
} \
catch( ... ) { \
catchAssertionHandler.useActiveException(); \
catchAssertionHandler.handleUnexpectedInflightException(); \
} \
INTERNAL_CATCH_REACT( catchAssertionHandler ) \
} while( Catch::alwaysFalse() )
} while( false )
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_THROWS( macroName, resultDisposition, ... ) \
@@ -97,15 +81,15 @@
if( catchAssertionHandler.allowThrows() ) \
try { \
static_cast<void>(__VA_ARGS__); \
catchAssertionHandler.handle( Catch::ResultWas::DidntThrowException ); \
catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \
} \
catch( ... ) { \
catchAssertionHandler.handle( Catch::ResultWas::Ok ); \
catchAssertionHandler.handleExceptionThrownAsExpected(); \
} \
else \
catchAssertionHandler.handle( Catch::ResultWas::Ok ); \
catchAssertionHandler.handleThrowingCallSkipped(); \
INTERNAL_CATCH_REACT( catchAssertionHandler ) \
} while( Catch::alwaysFalse() )
} while( false )
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \
@@ -114,31 +98,31 @@
if( catchAssertionHandler.allowThrows() ) \
try { \
static_cast<void>(expr); \
catchAssertionHandler.handle( Catch::ResultWas::DidntThrowException ); \
catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \
} \
catch( exceptionType const& ) { \
catchAssertionHandler.handle( Catch::ResultWas::Ok ); \
catchAssertionHandler.handleExceptionThrownAsExpected(); \
} \
catch( ... ) { \
catchAssertionHandler.useActiveException(); \
catchAssertionHandler.handleUnexpectedInflightException(); \
} \
else \
catchAssertionHandler.handle( Catch::ResultWas::Ok ); \
catchAssertionHandler.handleThrowingCallSkipped(); \
INTERNAL_CATCH_REACT( catchAssertionHandler ) \
} while( Catch::alwaysFalse() )
} while( false )
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \
do { \
Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \
catchAssertionHandler.handle( messageType, ( Catch::MessageStream() << __VA_ARGS__ + ::Catch::StreamEndStop() ).m_stream.str() ); \
catchAssertionHandler.handleMessage( messageType, ( Catch::MessageStream() << __VA_ARGS__ + ::Catch::StreamEndStop() ).m_stream.str() ); \
INTERNAL_CATCH_REACT( catchAssertionHandler ) \
} while( Catch::alwaysFalse() )
} while( false )
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_INFO( macroName, log ) \
Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log;
Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage )( Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log );
///////////////////////////////////////////////////////////////////////////////
// Although this is matcher-based, it can be used with just a string
@@ -148,15 +132,15 @@
if( catchAssertionHandler.allowThrows() ) \
try { \
static_cast<void>(__VA_ARGS__); \
catchAssertionHandler.handle( Catch::ResultWas::DidntThrowException ); \
catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \
} \
catch( ... ) { \
handleExceptionMatchExpr( catchAssertionHandler, matcher, #matcher ); \
Catch::handleExceptionMatchExpr( catchAssertionHandler, matcher, #matcher ); \
} \
else \
catchAssertionHandler.handle( Catch::ResultWas::Ok ); \
catchAssertionHandler.handleThrowingCallSkipped(); \
INTERNAL_CATCH_REACT( catchAssertionHandler ) \
} while( Catch::alwaysFalse() )
} while( false )
#endif // CATCH_CONFIG_DISABLE

View File

@@ -18,7 +18,7 @@ namespace Catch {
void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef matcherString ) {
std::string exceptionMessage = Catch::translateActiveException();
MatchExpr<std::string, StringMatcher const&> expr( exceptionMessage, matcher, matcherString );
handler.handle( expr );
handler.handleExpr( expr );
}
} // namespace Catch

View File

@@ -10,6 +10,7 @@
#include "catch_capture.hpp"
#include "catch_matchers.h"
#include "catch_matchers_floating.h"
#include "catch_matchers_string.h"
#include "catch_matchers_vector.h"
@@ -20,18 +21,14 @@ namespace Catch {
ArgT const& m_arg;
MatcherT m_matcher;
StringRef m_matcherString;
bool m_result;
public:
MatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef matcherString )
: m_arg( arg ),
: ITransientExpression{ true, matcher.match( arg ) },
m_arg( arg ),
m_matcher( matcher ),
m_matcherString( matcherString ),
m_result( matcher.match( arg ) )
m_matcherString( matcherString )
{}
auto isBinaryExpression() const -> bool override { return true; }
auto getResult() const -> bool override { return m_result; }
void streamReconstructedExpression( std::ostream &os ) const override {
auto matcherAsString = m_matcher.toString();
os << Catch::Detail::stringify( m_arg ) << ' ';
@@ -58,11 +55,11 @@ namespace Catch {
#define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \
do { \
Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \
INTERNAL_CATCH_TRY( catchAssertionHandler ) { \
catchAssertionHandler.handle( Catch::makeMatchExpr( arg, matcher, #matcher ) ); \
INTERNAL_CATCH_TRY { \
catchAssertionHandler.handleExpr( Catch::makeMatchExpr( arg, matcher, #matcher ) ); \
} INTERNAL_CATCH_CATCH( catchAssertionHandler ) \
INTERNAL_CATCH_REACT( catchAssertionHandler ) \
} while( Catch::alwaysFalse() )
} while( false )
///////////////////////////////////////////////////////////////////////////////
@@ -72,17 +69,17 @@ namespace Catch {
if( catchAssertionHandler.allowThrows() ) \
try { \
static_cast<void>(__VA_ARGS__ ); \
catchAssertionHandler.handle( Catch::ResultWas::DidntThrowException ); \
catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \
} \
catch( exceptionType const& ex ) { \
catchAssertionHandler.handle( Catch::makeMatchExpr( ex, matcher, #matcher ) ); \
catchAssertionHandler.handleExpr( Catch::makeMatchExpr( ex, matcher, #matcher ) ); \
} \
catch( ... ) { \
catchAssertionHandler.useActiveException(); \
catchAssertionHandler.handleUnexpectedInflightException(); \
} \
else \
catchAssertionHandler.handle( Catch::ResultWas::Ok ); \
catchAssertionHandler.handleThrowingCallSkipped(); \
INTERNAL_CATCH_REACT( catchAssertionHandler ) \
} while( Catch::alwaysFalse() )
} while( false )
#endif // TWOBLUECUBES_CATCH_CAPTURE_MATCHERS_HPP_INCLUDED

View File

@@ -15,10 +15,6 @@
namespace Catch {
SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line ) noexcept
: file( _file ),
line( _line )
{}
bool SourceLineInfo::empty() const noexcept {
return file[0] == '\0';
}
@@ -38,10 +34,6 @@ namespace Catch {
return os;
}
bool isTrue( bool value ){ return value; }
bool alwaysTrue() { return true; }
bool alwaysFalse() { return false; }
std::string StreamEndStop::operator+() const {
return std::string();
}

View File

@@ -43,7 +43,10 @@ namespace Catch {
struct SourceLineInfo {
SourceLineInfo() = delete;
SourceLineInfo( char const* _file, std::size_t _line ) noexcept;
SourceLineInfo( char const* _file, std::size_t _line ) noexcept
: file( _file ),
line( _line )
{}
SourceLineInfo( SourceLineInfo const& other ) = default;
SourceLineInfo( SourceLineInfo && ) = default;
@@ -60,11 +63,6 @@ namespace Catch {
std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info );
// This is just here to avoid compiler warnings with macro constants and boolean literals
bool isTrue( bool value );
bool alwaysTrue();
bool alwaysFalse();
// Use this in variadic streaming macros to allow
// >> +StreamEndStop
// as well as

View File

@@ -7,7 +7,7 @@
#include "catch_config.hpp"
#include "catch_enforce.h"
#include "catch_stream.h"
#include "catch_stringref.h"
namespace Catch {
@@ -58,16 +58,7 @@ namespace Catch {
Verbosity Config::verbosity() const { return m_data.verbosity; }
IStream const* Config::openStream() {
if( m_data.outputFilename.empty() )
return new CoutStream();
else if( m_data.outputFilename[0] == '%' ) {
if( m_data.outputFilename == "%debug" )
return new DebugOutStream();
else
CATCH_ERROR( "Unrecognised stream: '" << m_data.outputFilename << "'" );
}
else
return new FileStream( m_data.outputFilename );
return Catch::makeStream(m_data.outputFilename);
}
} // end namespace Catch

View File

@@ -23,6 +23,8 @@
#include "catch_debugger.h"
#include "catch_windows_h_proxy.h"
#include <sstream>
namespace Catch {
namespace {

View File

@@ -20,7 +20,7 @@ namespace Catch {
return m_runner;
}
virtual IConfigPtr getConfig() const override {
virtual IConfigPtr const& getConfig() const override {
return m_config;
}
@@ -45,21 +45,16 @@ namespace Catch {
IResultCapture* m_resultCapture = nullptr;
};
namespace {
Context* currentContext = nullptr;
}
IMutableContext& getCurrentMutableContext() {
if( !currentContext )
currentContext = new Context();
return *currentContext;
}
IContext& getCurrentContext() {
return getCurrentMutableContext();
IMutableContext *IMutableContext::currentContext = nullptr;
void IMutableContext::createContext()
{
currentContext = new Context();
}
void cleanUpContext() {
delete currentContext;
currentContext = nullptr;
delete IMutableContext::currentContext;
IMutableContext::currentContext = nullptr;
}
IContext::~IContext() = default;
IMutableContext::~IMutableContext() = default;

View File

@@ -15,6 +15,7 @@ namespace Catch {
struct IResultCapture;
struct IRunner;
struct IConfig;
struct IMutableContext;
using IConfigPtr = std::shared_ptr<IConfig const>;
@@ -24,7 +25,7 @@ namespace Catch {
virtual IResultCapture* getResultCapture() = 0;
virtual IRunner* getRunner() = 0;
virtual IConfigPtr getConfig() const = 0;
virtual IConfigPtr const& getConfig() const = 0;
};
struct IMutableContext : IContext
@@ -33,10 +34,26 @@ namespace Catch {
virtual void setResultCapture( IResultCapture* resultCapture ) = 0;
virtual void setRunner( IRunner* runner ) = 0;
virtual void setConfig( IConfigPtr const& config ) = 0;
private:
static IMutableContext *currentContext;
friend IMutableContext& getCurrentMutableContext();
friend void cleanUpContext();
static void createContext();
};
IContext& getCurrentContext();
IMutableContext& getCurrentMutableContext();
inline IMutableContext& getCurrentMutableContext()
{
if( !IMutableContext::currentContext )
IMutableContext::createContext();
return *IMutableContext::currentContext;
}
inline IContext& getCurrentContext()
{
return getCurrentMutableContext();
}
void cleanUpContext();
}

View File

@@ -14,13 +14,15 @@
#ifdef CATCH_PLATFORM_MAC
#include <assert.h>
#include <stdbool.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/sysctl.h>
# include <assert.h>
# include <stdbool.h>
# include <sys/types.h>
# include <unistd.h>
# include <sys/sysctl.h>
# include <cstddef>
# include <ostream>
namespace Catch {
namespace Catch {
// The following function is taken directly from the following technical note:
// http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html

View File

@@ -40,7 +40,7 @@ namespace Catch {
#ifdef CATCH_TRAP
#define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { CATCH_TRAP(); }
#else
#define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue();
#define CATCH_BREAK_INTO_DEBUGGER() (void)0, 0
#endif
#endif // TWOBLUECUBES_CATCH_DEBUGGER_H_INCLUDED

View File

@@ -11,7 +11,7 @@
#include "catch_tostring.h"
#include "catch_stringref.h"
#include <ostream>
#include <iosfwd>
#ifdef _MSC_VER
#pragma warning(push)
@@ -24,27 +24,32 @@
namespace Catch {
struct ITransientExpression {
virtual auto isBinaryExpression() const -> bool = 0;
virtual auto getResult() const -> bool = 0;
auto isBinaryExpression() const -> bool { return m_isBinaryExpression; }
auto getResult() const -> bool { return m_result; }
virtual void streamReconstructedExpression( std::ostream &os ) const = 0;
// We don't actually need a virtual destructore, but many static analysers
ITransientExpression( bool isBinaryExpression, bool result )
: m_isBinaryExpression( isBinaryExpression ),
m_result( result )
{}
// We don't actually need a virtual destructor, but many static analysers
// complain if it's not here :-(
virtual ~ITransientExpression();
bool m_isBinaryExpression;
bool m_result;
};
void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs );
template<typename LhsT, typename RhsT>
class BinaryExpr : public ITransientExpression {
bool m_result;
LhsT m_lhs;
StringRef m_op;
RhsT m_rhs;
auto isBinaryExpression() const -> bool override { return true; }
auto getResult() const -> bool override { return m_result; }
void streamReconstructedExpression( std::ostream &os ) const override {
formatReconstructedExpression
( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) );
@@ -52,7 +57,7 @@ namespace Catch {
public:
BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs )
: m_result( comparisonResult ),
: ITransientExpression{ true, comparisonResult },
m_lhs( lhs ),
m_op( op ),
m_rhs( rhs )
@@ -63,15 +68,15 @@ namespace Catch {
class UnaryExpr : public ITransientExpression {
LhsT m_lhs;
auto isBinaryExpression() const -> bool override { return false; }
auto getResult() const -> bool override { return m_lhs ? true : false; }
void streamReconstructedExpression( std::ostream &os ) const override {
os << Catch::Detail::stringify( m_lhs );
}
public:
UnaryExpr( LhsT lhs ) : m_lhs( lhs ) {}
explicit UnaryExpr( LhsT lhs )
: ITransientExpression{ false, lhs ? true : false },
m_lhs( lhs )
{}
};
@@ -103,43 +108,43 @@ namespace Catch {
class ExprLhs {
LhsT m_lhs;
public:
ExprLhs( LhsT lhs ) : m_lhs( lhs ) {}
explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {}
template<typename RhsT>
auto operator == ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
return BinaryExpr<LhsT, RhsT const&>( compareEqual( m_lhs, rhs ), m_lhs, "==", rhs );
return { compareEqual( m_lhs, rhs ), m_lhs, "==", rhs };
}
auto operator == ( bool rhs ) -> BinaryExpr<LhsT, bool> const {
return BinaryExpr<LhsT, bool>( m_lhs == rhs, m_lhs, "==", rhs );
return { m_lhs == rhs, m_lhs, "==", rhs };
}
template<typename RhsT>
auto operator != ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
return BinaryExpr<LhsT, RhsT const&>( compareNotEqual( m_lhs, rhs ), m_lhs, "!=", rhs );
return { compareNotEqual( m_lhs, rhs ), m_lhs, "!=", rhs };
}
auto operator != ( bool rhs ) -> BinaryExpr<LhsT, bool> const {
return BinaryExpr<LhsT, bool>( m_lhs != rhs, m_lhs, "!=", rhs );
return { m_lhs != rhs, m_lhs, "!=", rhs };
}
template<typename RhsT>
auto operator > ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
return BinaryExpr<LhsT, RhsT const&>( m_lhs > rhs, m_lhs, ">", rhs );
return { m_lhs > rhs, m_lhs, ">", rhs };
}
template<typename RhsT>
auto operator < ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
return BinaryExpr<LhsT, RhsT const&>( m_lhs < rhs, m_lhs, "<", rhs );
return { m_lhs < rhs, m_lhs, "<", rhs };
}
template<typename RhsT>
auto operator >= ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
return BinaryExpr<LhsT, RhsT const&>( m_lhs >= rhs, m_lhs, ">=", rhs );
return { m_lhs >= rhs, m_lhs, ">=", rhs };
}
template<typename RhsT>
auto operator <= ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
return BinaryExpr<LhsT, RhsT const&>( m_lhs <= rhs, m_lhs, "<=", rhs );
return { m_lhs <= rhs, m_lhs, "<=", rhs };
}
auto makeUnaryExpr() const -> UnaryExpr<LhsT> {
return UnaryExpr<LhsT>( m_lhs );
return UnaryExpr<LhsT>{ m_lhs };
}
};
@@ -153,10 +158,11 @@ namespace Catch {
struct Decomposer {
template<typename T>
auto operator <= ( T const& lhs ) -> ExprLhs<T const&> {
return ExprLhs<T const&>( lhs );
return ExprLhs<T const&>{ lhs };
}
auto operator <=( bool value ) -> ExprLhs<bool> {
return ExprLhs<bool>( value );
return ExprLhs<bool>{ value };
}
};

View File

@@ -8,12 +8,13 @@
#define TWOBLUECUBES_CATCH_ENFORCE_H_INCLUDED
#include "catch_common.h"
#include "catch_stream.h"
#include <sstream>
#include <stdexcept>
#include <iosfwd>
#define CATCH_PREPARE_EXCEPTION( type, msg ) \
type( static_cast<std::ostringstream&&>( std::ostringstream() << msg ).str() )
type( static_cast<std::ostringstream&&>( Catch::ReusableStringStream().get() << msg ).str() )
#define CATCH_INTERNAL_ERROR( msg ) \
throw CATCH_PREPARE_EXCEPTION( std::logic_error, CATCH_INTERNAL_LINEINFO << ": Internal Catch error: " << msg);
#define CATCH_ERROR( msg ) \

View File

@@ -11,4 +11,10 @@
#include "catch_console_colour.h"
#include "catch_reporter_registrars.hpp"
// Allow users to base their work off existing reporters
#include "../reporters/catch_reporter_compact.h"
#include "../reporters/catch_reporter_console.h"
#include "../reporters/catch_reporter_junit.h"
#include "../reporters/catch_reporter_xml.h"
#endif // TWOBLUECUBES_CATCH_EXTERNAL_INTERFACES_H_INCLUDED

View File

@@ -12,6 +12,11 @@
#include "catch_context.h"
#include "catch_interfaces_capture.h"
#if defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
#endif
namespace {
// Report the error condition
void reportFatal( char const * const message ) {
@@ -174,3 +179,7 @@ namespace Catch {
# endif // CATCH_CONFIG_POSIX_SIGNALS
#endif // not Windows
#if defined(__GNUC__)
# pragma GCC diagnostic pop
#endif

View File

@@ -11,6 +11,7 @@
#include <string>
#include "catch_stringref.h"
#include "catch_result_type.h"
namespace Catch {
@@ -22,13 +23,14 @@ namespace Catch {
struct Counts;
struct BenchmarkInfo;
struct BenchmarkStats;
struct AssertionReaction;
struct ITransientExpression;
struct IResultCapture {
virtual ~IResultCapture();
virtual void assertionStarting( AssertionInfo const& info ) = 0;
virtual void assertionEnded( AssertionResult const& result ) = 0;
virtual bool sectionStarted( SectionInfo const& sectionInfo,
Counts& assertions ) = 0;
virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0;
@@ -40,16 +42,40 @@ namespace Catch {
virtual void pushScopedMessage( MessageInfo const& message ) = 0;
virtual void popScopedMessage( MessageInfo const& message ) = 0;
virtual std::string getCurrentTestName() const = 0;
virtual const AssertionResult* getLastResult() const = 0;
virtual void exceptionEarlyReported() = 0;
virtual void handleFatalErrorCondition( StringRef message ) = 0;
virtual void handleExpr
( AssertionInfo const& info,
ITransientExpression const& expr,
AssertionReaction& reaction ) = 0;
virtual void handleMessage
( AssertionInfo const& info,
ResultWas::OfType resultType,
StringRef const& message,
AssertionReaction& reaction ) = 0;
virtual void handleUnexpectedExceptionNotThrown
( AssertionInfo const& info,
AssertionReaction& reaction ) = 0;
virtual void handleUnexpectedInflightException
( AssertionInfo const& info,
std::string const& message,
AssertionReaction& reaction ) = 0;
virtual void handleIncomplete
( AssertionInfo const& info ) = 0;
virtual void handleNonExpr
( AssertionInfo const &info,
ResultWas::OfType resultType,
AssertionReaction &reaction ) = 0;
virtual bool lastAssertionPassed() = 0;
virtual void assertionPassed() = 0;
virtual void assertionRun() = 0;
// Deprecated, do not use:
virtual std::string getCurrentTestName() const = 0;
virtual const AssertionResult* getLastResult() const = 0;
virtual void exceptionEarlyReported() = 0;
};
IResultCapture& getResultCapture();

View File

@@ -73,7 +73,9 @@ namespace Catch {
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \
static std::string translatorName( signature ); \
namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); }\
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); } \
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
static std::string translatorName( signature )
#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature )

View File

@@ -8,11 +8,11 @@
#include "catch_leak_detector.h"
namespace Catch {
#ifdef CATCH_CONFIG_WINDOWS_CRTDBG
#include <crtdbg.h>
namespace Catch {
LeakDetector::LeakDetector() {
int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
flag |= _CRTDBG_LEAK_CHECK_DF;
@@ -23,11 +23,10 @@ namespace Catch {
// Change this to leaking allocation's number to break there
_CrtSetBreakAlloc(-1);
}
}
#else
LeakDetector::LeakDetector(){}
Catch::LeakDetector::LeakDetector() {}
#endif
}

View File

@@ -115,13 +115,14 @@ namespace Catch {
}
for( auto const& tagCount : tagCounts ) {
std::ostringstream oss;
oss << " " << std::setw(2) << tagCount.second.count << " ";
ReusableStringStream rss;
rss << " " << std::setw(2) << tagCount.second.count << " ";
auto str = rss.str();
auto wrapper = Column( tagCount.second.all() )
.initialIndent( 0 )
.indent( oss.str().size() )
.indent( str.size() )
.width( CATCH_CONFIG_CONSOLE_WIDTH-10 );
Catch::cout() << oss.str() << wrapper << '\n';
Catch::cout() << str << wrapper << '\n';
}
Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl;
return tagCounts.size();

View File

@@ -43,13 +43,13 @@ namespace Matchers {
virtual bool match( PtrT* arg ) const = 0;
};
template<typename ObjectT, typename ComparatorT = ObjectT>
struct MatcherBase : MatcherUntypedBase, MatcherMethod<ObjectT> {
template<typename T>
struct MatcherBase : MatcherUntypedBase, MatcherMethod<T> {
MatchAllOf<ComparatorT> operator && ( MatcherBase const& other ) const;
MatchAnyOf<ComparatorT> operator || ( MatcherBase const& other ) const;
MatchNotOf<ComparatorT> operator ! () const;
MatchAllOf<T> operator && ( MatcherBase const& other ) const;
MatchAnyOf<T> operator || ( MatcherBase const& other ) const;
MatchNotOf<T> operator ! () const;
};
template<typename ArgT>
@@ -133,17 +133,17 @@ namespace Matchers {
MatcherBase<ArgT> const& m_underlyingMatcher;
};
template<typename ObjectT, typename ComparatorT>
MatchAllOf<ComparatorT> MatcherBase<ObjectT, ComparatorT>::operator && ( MatcherBase const& other ) const {
return MatchAllOf<ComparatorT>() && *this && other;
template<typename T>
MatchAllOf<T> MatcherBase<T>::operator && ( MatcherBase const& other ) const {
return MatchAllOf<T>() && *this && other;
}
template<typename ObjectT, typename ComparatorT>
MatchAnyOf<ComparatorT> MatcherBase<ObjectT, ComparatorT>::operator || ( MatcherBase const& other ) const {
return MatchAnyOf<ComparatorT>() || *this || other;
template<typename T>
MatchAnyOf<T> MatcherBase<T>::operator || ( MatcherBase const& other ) const {
return MatchAnyOf<T>() || *this || other;
}
template<typename ObjectT, typename ComparatorT>
MatchNotOf<ComparatorT> MatcherBase<ObjectT, ComparatorT>::operator ! () const {
return MatchNotOf<ComparatorT>( *this );
template<typename T>
MatchNotOf<T> MatcherBase<T>::operator ! () const {
return MatchNotOf<T>( *this );
}
} // namespace Impl

View File

@@ -0,0 +1,139 @@
/*
* Created by Martin on 07/11/2017.
*
* Distributed under the Boost Software License, Version 1.0. (See accompanying
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*/
#include "catch_matchers_floating.h"
#include "catch_tostring.h"
#include <cstdlib>
#include <cstdint>
#include <cstring>
#include <stdexcept>
namespace Catch {
namespace Matchers {
namespace Floating {
enum class FloatingPointKind : uint8_t {
Float,
Double
};
}
}
}
namespace {
template <typename T>
struct Converter;
template <>
struct Converter<float> {
static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated");
Converter(float f) {
std::memcpy(&i, &f, sizeof(f));
}
int32_t i;
};
template <>
struct Converter<double> {
static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated");
Converter(double d) {
std::memcpy(&i, &d, sizeof(d));
}
int64_t i;
};
template <typename T>
auto convert(T t) -> Converter<T> {
return Converter<T>(t);
}
template <typename FP>
bool almostEqualUlps(FP lhs, FP rhs, int maxUlpDiff) {
// Comparison with NaN should always be false.
// This way we can rule it out before getting into the ugly details
if (std::isnan(lhs) || std::isnan(rhs)) {
return false;
}
auto lc = convert(lhs);
auto rc = convert(rhs);
if ((lc.i < 0) != (rc.i < 0)) {
// Potentially we can have +0 and -0
return lhs == rhs;
}
auto ulpDiff = std::abs(lc.i - rc.i);
return ulpDiff <= maxUlpDiff;
}
}
namespace Catch {
namespace Matchers {
namespace Floating {
WithinAbsMatcher::WithinAbsMatcher(double target, double margin)
:m_target{ target }, m_margin{ margin } {
if (m_margin < 0) {
throw std::domain_error("Allowed margin difference has to be >= 0");
}
}
// Performs equivalent check of std::fabs(lhs - rhs) <= margin
// But without the subtraction to allow for INFINITY in comparison
bool WithinAbsMatcher::match(double const& matchee) const {
return (matchee + m_margin >= m_target) && (m_target + m_margin >= m_margin);
}
std::string WithinAbsMatcher::describe() const {
return "is within " + ::Catch::Detail::stringify(m_margin) + " of " + ::Catch::Detail::stringify(m_target);
}
WithinUlpsMatcher::WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType)
:m_target{ target }, m_ulps{ ulps }, m_type{ baseType } {
if (m_ulps < 0) {
throw std::domain_error("Allowed ulp difference has to be >= 0");
}
}
bool WithinUlpsMatcher::match(double const& matchee) const {
switch (m_type) {
case FloatingPointKind::Float:
return almostEqualUlps<float>(static_cast<float>(matchee), static_cast<float>(m_target), m_ulps);
case FloatingPointKind::Double:
return almostEqualUlps<double>(matchee, m_target, m_ulps);
default:
throw std::domain_error("Unknown FloatingPointKind value");
}
}
std::string WithinUlpsMatcher::describe() const {
return "is within " + std::to_string(m_ulps) + " ULPs of " + ::Catch::Detail::stringify(m_target) + ((m_type == FloatingPointKind::Float)? "f" : "");
}
}// namespace Floating
Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff) {
return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Double);
}
Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff) {
return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Float);
}
Floating::WithinAbsMatcher WithinAbs(double target, double margin) {
return Floating::WithinAbsMatcher(target, margin);
}
} // namespace Matchers
} // namespace Catch

View File

@@ -0,0 +1,53 @@
/*
* Created by Martin on 07/11/2017.
*
* Distributed under the Boost Software License, Version 1.0. (See accompanying
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*/
#ifndef TWOBLUECUBES_CATCH_MATCHERS_FLOATING_H_INCLUDED
#define TWOBLUECUBES_CATCH_MATCHERS_FLOATING_H_INCLUDED
#include "catch_matchers.h"
#include <type_traits>
#include <cmath>
namespace Catch {
namespace Matchers {
namespace Floating {
enum class FloatingPointKind : uint8_t;
struct WithinAbsMatcher : MatcherBase<double> {
WithinAbsMatcher(double target, double margin);
bool match(double const& matchee) const override;
std::string describe() const override;
private:
double m_target;
double m_margin;
};
struct WithinUlpsMatcher : MatcherBase<double> {
WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType);
bool match(double const& matchee) const override;
std::string describe() const override;
private:
double m_target;
int m_ulps;
FloatingPointKind m_type;
};
} // namespace Floating
// The following functions create the actual matcher objects.
// This allows the types to be inferred
Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff);
Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff);
Floating::WithinAbsMatcher WithinAbs(double target, double margin);
} // namespace Matchers
} // namespace Catch
#endif // TWOBLUECUBES_CATCH_MATCHERS_FLOATING_H_INCLUDED

View File

@@ -8,6 +8,9 @@
#include "catch_matchers_string.h"
#include "catch_string_manip.h"
#include "catch_tostring.h"
#include <regex>
namespace Catch {
namespace Matchers {
@@ -74,6 +77,23 @@ namespace Matchers {
return endsWith( m_comparator.adjustString( source ), m_comparator.m_str );
}
RegexMatcher::RegexMatcher(std::string regex, CaseSensitive::Choice caseSensitivity): m_regex(std::move(regex)), m_caseSensitivity(caseSensitivity) {}
bool RegexMatcher::match(std::string const& matchee) const {
auto flags = std::regex::ECMAScript; // ECMAScript is the default syntax option anyway
if (m_caseSensitivity == CaseSensitive::Choice::No) {
flags |= std::regex::icase;
}
auto reg = std::regex(m_regex, flags);
return std::regex_match(matchee, reg);
}
std::string RegexMatcher::describe() const {
return "matches " + ::Catch::Detail::stringify(m_regex) + ((m_caseSensitivity == CaseSensitive::Choice::Yes)? " case sensitively" : " case insensitively");
}
} // namespace StdString
@@ -90,5 +110,9 @@ namespace Matchers {
return StdString::StartsWithMatcher( StdString::CasedString( str, caseSensitivity) );
}
StdString::RegexMatcher Matches(std::string const& regex, CaseSensitive::Choice caseSensitivity) {
return StdString::RegexMatcher(regex, caseSensitivity);
}
} // namespace Matchers
} // namespace Catch

View File

@@ -52,6 +52,16 @@ namespace Matchers {
bool match( std::string const& source ) const override;
};
struct RegexMatcher : MatcherBase<std::string> {
RegexMatcher( std::string regex, CaseSensitive::Choice caseSensitivity );
bool match( std::string const& matchee ) const override;
std::string describe() const override;
private:
std::string m_regex;
CaseSensitive::Choice m_caseSensitivity;
};
} // namespace StdString
@@ -62,6 +72,7 @@ namespace Matchers {
StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
StdString::RegexMatcher Matches( std::string const& regex, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
} // namespace Matchers
} // namespace Catch

View File

@@ -10,13 +10,36 @@
#include "catch_matchers.h"
#include <algorithm>
namespace Catch {
namespace Matchers {
namespace Vector {
namespace Detail {
template <typename InputIterator, typename T>
size_t count(InputIterator first, InputIterator last, T const& item) {
size_t cnt = 0;
for (; first != last; ++first) {
if (*first == item) {
++cnt;
}
}
return cnt;
}
template <typename InputIterator, typename T>
bool contains(InputIterator first, InputIterator last, T const& item) {
for (; first != last; ++first) {
if (*first == item) {
return true;
}
}
return false;
}
}
template<typename T>
struct ContainsElementMatcher : MatcherBase<std::vector<T>, T> {
struct ContainsElementMatcher : MatcherBase<std::vector<T>> {
ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {}
@@ -37,7 +60,7 @@ namespace Matchers {
};
template<typename T>
struct ContainsMatcher : MatcherBase<std::vector<T>, std::vector<T> > {
struct ContainsMatcher : MatcherBase<std::vector<T>> {
ContainsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {}
@@ -67,7 +90,7 @@ namespace Matchers {
};
template<typename T>
struct EqualsMatcher : MatcherBase<std::vector<T>, std::vector<T> > {
struct EqualsMatcher : MatcherBase<std::vector<T>> {
EqualsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {}
@@ -89,6 +112,46 @@ namespace Matchers {
std::vector<T> const& m_comparator;
};
template<typename T>
struct UnorderedEqualsMatcher : MatcherBase<std::vector<T>> {
UnorderedEqualsMatcher(std::vector<T> const& target) : m_target(target) {}
bool match(std::vector<T> const& vec) const override {
// Note: This is a reimplementation of std::is_permutation,
// because I don't want to include <algorithm> inside the common path
if (m_target.size() != vec.size()) {
return false;
}
auto lfirst = m_target.begin(), llast = m_target.end();
auto rfirst = vec.begin(), rlast = vec.end();
// Cut common prefix to optimize checking of permuted parts
while (lfirst != llast && *lfirst != *rfirst) {
++lfirst; ++rfirst;
}
if (lfirst == llast) {
return true;
}
for (auto mid = lfirst; mid != llast; ++mid) {
// Skip already counted items
if (Detail::contains(lfirst, mid, *mid)) {
continue;
}
size_t num_vec = Detail::count(rfirst, rlast, *mid);
if (num_vec == 0 || Detail::count(lfirst, llast, *mid) != num_vec) {
return false;
}
}
return true;
}
std::string describe() const override {
return "UnorderedEquals: " + ::Catch::Detail::stringify(m_target);
}
private:
std::vector<T> const& m_target;
};
} // namespace Vector
// The following functions create the actual matcher objects.
@@ -109,6 +172,11 @@ namespace Matchers {
return Vector::EqualsMatcher<T>( comparator );
}
template<typename T>
Vector::UnorderedEqualsMatcher<T> UnorderedEquals(std::vector<T> const& target) {
return Vector::UnorderedEqualsMatcher<T>(target);
}
} // namespace Matchers
} // namespace Catch

View File

@@ -49,11 +49,18 @@ namespace Catch {
getResultCapture().pushScopedMessage( m_info );
}
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable:4996) // std::uncaught_exception is deprecated in C++17
#endif
ScopedMessage::~ScopedMessage() {
if ( !std::uncaught_exception() ){
getResultCapture().popScopedMessage(m_info);
}
}
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
} // end namespace Catch

View File

@@ -9,9 +9,9 @@
#define TWOBLUECUBES_CATCH_MESSAGE_H_INCLUDED
#include <string>
#include <sstream>
#include "catch_result_type.h"
#include "catch_common.h"
#include "catch_stream.h"
namespace Catch {
@@ -40,8 +40,7 @@ namespace Catch {
return *this;
}
// !TBD reuse a global/ thread-local stream
std::ostringstream m_stream;
ReusableStringStream m_stream;
};
struct MessageBuilder : MessageStream {
@@ -60,7 +59,7 @@ namespace Catch {
class ScopedMessage {
public:
ScopedMessage( MessageBuilder const& builder );
explicit ScopedMessage( MessageBuilder const& builder );
~ScopedMessage();
MessageInfo m_info;

View File

@@ -11,7 +11,7 @@
#ifdef __APPLE__
# include <TargetConditionals.h>
# if TARGET_OS_MAC == 1
# if TARGET_OS_OSX == 1
# define CATCH_PLATFORM_MAC
# elif TARGET_OS_IPHONE == 1
# define CATCH_PLATFORM_IPHONE

View File

@@ -7,8 +7,6 @@
#ifndef TWOBLUECUBES_CATCH_RANDOM_NUMBER_GENERATOR_H_INCLUDED
#define TWOBLUECUBES_CATCH_RANDOM_NUMBER_GENERATOR_H_INCLUDED
#include "catch_random_number_generator.h"
#include <algorithm>
namespace Catch {

View File

@@ -87,6 +87,7 @@ namespace Catch {
delete getTheRegistryHub();
getTheRegistryHub() = nullptr;
cleanUpContext();
ReusableStringStream::cleanup();
}
std::string translateActiveException() {
return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException();

View File

@@ -29,7 +29,7 @@ namespace Catch {
public:
ReporterRegistrar( std::string const& name ) {
explicit ReporterRegistrar( std::string const& name ) {
getMutableRegistryHub().registerReporter( name, std::make_shared<ReporterFactory>() );
}
};

View File

@@ -22,7 +22,6 @@ namespace Catch {
}
bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; }
bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; }
bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; }
} // end namespace Catch

View File

@@ -47,7 +47,7 @@ namespace Catch {
ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs );
bool shouldContinueOnFailure( int flags );
bool isFalseTest( int flags );
inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; }
bool shouldSuppressFailure( int flags );
} // end namespace Catch

View File

@@ -6,40 +6,59 @@
#include <cassert>
#include <algorithm>
#include <sstream>
namespace Catch {
StreamRedirect::StreamRedirect(std::ostream& stream, std::string& targetString)
: m_stream(stream),
m_prevBuf(stream.rdbuf()),
m_targetString(targetString) {
stream.rdbuf(m_oss.rdbuf());
}
class RedirectedStream {
std::ostream& m_originalStream;
std::ostream& m_redirectionStream;
std::streambuf* m_prevBuf;
StreamRedirect::~StreamRedirect() {
m_targetString += m_oss.str();
m_stream.rdbuf(m_prevBuf);
}
public:
RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream )
: m_originalStream( originalStream ),
m_redirectionStream( redirectionStream ),
m_prevBuf( m_originalStream.rdbuf() )
{
m_originalStream.rdbuf( m_redirectionStream.rdbuf() );
}
~RedirectedStream() {
m_originalStream.rdbuf( m_prevBuf );
}
};
StdErrRedirect::StdErrRedirect(std::string & targetString)
:m_cerrBuf(cerr().rdbuf()), m_clogBuf(clog().rdbuf()),
m_targetString(targetString) {
cerr().rdbuf(m_oss.rdbuf());
clog().rdbuf(m_oss.rdbuf());
}
class RedirectedStdOut {
ReusableStringStream m_rss;
RedirectedStream m_cout;
public:
RedirectedStdOut() : m_cout( Catch::cout(), m_rss.get() ) {}
auto str() const -> std::string { return m_rss.str(); }
};
// StdErr has two constituent streams in C++, std::cerr and std::clog
// This means that we need to redirect 2 streams into 1 to keep proper
// order of writes
class RedirectedStdErr {
ReusableStringStream m_rss;
RedirectedStream m_cerr;
RedirectedStream m_clog;
public:
RedirectedStdErr()
: m_cerr( Catch::cerr(), m_rss.get() ),
m_clog( Catch::clog(), m_rss.get() )
{}
auto str() const -> std::string { return m_rss.str(); }
};
StdErrRedirect::~StdErrRedirect() {
m_targetString += m_oss.str();
cerr().rdbuf(m_cerrBuf);
clog().rdbuf(m_clogBuf);
}
RunContext::RunContext(IConfigPtr const& _config, IStreamingReporterPtr&& reporter)
: m_runInfo(_config->name()),
m_context(getCurrentMutableContext()),
m_config(_config),
m_reporter(std::move(reporter)),
m_lastAssertionInfo{ "", SourceLineInfo("",0), "", ResultDisposition::Normal }
m_lastAssertionInfo{ "", SourceLineInfo("",0), "", ResultDisposition::Normal },
m_includeSuccessfulResults( m_config->includeSuccessfulResults() )
{
m_context.setRunner(this);
m_context.setConfig(m_config);
@@ -108,27 +127,33 @@ namespace Catch {
return *m_reporter;
}
void RunContext::assertionStarting(AssertionInfo const& info) {
m_reporter->assertionStarting( info );
}
void RunContext::assertionEnded(AssertionResult const & result) {
if (result.getResultType() == ResultWas::Ok) {
m_totals.assertions.passed++;
m_lastAssertionPassed = true;
} else if (!result.isOk()) {
m_lastAssertionPassed = false;
if( m_activeTestCase->getTestCaseInfo().okToFail() )
m_totals.assertions.failedButOk++;
else
m_totals.assertions.failed++;
}
else {
m_lastAssertionPassed = true;
}
// We have no use for the return value (whether messages should be cleared), because messages were made scoped
// and should be let to clear themselves out.
static_cast<void>(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals)));
// Reset working state
m_lastAssertionInfo = { "", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}", m_lastAssertionInfo.resultDisposition };
resetAssertionInfo();
m_lastResult = result;
}
void RunContext::resetAssertionInfo() {
m_lastAssertionInfo.macroName = StringRef();
m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr;
}
bool RunContext::sectionStarted(SectionInfo const & sectionInfo, Counts & assertions) {
ITracker& sectionTracker = SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(sectionInfo.name, sectionInfo.lineInfo));
@@ -218,7 +243,7 @@ namespace Catch {
tempResult.message = message;
AssertionResult result(m_lastAssertionInfo, tempResult);
getResultCapture().assertionEnded(result);
assertionEnded(result);
handleUnfinishedSections();
@@ -247,17 +272,13 @@ namespace Catch {
}
bool RunContext::lastAssertionPassed() {
return m_totals.assertions.passed == (m_prevPassed + 1);
return m_lastAssertionPassed;
}
void RunContext::assertionPassed() {
m_lastAssertionPassed = true;
++m_totals.assertions.passed;
m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}";
m_lastAssertionInfo.macroName = "";
}
void RunContext::assertionRun() {
m_prevPassed = m_totals.assertions.passed;
resetAssertionInfo();
}
bool RunContext::aborting() const {
@@ -271,18 +292,22 @@ namespace Catch {
Counts prevAssertions = m_totals.assertions;
double duration = 0;
m_shouldReportUnexpected = true;
m_lastAssertionInfo = { "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal };
seedRng(*m_config);
Timer timer;
try {
m_lastAssertionInfo = { "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal };
seedRng(*m_config);
Timer timer;
timer.start();
if (m_reporter->getPreferences().shouldRedirectStdOut) {
StreamRedirect coutRedir(cout(), redirectedCout);
StdErrRedirect errRedir(redirectedCerr);
RedirectedStdOut redirectedStdOut;
RedirectedStdErr redirectedStdErr;
timer.start();
invokeActiveTestCase();
redirectedCout += redirectedStdOut.str();
redirectedCerr += redirectedStdErr.str();
} else {
timer.start();
invokeActiveTestCase();
}
duration = timer.getElapsedSeconds();
@@ -291,12 +316,9 @@ namespace Catch {
} catch (...) {
// Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions
// are reported without translation at the point of origin.
if (m_shouldReportUnexpected) {
AssertionHandler
( m_lastAssertionInfo.macroName,
m_lastAssertionInfo.lineInfo,
m_lastAssertionInfo.capturedExpression,
m_lastAssertionInfo.resultDisposition ).useActiveException();
if( m_shouldReportUnexpected ) {
AssertionReaction dummyReaction;
handleUnexpectedInflightException( m_lastAssertionInfo, translateActiveException(), dummyReaction );
}
}
m_testCaseTracker->close();
@@ -326,6 +348,113 @@ namespace Catch {
m_unfinishedSections.clear();
}
void RunContext::handleExpr(
AssertionInfo const& info,
ITransientExpression const& expr,
AssertionReaction& reaction
) {
m_reporter->assertionStarting( info );
bool negated = isFalseTest( info.resultDisposition );
bool result = expr.getResult() != negated;
if( result ) {
if (!m_includeSuccessfulResults) {
assertionPassed();
}
else {
reportExpr(info, ResultWas::Ok, &expr, negated);
}
}
else {
reportExpr(info, ResultWas::ExpressionFailed, &expr, negated );
populateReaction( reaction );
}
}
void RunContext::reportExpr(
AssertionInfo const &info,
ResultWas::OfType resultType,
ITransientExpression const *expr,
bool negated ) {
m_lastAssertionInfo = info;
AssertionResultData data( resultType, LazyExpression( negated ) );
AssertionResult assertionResult{ info, data };
assertionResult.m_resultData.lazyExpression.m_transientExpression = expr;
assertionEnded( assertionResult );
}
void RunContext::handleMessage(
AssertionInfo const& info,
ResultWas::OfType resultType,
StringRef const& message,
AssertionReaction& reaction
) {
m_reporter->assertionStarting( info );
m_lastAssertionInfo = info;
AssertionResultData data( resultType, LazyExpression( false ) );
data.message = message;
AssertionResult assertionResult{ m_lastAssertionInfo, data };
assertionEnded( assertionResult );
if( !assertionResult.isOk() )
populateReaction( reaction );
}
void RunContext::handleUnexpectedExceptionNotThrown(
AssertionInfo const& info,
AssertionReaction& reaction
) {
handleNonExpr(info, Catch::ResultWas::DidntThrowException, reaction);
}
void RunContext::handleUnexpectedInflightException(
AssertionInfo const& info,
std::string const& message,
AssertionReaction& reaction
) {
m_lastAssertionInfo = info;
AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) );
data.message = message;
AssertionResult assertionResult{ info, data };
assertionEnded( assertionResult );
populateReaction( reaction );
}
void RunContext::populateReaction( AssertionReaction& reaction ) {
reaction.shouldDebugBreak = m_config->shouldDebugBreak();
reaction.shouldThrow = aborting() || (m_lastAssertionInfo.resultDisposition & ResultDisposition::Normal);
}
void RunContext::handleIncomplete(
AssertionInfo const& info
) {
m_lastAssertionInfo = info;
AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) );
data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE";
AssertionResult assertionResult{ info, data };
assertionEnded( assertionResult );
}
void RunContext::handleNonExpr(
AssertionInfo const &info,
ResultWas::OfType resultType,
AssertionReaction &reaction
) {
m_lastAssertionInfo = info;
AssertionResultData data( resultType, LazyExpression( false ) );
AssertionResult assertionResult{ info, data };
assertionEnded( assertionResult );
if( !assertionResult.isOk() )
populateReaction( reaction );
}
IResultCapture& getResultCapture() {
if (auto* capture = getCurrentContext().getResultCapture())
return *capture;

View File

@@ -28,34 +28,6 @@ namespace Catch {
struct IMutableContext;
class StreamRedirect {
public:
StreamRedirect(std::ostream& stream, std::string& targetString);
~StreamRedirect();
private:
std::ostream& m_stream;
std::streambuf* m_prevBuf;
std::ostringstream m_oss;
std::string& m_targetString;
};
// StdErr has two constituent streams in C++, std::cerr and std::clog
// This means that we need to redirect 2 streams into 1 to keep proper
// order of writes and cannot use StreamRedirect on its own
class StdErrRedirect {
public:
StdErrRedirect(std::string& targetString);
~StdErrRedirect();
private:
std::streambuf* m_cerrBuf;
std::streambuf* m_clogBuf;
std::ostringstream m_oss;
std::string& m_targetString;
};
///////////////////////////////////////////////////////////////////////////
class RunContext : public IResultCapture, public IRunner {
@@ -64,35 +36,54 @@ namespace Catch {
RunContext( RunContext const& ) = delete;
RunContext& operator =( RunContext const& ) = delete;
explicit RunContext(IConfigPtr const& _config, IStreamingReporterPtr&& reporter);
explicit RunContext( IConfigPtr const& _config, IStreamingReporterPtr&& reporter );
virtual ~RunContext();
~RunContext() override;
void testGroupStarting(std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount);
void testGroupEnded(std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount);
void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount );
void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount );
Totals runTest(TestCase const& testCase);
IConfigPtr config() const;
IStreamingReporter& reporter() const;
private: // IResultCapture
public: // IResultCapture
void assertionStarting(AssertionInfo const& info) override;
void assertionEnded(AssertionResult const& result) override;
// Assertion handlers
void handleExpr
( AssertionInfo const& info,
ITransientExpression const& expr,
AssertionReaction& reaction ) override;
void handleMessage
( AssertionInfo const& info,
ResultWas::OfType resultType,
StringRef const& message,
AssertionReaction& reaction ) override;
void handleUnexpectedExceptionNotThrown
( AssertionInfo const& info,
AssertionReaction& reaction ) override;
void handleUnexpectedInflightException
( AssertionInfo const& info,
std::string const& message,
AssertionReaction& reaction ) override;
void handleIncomplete
( AssertionInfo const& info ) override;
void handleNonExpr
( AssertionInfo const &info,
ResultWas::OfType resultType,
AssertionReaction &reaction ) override;
bool sectionStarted( SectionInfo const& sectionInfo, Counts& assertions ) override;
bool testForMissingAssertions(Counts& assertions);
void sectionEnded(SectionEndInfo const& endInfo) override;
void sectionEndedEarly(SectionEndInfo const& endInfo) override;
void sectionEnded( SectionEndInfo const& endInfo ) override;
void sectionEndedEarly( SectionEndInfo const& endInfo ) override;
void benchmarkStarting( BenchmarkInfo const& info ) override;
void benchmarkEnded( BenchmarkStats const& stats ) override;
void pushScopedMessage(MessageInfo const& message) override;
void popScopedMessage(MessageInfo const& message) override;
void pushScopedMessage( MessageInfo const& message ) override;
void popScopedMessage( MessageInfo const& message ) override;
std::string getCurrentTestName() const override;
@@ -106,17 +97,27 @@ namespace Catch {
void assertionPassed() override;
void assertionRun() override;
public:
// !TBD We need to do this another way!
bool aborting() const override;
private:
void runCurrentTest(std::string& redirectedCout, std::string& redirectedCerr);
void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr );
void invokeActiveTestCase();
void resetAssertionInfo();
bool testForMissingAssertions( Counts& assertions );
void assertionEnded( AssertionResult const& result );
void reportExpr
( AssertionInfo const &info,
ResultWas::OfType resultType,
ITransientExpression const *expr,
bool negated );
void populateReaction( AssertionReaction& reaction );
private:
void handleUnfinishedSections();
@@ -135,12 +136,11 @@ namespace Catch {
std::vector<SectionEndInfo> m_unfinishedSections;
std::vector<ITracker*> m_activeSections;
TrackerContext m_trackerContext;
std::size_t m_prevPassed = 0;
bool m_lastAssertionPassed = false;
bool m_shouldReportUnexpected = true;
bool m_includeSuccessfulResults;
};
IResultCapture& getResultCapture();
} // end namespace Catch
#endif // TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED

View File

@@ -22,102 +22,114 @@
#include <cstdlib>
#include <iomanip>
namespace Catch {
namespace {
const int MaxExitCode = 255;
using Catch::IStreamingReporterPtr;
using Catch::IConfigPtr;
using Catch::Config;
namespace {
const int MaxExitCode = 255;
IStreamingReporterPtr createReporter(std::string const& reporterName, IConfigPtr const& config) {
auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, config);
CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << "'");
IStreamingReporterPtr createReporter(std::string const& reporterName, IConfigPtr const& config) {
auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, config);
CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << "'");
return reporter;
}
return reporter;
}
#ifndef CATCH_CONFIG_DEFAULT_REPORTER
#define CATCH_CONFIG_DEFAULT_REPORTER "console"
#endif
IStreamingReporterPtr makeReporter(std::shared_ptr<Config> const& config) {
auto const& reporterNames = config->getReporterNames();
if (reporterNames.empty())
return createReporter(CATCH_CONFIG_DEFAULT_REPORTER, config);
IStreamingReporterPtr makeReporter(std::shared_ptr<Config> const& config) {
auto const& reporterNames = config->getReporterNames();
if (reporterNames.empty())
return createReporter(CATCH_CONFIG_DEFAULT_REPORTER, config);
IStreamingReporterPtr reporter;
for (auto const& name : reporterNames)
addReporter(reporter, createReporter(name, config));
return reporter;
}
IStreamingReporterPtr reporter;
for (auto const& name : reporterNames)
addReporter(reporter, createReporter(name, config));
return reporter;
}
#undef CATCH_CONFIG_DEFAULT_REPORTER
void addListeners(IStreamingReporterPtr& reporters, IConfigPtr const& config) {
auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners();
for (auto const& listener : listeners)
addReporter(reporters, listener->create(Catch::ReporterConfig(config)));
}
Catch::Totals runTests(std::shared_ptr<Config> const& config) {
using namespace Catch;
IStreamingReporterPtr reporter = makeReporter(config);
addListeners(reporter, config);
RunContext context(config, std::move(reporter));
Totals totals;
context.testGroupStarting(config->name(), 1, 1);
TestSpec testSpec = config->testSpec();
if (!testSpec.hasFilters())
testSpec = TestSpecParser(ITagAliasRegistry::get()).parse("~[.]").testSpec(); // All not hidden tests
auto const& allTestCases = getAllTestCasesSorted(*config);
for (auto const& testCase : allTestCases) {
if (!context.aborting() && matchTest(testCase, testSpec, *config))
totals += context.runTest(testCase);
else
context.reporter().skipTest(testCase);
void addListeners(IStreamingReporterPtr& reporters, IConfigPtr const& config) {
auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners();
for (auto const& listener : listeners)
addReporter(reporters, listener->create(Catch::ReporterConfig(config)));
}
context.testGroupEnded(config->name(), totals, 1, 1);
return totals;
}
void applyFilenamesAsTags(Catch::IConfig const& config) {
using namespace Catch;
auto& tests = const_cast<std::vector<TestCase>&>(getAllTestCasesSorted(config));
for (auto& testCase : tests) {
auto tags = testCase.tags;
Catch::Totals runTests(std::shared_ptr<Config> const& config) {
IStreamingReporterPtr reporter = makeReporter(config);
addListeners(reporter, config);
std::string filename = testCase.lineInfo.file;
auto lastSlash = filename.find_last_of("\\/");
if (lastSlash != std::string::npos) {
filename.erase(0, lastSlash);
filename[0] = '#';
RunContext context(config, std::move(reporter));
Totals totals;
context.testGroupStarting(config->name(), 1, 1);
TestSpec testSpec = config->testSpec();
if (!testSpec.hasFilters())
testSpec = TestSpecParser(ITagAliasRegistry::get()).parse("~[.]").testSpec(); // All not hidden tests
auto const& allTestCases = getAllTestCasesSorted(*config);
for (auto const& testCase : allTestCases) {
if (!context.aborting() && matchTest(testCase, testSpec, *config))
totals += context.runTest(testCase);
else
context.reporter().skipTest(testCase);
}
auto lastDot = filename.find_last_of('.');
if (lastDot != std::string::npos) {
filename.erase(lastDot);
}
tags.push_back(std::move(filename));
setTags(testCase, tags);
context.testGroupEnded(config->name(), totals, 1, 1);
return totals;
}
}
}
void applyFilenamesAsTags(Catch::IConfig const& config) {
auto& tests = const_cast<std::vector<TestCase>&>(getAllTestCasesSorted(config));
for (auto& testCase : tests) {
auto tags = testCase.tags;
namespace Catch {
std::string filename = testCase.lineInfo.file;
auto lastSlash = filename.find_last_of("\\/");
if (lastSlash != std::string::npos) {
filename.erase(0, lastSlash);
filename[0] = '#';
}
auto lastDot = filename.find_last_of('.');
if (lastDot != std::string::npos) {
filename.erase(lastDot);
}
tags.push_back(std::move(filename));
setTags(testCase, tags);
}
}
} // anon namespace
Session::Session() {
static bool alreadyInstantiated = false;
if( alreadyInstantiated )
CATCH_INTERNAL_ERROR( "Only one instance of Catch::Session can ever be used" );
if( alreadyInstantiated ) {
try { CATCH_INTERNAL_ERROR( "Only one instance of Catch::Session can ever be used" ); }
catch(...) { getMutableRegistryHub().registerStartupException(); }
}
const auto& exceptions = getRegistryHub().getStartupExceptionRegistry().getExceptions();
if ( !exceptions.empty() ) {
m_startupExceptions = true;
Colour colourGuard( Colour::Red );
Catch::cerr() << "Errors occured during startup!" << '\n';
// iterate over all exceptions and notify user
for ( const auto& ex_ptr : exceptions ) {
try {
std::rethrow_exception(ex_ptr);
} catch ( std::exception const& ex ) {
Catch::cerr() << Column( ex.what() ).indent(2) << '\n';
}
}
}
alreadyInstantiated = true;
m_cli = makeCommandLineParser( m_configData );
}
@@ -140,6 +152,9 @@ namespace Catch {
}
int Session::applyCommandLine( int argc, char* argv[] ) {
if( m_startupExceptions )
return 1;
auto result = m_cli.parse( clara::Args( argc, argv ) );
if( !result ) {
Catch::cerr()
@@ -165,19 +180,8 @@ namespace Catch {
}
int Session::run( int argc, char* argv[] ) {
const auto& exceptions = getRegistryHub().getStartupExceptionRegistry().getExceptions();
if ( !exceptions.empty() ) {
Catch::cerr() << "Errors occured during startup!" << '\n';
// iterate over all exceptions and notify user
for ( const auto& ex_ptr : exceptions ) {
try {
std::rethrow_exception(ex_ptr);
} catch ( std::exception const& ex ) {
Catch::cerr() << ex.what() << '\n';
}
}
if( m_startupExceptions )
return 1;
}
int returnCode = applyCommandLine( argc, argv );
if( returnCode == 0 )
returnCode = run();
@@ -236,6 +240,9 @@ namespace Catch {
}
int Session::runInternal() {
if( m_startupExceptions )
return 1;
if( m_configData.showHelp || m_configData.libIdentify )
return 0;

View File

@@ -45,6 +45,7 @@ namespace Catch {
clara::Parser m_cli;
ConfigData m_configData;
std::shared_ptr<Config> m_config;
bool m_startupExceptions = false;
};
} // end namespace Catch

View File

@@ -11,98 +11,203 @@
#include "catch_enforce.h"
#include "catch_stream.h"
#include "catch_debug_console.h"
#include "catch_stringref.h"
#include <stdexcept>
#include <cstdio>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <memory>
#if defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wexit-time-destructors"
#endif
namespace Catch {
template<typename WriterF, std::size_t bufferSize=256>
class StreamBufImpl : public StreamBufBase {
char data[bufferSize];
WriterF m_writer;
Catch::IStream::~IStream() = default;
public:
StreamBufImpl() {
setp( data, data + sizeof(data) );
}
namespace detail { namespace {
template<typename WriterF, std::size_t bufferSize=256>
class StreamBufImpl : public std::streambuf {
char data[bufferSize];
WriterF m_writer;
~StreamBufImpl() noexcept {
StreamBufImpl::sync();
}
private:
int overflow( int c ) override {
sync();
if( c != EOF ) {
if( pbase() == epptr() )
m_writer( std::string( 1, static_cast<char>( c ) ) );
else
sputc( static_cast<char>( c ) );
public:
StreamBufImpl() {
setp( data, data + sizeof(data) );
}
~StreamBufImpl() noexcept {
StreamBufImpl::sync();
}
private:
int overflow( int c ) override {
sync();
if( c != EOF ) {
if( pbase() == epptr() )
m_writer( std::string( 1, static_cast<char>( c ) ) );
else
sputc( static_cast<char>( c ) );
}
return 0;
}
int sync() override {
if( pbase() != pptr() ) {
m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) );
setp( pbase(), epptr() );
}
return 0;
}
};
///////////////////////////////////////////////////////////////////////////
struct OutputDebugWriter {
void operator()( std::string const&str ) {
writeToDebugConsole( str );
}
};
///////////////////////////////////////////////////////////////////////////
class FileStream : public IStream {
mutable std::ofstream m_ofs;
public:
FileStream( StringRef filename ) {
m_ofs.open( filename.c_str() );
CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << "'" );
}
~FileStream() override = default;
public: // IStream
std::ostream& stream() const override {
return m_ofs;
}
};
///////////////////////////////////////////////////////////////////////////
class CoutStream : public IStream {
mutable std::ostream m_os;
public:
// Store the streambuf from cout up-front because
// cout may get redirected when running tests
CoutStream() : m_os( Catch::cout().rdbuf() ) {}
~CoutStream() override = default;
public: // IStream
std::ostream& stream() const override { return m_os; }
};
///////////////////////////////////////////////////////////////////////////
class DebugOutStream : public IStream {
std::unique_ptr<StreamBufImpl<OutputDebugWriter>> m_streamBuf;
mutable std::ostream m_os;
public:
DebugOutStream()
: m_streamBuf( new StreamBufImpl<OutputDebugWriter>() ),
m_os( m_streamBuf.get() )
{}
~DebugOutStream() override = default;
public: // IStream
std::ostream& stream() const override { return m_os; }
};
}} // namespace anon::detail
///////////////////////////////////////////////////////////////////////////
auto makeStream( StringRef const &filename ) -> IStream const* {
if( filename.empty() )
return new detail::CoutStream();
else if( filename[0] == '%' ) {
if( filename == "%debug" )
return new detail::DebugOutStream();
else
CATCH_ERROR( "Unrecognised stream: '" << filename << "'" );
}
else
return new detail::FileStream( filename );
}
// This class encapsulates the idea of a pool of ostringstreams that can be reused.
struct StringStreams {
std::vector<std::unique_ptr<std::ostringstream>> m_streams;
std::vector<std::size_t> m_unused;
std::ostringstream m_referenceStream; // Used for copy state/ flags from
static StringStreams* s_instance;
auto add() -> std::size_t {
if( m_unused.empty() ) {
m_streams.push_back( std::unique_ptr<std::ostringstream>( new std::ostringstream ) );
return m_streams.size()-1;
}
else {
auto index = m_unused.back();
m_unused.pop_back();
return index;
}
return 0;
}
int sync() override {
if( pbase() != pptr() ) {
m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) );
setp( pbase(), epptr() );
}
return 0;
void release( std::size_t index ) {
m_streams[index]->copyfmt( m_referenceStream ); // Restore initial flags and other state
m_unused.push_back(index);
}
// !TBD: put in TLS
static auto instance() -> StringStreams& {
if( !s_instance )
s_instance = new StringStreams();
return *s_instance;
}
static void cleanup() {
delete s_instance;
s_instance = nullptr;
}
};
StringStreams* StringStreams::s_instance = nullptr;
void ReusableStringStream::cleanup() {
StringStreams::cleanup();
}
ReusableStringStream::ReusableStringStream()
: m_index( StringStreams::instance().add() ),
m_oss( StringStreams::instance().m_streams[m_index].get() )
{}
ReusableStringStream::~ReusableStringStream() {
static_cast<std::ostringstream*>( m_oss )->str("");
m_oss->clear();
StringStreams::instance().release( m_index );
}
auto ReusableStringStream::str() const -> std::string {
return static_cast<std::ostringstream*>( m_oss )->str();
}
///////////////////////////////////////////////////////////////////////////
Catch::IStream::~IStream() = default;
FileStream::FileStream( std::string const& filename ) {
m_ofs.open( filename.c_str() );
CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << "'" );
}
std::ostream& FileStream::stream() const {
return m_ofs;
}
struct OutputDebugWriter {
void operator()( std::string const&str ) {
writeToDebugConsole( str );
}
};
DebugOutStream::DebugOutStream()
: m_streamBuf( new StreamBufImpl<OutputDebugWriter>() ),
m_os( m_streamBuf.get() )
{}
std::ostream& DebugOutStream::stream() const {
return m_os;
}
// Store the streambuf from cout up-front because
// cout may get redirected when running tests
CoutStream::CoutStream()
: m_os( Catch::cout().rdbuf() )
{}
std::ostream& CoutStream::stream() const {
return m_os;
}
#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions
std::ostream& cout() {
return std::cout;
}
std::ostream& cerr() {
return std::cerr;
}
std::ostream& clog() {
return std::clog;
}
std::ostream& cout() { return std::cout; }
std::ostream& cerr() { return std::cerr; }
std::ostream& clog() { return std::clog; }
#endif
}
#if defined(__clang__)
# pragma clang diagnostic pop
#endif

View File

@@ -9,12 +9,9 @@
#ifndef TWOBLUECUBES_CATCH_STREAM_H_INCLUDED
#define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED
#include "catch_streambuf.h"
#include <streambuf>
#include <iosfwd>
#include <cstddef>
#include <ostream>
#include <fstream>
#include <memory>
namespace Catch {
@@ -22,42 +19,32 @@ namespace Catch {
std::ostream& cerr();
std::ostream& clog();
class StringRef;
struct IStream {
virtual ~IStream();
virtual std::ostream& stream() const = 0;
};
class FileStream : public IStream {
mutable std::ofstream m_ofs;
auto makeStream( StringRef const &filename ) -> IStream const*;
class ReusableStringStream {
std::size_t m_index;
std::ostream* m_oss;
public:
FileStream( std::string const& filename );
~FileStream() override = default;
public: // IStream
std::ostream& stream() const override;
};
ReusableStringStream();
~ReusableStringStream();
auto str() const -> std::string;
class CoutStream : public IStream {
mutable std::ostream m_os;
public:
CoutStream();
~CoutStream() override = default;
template<typename T>
auto operator << ( T const& value ) -> ReusableStringStream& {
*m_oss << value;
return *this;
}
auto get() -> std::ostream& { return *m_oss; }
public: // IStream
std::ostream& stream() const override;
};
class DebugOutStream : public IStream {
std::unique_ptr<StreamBufBase> m_streamBuf;
mutable std::ostream m_os;
public:
DebugOutStream();
~DebugOutStream() override = default;
public: // IStream
std::ostream& stream() const override;
static void cleanup();
};
}

View File

@@ -1,12 +0,0 @@
/*
* Created by Martin on 31/08/2017.
*
* Distributed under the Boost Software License, Version 1.0. (See accompanying
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*/
#include "catch_streambuf.h"
namespace Catch {
StreamBufBase::~StreamBufBase() = default;
}

View File

@@ -1,21 +0,0 @@
/*
* Created by Phil on 27/11/2012.
* Copyright 2012 Two Blue Cubes Ltd. All rights reserved.
*
* Distributed under the Boost Software License, Version 1.0. (See accompanying
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*/
#ifndef TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED
#define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED
#include <streambuf>
namespace Catch {
class StreamBufBase : public std::streambuf {
public:
virtual ~StreamBufBase();
};
}
#endif // TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED

View File

@@ -10,68 +10,17 @@
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wexit-time-destructors"
#endif
#include "catch_stringref.h"
#include <ostream>
#include <cassert>
#include <cstring>
namespace Catch {
auto getEmptyStringRef() -> StringRef {
static StringRef s_emptyStringRef("");
return s_emptyStringRef;
}
StringRef::StringRef() noexcept
: StringRef( getEmptyStringRef() )
{}
StringRef::StringRef( StringRef const& other ) noexcept
: m_start( other.m_start ),
m_size( other.m_size )
{}
StringRef::StringRef( StringRef&& other ) noexcept
: m_start( other.m_start ),
m_size( other.m_size ),
m_data( other.m_data )
{
other.m_data = nullptr;
}
StringRef::StringRef( char const* rawChars ) noexcept
: m_start( rawChars ),
m_size( static_cast<size_type>( std::strlen( rawChars ) ) )
{
assert( rawChars != nullptr );
}
StringRef::StringRef( char const* rawChars, size_type size ) noexcept
: m_start( rawChars ),
m_size( size )
{
size_type rawSize = rawChars == nullptr ? 0 : static_cast<size_type>( std::strlen( rawChars ) );
if( rawSize < size )
m_size = rawSize;
}
StringRef::StringRef( std::string const& stdString ) noexcept
: m_start( stdString.c_str() ),
m_size( stdString.size() )
: StringRef( rawChars, static_cast<StringRef::size_type>(std::strlen(rawChars) ) )
{}
StringRef::~StringRef() noexcept {
delete[] m_data;
}
auto StringRef::operator = ( StringRef other ) noexcept -> StringRef& {
swap( other );
return *this;
}
StringRef::operator std::string() const {
return std::string( m_start, m_size );
}
@@ -81,7 +30,7 @@ namespace Catch {
std::swap( m_size, other.m_size );
std::swap( m_data, other.m_data );
}
auto StringRef::c_str() const -> char const* {
if( isSubstring() )
const_cast<StringRef*>( this )->takeOwnership();
@@ -97,7 +46,7 @@ namespace Catch {
auto StringRef::isSubstring() const noexcept -> bool {
return m_start[m_size] != '\0';
}
void StringRef::takeOwnership() {
if( !isOwned() ) {
m_data = new char[m_size+1];
@@ -125,13 +74,6 @@ namespace Catch {
return m_start[index];
}
auto StringRef::empty() const noexcept -> bool {
return m_size == 0;
}
auto StringRef::size() const noexcept -> size_type {
return m_size;
}
auto StringRef::numberOfCharacters() const noexcept -> size_type {
size_type noChars = m_size;
// Make adjustments for uft encodings
@@ -166,7 +108,7 @@ namespace Catch {
auto operator << ( std::ostream& os, StringRef const& str ) -> std::ostream& {
return os << str.c_str();
}
} // namespace Catch
#if defined(__clang__)

View File

@@ -23,27 +23,63 @@ namespace Catch {
/// visible - but it does mean (substring) StringRefs should not be shared between
/// threads.
class StringRef {
public:
using size_type = std::size_t;
private:
friend struct StringRefTestAccess;
using size_type = std::size_t;
char const* m_start;
size_type m_size;
char* m_data = nullptr;
void takeOwnership();
static constexpr char const* const s_empty = "";
public: // construction/ assignment
StringRef() noexcept;
StringRef( StringRef const& other ) noexcept;
StringRef( StringRef&& other ) noexcept;
StringRef() noexcept
: StringRef( s_empty, 0 )
{}
StringRef( StringRef const& other ) noexcept
: m_start( other.m_start ),
m_size( other.m_size )
{}
StringRef( StringRef&& other ) noexcept
: m_start( other.m_start ),
m_size( other.m_size ),
m_data( other.m_data )
{
other.m_data = nullptr;
}
StringRef( char const* rawChars ) noexcept;
StringRef( char const* rawChars, size_type size ) noexcept;
StringRef( std::string const& stdString ) noexcept;
~StringRef() noexcept;
auto operator = ( StringRef other ) noexcept -> StringRef&;
StringRef( char const* rawChars, size_type size ) noexcept
: m_start( rawChars ),
m_size( size )
{}
StringRef( std::string const& stdString ) noexcept
: m_start( stdString.c_str() ),
m_size( stdString.size() )
{}
~StringRef() noexcept {
delete[] m_data;
}
auto operator = ( StringRef const &other ) noexcept -> StringRef& {
delete[] m_data;
m_data = nullptr;
m_start = other.m_start;
m_size = other.m_size;
return *this;
}
operator std::string() const;
void swap( StringRef& other ) noexcept;
@@ -55,8 +91,13 @@ namespace Catch {
auto operator[] ( size_type index ) const noexcept -> char;
public: // named queries
auto empty() const noexcept -> bool;
auto size() const noexcept -> size_type;
auto empty() const noexcept -> bool {
return m_size == 0;
}
auto size() const noexcept -> size_type {
return m_size;
}
auto numberOfCharacters() const noexcept -> size_type;
auto c_str() const -> char const*;
@@ -75,6 +116,10 @@ namespace Catch {
auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&;
inline auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef {
return StringRef( rawChars, size );
}
} // namespace Catch
#endif // CATCH_STRINGREF_H_INCLUDED

View File

@@ -10,9 +10,6 @@
# pragma warning(push)
# pragma warning(disable: 161 1682)
# else // __ICC
# pragma clang diagnostic ignored "-Wglobal-constructors"
# pragma clang diagnostic ignored "-Wvariadic-macros"
# pragma clang diagnostic ignored "-Wc99-extensions"
# pragma clang diagnostic ignored "-Wunused-variable"
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wpadded"
@@ -20,10 +17,8 @@
# pragma clang diagnostic ignored "-Wcovered-switch-default"
# endif
#elif defined __GNUC__
# pragma GCC diagnostic ignored "-Wvariadic-macros"
# pragma GCC diagnostic ignored "-Wunused-variable"
# pragma GCC diagnostic ignored "-Wparentheses"
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wpadded"
#endif

View File

@@ -17,6 +17,9 @@ namespace Catch {
} // end namespace Catch
#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); }
#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
#endif // TWOBLUECUBES_CATCH_TAG_ALIAS_AUTOREGISTRAR_H_INCLUDED

View File

@@ -10,9 +10,10 @@
#include "catch_console_colour.h"
#include "catch_enforce.h"
#include "catch_interfaces_registry_hub.h"
#include "catch_stream.h"
#include "catch_string_manip.h"
#include <sstream>
namespace Catch {
TagAliasRegistry::~TagAliasRegistry() {}

View File

@@ -15,6 +15,7 @@
#include <cctype>
#include <exception>
#include <algorithm>
#include <sstream>
namespace Catch {

View File

@@ -66,9 +66,9 @@ namespace Catch {
void TestRegistry::registerTest( TestCase const& testCase ) {
std::string name = testCase.getTestCaseInfo().name;
if( name.empty() ) {
std::ostringstream oss;
oss << "Anonymous test case " << ++m_unnamedCount;
return registerTest( testCase.withName( oss.str() ) );
ReusableStringStream rss;
rss << "Anonymous test case " << ++m_unnamedCount;
return registerTest( testCase.withName( rss.str() ) );
}
m_functions.push_back( testCase );
}

View File

@@ -13,6 +13,7 @@
#include <assert.h>
#include <stdexcept>
#include <memory>
#include <sstream>
#if defined(__clang__)
# pragma clang diagnostic push

View File

@@ -35,7 +35,7 @@ auto makeTestInvoker( void (C::*testAsMethod)() ) noexcept -> ITestInvoker* {
}
struct NameAndTags {
NameAndTags( StringRef name_ = "", StringRef tags_ = "" ) noexcept;
NameAndTags( StringRef name_ = StringRef(), StringRef tags_ = StringRef() ) noexcept;
StringRef name;
StringRef tags;
};

View File

@@ -45,11 +45,11 @@ namespace Catch {
void Timer::start() {
m_nanoseconds = getCurrentNanosecondsSinceEpoch();
}
auto Timer::getElapsedNanoseconds() const -> unsigned int {
return static_cast<unsigned int>(getCurrentNanosecondsSinceEpoch() - m_nanoseconds);
auto Timer::getElapsedNanoseconds() const -> uint64_t {
return getCurrentNanosecondsSinceEpoch() - m_nanoseconds;
}
auto Timer::getElapsedMicroseconds() const -> unsigned int {
return static_cast<unsigned int>(getElapsedNanoseconds()/1000);
auto Timer::getElapsedMicroseconds() const -> uint64_t {
return getElapsedNanoseconds()/1000;
}
auto Timer::getElapsedMilliseconds() const -> unsigned int {
return static_cast<unsigned int>(getElapsedMicroseconds()/1000);

View File

@@ -19,8 +19,8 @@ namespace Catch {
uint64_t m_nanoseconds = 0;
public:
void start();
auto getElapsedNanoseconds() const -> unsigned int;
auto getElapsedMicroseconds() const -> unsigned int;
auto getElapsedNanoseconds() const -> uint64_t;
auto getElapsedMicroseconds() const -> uint64_t;
auto getElapsedMilliseconds() const -> unsigned int;
auto getElapsedSeconds() const -> double;
};

View File

@@ -12,11 +12,16 @@
# pragma clang diagnostic ignored "-Wglobal-constructors"
#endif
// Enable specific decls locally
#if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER)
#define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
#endif
#include "catch_tostring.h"
#include "catch_interfaces_config.h"
#include "catch_context.h"
#include <cmath>
#include <iomanip>
namespace Catch {
@@ -52,22 +57,26 @@ namespace Detail {
}
unsigned char const *bytes = static_cast<unsigned char const *>(object);
std::ostringstream os;
os << "0x" << std::setfill('0') << std::hex;
ReusableStringStream rss;
rss << "0x" << std::setfill('0') << std::hex;
for( ; i != end; i += inc )
os << std::setw(2) << static_cast<unsigned>(bytes[i]);
return os.str();
rss << std::setw(2) << static_cast<unsigned>(bytes[i]);
return rss.str();
}
}
template<typename T>
std::string fpToString( T value, int precision ) {
std::ostringstream oss;
oss << std::setprecision( precision )
if (std::isnan(value)) {
return "nan";
}
ReusableStringStream rss;
rss << std::setprecision( precision )
<< std::fixed
<< value;
std::string d = oss.str();
std::string d = rss.str();
std::size_t i = d.find_last_not_of( '0' );
if( i != std::string::npos && i != d.size()-1 ) {
if( d[i] == '.' )
@@ -153,12 +162,12 @@ std::string StringMaker<long>::convert(long value) {
return ::Catch::Detail::stringify(static_cast<long long>(value));
}
std::string StringMaker<long long>::convert(long long value) {
std::ostringstream oss;
oss << value;
ReusableStringStream rss;
rss << value;
if (value > Detail::hexThreshold) {
oss << " (0x" << std::hex << value << ')';
rss << " (0x" << std::hex << value << ')';
}
return oss.str();
return rss.str();
}
std::string StringMaker<unsigned int>::convert(unsigned int value) {
@@ -168,12 +177,12 @@ std::string StringMaker<unsigned long>::convert(unsigned long value) {
return ::Catch::Detail::stringify(static_cast<unsigned long long>(value));
}
std::string StringMaker<unsigned long long>::convert(unsigned long long value) {
std::ostringstream oss;
oss << value;
ReusableStringStream rss;
rss << value;
if (value > Detail::hexThreshold) {
oss << " (0x" << std::hex << value << ')';
rss << " (0x" << std::hex << value << ')';
}
return oss.str();
return rss.str();
}
@@ -216,6 +225,13 @@ std::string StringMaker<double>::convert(double value) {
return fpToString(value, 10);
}
std::string ratio_string<std::atto>::symbol() { return "a"; }
std::string ratio_string<std::femto>::symbol() { return "f"; }
std::string ratio_string<std::pico>::symbol() { return "p"; }
std::string ratio_string<std::nano>::symbol() { return "n"; }
std::string ratio_string<std::micro>::symbol() { return "u"; }
std::string ratio_string<std::milli>::symbol() { return "m"; }
} // end namespace Catch

View File

@@ -9,11 +9,11 @@
#define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED
#include <sstream>
#include <vector>
#include <cstddef>
#include <type_traits>
#include <string>
#include "catch_stream.h"
#ifdef __OBJC__
#include "catch_objc_arc.hpp"
@@ -26,7 +26,7 @@
// We need a dummy global operator<< so we can bring it into Catch namespace later
struct Catch_global_namespace_dummy;
struct Catch_global_namespace_dummy {};
std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy);
namespace Catch {
@@ -57,25 +57,38 @@ namespace Catch {
static const bool value = decltype(test<std::ostream, const T&>(0))::value;
};
template<typename E>
std::string convertUnknownEnumToString( E e );
template<typename T>
typename std::enable_if<!std::is_enum<T>::value, std::string>::type convertUnstreamable( T const& ) {
return Detail::unprintableString;
};
template<typename T>
typename std::enable_if<std::is_enum<T>::value, std::string>::type convertUnstreamable( T const& value ) {
return convertUnknownEnumToString( value );
};
} // namespace Detail
// If we decide for C++14, change these to enable_if_ts
template <typename T>
template <typename T, typename = void>
struct StringMaker {
template <typename Fake = T>
static
typename std::enable_if<::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>::type
convert(const Fake& t) {
std::ostringstream sstr;
sstr << t;
return sstr.str();
convert(const Fake& value) {
ReusableStringStream rss;
rss << value;
return rss.str();
}
template <typename Fake = T>
static
typename std::enable_if<!::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>::type
convert(const Fake&) {
return Detail::unprintableString;
convert( const Fake& value ) {
return Detail::convertUnstreamable( value );
}
};
@@ -88,8 +101,12 @@ namespace Catch {
return ::Catch::StringMaker<typename std::remove_cv<typename std::remove_reference<T>::type>::type>::convert(e);
}
} // namespace Detail
template<typename E>
std::string convertUnknownEnumToString( E e ) {
return ::Catch::Detail::stringify(static_cast<typename std::underlying_type<E>::type>(e));
}
} // namespace Detail
// Some predefined specializations
@@ -119,6 +136,18 @@ namespace Catch {
static std::string convert(wchar_t * str);
};
template<typename T>
struct is_string_array : std::false_type {};
template<std::size_t N>
struct is_string_array<char[N]> : std::true_type {};
template<std::size_t N>
struct is_string_array<signed char[N]> : std::true_type {};
template<std::size_t N>
struct is_string_array<unsigned char[N]> : std::true_type {};
template<int SZ>
struct StringMaker<char[SZ]> {
static std::string convert(const char* str) {
@@ -221,32 +250,18 @@ namespace Catch {
namespace Detail {
template<typename InputIterator>
std::string rangeToString(InputIterator first, InputIterator last) {
std::ostringstream oss;
oss << "{ ";
ReusableStringStream rss;
rss << "{ ";
if (first != last) {
oss << ::Catch::Detail::stringify(*first);
rss << ::Catch::Detail::stringify(*first);
for (++first; first != last; ++first)
oss << ", " << ::Catch::Detail::stringify(*first);
rss << ", " << ::Catch::Detail::stringify(*first);
}
oss << " }";
return oss.str();
rss << " }";
return rss.str();
}
}
template<typename T, typename Allocator>
struct StringMaker<std::vector<T, Allocator> > {
static std::string convert( std::vector<T,Allocator> const& v ) {
return ::Catch::Detail::rangeToString( v.begin(), v.end() );
}
};
template<typename T>
struct EnumStringMaker {
static std::string convert(const T& t) {
return ::Catch::Detail::stringify(static_cast<typename std::underlying_type<T>::type>(t));
}
};
#ifdef __OBJC__
template<>
struct StringMaker<NSString*> {
@@ -290,13 +305,13 @@ namespace Catch {
template<typename T1, typename T2>
struct StringMaker<std::pair<T1, T2> > {
static std::string convert(const std::pair<T1, T2>& pair) {
std::ostringstream oss;
oss << "{ "
ReusableStringStream rss;
rss << "{ "
<< ::Catch::Detail::stringify(pair.first)
<< ", "
<< ::Catch::Detail::stringify(pair.second)
<< " }";
return oss.str();
return rss.str();
}
};
}
@@ -334,16 +349,63 @@ namespace Catch {
template<typename ...Types>
struct StringMaker<std::tuple<Types...>> {
static std::string convert(const std::tuple<Types...>& tuple) {
std::ostringstream os;
os << '{';
Detail::TupleElementPrinter<std::tuple<Types...>>::print(tuple, os);
os << " }";
return os.str();
ReusableStringStream rss;
rss << '{';
Detail::TupleElementPrinter<std::tuple<Types...>>::print(tuple, rss.get());
rss << " }";
return rss.str();
}
};
}
#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
namespace Catch {
struct not_this_one {}; // Tag type for detecting which begin/ end are being selected
// Import begin/ end from std here so they are considered alongside the fallback (...) overloads in this namespace
using std::begin;
using std::end;
not_this_one begin( ... );
not_this_one end( ... );
template <typename T>
struct is_range {
static const bool value =
!std::is_same<decltype(begin(std::declval<T>())), not_this_one>::value &&
!std::is_same<decltype(end(std::declval<T>())), not_this_one>::value;
};
template<typename Range>
std::string rangeToString( Range const& range ) {
return ::Catch::Detail::rangeToString( begin( range ), end( range ) );
}
// Handle vector<bool> specially
template<typename Allocator>
std::string rangeToString( std::vector<bool, Allocator> const& v ) {
ReusableStringStream rss;
rss << "{ ";
bool first = true;
for( bool b : v ) {
if( first )
first = false;
else
rss << ", ";
rss << ::Catch::Detail::stringify( b );
}
rss << " }";
return rss.str();
}
template<typename R>
struct StringMaker<R, typename std::enable_if<is_range<R>::value && !is_string_array<R>::value>::type> {
static std::string convert( R const& range ) {
return rangeToString( range );
}
};
} // namespace Catch
// Separate std::chrono::duration specialization
#if defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER)
@@ -351,6 +413,9 @@ namespace Catch {
#include <ratio>
#include <chrono>
namespace Catch {
template <class Ratio>
struct ratio_string {
static std::string symbol();
@@ -358,69 +423,68 @@ struct ratio_string {
template <class Ratio>
std::string ratio_string<Ratio>::symbol() {
std::ostringstream oss;
oss << '[' << Ratio::num << '/'
Catch::ReusableStringStream rss;
rss << '[' << Ratio::num << '/'
<< Ratio::den << ']';
return oss.str();
return rss.str();
}
template <>
struct ratio_string<std::atto> {
static std::string symbol() { return "a"; }
static std::string symbol();
};
template <>
struct ratio_string<std::femto> {
static std::string symbol() { return "f"; }
static std::string symbol();
};
template <>
struct ratio_string<std::pico> {
static std::string symbol() { return "p"; }
static std::string symbol();
};
template <>
struct ratio_string<std::nano> {
static std::string symbol() { return "n"; }
static std::string symbol();
};
template <>
struct ratio_string<std::micro> {
static std::string symbol() { return "u"; }
static std::string symbol();
};
template <>
struct ratio_string<std::milli> {
static std::string symbol() { return "m"; }
static std::string symbol();
};
namespace Catch {
////////////
// std::chrono::duration specializations
template<typename Value, typename Ratio>
struct StringMaker<std::chrono::duration<Value, Ratio>> {
static std::string convert(std::chrono::duration<Value, Ratio> const& duration) {
std::ostringstream oss;
oss << duration.count() << ' ' << ratio_string<Ratio>::symbol() << 's';
return oss.str();
ReusableStringStream rss;
rss << duration.count() << ' ' << ratio_string<Ratio>::symbol() << 's';
return rss.str();
}
};
template<typename Value>
struct StringMaker<std::chrono::duration<Value, std::ratio<1>>> {
static std::string convert(std::chrono::duration<Value, std::ratio<1>> const& duration) {
std::ostringstream oss;
oss << duration.count() << " s";
return oss.str();
ReusableStringStream rss;
rss << duration.count() << " s";
return rss.str();
}
};
template<typename Value>
struct StringMaker<std::chrono::duration<Value, std::ratio<60>>> {
static std::string convert(std::chrono::duration<Value, std::ratio<60>> const& duration) {
std::ostringstream oss;
oss << duration.count() << " m";
return oss.str();
ReusableStringStream rss;
rss << duration.count() << " m";
return rss.str();
}
};
template<typename Value>
struct StringMaker<std::chrono::duration<Value, std::ratio<3600>>> {
static std::string convert(std::chrono::duration<Value, std::ratio<3600>> const& duration) {
std::ostringstream oss;
oss << duration.count() << " h";
return oss.str();
ReusableStringStream rss;
rss << duration.count() << " h";
return rss.str();
}
};

View File

@@ -0,0 +1,18 @@
/*
* Created by Martin on 21/11/2017.
*
* This file collects declaration that we want to expose to test files.
* These declarations are expected to be duplicated elsewhere,
* together with their implementation.
*
* Distributed under the Boost Software License, Version 1.0. (See accompanying
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*/
#ifndef TWOBLUECUBES_CATCH_USER_INTERFACES_H_INCLUDED
#define TWOBLUECUBES_CATCH_USER_INTERFACES_H_INCLUDED
namespace Catch {
unsigned int rngSeed();
}
#endif // TWOBLUECUBES_CATCH_USER_INTERFACES_H_INCLUDED

Some files were not shown because too many files have changed in this diff Show More