Compare commits

...

40 Commits

Author SHA1 Message Date
d10b9bd02e v2.11.1 2019-12-28 21:24:04 +01:00
55794e9b27 Avoid detecting Clang as having MSVC's traditional preprocessor
Fixes #1806
2019-12-28 18:57:24 +01:00
fa6211bfc2 catch_debugger.h - implement break into debugger assembler instructions for iOS 2019-12-23 21:26:13 +01:00
4e90f910dc catch_console_colour.cpp - adjust useColourOnPlatform for iOS 2019-12-23 21:26:13 +01:00
0c59cc83cf catch_debugger.cpp - debugger detection is identical on Mac OS X and iOS 2019-12-23 21:26:13 +01:00
e4004e0adb Provide const overload of ObjectStorage::stored_object()
Fixes #1820
2019-12-23 21:22:32 +01:00
6c9a255dc2 Fix forwarding in SingleValueGenerator and generator creation
Fixes #1809
2019-12-15 20:50:43 +01:00
9a8963133f Update single header generation script to warn about unused headers 2019-11-21 16:22:04 +01:00
cfba9dce97 Fix wrong namespacing of benchmarking constructor helpers 2019-11-21 16:22:04 +01:00
a537ccae22 Suppress using-namespace lint in GENERATE* macros
Closes #1799
2019-11-16 17:39:28 +01:00
e1c9d5569d v2.11.0 2019-11-15 15:06:17 +01:00
d512decaac Mention the sonarqube reporter in release process 2019-11-15 14:59:35 +01:00
f23f96883a Ensure the full benchmarking support is present in the single header
Fixes #1800
2019-11-15 11:59:43 +01:00
d7b8c3ace3 Minor cleanup in the benchmarking tests 2019-11-15 11:59:35 +01:00
32733e08c0 Use newer version of Python on AppVeyor 2019-11-13 20:58:15 +01:00
930f49a641 Split [.foo] into [.][foo] when parsing test specs
b77cec05c0 fixed this problem for tagging tests, so that a test
case tagged with `[.foo]` would be parsed as tagged with `[.][foo]`.
This does the same for the test spec parsing.

Fixes #1798
2019-11-05 23:28:47 +01:00
c409dccee5 Cleanup tests for C++20 2019-11-04 10:42:34 +01:00
95bfb33167 Forbid copying ReusableStringStream
Copying a `ReusableStringStream` would lead to "double free" of
the stream, and thus it could be used in multiple places at the
same time, breaking the output.
2019-11-04 00:05:35 +01:00
59d2d08c0f Merge pull request #1794 from cericks0n/patch-1
Fix error when period of steady_clock is not nano
2019-11-03 18:22:56 +01:00
fa6d52e2a3 Fix error when period of steady_clock is not nano
On systems where std::chrono::steady_clock::period is not std::nano, benchmark tests fail to compile due to trying to convert analysis.samples from a vector of duration<double, clock::period> to a vector of std::chrono::duration<double, std::nano>.
2019-11-01 15:52:38 -05:00
5ac348cd6e Fix compilation error in examples 2019-10-31 16:08:37 +01:00
776a4686c7 Warning fixes in examples and tests 2019-10-31 14:29:59 +01:00
3136c4fb6a Refactored XMLWriter to provide finer-grained control over formatting 2019-10-29 13:59:18 +01:00
74e0e737a6 Remove useless test 2019-10-29 11:44:24 +01:00
0685216175 Mark type erasure in PredicateFunction as deprecated 2019-10-28 15:15:13 +01:00
fc320f6b8f Extract FunctionReturnType to catch_meta.hpp 2019-10-28 15:15:13 +01:00
5290d4bedc Merge pull request #1791 from catchorg/dev-reorganize-warning-suppression
Rework how warning suppression in macros is done
2019-10-28 13:13:23 +01:00
7ada02e21e Avoid technically UB type punning when determining endianness 2019-10-27 22:07:10 +01:00
849f2848bd Rework how warning suppression in macros is done
Previously, each warning suppression was self-contained, with its
own pair of `SUPPRESS_X_WARNING` and `UNSUPPRESS_X_WARNING` macros.
This had the obvious advantage of being self-containing, but it
also meant that if we needed to suppress more than one warning
in a single place, then we would manipulate the compiler's warning
state multiple times, even though logically we would only need one
layer.

The new way of suppressing warnings in macros is to push compiler's
warning state with `CATCH_INTERNAL_START_WARNINGS_SUPPRESSION` macro,
then disable whatever macros we need with the
`CATCH_INTERNAL_SUPPRESS_X_WARNINGS` macro, and then return to the
previous state using `CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION`.
2019-10-27 21:07:21 +01:00
2fbd66c51c Improve JUnit failure report
The JUnit report is improved in that:
* The message shows the testing condition, not the result
* The actual message has similar output than the console one
2019-10-27 18:02:14 +01:00
51b29ced1a Add SonarQube Generic Test Data reporter
It outputs reports in the `Generic Execution Test Data` format, see
https://docs.sonarqube.org/latest/analysis/generic-test/, specifically
https://docs.sonarqube.org/latest/analysis/generic-test/#header-2

Close #1738 (this is a cherry-pick and fixup of that PR)
2019-10-27 10:02:41 +01:00
9a558171d8 Merge pull request #1776 from mvd4/bugfix/enable-floating-point-ranges
Enable floating point ranges
2019-10-27 09:17:15 +01:00
c5c688820c Document that matcher combinators (&&, ||, !) do not take ownership
Closes #1781
2019-10-26 21:07:38 +02:00
6a08225863 Add documentation for floating point range generator 2019-10-26 20:52:40 +02:00
4327baba40 Enable range generator for floating point types 2019-10-26 20:52:09 +02:00
50cc14c94c Rework StringRef interface and internals
Now it no longer tries to be this weird hybrid between an owning
and non-owning reference, and is only ever non-owning. This is also
reflected in its interface, for example `StringRef::isNullTerminated`
is now public, and `StringRef::c_str()` has the precondition that it
is true.

Overview of the changes:
* The `StringRef::m_data` member has been completely removed, as it
had no more uses.
* `StringRef::isSubstring()` has been made public and renamed to
`StringRef::isNullTerminated()`, so that the name reflects what the
method actually does.
* `StringRef::currentData()` has been renamed to `StringRef::data()`,
to be in line with common C++ containers and container-alikes.
* `StringRef::c_str()` will no longer silently make copies. It instead
has a precondition that `isNullTerminated()` is true.
* If the user needs a null-terminated string, they should use the
`std::string` conversion operator and call `c_str()` on the resulting
`std::string`.
* Some small optimizations in various places.
* Basic functionality is now `constexpr`.
2019-10-25 13:57:52 +02:00
87b745da66 v2.10.2 2019-10-24 18:41:25 +02:00
7d0b205564 Prevent warning suppression from leaking when registering a listener 2019-10-22 00:10:01 +02:00
8fb1219013 docs: command-line: Add example to specify tests by file name. 2019-10-21 23:51:33 +02:00
23c80bcc92 Provide workaround for platforms where INFINITY is double
Fixes #1782
2019-10-21 18:33:26 +02:00
69 changed files with 5632 additions and 1833 deletions

View File

@ -14,7 +14,7 @@ if (CMAKE_BINARY_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
endif()
project(Catch2 LANGUAGES CXX VERSION 2.10.1)
project(Catch2 LANGUAGES CXX VERSION 2.11.1)
# Provide path for scripts
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/CMake")

View File

@ -5,11 +5,11 @@
[![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/oT2uthEb4CcTd0hb)
[![Try online](https://img.shields.io/badge/try-online-blue.svg)](https://wandbox.org/permlink/Fj98nizVNqgaWH3i)
[![Join the chat in Discord: https://discord.gg/4CWS9zD](https://img.shields.io/badge/Discord-Chat!-brightgreen.svg)](https://discord.gg/4CWS9zD)
<a href="https://github.com/catchorg/Catch2/releases/download/v2.10.1/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.11.1/catch.hpp">The latest version of the single header can be downloaded directly using this link</a>
## Catch2 is released!

View File

@ -1,6 +1,11 @@
# version string format -- This will be overwritten later anyway
version: "{build}"
# We need a more up to date pip because Python 2.7 is EOL soon
init:
- set PATH=C:\Python35\Scripts;%PATH%
branches:
except:
- /dev-travis.+/
@ -62,7 +67,7 @@ matrix:
install:
- ps: if (($env:CONFIGURATION) -eq "Debug" -And ($env:coverage) -eq "1" ) { python -m pip --disable-pip-version-check install codecov }
- ps: if (($env:CONFIGURATION) -eq "Debug" -And ($env:coverage) -eq "1" ) { pip --disable-pip-version-check install codecov }
- ps: if (($env:CONFIGURATION) -eq "Debug" -And ($env:coverage) -eq "1" ) { .\misc\installOpenCppCoverage.ps1 }
# Win32 and x64 are CMake-compatible solution platform names.

View File

@ -14,6 +14,7 @@ coverage:
- "**/catch_reporter_tap.hpp"
- "**/catch_reporter_automake.hpp"
- "**/catch_reporter_teamcity.hpp"
- "**/catch_reporter_sonarqube.hpp"
- "**/external/clara.hpp"

View File

@ -168,7 +168,7 @@ Note that it is not possible to simply use the same instance for different runs
and resetting it between each run since that would pollute the measurements with
the resetting code.
It is also possible to just provide an argument name to the simple `BENCHMARK` macro to get
It is also possible to just provide an argument name to the simple `BENCHMARK` macro to get
the same semantics as providing a callable to `meter.measure` with `int` argument:
```c++
@ -189,19 +189,17 @@ construct and destroy objects without dynamic allocation and in a way that lets
you measure construction and destruction separately.
```c++
BENCHMARK_ADVANCED("construct")(Catch::Benchmark::Chronometer meter)
{
BENCHMARK_ADVANCED("construct")(Catch::Benchmark::Chronometer meter) {
std::vector<Catch::Benchmark::storage_for<std::string>> storage(meter.runs());
meter.measure([&](int i) { storage[i].construct("thing"); });
})
};
BENCHMARK_ADVANCED("destroy", [](Catch::Benchmark::Chronometer meter)
{
BENCHMARK_ADVANCED("destroy")(Catch::Benchmark::Chronometer meter) {
std::vector<Catch::Benchmark::destructable_object<std::string>> storage(meter.runs());
for(auto&& o : storage)
o.construct("thing");
meter.measure([&](int i) { storage[i].destruct(); });
})
};
```
`Catch::Benchmark::storage_for<T>` objects are just pieces of raw storage suitable for `T`

View File

@ -12,7 +12,7 @@ Build Systems may refer to low-level tools, like CMake, or larger systems that r
## Continuous Integration systems
Probably the most important aspect to using Catch with a build server is the use of different reporters. Catch comes bundled with three reporters that should cover the majority of build servers out there - although adding more for better integration with some is always a possibility (currently we also offer TeamCity, TAP and Automake reporters).
Probably the most important aspect to using Catch with a build server is the use of different reporters. Catch comes bundled with three reporters that should cover the majority of build servers out there - although adding more for better integration with some is always a possibility (currently we also offer TeamCity, TAP, Automake and SonarQube reporters).
Two of these reporters are built in (XML and JUnit) and the third (TeamCity) is included as a separate header. It's possible that the other two may be split out in the future too - as that would make the core of Catch smaller for those that don't need them.
@ -65,6 +65,10 @@ The Automake Reporter writes out the [meta tags](https://www.gnu.org/software/au
Because of the incremental nature of Catch's test suites and ability to run specific tests, our implementation of TAP reporter writes out the number of tests in a suite last.
### SonarQube Reporter
```-r sonarqube```
[SonarQube Generic Test Data](https://docs.sonarqube.org/latest/analysis/generic-test/) XML format for tests metrics.
## Low-level tools
### Precompiled headers (PCHs)

View File

@ -99,6 +99,7 @@ exclude:notThis Matches all tests except, 'notThis'
~*private* Matches all tests except those that contain 'private'
a* ~ab* abc Matches all tests that start with 'a', except those that
start with 'ab', except 'abc', which is included
-# [#somefile] Matches all tests from the file 'somefile.cpp'
</pre>
Names within square brackets are interpreted as tags.

View File

@ -93,6 +93,17 @@ positively match a testspec.
The API for Catch2's console colour will be changed to take an extra
argument, the stream to which the colour code should be applied.
### Type erasure in the `PredicateMatcher`
Currently, the `PredicateMatcher` uses `std::function` for type erasure,
so that type of the matcher is always `PredicateMatcher<T>`, regardless
of the type of the predicate. Because of the high compilation overhead
of `std::function`, and the fact that the type erasure is used only rarely,
`PredicateMatcher` will no longer be type erased in the future. Instead,
the predicate type will be made part of the PredicateMatcher's type.
---
[Home](Readme.md#top)

View File

@ -49,7 +49,7 @@ a test case,
* 4 specific purpose generators
* `RandomIntegerGenerator<Integral>` -- generates random Integrals from range
* `RandomFloatGenerator<Float>` -- generates random Floats from range
* `RangeGenerator<T>` -- generates all values inside a specific range
* `RangeGenerator<T>` -- generates all values inside an arithmetic range
* `IteratorGenerator<T>` -- copies and returns values from an iterator range
> `ChunkGenerator<T>`, `RandomIntegerGenerator<Integral>`, `RandomFloatGenerator<Float>` and `RangeGenerator<T>` were introduced in Catch 2.7.0.
@ -69,8 +69,8 @@ type, making their usage much nicer. These are
* `map<T>(func, GeneratorWrapper<U>&&)` for `MapGenerator<T, U, Func>` (map `U` to `T`)
* `chunk(chunk-size, GeneratorWrapper<T>&&)` for `ChunkGenerator<T>`
* `random(IntegerOrFloat a, IntegerOrFloat b)` for `RandomIntegerGenerator` or `RandomFloatGenerator`
* `range(start, end)` for `RangeGenerator<T>` with a step size of `1`
* `range(start, end, step)` for `RangeGenerator<T>` with a custom step size
* `range(Arithemtic start, Arithmetic end)` for `RangeGenerator<Arithmetic>` with a step size of `1`
* `range(Arithmetic start, Arithmetic end, Arithmetic step)` for `RangeGenerator<Arithmetic>` with a custom step size
* `from_range(InputIterator from, InputIterator to)` for `IteratorGenerator<T>`
* `from_range(Container const&)` for `IteratorGenerator<T>`
@ -78,6 +78,8 @@ type, making their usage much nicer. These are
> `from_range` has been introduced in Catch 2.10.0
> `range()` for floating point numbers has been introduced in Catch 2.11.0
And can be used as shown in the example below to create a generator
that returns 100 odd random number:

View File

@ -17,7 +17,7 @@ For example, to assert that a string ends with a certain substring:
using Catch::Matchers::EndsWith; // or Catch::EndsWith
std::string str = getStringFromSomewhere();
REQUIRE_THAT( str, EndsWith( "as a service" ) );
```
```
The matcher objects can take multiple arguments, allowing more fine tuning.
The built-in string matchers, for example, take a second argument specifying whether the comparison is
@ -35,6 +35,22 @@ REQUIRE_THAT( str,
(StartsWith( "Big data" ) && !Contains( "web scale" ) ) );
```
_The combining operators do not take ownership of the matcher objects.
This means that if you store the combined object, you have to ensure that
the matcher objects outlive its last use. What this means is that code
like this leads to a use-after-free and (hopefully) a crash:_
```cpp
TEST_CASE("Bugs, bugs, bugs", "[Bug]"){
std::string str = "Bugs as a service";
auto match_expression = Catch::EndsWith( "as a service" ) ||
(Catch::StartsWith( "Big data" ) && !Catch::Contains( "web scale" ) );
REQUIRE_THAT(str, match_expression);
}
```
## Built in matchers
Catch2 provides some matchers by default. They can be found in the
`Catch::Matchers::foo` namespace and are imported into the `Catch`

View File

@ -2,6 +2,8 @@
# Release notes
**Contents**<br>
[2.11.0](#2110)<br>
[2.10.2](#2102)<br>
[2.10.1](#2101)<br>
[2.10.0](#2100)<br>
[2.9.2](#292)<br>
@ -29,6 +31,46 @@
[Older versions](#older-versions)<br>
[Even Older versions](#even-older-versions)<br>
## 2.11.1
### Improvements
* Breaking into debugger is supported on iOS (#1817)
* `google-build-using-namespace` clang-tidy warning is suppressed (#1799)
### Fixes
* Clang on Windows is no longer assumed to implement MSVC's traditional preprocessor (#1806)
* `ObjectStorage` now behaves properly in `const` contexts (#1820)
* `GENERATE_COPY(a, b)` now compiles properly (#1809, #1815)
* Some more cleanups in the benchmarking support
## 2.11.0
### Improvements
* JUnit reporter output now contains more details in case of failure (#1347, #1719)
* Added SonarQube Test Data reporter (#1738)
* It is in a separate header, just like the TAP, Automake, and TeamCity reporters
* `range` generator now allows floating point numbers (#1776)
* Reworked part of internals to increase throughput
### Fixes
* The single header version should contain full benchmarking support (#1800)
* `[.foo]` is now properly parsed as `[.][foo]` when used on the command line (#1798)
* Fixed compilation of benchmarking on platforms where `steady_clock::period` is not `std::nano` (#1794)
## 2.10.2
### Improvements
* Catch2 will now compile on platform where `INFINITY` is double (#1782)
### Fixes
* Warning suppressed during listener registration will no longer leak
## 2.10.1

View File

@ -42,8 +42,8 @@ Tag version and release title should be same as the new version,
description should contain the release notes for the current release.
Single header version of `catch.hpp` *needs* to be attached as a binary,
as that is where the official download link links to. Preferably
it should use linux line endings. All non-bundled reporters (Automake,
TAP, TeamCity) should also be attached as binaries, as they might be
it should use linux line endings. All non-bundled reporters (Automake, TAP,
TeamCity, SonarQube) should also be attached as binaries, as they might be
dependent on a specific version of the single-include header.
Since 2.5.0, the release tag and the "binaries" (headers) should be PGP
@ -67,6 +67,7 @@ $ gpg2 --armor --output catch.hpp.asc --detach-sig catch.hpp
$ gpg2 --armor --output catch_reporter_automake.hpp.asc --detach-sig catch_reporter_automake.hpp
$ gpg2 --armor --output catch_reporter_teamcity.hpp.asc --detach-sig catch_reporter_teamcity.hpp
$ gpg2 --armor --output catch_reporter_tap.hpp.asc --detach-sig catch_reporter_tap.hpp
$ gpg2 --armor --output catch_reporter_sonarqube.hpp.asc --detach-sig catch_reporter_sonarqube.hpp
```
_GPG does not support signing multiple files in single invocation._

View File

@ -29,6 +29,7 @@ Do this in one source file - the same one you have `CATCH_CONFIG_MAIN` or `CATCH
Use this when building as part of a TeamCity build to see results as they happen ([code example](../examples/207-Rpt-TeamCityReporter.cpp)).
* `tap` writes in the TAP ([Test Anything Protocol](https://en.wikipedia.org/wiki/Test_Anything_Protocol)) format.
* `automake` writes in a format that correspond to [automake .trs](https://www.gnu.org/software/automake/manual/html_node/Log-files-generation-and-test-results-recording.html) files
* `sonarqube` writes the [SonarQube Generic Test Data](https://docs.sonarqube.org/latest/analysis/generic-test/) XML format.
You see what reporters are available from the command line by running with `--list-reporters`.

View File

@ -10,11 +10,12 @@
#define CATCH_CONFIG_MAIN
#include <catch2/catch.hpp>
class out_buff : public std::stringbuf {
std::FILE* m_stream;
public:
out_buff(std::FILE* stream) :m_stream(stream) {}
~out_buff() { pubsync(); }
out_buff(std::FILE* stream):m_stream(stream) {}
~out_buff();
int sync() {
int ret = 0;
for (unsigned char c : str()) {
@ -29,6 +30,12 @@ public:
}
};
out_buff::~out_buff() { pubsync(); }
#if defined(__clang__)
#pragma clang diagnostic ignored "-Wexit-time-destructors" // static variables in cout/cerr/clog
#endif
namespace Catch {
std::ostream& cout() {
static std::ostream ret(new out_buff(stdout));

View File

@ -22,15 +22,17 @@ public:
}
}
std::string const& get() const override {
return m_line;
}
std::string const& get() const override;
bool next() override {
return !!std::getline(m_stream, m_line);
}
};
std::string const& LineGenerator::get() const {
return m_line;
}
// This helper function provides a nicer UX when instantiating the generator
// Notice that it returns an instance of GeneratorWrapper<std::string>, which
// is a value-wrapper around std::unique_ptr<IGenerator<std::string>>.

View File

@ -10,7 +10,7 @@
#define TWOBLUECUBES_CATCH_HPP_INCLUDED
#define CATCH_VERSION_MAJOR 2
#define CATCH_VERSION_MINOR 10
#define CATCH_VERSION_MINOR 11
#define CATCH_VERSION_PATCH 1
#ifdef __clang__
@ -80,7 +80,7 @@
#endif
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
#include "internal/benchmark/catch_benchmark.hpp"
#include "internal/benchmark/catch_benchmarking_all.hpp"
#endif
#endif // ! CATCH_CONFIG_IMPL_ONLY

View File

@ -79,7 +79,7 @@ namespace Catch {
});
auto analysis = Detail::analyse(*cfg, env, samples.begin(), samples.end());
BenchmarkStats<std::chrono::duration<double, std::nano>> stats{ info, analysis.samples, analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance };
BenchmarkStats<FloatDuration<Clock>> stats{ info, analysis.samples, analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance };
getResultCapture().benchmarkEnded(stats);
} CATCH_CATCH_ALL{

View File

@ -0,0 +1,29 @@
/*
* 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)
*/
// A proxy header that includes all of the benchmarking headers to allow
// concise include of the benchmarking features. You should prefer the
// individual includes in standard use.
#include "catch_benchmark.hpp"
#include "catch_chronometer.hpp"
#include "catch_clock.hpp"
#include "catch_constructor.hpp"
#include "catch_environment.hpp"
#include "catch_estimate.hpp"
#include "catch_execution_plan.hpp"
#include "catch_optimizer.hpp"
#include "catch_outlier_classification.hpp"
#include "catch_sample_analysis.hpp"
#include "detail/catch_analyse.hpp"
#include "detail/catch_benchmark_function.hpp"
#include "detail/catch_complete_invoke.hpp"
#include "detail/catch_estimate_clock.hpp"
#include "detail/catch_measure.hpp"
#include "detail/catch_repeat.hpp"
#include "detail/catch_run_for_at_least.hpp"
#include "detail/catch_stats.hpp"
#include "detail/catch_timing.hpp"

View File

@ -14,60 +14,66 @@
#include <type_traits>
namespace Catch {
namespace Detail {
template <typename T, bool Destruct>
struct ObjectStorage
{
using TStorage = typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type;
ObjectStorage() : data() {}
ObjectStorage(const ObjectStorage& other)
namespace Benchmark {
namespace Detail {
template <typename T, bool Destruct>
struct ObjectStorage
{
new(&data) T(other.stored_object());
}
using TStorage = typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type;
ObjectStorage(ObjectStorage&& other)
{
new(&data) T(std::move(other.stored_object()));
}
ObjectStorage() : data() {}
~ObjectStorage() { destruct_on_exit<T>(); }
ObjectStorage(const ObjectStorage& other)
{
new(&data) T(other.stored_object());
}
template <typename... Args>
void construct(Args&&... args)
{
new (&data) T(std::forward<Args>(args)...);
}
ObjectStorage(ObjectStorage&& other)
{
new(&data) T(std::move(other.stored_object()));
}
template <bool AllowManualDestruction = !Destruct>
typename std::enable_if<AllowManualDestruction>::type destruct()
{
stored_object().~T();
}
~ObjectStorage() { destruct_on_exit<T>(); }
private:
// If this is a constructor benchmark, destruct the underlying object
template <typename U>
void destruct_on_exit(typename std::enable_if<Destruct, U>::type* = 0) { destruct<true>(); }
// Otherwise, don't
template <typename U>
void destruct_on_exit(typename std::enable_if<!Destruct, U>::type* = 0) { }
template <typename... Args>
void construct(Args&&... args)
{
new (&data) T(std::forward<Args>(args)...);
}
T& stored_object()
{
return *static_cast<T*>(static_cast<void*>(&data));
}
template <bool AllowManualDestruction = !Destruct>
typename std::enable_if<AllowManualDestruction>::type destruct()
{
stored_object().~T();
}
TStorage data;
};
private:
// If this is a constructor benchmark, destruct the underlying object
template <typename U>
void destruct_on_exit(typename std::enable_if<Destruct, U>::type* = 0) { destruct<true>(); }
// Otherwise, don't
template <typename U>
void destruct_on_exit(typename std::enable_if<!Destruct, U>::type* = 0) { }
T& stored_object() {
return *static_cast<T*>(static_cast<void*>(&data));
}
T const& stored_object() const {
return *static_cast<T*>(static_cast<void*>(&data));
}
TStorage data;
};
}
template <typename T>
using storage_for = Detail::ObjectStorage<T, true>;
template <typename T>
using destructable_object = Detail::ObjectStorage<T, false>;
}
template <typename T>
using storage_for = Detail::ObjectStorage<T, true>;
template <typename T>
using destructable_object = Detail::ObjectStorage<T, false>;
}
#endif // TWOBLUECUBES_CATCH_CONSTRUCTOR_HPP_INCLUDED

View File

@ -176,9 +176,10 @@ namespace Catch {
bootstrap_analysis analyse_samples(double confidence_level, int n_resamples, std::vector<double>::iterator first, std::vector<double>::iterator last) {
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
static std::random_device entropy;
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
auto n = static_cast<int>(last - first); // seriously, one can't use integral types without hell in C++

View File

@ -43,9 +43,10 @@
do { \
Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \
INTERNAL_CATCH_TRY { \
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); \
CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
} INTERNAL_CATCH_CATCH( catchAssertionHandler ) \
INTERNAL_CATCH_REACT( catchAssertionHandler ) \
} 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

View File

@ -43,38 +43,34 @@
# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
#endif
#ifdef __clang__
// We have to avoid both ICC and Clang, because they try to mask themselves
// as gcc, and we want only GCC in this block
#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC)
# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" )
# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" )
#endif
# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
_Pragma( "clang diagnostic push" ) \
_Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \
_Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"")
# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
_Pragma( "clang diagnostic pop" )
#if defined(__clang__)
# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
_Pragma( "clang diagnostic push" ) \
_Pragma( "clang diagnostic ignored \"-Wparentheses\"" )
# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
_Pragma( "clang diagnostic pop" )
# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" )
# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" )
# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
_Pragma( "clang diagnostic push" ) \
_Pragma( "clang diagnostic ignored \"-Wunused-variable\"" )
# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS \
_Pragma( "clang diagnostic pop" )
# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
_Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \
_Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"")
# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
_Pragma( "clang diagnostic push" ) \
_Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" )
# define CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS \
_Pragma( "clang diagnostic pop" )
# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
_Pragma( "clang diagnostic ignored \"-Wparentheses\"" )
# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
_Pragma( "clang diagnostic ignored \"-Wunused-variable\"" )
# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
_Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" )
# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
_Pragma( "clang diagnostic ignored \"-Wunused-template\"" )
# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
_Pragma( "clang diagnostic push" ) \
_Pragma( "clang diagnostic ignored \"-Wunused-template\"" )
# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_TEMPLATE_WARNINGS \
_Pragma( "clang diagnostic pop" )
#endif // __clang__
@ -133,8 +129,10 @@
////////////////////////////////////////////////////////////////////////////////
// Visual C++
#ifdef _MSC_VER
#if defined(_MSC_VER)
# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) )
# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) )
# if _MSC_VER >= 1900 // Visual Studio 2015 or newer
# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
@ -151,9 +149,12 @@
// MSVC traditional preprocessor needs some workaround for __VA_ARGS__
// _MSVC_TRADITIONAL == 0 means new conformant preprocessor
// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor
# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL)
# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
# endif
# if !defined(__clang__) // Handle Clang masquerading for msvc
# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL)
# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
# endif // MSVC_TRADITIONAL
# endif // __clang__
#endif // _MSC_VER
#if defined(_REENTRANT) || defined(_MSC_VER)
@ -309,34 +310,37 @@
# define CATCH_CONFIG_GLOBAL_NEXTAFTER
#endif
// Even if we do not think the compiler has that warning, we still have
// to provide a macro that can be used by the code.
#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION)
# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
#endif
#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION)
# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
#endif
#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS)
# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS
# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS
#endif
#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS)
# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
#endif
#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS)
# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS
# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS
#endif
#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS)
# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS
# define CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS
#endif
#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10)
# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
# undef CATCH_INTERNAL_UNSUPPRESS_UNUSED_TEMPLATE_WARNINGS
#elif defined(__clang__) && (__clang_major__ < 5)
# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
# undef CATCH_INTERNAL_UNSUPPRESS_UNUSED_TEMPLATE_WARNINGS
#endif
#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS)
# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_TEMPLATE_WARNINGS
#endif
#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)

View File

@ -167,7 +167,7 @@ namespace {
bool useColourOnPlatform() {
return
#ifdef CATCH_PLATFORM_MAC
#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE)
!isDebuggerActive() &&
#endif
#if !(defined(__DJGPP__) && defined(__STRICT_ANSI__))

View File

@ -12,7 +12,7 @@
#include "catch_stream.h"
#include "catch_platform.h"
#ifdef CATCH_PLATFORM_MAC
#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE)
# include <assert.h>
# include <stdbool.h>

View File

@ -19,6 +19,17 @@ namespace Catch {
#define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */
#elif defined(CATCH_PLATFORM_IPHONE)
// use inline assembler
#if defined(__i386__) || defined(__x86_64__)
#define CATCH_TRAP() __asm__("int $3")
#elif defined(__aarch64__)
#define CATCH_TRAP() __asm__(".inst 0xd4200000")
#elif defined(__arm__)
#define CATCH_TRAP() __asm__(".inst 0xe7f001f0")
#endif
#elif defined(CATCH_PLATFORM_LINUX)
// If we can use inline assembler, do it because this allows us to break
// directly at the location of the failing check instead of breaking inside

View File

@ -57,7 +57,6 @@ namespace Generators {
class SingleValueGenerator final : public IGenerator<T> {
T m_value;
public:
SingleValueGenerator(T const& value) : m_value( value ) {}
SingleValueGenerator(T&& value) : m_value(std::move(value)) {}
T const& get() const override {
@ -120,21 +119,21 @@ namespace Generators {
m_generators.emplace_back(std::move(generator));
}
void populate(T&& val) {
m_generators.emplace_back(value(std::move(val)));
m_generators.emplace_back(value(std::forward<T>(val)));
}
template<typename U>
void populate(U&& val) {
populate(T(std::move(val)));
populate(T(std::forward<U>(val)));
}
template<typename U, typename... Gs>
void populate(U&& valueOrGenerator, Gs... moreGenerators) {
void populate(U&& valueOrGenerator, Gs &&... moreGenerators) {
populate(std::forward<U>(valueOrGenerator));
populate(std::forward<Gs>(moreGenerators)...);
}
public:
template <typename... Gs>
Generators(Gs... moreGenerators) {
Generators(Gs &&... moreGenerators) {
m_generators.reserve(sizeof...(Gs));
populate(std::forward<Gs>(moreGenerators)...);
}
@ -166,7 +165,7 @@ namespace Generators {
struct as {};
template<typename T, typename... Gs>
auto makeGenerators( GeneratorWrapper<T>&& generator, Gs... moreGenerators ) -> Generators<T> {
auto makeGenerators( GeneratorWrapper<T>&& generator, Gs &&... moreGenerators ) -> Generators<T> {
return Generators<T>(std::move(generator), std::forward<Gs>(moreGenerators)...);
}
template<typename T>
@ -174,11 +173,11 @@ namespace Generators {
return Generators<T>(std::move(generator));
}
template<typename T, typename... Gs>
auto makeGenerators( T&& val, Gs... moreGenerators ) -> Generators<T> {
auto makeGenerators( T&& val, Gs &&... moreGenerators ) -> Generators<T> {
return makeGenerators( value( std::forward<T>( val ) ), std::forward<Gs>( moreGenerators )... );
}
template<typename T, typename U, typename... Gs>
auto makeGenerators( as<T>, U&& val, Gs... moreGenerators ) -> Generators<T> {
auto makeGenerators( as<T>, U&& val, Gs &&... moreGenerators ) -> Generators<T> {
return makeGenerators( value( T( std::forward<U>( val ) ) ), std::forward<Gs>( moreGenerators )... );
}
@ -204,10 +203,10 @@ namespace Generators {
} // namespace Catch
#define GENERATE( ... ) \
Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } )
Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
#define GENERATE_COPY( ... ) \
Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } )
Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
#define GENERATE_REF( ... ) \
Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } )
Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
#endif // TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED

View File

@ -8,6 +8,7 @@
#define TWOBLUECUBES_CATCH_GENERATORS_GENERIC_HPP_INCLUDED
#include "catch_generators.hpp"
#include "catch_meta.hpp"
namespace Catch {
namespace Generators {
@ -172,18 +173,7 @@ namespace Generators {
}
};
#if defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703
// std::result_of is deprecated in C++17 and removed in C++20. Hence, it is
// replaced with std::invoke_result here. Also *_t format is preferred over
// typename *::type format.
template <typename Func, typename U>
using MapFunctionReturnType = std::remove_reference_t<std::remove_cv_t<std::invoke_result_t<Func, U>>>;
#else
template <typename Func, typename U>
using MapFunctionReturnType = typename std::remove_reference<typename std::remove_cv<typename std::result_of<Func(U)>::type>::type>::type;
#endif
template <typename Func, typename U, typename T = MapFunctionReturnType<Func, U>>
template <typename Func, typename U, typename T = FunctionReturnType<Func, U>>
GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<U>&& generator) {
return GeneratorWrapper<T>(
pf::make_unique<MapGenerator<T, U, Func>>(std::forward<Func>(function), std::move(generator))

View File

@ -117,7 +117,7 @@ public:
template <typename T>
GeneratorWrapper<T> range(T const& start, T const& end, T const& step) {
static_assert(std::is_integral<T>::value && !std::is_same<T, bool>::value, "Type must be an integer");
static_assert(std::is_arithmetic<T>::value && !std::is_same<T, bool>::value, "Type must be numeric");
return GeneratorWrapper<T>(pf::make_unique<RangeGenerator<T>>(start, end, step));
}

View File

@ -73,9 +73,10 @@ namespace Catch {
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \
static std::string translatorName( signature ); \
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); } \
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
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

@ -188,9 +188,10 @@ namespace Floating {
ret << ", ";
write(ret, step(m_target, static_cast<double>( INFINITY), m_ulps));
} else {
write(ret, step(static_cast<float>(m_target), -INFINITY, m_ulps));
// We have to cast INFINITY to float because of MinGW, see #1782
write(ret, step(static_cast<float>(m_target), static_cast<float>(-INFINITY), m_ulps));
ret << ", ";
write(ret, step(static_cast<float>(m_target), INFINITY, m_ulps));
write(ret, step(static_cast<float>(m_target), static_cast<float>( INFINITY), m_ulps));
}
ret << "])";

View File

@ -12,22 +12,34 @@
#include <type_traits>
namespace Catch {
template<typename T>
struct always_false : std::false_type {};
template<typename T>
struct always_false : std::false_type {};
template <typename> struct true_given : std::true_type {};
struct is_callable_tester {
template <typename Fun, typename... Args>
true_given<decltype(std::declval<Fun>()(std::declval<Args>()...))> static test(int);
template <typename...>
std::false_type static test(...);
};
template <typename T>
struct is_callable;
template <typename> struct true_given : std::true_type {};
struct is_callable_tester {
template <typename Fun, typename... Args>
true_given<decltype(std::declval<Fun>()(std::declval<Args>()...))> static test(int);
template <typename...>
std::false_type static test(...);
};
struct is_callable<Fun(Args...)> : decltype(is_callable_tester::test<Fun, Args...>(0)) {};
template <typename T>
struct is_callable;
template <typename Fun, typename... Args>
struct is_callable<Fun(Args...)> : decltype(is_callable_tester::test<Fun, Args...>(0)) {};
#if defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703
// std::result_of is deprecated in C++17 and removed in C++20. Hence, it is
// replaced with std::invoke_result here. Also *_t format is preferred over
// typename *::type format.
template <typename Func, typename U>
using FunctionReturnType = std::remove_reference_t<std::remove_cv_t<std::invoke_result_t<Func, U>>>;
#else
template <typename Func, typename U>
using FunctionReturnType = typename std::remove_reference<typename std::remove_cv<typename std::result_of<Func(U)>::type>::type>::type;
#endif
} // namespace Catch

View File

@ -58,14 +58,16 @@ namespace Catch {
#if !defined(CATCH_CONFIG_DISABLE)
#define CATCH_REGISTER_REPORTER( name, reporterType ) \
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
namespace{ Catch::ReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); } \
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
#define CATCH_REGISTER_LISTENER( listenerType ) \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; } \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; } \
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
#else // CATCH_CONFIG_DISABLE
#define CATCH_REGISTER_REPORTER(name, reporterType)

View File

@ -37,13 +37,15 @@ namespace Catch {
} // end namespace Catch
#define INTERNAL_CATCH_SECTION( ... ) \
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \
CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
#define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, (Catch::ReusableStringStream() << __VA_ARGS__).str() ) ) \
CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
#endif // TWOBLUECUBES_CATCH_SECTION_H_INCLUDED

View File

@ -9,6 +9,8 @@
#ifndef TWOBLUECUBES_CATCH_STREAM_H_INCLUDED
#define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED
#include "catch_common.h"
#include <iosfwd>
#include <cstddef>
#include <ostream>
@ -28,7 +30,7 @@ namespace Catch {
auto makeStream( StringRef const &filename ) -> IStream const*;
class ReusableStringStream {
class ReusableStringStream : NonCopyable {
std::size_t m_index;
std::ostream* m_oss;
public:

View File

@ -5,14 +5,10 @@
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*/
#if defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wexit-time-destructors"
#endif
#include "catch_enforce.h"
#include "catch_stringref.h"
#include <algorithm>
#include <ostream>
#include <cstring>
#include <cstdint>
@ -22,63 +18,33 @@ namespace Catch {
: StringRef( rawChars, static_cast<StringRef::size_type>(std::strlen(rawChars) ) )
{}
void StringRef::swap( StringRef& other ) noexcept {
std::swap( m_start, other.m_start );
std::swap( m_size, other.m_size );
std::swap( m_data, other.m_data );
}
auto StringRef::c_str() const -> char const* {
if( !isSubstring() )
return m_start;
const_cast<StringRef *>( this )->takeOwnership();
return m_data;
CATCH_ENFORCE(isNullTerminated(), "Called StringRef::c_str() on a non-null-terminated instance");
return m_start;
}
auto StringRef::currentData() const noexcept -> char const* {
auto StringRef::data() const noexcept -> char const* {
return m_start;
}
auto StringRef::isOwned() const noexcept -> bool {
return m_data != nullptr;
}
auto StringRef::isSubstring() const noexcept -> bool {
return m_start[m_size] != '\0';
}
void StringRef::takeOwnership() {
if( !isOwned() ) {
m_data = new char[m_size+1];
memcpy( m_data, m_start, m_size );
m_data[m_size] = '\0';
auto StringRef::substr( size_type start, size_type size ) const noexcept -> StringRef {
if (start < m_size) {
return StringRef(m_start + start, (std::min)(m_size - start, size));
} else {
return StringRef();
}
}
auto StringRef::substr( size_type start, size_type size ) const noexcept -> StringRef {
if( start < m_size )
return StringRef( m_start+start, size );
else
return StringRef();
}
auto StringRef::operator == ( StringRef const& other ) const noexcept -> bool {
return
size() == other.size() &&
(std::strncmp( m_start, other.m_start, size() ) == 0);
}
auto StringRef::operator != ( StringRef const& other ) const noexcept -> bool {
return !operator==( other );
return m_size == other.m_size
&& (std::memcmp( m_start, other.m_start, m_size ) == 0);
}
auto operator << ( std::ostream& os, StringRef const& str ) -> std::ostream& {
return os.write(str.currentData(), str.size());
return os.write(str.data(), str.size());
}
auto operator+=( std::string& lhs, StringRef const& rhs ) -> std::string& {
lhs.append(rhs.currentData(), rhs.size());
lhs.append(rhs.data(), rhs.size());
return lhs;
}
} // namespace Catch
#if defined(__clang__)
# pragma clang diagnostic pop
#endif

View File

@ -16,49 +16,24 @@ namespace Catch {
/// A non-owning string class (similar to the forthcoming std::string_view)
/// Note that, because a StringRef may be a substring of another string,
/// it may not be null terminated. c_str() must return a null terminated
/// string, however, and so the StringRef will internally take ownership
/// (taking a copy), if necessary. In theory this ownership is not externally
/// visible - but it does mean (substring) StringRefs should not be shared between
/// threads.
/// it may not be null terminated.
class StringRef {
public:
using size_type = std::size_t;
using const_iterator = const char*;
private:
friend struct StringRefTestAccess;
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( s_empty, 0 )
{}
char const* m_start = s_empty;
size_type m_size = 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;
}
public: // construction
constexpr StringRef() noexcept = default;
StringRef( char const* rawChars ) noexcept;
StringRef( char const* rawChars, size_type size ) noexcept
constexpr StringRef( char const* rawChars, size_type size ) noexcept
: m_start( rawChars ),
m_size( size )
{}
@ -68,27 +43,15 @@ namespace Catch {
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;
}
explicit operator std::string() const {
return std::string(m_start, m_size);
}
void swap( StringRef& other ) noexcept;
public: // operators
auto operator == ( StringRef const& other ) const noexcept -> bool;
auto operator != ( StringRef const& other ) const noexcept -> bool;
auto operator != (StringRef const& other) const noexcept -> bool {
return !(*this == other);
}
auto operator[] ( size_type index ) const noexcept -> char {
assert(index < m_size);
@ -96,42 +59,45 @@ namespace Catch {
}
public: // named queries
auto empty() const noexcept -> bool {
constexpr auto empty() const noexcept -> bool {
return m_size == 0;
}
auto size() const noexcept -> size_type {
constexpr auto size() const noexcept -> size_type {
return m_size;
}
// Returns the current start pointer. If the StringRef is not
// null-terminated, throws std::domain_exception
auto c_str() const -> char const*;
public: // substrings and searches
auto substr( size_type start, size_type size ) const noexcept -> StringRef;
// Returns a substring of [start, start + length).
// If start + length > size(), then the substring is [start, size()).
// If start > size(), then the substring is empty.
auto substr( size_type start, size_type length ) const noexcept -> StringRef;
// Returns the current start pointer.
// Note that the pointer can change when if the StringRef is a substring
auto currentData() const noexcept -> char const*;
// Returns the current start pointer. May not be null-terminated.
auto data() const noexcept -> char const*;
constexpr auto isNullTerminated() const noexcept -> bool {
return m_start[m_size] == '\0';
}
public: // iterators
const_iterator begin() const { return m_start; }
const_iterator end() const { return m_start + m_size; }
private: // ownership queries - may not be consistent between calls
auto isOwned() const noexcept -> bool;
auto isSubstring() const noexcept -> bool;
constexpr const_iterator begin() const { return m_start; }
constexpr const_iterator end() const { return m_start + m_size; }
};
auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&;
auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&;
inline auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef {
constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef {
return StringRef( rawChars, size );
}
} // namespace Catch
inline auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef {
constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef {
return Catch::StringRef( rawChars, size );
}

View File

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

View File

@ -75,7 +75,7 @@ struct AutoReg : NonCopyable {
#else
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \
INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ ) )
#endif
#endif
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(Name, Tags, Signature, ...) \
@ -105,21 +105,24 @@ struct AutoReg : NonCopyable {
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \
static void TestName(); \
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
static void TestName()
#define INTERNAL_CATCH_TESTCASE( ... ) \
INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ )
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
namespace{ \
struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \
@ -127,19 +130,21 @@ struct AutoReg : NonCopyable {
}; \
Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
} \
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
void TestName::test()
#define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \
INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ )
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_2(TestName, TestFunc, Name, Tags, Signature, ... )\
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
@ -164,9 +169,7 @@ struct AutoReg : NonCopyable {
}();\
}\
}\
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS \
CATCH_INTERNAL_UNSUPPRESS_UNUSED_TEMPLATE_WARNINGS \
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc,INTERNAL_CATCH_REMOVE_PARENS(Signature))
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
@ -175,7 +178,7 @@ struct AutoReg : NonCopyable {
#else
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \
INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ ) )
#endif
#endif
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \
@ -186,9 +189,10 @@ struct AutoReg : NonCopyable {
#endif
#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(TestName, TestFuncName, Name, Tags, Signature, TmplTypes, TypesList) \
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
template<typename TestType> static void TestFuncName(); \
namespace {\
namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) { \
@ -213,9 +217,7 @@ struct AutoReg : NonCopyable {
}(); \
} \
} \
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS \
CATCH_INTERNAL_UNSUPPRESS_UNUSED_TEMPLATE_WARNINGS \
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
template<typename TestType> \
static void TestFuncName()
@ -236,6 +238,7 @@ struct AutoReg : NonCopyable {
#endif
#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2(TestName, TestFunc, Name, Tags, TmplList)\
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
template<typename TestType> static void TestFunc(); \
@ -255,10 +258,9 @@ struct AutoReg : NonCopyable {
TestInit t; \
t.reg_tests(); \
return 0; \
}(); \
}(); \
}}\
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
CATCH_INTERNAL_UNSUPPRESS_UNUSED_TEMPLATE_WARNINGS \
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
template<typename TestType> \
static void TestFunc()
@ -267,6 +269,7 @@ struct AutoReg : NonCopyable {
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, Signature, ... ) \
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
@ -291,9 +294,7 @@ struct AutoReg : NonCopyable {
}();\
}\
}\
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS\
CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS\
CATCH_INTERNAL_UNSUPPRESS_UNUSED_TEMPLATE_WARNINGS\
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
@ -313,6 +314,7 @@ struct AutoReg : NonCopyable {
#endif
#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(TestNameClass, TestName, ClassName, Name, Tags, Signature, TmplTypes, TypesList)\
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
@ -343,9 +345,7 @@ struct AutoReg : NonCopyable {
}(); \
}\
}\
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS \
CATCH_INTERNAL_UNSUPPRESS_UNUSED_TEMPLATE_WARNINGS \
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
template<typename TestType> \
void TestName<TestType>::test()
@ -366,6 +366,7 @@ struct AutoReg : NonCopyable {
#endif
#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, TmplList) \
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
template<typename TestType> \
@ -390,8 +391,7 @@ struct AutoReg : NonCopyable {
return 0;\
}(); \
}}\
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
CATCH_INTERNAL_UNSUPPRESS_UNUSED_TEMPLATE_WARNINGS \
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
template<typename TestType> \
void TestName<TestType>::test()

View File

@ -20,10 +20,10 @@ namespace Catch {
m_substring.reserve(m_arg.size());
m_patternName.reserve(m_arg.size());
m_realPatternPos = 0;
for( m_pos = 0; m_pos < m_arg.size(); ++m_pos )
//if visitChar fails
if( !visitChar( m_arg[m_pos] ) ){
if( !visitChar( m_arg[m_pos] ) ){
m_testSpec.m_invalidArgs.push_back(arg);
break;
}
@ -113,9 +113,9 @@ namespace Catch {
switch( m_mode ) {
case Name:
case QuotedName:
return addPattern<TestSpec::NamePattern>();
return addNamePattern();
case Tag:
return addPattern<TestSpec::TagPattern>();
return addTagPattern();
case EscapedName:
revertBackToLastMode();
return;
@ -156,12 +156,12 @@ namespace Catch {
void TestSpecParser::saveLastMode() {
lastMode = m_mode;
}
void TestSpecParser::revertBackToLastMode() {
m_mode = lastMode;
}
bool TestSpecParser::separate() {
bool TestSpecParser::separate() {
if( (m_mode==QuotedName) || (m_mode==Tag) ){
//invalid argument, signal failure to previous scope.
m_mode = None;
@ -174,7 +174,63 @@ namespace Catch {
addFilter();
return true; //success
}
std::string TestSpecParser::preprocessPattern() {
std::string token = m_patternName;
for (std::size_t i = 0; i < m_escapeChars.size(); ++i)
token = token.substr(0, m_escapeChars[i] - i) + token.substr(m_escapeChars[i] - i + 1);
m_escapeChars.clear();
if (startsWith(token, "exclude:")) {
m_exclusion = true;
token = token.substr(8);
}
m_patternName.clear();
return token;
}
void TestSpecParser::addNamePattern() {
auto token = preprocessPattern();
if (!token.empty()) {
TestSpec::PatternPtr pattern = std::make_shared<TestSpec::NamePattern>(token, m_substring);
if (m_exclusion)
pattern = std::make_shared<TestSpec::ExcludedPattern>(pattern);
m_currentFilter.m_patterns.push_back(pattern);
}
m_substring.clear();
m_exclusion = false;
m_mode = None;
}
void TestSpecParser::addTagPattern() {
auto token = preprocessPattern();
if (!token.empty()) {
// If the tag pattern is the "hide and tag" shorthand (e.g. [.foo])
// we have to create a separate hide tag and shorten the real one
if (token.size() > 1 && token[0] == '.') {
token.erase(token.begin());
TestSpec::PatternPtr pattern = std::make_shared<TestSpec::TagPattern>(".", m_substring);
if (m_exclusion) {
pattern = std::make_shared<TestSpec::ExcludedPattern>(pattern);
}
m_currentFilter.m_patterns.push_back(pattern);
}
TestSpec::PatternPtr pattern = std::make_shared<TestSpec::TagPattern>(token, m_substring);
if (m_exclusion) {
pattern = std::make_shared<TestSpec::ExcludedPattern>(pattern);
}
m_currentFilter.m_patterns.push_back(pattern);
}
m_substring.clear();
m_exclusion = false;
m_mode = None;
}
TestSpec parseTestSpec( std::string const& arg ) {
return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec();
}

View File

@ -53,35 +53,20 @@ namespace Catch {
void revertBackToLastMode();
void addFilter();
bool separate();
template<typename T>
void addPattern() {
std::string token = m_patternName;
for( std::size_t i = 0; i < m_escapeChars.size(); ++i )
token = token.substr( 0, m_escapeChars[i] - i ) + token.substr( m_escapeChars[i] -i +1 );
m_escapeChars.clear();
if( startsWith( token, "exclude:" ) ) {
m_exclusion = true;
token = token.substr( 8 );
}
if( !token.empty() ) {
TestSpec::PatternPtr pattern = std::make_shared<T>( token, m_substring );
if( m_exclusion )
pattern = std::make_shared<TestSpec::ExcludedPattern>( pattern );
m_currentFilter.m_patterns.push_back( pattern );
}
m_substring.clear();
m_patternName.clear();
m_exclusion = false;
m_mode = None;
}
// Handles common preprocessing of the pattern for name/tag patterns
std::string preprocessPattern();
// Adds the current pattern as a test name
void addNamePattern();
// Adds the current pattern as a tag
void addTagPattern();
inline void addCharToPattern(char c) {
m_substring += c;
m_patternName += c;
m_realPatternPos++;
}
};
TestSpec parseTestSpec( std::string const& arg );

View File

@ -38,13 +38,11 @@ namespace Detail {
enum Arch { Big, Little };
static Arch which() {
union _{
int asInt;
char asChar[sizeof (int)];
} u;
u.asInt = 1;
return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little;
int one = 1;
// If the lowest byte we read is non-zero, we can assume
// that little endian format is used.
auto value = *reinterpret_cast<char*>(&one);
return value ? Little : Big;
}
};
}
@ -241,13 +239,13 @@ std::string StringMaker<std::nullptr_t>::convert(std::nullptr_t) {
}
int StringMaker<float>::precision = 5;
std::string StringMaker<float>::convert(float value) {
return fpToString(value, precision) + 'f';
}
int StringMaker<double>::precision = 10;
std::string StringMaker<double>::convert(double value) {
return fpToString(value, precision);
}

View File

@ -37,7 +37,7 @@ namespace Catch {
}
Version const& libraryVersion() {
static Version version( 2, 10, 1, "", 0 );
static Version version( 2, 11, 1, "", 0 );
return version;
}

View File

@ -10,6 +10,7 @@
#include "catch_enforce.h"
#include <iomanip>
#include <type_traits>
using uchar = unsigned char;
@ -51,8 +52,31 @@ namespace {
os.flags(f);
}
bool shouldNewline(XmlFormatting fmt) {
return !!(static_cast<std::underlying_type<XmlFormatting>::type>(fmt & XmlFormatting::Newline));
}
bool shouldIndent(XmlFormatting fmt) {
return !!(static_cast<std::underlying_type<XmlFormatting>::type>(fmt & XmlFormatting::Indent));
}
} // anonymous namespace
XmlFormatting operator | (XmlFormatting lhs, XmlFormatting rhs) {
return static_cast<XmlFormatting>(
static_cast<std::underlying_type<XmlFormatting>::type>(lhs) |
static_cast<std::underlying_type<XmlFormatting>::type>(rhs)
);
}
XmlFormatting operator & (XmlFormatting lhs, XmlFormatting rhs) {
return static_cast<XmlFormatting>(
static_cast<std::underlying_type<XmlFormatting>::type>(lhs) &
static_cast<std::underlying_type<XmlFormatting>::type>(rhs)
);
}
XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat )
: m_str( str ),
m_forWhat( forWhat )
@ -157,13 +181,17 @@ namespace {
return os;
}
XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer )
: m_writer( writer )
XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer, XmlFormatting fmt )
: m_writer( writer ),
m_fmt(fmt)
{}
XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) noexcept
: m_writer( other.m_writer ){
: m_writer( other.m_writer ),
m_fmt(other.m_fmt)
{
other.m_writer = nullptr;
other.m_fmt = XmlFormatting::None;
}
XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) noexcept {
if ( m_writer ) {
@ -171,17 +199,20 @@ namespace {
}
m_writer = other.m_writer;
other.m_writer = nullptr;
m_fmt = other.m_fmt;
other.m_fmt = XmlFormatting::None;
return *this;
}
XmlWriter::ScopedElement::~ScopedElement() {
if( m_writer )
m_writer->endElement();
if (m_writer) {
m_writer->endElement(m_fmt);
}
}
XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, bool indent ) {
m_writer->writeText( text, indent );
XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, XmlFormatting fmt ) {
m_writer->writeText( text, fmt );
return *this;
}
@ -191,37 +222,47 @@ namespace {
}
XmlWriter::~XmlWriter() {
while( !m_tags.empty() )
while (!m_tags.empty()) {
endElement();
}
newlineIfNecessary();
}
XmlWriter& XmlWriter::startElement( std::string const& name ) {
XmlWriter& XmlWriter::startElement( std::string const& name, XmlFormatting fmt ) {
ensureTagClosed();
newlineIfNecessary();
m_os << m_indent << '<' << name;
if (shouldIndent(fmt)) {
m_os << m_indent;
m_indent += " ";
}
m_os << '<' << name;
m_tags.push_back( name );
m_indent += " ";
m_tagIsOpen = true;
applyFormatting(fmt);
return *this;
}
XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name ) {
ScopedElement scoped( this );
startElement( name );
XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name, XmlFormatting fmt ) {
ScopedElement scoped( this, fmt );
startElement( name, fmt );
return scoped;
}
XmlWriter& XmlWriter::endElement() {
newlineIfNecessary();
m_indent = m_indent.substr( 0, m_indent.size()-2 );
XmlWriter& XmlWriter::endElement(XmlFormatting fmt) {
m_indent = m_indent.substr(0, m_indent.size() - 2);
if( m_tagIsOpen ) {
m_os << "/>";
m_tagIsOpen = false;
} else {
newlineIfNecessary();
if (shouldIndent(fmt)) {
m_os << m_indent;
}
m_os << "</" << m_tags.back() << ">";
}
else {
m_os << m_indent << "</" << m_tags.back() << ">";
}
m_os << std::endl;
m_os << std::flush;
applyFormatting(fmt);
m_tags.pop_back();
return *this;
}
@ -237,22 +278,26 @@ namespace {
return *this;
}
XmlWriter& XmlWriter::writeText( std::string const& text, bool indent ) {
XmlWriter& XmlWriter::writeText( std::string const& text, XmlFormatting fmt) {
if( !text.empty() ){
bool tagWasOpen = m_tagIsOpen;
ensureTagClosed();
if( tagWasOpen && indent )
if (tagWasOpen && shouldIndent(fmt)) {
m_os << m_indent;
}
m_os << XmlEncode( text );
m_needsNewline = true;
applyFormatting(fmt);
}
return *this;
}
XmlWriter& XmlWriter::writeComment( std::string const& text ) {
XmlWriter& XmlWriter::writeComment( std::string const& text, XmlFormatting fmt) {
ensureTagClosed();
m_os << m_indent << "<!--" << text << "-->";
m_needsNewline = true;
if (shouldIndent(fmt)) {
m_os << m_indent;
}
m_os << "<!--" << text << "-->";
applyFormatting(fmt);
return *this;
}
@ -268,11 +313,16 @@ namespace {
void XmlWriter::ensureTagClosed() {
if( m_tagIsOpen ) {
m_os << ">" << std::endl;
m_os << '>' << std::flush;
newlineIfNecessary();
m_tagIsOpen = false;
}
}
void XmlWriter::applyFormatting(XmlFormatting fmt) {
m_needsNewline = shouldNewline(fmt);
}
void XmlWriter::writeDeclaration() {
m_os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
}

View File

@ -14,6 +14,14 @@
#include <vector>
namespace Catch {
enum class XmlFormatting {
None = 0x00,
Indent = 0x01,
Newline = 0x02,
};
XmlFormatting operator | (XmlFormatting lhs, XmlFormatting rhs);
XmlFormatting operator & (XmlFormatting lhs, XmlFormatting rhs);
class XmlEncode {
public:
@ -35,14 +43,14 @@ namespace Catch {
class ScopedElement {
public:
ScopedElement( XmlWriter* writer );
ScopedElement( XmlWriter* writer, XmlFormatting fmt );
ScopedElement( ScopedElement&& other ) noexcept;
ScopedElement& operator=( ScopedElement&& other ) noexcept;
~ScopedElement();
ScopedElement& writeText( std::string const& text, bool indent = true );
ScopedElement& writeText( std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent );
template<typename T>
ScopedElement& writeAttribute( std::string const& name, T const& attribute ) {
@ -52,6 +60,7 @@ namespace Catch {
private:
mutable XmlWriter* m_writer = nullptr;
XmlFormatting m_fmt;
};
XmlWriter( std::ostream& os = Catch::cout() );
@ -60,11 +69,11 @@ namespace Catch {
XmlWriter( XmlWriter const& ) = delete;
XmlWriter& operator=( XmlWriter const& ) = delete;
XmlWriter& startElement( std::string const& name );
XmlWriter& startElement( std::string const& name, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
ScopedElement scopedElement( std::string const& name );
ScopedElement scopedElement( std::string const& name, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
XmlWriter& endElement();
XmlWriter& endElement(XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
XmlWriter& writeAttribute( std::string const& name, std::string const& attribute );
@ -77,9 +86,9 @@ namespace Catch {
return writeAttribute( name, rss.str() );
}
XmlWriter& writeText( std::string const& text, bool indent = true );
XmlWriter& writeText( std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
XmlWriter& writeComment( std::string const& text );
XmlWriter& writeComment(std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
void writeStylesheetRef( std::string const& url );
@ -89,6 +98,8 @@ namespace Catch {
private:
void applyFormatting(XmlFormatting fmt);
void writeDeclaration();
void newlineIfNecessary();

View File

@ -12,6 +12,7 @@
#include "../internal/catch_tostring.h"
#include "../internal/catch_reporter_registrars.hpp"
#include "../internal/catch_text.h"
#include <cassert>
#include <sstream>
@ -146,8 +147,8 @@ namespace Catch {
for( auto const& child : groupNode.children )
writeTestCase( *child );
xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite ), false );
xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite ), false );
xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite ), XmlFormatting::Newline );
xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite ), XmlFormatting::Newline );
}
void JunitReporter::writeTestCase( TestCaseNode const& testCaseNode ) {
@ -196,9 +197,9 @@ namespace Catch {
writeAssertions( sectionNode );
if( !sectionNode.stdOut.empty() )
xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false );
xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), XmlFormatting::Newline );
if( !sectionNode.stdErr.empty() )
xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false );
xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), XmlFormatting::Newline );
}
for( auto const& childNode : sectionNode.childSections )
if( className.empty() )
@ -244,10 +245,25 @@ namespace Catch {
XmlWriter::ScopedElement e = xml.scopedElement( elementName );
xml.writeAttribute( "message", result.getExpandedExpression() );
xml.writeAttribute( "message", result.getExpression() );
xml.writeAttribute( "type", result.getTestMacroName() );
ReusableStringStream rss;
if (stats.totals.assertions.total() > 0) {
rss << "FAILED" << ":\n";
if (result.hasExpression()) {
rss << " ";
rss << result.getExpressionInMacro();
rss << '\n';
}
if (result.hasExpandedExpression()) {
rss << "with expansion:\n";
rss << Column(result.getExpandedExpression()).indent(2) << '\n';
}
} else {
rss << '\n';
}
if( !result.getMessage().empty() )
rss << result.getMessage() << '\n';
for( auto const& msg : stats.infoMessages )
@ -255,7 +271,7 @@ namespace Catch {
rss << msg.message << '\n';
rss << "at " << result.getSourceInfo();
xml.writeText( rss.str(), false );
xml.writeText( rss.str(), XmlFormatting::Newline );
}
}

View File

@ -0,0 +1,181 @@
/*
* Created by Daniel Garcia on 2018-12-04.
* Copyright Social Point SL. 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 CATCH_REPORTER_SONARQUBE_HPP_INCLUDED
#define CATCH_REPORTER_SONARQUBE_HPP_INCLUDED
// Don't #include any Catch headers here - we can assume they are already
// included before this header.
// This is not good practice in general but is necessary in this case so this
// file can be distributed as a single header that works with the main
// Catch single header.
#include <map>
namespace Catch {
struct SonarQubeReporter : CumulativeReporterBase<SonarQubeReporter> {
SonarQubeReporter(ReporterConfig const& config)
: CumulativeReporterBase(config)
, xml(config.stream()) {
m_reporterPrefs.shouldRedirectStdOut = true;
m_reporterPrefs.shouldReportAllAssertions = true;
}
~SonarQubeReporter() override;
static std::string getDescription() {
return "Reports test results in the Generic Test Data SonarQube XML format";
}
static std::set<Verbosity> getSupportedVerbosities() {
return { Verbosity::Normal };
}
void noMatchingTestCases(std::string const& /*spec*/) override {}
void testRunStarting(TestRunInfo const& testRunInfo) override {
CumulativeReporterBase::testRunStarting(testRunInfo);
xml.startElement("testExecutions");
xml.writeAttribute("version", "1");
}
void testGroupEnded(TestGroupStats const& testGroupStats) override {
CumulativeReporterBase::testGroupEnded(testGroupStats);
writeGroup(*m_testGroups.back());
}
void testRunEndedCumulative() override {
xml.endElement();
}
void writeGroup(TestGroupNode const& groupNode) {
std::map<std::string, TestGroupNode::ChildNodes> testsPerFile;
for(auto const& child : groupNode.children)
testsPerFile[child->value.testInfo.lineInfo.file].push_back(child);
for(auto const& kv : testsPerFile)
writeTestFile(kv.first.c_str(), kv.second);
}
void writeTestFile(const char* filename, TestGroupNode::ChildNodes const& testCaseNodes) {
XmlWriter::ScopedElement e = xml.scopedElement("file");
xml.writeAttribute("path", filename);
for(auto const& child : testCaseNodes)
writeTestCase(*child);
}
void writeTestCase(TestCaseNode const& testCaseNode) {
// All test cases have exactly one section - which represents the
// test case itself. That section may have 0-n nested sections
assert(testCaseNode.children.size() == 1);
SectionNode const& rootSection = *testCaseNode.children.front();
writeSection("", rootSection, testCaseNode.value.testInfo.okToFail());
}
void writeSection(std::string const& rootName, SectionNode const& sectionNode, bool okToFail) {
std::string name = trim(sectionNode.stats.sectionInfo.name);
if(!rootName.empty())
name = rootName + '/' + name;
if(!sectionNode.assertions.empty() || !sectionNode.stdOut.empty() || !sectionNode.stdErr.empty()) {
XmlWriter::ScopedElement e = xml.scopedElement("testCase");
xml.writeAttribute("name", name);
xml.writeAttribute("duration", static_cast<long>(sectionNode.stats.durationInSeconds * 1000));
writeAssertions(sectionNode, okToFail);
}
for(auto const& childNode : sectionNode.childSections)
writeSection(name, *childNode, okToFail);
}
void writeAssertions(SectionNode const& sectionNode, bool okToFail) {
for(auto const& assertion : sectionNode.assertions)
writeAssertion( assertion, okToFail);
}
void writeAssertion(AssertionStats const& stats, bool okToFail) {
AssertionResult const& result = stats.assertionResult;
if(!result.isOk()) {
std::string elementName;
if(okToFail) {
elementName = "skipped";
}
else {
switch(result.getResultType()) {
case ResultWas::ThrewException:
case ResultWas::FatalErrorCondition:
elementName = "error";
break;
case ResultWas::ExplicitFailure:
elementName = "failure";
break;
case ResultWas::ExpressionFailed:
elementName = "failure";
break;
case ResultWas::DidntThrowException:
elementName = "failure";
break;
// We should never see these here:
case ResultWas::Info:
case ResultWas::Warning:
case ResultWas::Ok:
case ResultWas::Unknown:
case ResultWas::FailureBit:
case ResultWas::Exception:
elementName = "internalError";
break;
}
}
XmlWriter::ScopedElement e = xml.scopedElement(elementName);
ReusableStringStream messageRss;
messageRss << result.getTestMacroName() << "(" << result.getExpression() << ")";
xml.writeAttribute("message", messageRss.str());
ReusableStringStream textRss;
if (stats.totals.assertions.total() > 0) {
textRss << "FAILED:\n";
if (result.hasExpression()) {
textRss << "\t" << result.getExpressionInMacro() << "\n";
}
if (result.hasExpandedExpression()) {
textRss << "with expansion:\n\t" << result.getExpandedExpression() << "\n";
}
}
if(!result.getMessage().empty())
textRss << result.getMessage() << "\n";
for(auto const& msg : stats.infoMessages)
if(msg.type == ResultWas::Info)
textRss << msg.message << "\n";
textRss << "at " << result.getSourceInfo();
xml.writeText(textRss.str(), XmlFormatting::Newline);
}
}
private:
XmlWriter xml;
};
#ifdef CATCH_IMPL
SonarQubeReporter::~SonarQubeReporter() {}
#endif
CATCH_REGISTER_REPORTER( "sonarqube", SonarQubeReporter )
} // end namespace Catch
#endif // CATCH_REPORTER_SONARQUBE_HPP_INCLUDED

View File

@ -193,9 +193,9 @@ namespace Catch {
e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() );
if( !testCaseStats.stdOut.empty() )
m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), false );
m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), XmlFormatting::Newline );
if( !testCaseStats.stdErr.empty() )
m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), false );
m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), XmlFormatting::Newline );
m_xml.endElement();
}

View File

@ -282,6 +282,7 @@ set(REPORTER_HEADERS
${HEADER_DIR}/reporters/catch_reporter_tap.hpp
${HEADER_DIR}/reporters/catch_reporter_teamcity.hpp
${HEADER_DIR}/reporters/catch_reporter_xml.h
${HEADER_DIR}/reporters/catch_reporter_sonarqube.hpp
)
set(REPORTER_SOURCES
${HEADER_DIR}/reporters/catch_reporter_bases.cpp

View File

@ -26,14 +26,6 @@ TEST_CASE("Benchmark factorial", "[benchmark]") {
BENCHMARK("factorial 14") {
return factorial(14);
};
//
// BENCHMARK("factorial 20") {
// return factorial(20);
// };
//
// BENCHMARK("factorial 35") {
// return factorial(35);
// };
}
TEST_CASE("Benchmark containers", "[.][benchmark]") {

View File

@ -631,6 +631,74 @@ GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 2 for: 2 == 2
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 5 for: 5 == 5
GeneratorsImpl.tests.cpp:<line number>: passed: !(gen.next()) for: !false
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == Approx(expected) for: -1.0 == Approx( -1.0 ) with 1 message: 'Current expected value is -1'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true with 1 message: 'Current expected value is -1'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == Approx(expected) for: -0.9 == Approx( -0.9 ) with 1 message: 'Current expected value is -0.9'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true with 1 message: 'Current expected value is -0.9'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == Approx(expected) for: -0.8 == Approx( -0.8 ) with 1 message: 'Current expected value is -0.8'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true with 1 message: 'Current expected value is -0.8'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == Approx(expected) for: -0.7 == Approx( -0.7 ) with 1 message: 'Current expected value is -0.7'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true with 1 message: 'Current expected value is -0.7'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == Approx(expected) for: -0.6 == Approx( -0.6 ) with 1 message: 'Current expected value is -0.6'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true with 1 message: 'Current expected value is -0.6'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == Approx(expected) for: -0.5 == Approx( -0.5 ) with 1 message: 'Current expected value is -0.5'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true with 1 message: 'Current expected value is -0.5'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == Approx(expected) for: -0.4 == Approx( -0.4 ) with 1 message: 'Current expected value is -0.4'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true with 1 message: 'Current expected value is -0.4'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == Approx(expected) for: -0.3 == Approx( -0.3 ) with 1 message: 'Current expected value is -0.3'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true with 1 message: 'Current expected value is -0.3'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == Approx(expected) for: -0.2 == Approx( -0.2 ) with 1 message: 'Current expected value is -0.2'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true with 1 message: 'Current expected value is -0.2'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == Approx(expected) for: -0.1 == Approx( -0.1 ) with 1 message: 'Current expected value is -0.1'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true with 1 message: 'Current expected value is -0.1'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == Approx(expected) for: -0.0 == Approx( -0.0 ) with 1 message: 'Current expected value is -1.38778e-16'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true with 1 message: 'Current expected value is -1.38778e-16'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == Approx(expected) for: 0.1 == Approx( 0.1 ) with 1 message: 'Current expected value is 0.1'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true with 1 message: 'Current expected value is 0.1'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == Approx(expected) for: 0.2 == Approx( 0.2 ) with 1 message: 'Current expected value is 0.2'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true with 1 message: 'Current expected value is 0.2'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == Approx(expected) for: 0.3 == Approx( 0.3 ) with 1 message: 'Current expected value is 0.3'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true with 1 message: 'Current expected value is 0.3'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == Approx(expected) for: 0.4 == Approx( 0.4 ) with 1 message: 'Current expected value is 0.4'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true with 1 message: 'Current expected value is 0.4'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == Approx(expected) for: 0.5 == Approx( 0.5 ) with 1 message: 'Current expected value is 0.5'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true with 1 message: 'Current expected value is 0.5'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == Approx(expected) for: 0.6 == Approx( 0.6 ) with 1 message: 'Current expected value is 0.6'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true with 1 message: 'Current expected value is 0.6'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == Approx(expected) for: 0.7 == Approx( 0.7 ) with 1 message: 'Current expected value is 0.7'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true with 1 message: 'Current expected value is 0.7'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == Approx(expected) for: 0.8 == Approx( 0.8 ) with 1 message: 'Current expected value is 0.8'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true with 1 message: 'Current expected value is 0.8'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == Approx(expected) for: 0.9 == Approx( 0.9 ) with 1 message: 'Current expected value is 0.9'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true with 1 message: 'Current expected value is 0.9'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == Approx( rangeEnd ) for: 1.0 == Approx( 1.0 )
GeneratorsImpl.tests.cpp:<line number>: passed: !(gen.next()) for: !false
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == Approx(expected) for: -1.0 == Approx( -1.0 ) with 1 message: 'Current expected value is -1'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true with 1 message: 'Current expected value is -1'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == Approx(expected) for: -0.7 == Approx( -0.7 ) with 1 message: 'Current expected value is -0.7'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true with 1 message: 'Current expected value is -0.7'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == Approx(expected) for: -0.4 == Approx( -0.4 ) with 1 message: 'Current expected value is -0.4'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true with 1 message: 'Current expected value is -0.4'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == Approx(expected) for: -0.1 == Approx( -0.1 ) with 1 message: 'Current expected value is -0.1'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true with 1 message: 'Current expected value is -0.1'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == Approx(expected) for: 0.2 == Approx( 0.2 ) with 1 message: 'Current expected value is 0.2'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true with 1 message: 'Current expected value is 0.2'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == Approx(expected) for: 0.5 == Approx( 0.5 ) with 1 message: 'Current expected value is 0.5'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true with 1 message: 'Current expected value is 0.5'
GeneratorsImpl.tests.cpp:<line number>: passed: !(gen.next()) for: !false
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == Approx(expected) for: -1.0 == Approx( -1.0 ) with 1 message: 'Current expected value is -1'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true with 1 message: 'Current expected value is -1'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == Approx(expected) for: -0.7 == Approx( -0.7 ) with 1 message: 'Current expected value is -0.7'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true with 1 message: 'Current expected value is -0.7'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == Approx(expected) for: -0.4 == Approx( -0.4 ) with 1 message: 'Current expected value is -0.4'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true with 1 message: 'Current expected value is -0.4'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == Approx(expected) for: -0.1 == Approx( -0.1 ) with 1 message: 'Current expected value is -0.1'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true with 1 message: 'Current expected value is -0.1'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == Approx(expected) for: 0.2 == Approx( 0.2 ) with 1 message: 'Current expected value is 0.2'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true with 1 message: 'Current expected value is 0.2'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == Approx(expected) for: 0.5 == Approx( 0.5 ) with 1 message: 'Current expected value is 0.5'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true with 1 message: 'Current expected value is 0.5'
GeneratorsImpl.tests.cpp:<line number>: passed: !(gen.next()) for: !false
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 5 for: 5 == 5
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 2 for: 2 == 2
@ -959,6 +1027,12 @@ CmdLine.tests.cpp:<line number>: passed: spec.matches( fakeTestCase( " aardvark
CmdLine.tests.cpp:<line number>: passed: spec.matches( fakeTestCase( " aardvark " ) ) for: true
CmdLine.tests.cpp:<line number>: passed: spec.matches( fakeTestCase( "aardvark " ) ) for: true
CmdLine.tests.cpp:<line number>: passed: spec.matches( fakeTestCase( "aardvark" ) ) for: true
CmdLine.tests.cpp:<line number>: passed: spec.matches(fakeTestCase("hidden and foo", "[.][foo]")) for: true
CmdLine.tests.cpp:<line number>: passed: !(spec.matches(fakeTestCase("only foo", "[foo]"))) for: !false
CmdLine.tests.cpp:<line number>: passed: !(spec.matches(fakeTestCase("hidden and foo", "[.][foo]"))) for: !false
CmdLine.tests.cpp:<line number>: passed: !(spec.matches(fakeTestCase("only foo", "[foo]"))) for: !false
CmdLine.tests.cpp:<line number>: passed: !(spec.matches(fakeTestCase("only hidden", "[.]"))) for: !false
CmdLine.tests.cpp:<line number>: passed: spec.matches(fakeTestCase("neither foo nor hidden", "[bar]")) for: true
Condition.tests.cpp:<line number>: passed: p == 0 for: 0 == 0
Condition.tests.cpp:<line number>: passed: p == pNULL for: 0 == 0
Condition.tests.cpp:<line number>: passed: p != 0 for: 0x<hex digits> != 0
@ -1095,37 +1169,32 @@ Matchers.tests.cpp:<line number>: passed: testStringForMatching(), EndsWith("sub
Matchers.tests.cpp:<line number>: passed: testStringForMatching(), EndsWith(" SuBsTrInG", Catch::CaseSensitive::No) for: "this string contains 'abc' as a substring" ends with: " substring" (case insensitive)
String.tests.cpp:<line number>: passed: empty.empty() for: true
String.tests.cpp:<line number>: passed: empty.size() == 0 for: 0 == 0
String.tests.cpp:<line number>: passed: empty.isNullTerminated() for: true
String.tests.cpp:<line number>: passed: std::strcmp( empty.c_str(), "" ) == 0 for: 0 == 0
String.tests.cpp:<line number>: passed: s.empty() == false for: false == false
String.tests.cpp:<line number>: passed: s.size() == 5 for: 5 == 5
String.tests.cpp:<line number>: passed: isSubstring( s ) == false for: false == false
String.tests.cpp:<line number>: passed: s.isNullTerminated() for: true
String.tests.cpp:<line number>: passed: std::strcmp( rawChars, "hello" ) == 0 for: 0 == 0
String.tests.cpp:<line number>: passed: isOwned( s ) == false for: false == false
String.tests.cpp:<line number>: passed: s.c_str()
String.tests.cpp:<line number>: passed: s.c_str() == rawChars for: "hello" == "hello"
String.tests.cpp:<line number>: passed: isOwned( s ) == false for: false == false
String.tests.cpp:<line number>: passed: s.data() == rawChars for: "hello" == "hello"
String.tests.cpp:<line number>: passed: original == "original"
String.tests.cpp:<line number>: passed: isSubstring( original ) for: true
String.tests.cpp:<line number>: passed: isOwned( original ) == false for: false == false
String.tests.cpp:<line number>: passed: isOwned( original ) for: true
String.tests.cpp:<line number>: passed: !(original.isNullTerminated()) for: !false
String.tests.cpp:<line number>: passed: original.c_str()
String.tests.cpp:<line number>: passed: original.data()
String.tests.cpp:<line number>: passed: ss.empty() == false for: false == false
String.tests.cpp:<line number>: passed: ss.size() == 5 for: 5 == 5
String.tests.cpp:<line number>: passed: std::strcmp( ss.c_str(), "hello" ) == 0 for: 0 == 0
String.tests.cpp:<line number>: passed: std::strncmp( ss.data(), "hello", 5 ) == 0 for: 0 == 0
String.tests.cpp:<line number>: passed: ss == "hello" for: hello == "hello"
String.tests.cpp:<line number>: passed: isSubstring( ss ) for: true
String.tests.cpp:<line number>: passed: isOwned( ss ) == false for: false == false
String.tests.cpp:<line number>: passed: rawChars == s.currentData() for: "hello world!" == "hello world!"
String.tests.cpp:<line number>: passed: ss.c_str() != rawChars for: "hello" != "hello world!"
String.tests.cpp:<line number>: passed: isOwned( ss ) for: true
String.tests.cpp:<line number>: passed: isOwned(ss) == false for: false == false
String.tests.cpp:<line number>: passed: ss == "hello" for: hello == "hello"
String.tests.cpp:<line number>: passed: rawChars == ss.currentData() for: "hello world!" == "hello world!"
String.tests.cpp:<line number>: passed: ss.size() == 6 for: 6 == 6
String.tests.cpp:<line number>: passed: std::strcmp( ss.c_str(), "world!" ) == 0 for: 0 == 0
String.tests.cpp:<line number>: passed: s.c_str() == s2.c_str() for: "hello world!" == "hello world!"
String.tests.cpp:<line number>: passed: s.c_str() != ss.c_str() for: "hello world!" != "hello"
String.tests.cpp:<line number>: passed: s.data() == s2.data() for: "hello world!" == "hello world!"
String.tests.cpp:<line number>: passed: s.data() == ss.data() for: "hello world!" == "hello world!"
String.tests.cpp:<line number>: passed: s.substr(s.size() + 1, 123).empty() for: true
String.tests.cpp:<line number>: passed: StringRef("hello") == StringRef("hello") for: hello == hello
String.tests.cpp:<line number>: passed: StringRef("hello") != StringRef("cello") for: hello != cello
String.tests.cpp:<line number>: passed: std::strcmp(ss.c_str(), "world!") == 0 for: 0 == 0
String.tests.cpp:<line number>: passed: (char*)buffer1 != (char*)buffer2 for: "Hello" != "Hello"
String.tests.cpp:<line number>: passed: left == right for: Hello == Hello
String.tests.cpp:<line number>: passed: left != left.substr(0, 3) for: Hello != Hel
String.tests.cpp:<line number>: passed: sr == "a standard string" for: a standard string == "a standard string"
String.tests.cpp:<line number>: passed: sr.size() == stdStr.size() for: 17 == 17
String.tests.cpp:<line number>: passed: sr == "a standard string" for: a standard string == "a standard string"
@ -1136,6 +1205,17 @@ String.tests.cpp:<line number>: passed: stdStr == "a stringref" for: "a stringre
String.tests.cpp:<line number>: passed: stdStr.size() == sr.size() for: 11 == 11
String.tests.cpp:<line number>: passed: stdStr == "a stringref" for: "a stringref" == "a stringref"
String.tests.cpp:<line number>: passed: stdStr.size() == sr.size() for: 11 == 11
String.tests.cpp:<line number>: passed: with 1 message: 'StringRef{}.size() == 0'
String.tests.cpp:<line number>: passed: with 1 message: 'StringRef{ "abc", 3 }.size() == 3'
String.tests.cpp:<line number>: passed: with 1 message: 'StringRef{ "abc", 3 }.isNullTerminated()'
String.tests.cpp:<line number>: passed: with 1 message: 'StringRef{ "abc", 2 }.size() == 2'
String.tests.cpp:<line number>: passed: with 1 message: '!(StringRef{ "abc", 2 }.isNullTerminated())'
String.tests.cpp:<line number>: passed: with 1 message: '!(sr1.empty())'
String.tests.cpp:<line number>: passed: with 1 message: 'sr1.size() == 3'
String.tests.cpp:<line number>: passed: with 1 message: 'sr1.isNullTerminated()'
String.tests.cpp:<line number>: passed: with 1 message: 'sr2.empty()'
String.tests.cpp:<line number>: passed: with 1 message: 'sr2.size() == 0'
String.tests.cpp:<line number>: passed: with 1 message: 'sr2.isNullTerminated()'
ToStringChrono.tests.cpp:<line number>: passed: minute == seconds for: 1 m == 60 s
ToStringChrono.tests.cpp:<line number>: passed: hour != seconds for: 1 h != 60 s
ToStringChrono.tests.cpp:<line number>: passed: micro != milli for: 1 us != 1 ms
@ -1487,48 +1567,6 @@ Xml.tests.cpp:<line number>: passed: encode( stringWithQuotes, Catch::XmlEncode:
"don't &quot;quote&quot; me on that"
Xml.tests.cpp:<line number>: passed: encode( "[\x01]" ) == "[\\x01]" for: "[\x01]" == "[\x01]"
Xml.tests.cpp:<line number>: passed: encode( "[\x7F]" ) == "[\\x7F]" for: "[\x7F]" == "[\x7F]"
Xml.tests.cpp:<line number>: passed: encode(u8"Here be 👾") == u8"Here be 👾" for: "Here be 👾" == "Here be 👾"
Xml.tests.cpp:<line number>: passed: encode(u8"šš") == u8"šš" for: "šš" == "šš"
Xml.tests.cpp:<line number>: passed: encode("\xDF\xBF") == "\xDF\xBF" for: "߿" == "߿"
Xml.tests.cpp:<line number>: passed: encode("\xE0\xA0\x80") == "\xE0\xA0\x80" for: "ࠀ" == "ࠀ"
Xml.tests.cpp:<line number>: passed: encode("\xED\x9F\xBF") == "\xED\x9F\xBF" for: "퟿" == "퟿"
Xml.tests.cpp:<line number>: passed: encode("\xEE\x80\x80") == "\xEE\x80\x80" for: "" == ""
Xml.tests.cpp:<line number>: passed: encode("\xEF\xBF\xBF") == "\xEF\xBF\xBF" for: "￿" == "￿"
Xml.tests.cpp:<line number>: passed: encode("\xF0\x90\x80\x80") == "\xF0\x90\x80\x80" for: "𐀀" == "𐀀"
Xml.tests.cpp:<line number>: passed: encode("\xF4\x8F\xBF\xBF") == "\xF4\x8F\xBF\xBF" for: "􏿿" == "􏿿"
Xml.tests.cpp:<line number>: passed: encode("Here \xFF be 👾") == u8"Here \\xFF be 👾" for: "Here \xFF be 👾" == "Here \xFF be 👾"
Xml.tests.cpp:<line number>: passed: encode("\xFF") == "\\xFF" for: "\xFF" == "\xFF"
Xml.tests.cpp:<line number>: passed: encode("\xC5\xC5\xA0") == u8"\\xC5Š" for: "\xC5Š" == "\xC5Š"
Xml.tests.cpp:<line number>: passed: encode("\xF4\x90\x80\x80") == u8"\\xF4\\x90\\x80\\x80" for: "\xF4\x90\x80\x80" == "\xF4\x90\x80\x80"
Xml.tests.cpp:<line number>: passed: encode("\xC0\x80") == u8"\\xC0\\x80" for: "\xC0\x80" == "\xC0\x80"
Xml.tests.cpp:<line number>: passed: encode("\xF0\x80\x80\x80") == u8"\\xF0\\x80\\x80\\x80" for: "\xF0\x80\x80\x80" == "\xF0\x80\x80\x80"
Xml.tests.cpp:<line number>: passed: encode("\xC1\xBF") == u8"\\xC1\\xBF" for: "\xC1\xBF" == "\xC1\xBF"
Xml.tests.cpp:<line number>: passed: encode("\xE0\x9F\xBF") == u8"\\xE0\\x9F\\xBF" for: "\xE0\x9F\xBF" == "\xE0\x9F\xBF"
Xml.tests.cpp:<line number>: passed: encode("\xF0\x8F\xBF\xBF") == u8"\\xF0\\x8F\\xBF\\xBF" for: "\xF0\x8F\xBF\xBF" == "\xF0\x8F\xBF\xBF"
Xml.tests.cpp:<line number>: passed: encode("\xED\xA0\x80") == "\xED\xA0\x80" for: "<22><><EFBFBD>" == "<22><><EFBFBD>"
Xml.tests.cpp:<line number>: passed: encode("\xED\xAF\xBF") == "\xED\xAF\xBF" for: "<22><><EFBFBD>" == "<22><><EFBFBD>"
Xml.tests.cpp:<line number>: passed: encode("\xED\xB0\x80") == "\xED\xB0\x80" for: "<22><><EFBFBD>" == "<22><><EFBFBD>"
Xml.tests.cpp:<line number>: passed: encode("\xED\xBF\xBF") == "\xED\xBF\xBF" for: "<22><><EFBFBD>" == "<22><><EFBFBD>"
Xml.tests.cpp:<line number>: passed: encode("\x80") == u8"\\x80" for: "\x80" == "\x80"
Xml.tests.cpp:<line number>: passed: encode("\x81") == u8"\\x81" for: "\x81" == "\x81"
Xml.tests.cpp:<line number>: passed: encode("\xBC") == u8"\\xBC" for: "\xBC" == "\xBC"
Xml.tests.cpp:<line number>: passed: encode("\xBF") == u8"\\xBF" for: "\xBF" == "\xBF"
Xml.tests.cpp:<line number>: passed: encode("\xF5\x80\x80\x80") == u8"\\xF5\\x80\\x80\\x80" for: "\xF5\x80\x80\x80" == "\xF5\x80\x80\x80"
Xml.tests.cpp:<line number>: passed: encode("\xF6\x80\x80\x80") == u8"\\xF6\\x80\\x80\\x80" for: "\xF6\x80\x80\x80" == "\xF6\x80\x80\x80"
Xml.tests.cpp:<line number>: passed: encode("\xF7\x80\x80\x80") == u8"\\xF7\\x80\\x80\\x80" for: "\xF7\x80\x80\x80" == "\xF7\x80\x80\x80"
Xml.tests.cpp:<line number>: passed: encode("\xDE") == u8"\\xDE" for: "\xDE" == "\xDE"
Xml.tests.cpp:<line number>: passed: encode("\xDF") == u8"\\xDF" for: "\xDF" == "\xDF"
Xml.tests.cpp:<line number>: passed: encode("\xE0") == u8"\\xE0" for: "\xE0" == "\xE0"
Xml.tests.cpp:<line number>: passed: encode("\xEF") == u8"\\xEF" for: "\xEF" == "\xEF"
Xml.tests.cpp:<line number>: passed: encode("\xF0") == u8"\\xF0" for: "\xF0" == "\xF0"
Xml.tests.cpp:<line number>: passed: encode("\xF4") == u8"\\xF4" for: "\xF4" == "\xF4"
Xml.tests.cpp:<line number>: passed: encode("\xE0\x80") == u8"\\xE0\\x80" for: "\xE0\x80" == "\xE0\x80"
Xml.tests.cpp:<line number>: passed: encode("\xE0\xBF") == u8"\\xE0\\xBF" for: "\xE0\xBF" == "\xE0\xBF"
Xml.tests.cpp:<line number>: passed: encode("\xE1\x80") == u8"\\xE1\\x80" for: "\xE1\x80" == "\xE1\x80"
Xml.tests.cpp:<line number>: passed: encode("\xF0\x80") == u8"\\xF0\\x80" for: "\xF0\x80" == "\xF0\x80"
Xml.tests.cpp:<line number>: passed: encode("\xF4\x80") == u8"\\xF4\\x80" for: "\xF4\x80" == "\xF4\x80"
Xml.tests.cpp:<line number>: passed: encode("\xF0\x80\x80") == u8"\\xF0\\x80\\x80" for: "\xF0\x80\x80" == "\xF0\x80\x80"
Xml.tests.cpp:<line number>: passed: encode("\xF4\x80\x80") == u8"\\xF4\\x80\\x80" for: "\xF4\x80\x80" == "\xF4\x80\x80"
ToStringVector.tests.cpp:<line number>: passed: Catch::Detail::stringify( empty ) == "{ }" for: "{ }" == "{ }"
ToStringVector.tests.cpp:<line number>: passed: Catch::Detail::stringify( oneValue ) == "{ 42 }" for: "{ 42 }" == "{ 42 }"
ToStringVector.tests.cpp:<line number>: passed: Catch::Detail::stringify( twoValues ) == "{ 42, 250 }" for: "{ 42, 250 }" == "{ 42, 250 }"

View File

@ -1381,5 +1381,5 @@ due to unexpected exception with message:
===============================================================================
test cases: 304 | 230 passed | 70 failed | 4 failed as expected
assertions: 1621 | 1469 passed | 131 failed | 21 failed as expected
assertions: 1659 | 1507 passed | 131 failed | 21 failed as expected

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -17,7 +17,7 @@
inline Catch::TestCase fakeTestCase(const char* name, const char* desc = "") { return Catch::makeTestCase(nullptr, "", { name, desc }, CATCH_INTERNAL_LINEINFO); }
TEST_CASE( "Parse test names and tags" ) {
TEST_CASE( "Parse test names and tags", "[command-line][test-spec]" ) {
using Catch::parseTestSpec;
using Catch::TestSpec;
@ -269,7 +269,6 @@ TEST_CASE( "Parse test names and tags" ) {
CHECK( spec.matches( fakeTestCase( " aardvark " ) ) );
CHECK( spec.matches( fakeTestCase( "aardvark " ) ) );
CHECK( spec.matches( fakeTestCase( "aardvark" ) ) );
}
SECTION( "Leading and trailing spaces in test name" ) {
TestSpec spec = parseTestSpec( "aardvark" );
@ -278,7 +277,18 @@ TEST_CASE( "Parse test names and tags" ) {
CHECK( spec.matches( fakeTestCase( " aardvark " ) ) );
CHECK( spec.matches( fakeTestCase( "aardvark " ) ) );
CHECK( spec.matches( fakeTestCase( "aardvark" ) ) );
}
SECTION("Shortened hide tags are split apart when parsing") {
TestSpec spec = parseTestSpec("[.foo]");
CHECK(spec.matches(fakeTestCase("hidden and foo", "[.][foo]")));
CHECK_FALSE(spec.matches(fakeTestCase("only foo", "[foo]")));
}
SECTION("Shortened hide tags also properly handle exclusion") {
TestSpec spec = parseTestSpec("~[.foo]");
CHECK_FALSE(spec.matches(fakeTestCase("hidden and foo", "[.][foo]")));
CHECK_FALSE(spec.matches(fakeTestCase("only foo", "[foo]")));
CHECK_FALSE(spec.matches(fakeTestCase("only hidden", "[.]")));
CHECK(spec.matches(fakeTestCase("neither foo nor hidden", "[bar]")));
}
}
@ -486,7 +496,7 @@ TEST_CASE( "Process can be configured on command line", "[config][command-line]"
REQUIRE(config.benchmarkSamples == 200);
}
SECTION("resamples") {
CHECK(cli.parse({ "test", "--benchmark-resamples=20000" }));

View File

@ -173,6 +173,58 @@ TEST_CASE("Generators internals", "[generators][internals]") {
REQUIRE_FALSE(gen.next());
}
}
SECTION("Floating Point") {
SECTION("Exact") {
const auto rangeStart = -1.;
const auto rangeEnd = 1.;
const auto step = .1;
auto gen = range(rangeStart, rangeEnd, step);
auto expected = rangeStart;
while( (rangeEnd - expected) > step ) {
INFO( "Current expected value is " << expected )
REQUIRE(gen.get() == Approx(expected));
REQUIRE(gen.next());
expected += step;
}
REQUIRE(gen.get() == Approx( rangeEnd ) );
REQUIRE_FALSE(gen.next());
}
SECTION("Slightly over end") {
const auto rangeStart = -1.;
const auto rangeEnd = 1.;
const auto step = .3;
auto gen = range(rangeStart, rangeEnd, step);
auto expected = rangeStart;
while( (rangeEnd - expected) > step ) {
INFO( "Current expected value is " << expected )
REQUIRE(gen.get() == Approx(expected));
REQUIRE(gen.next());
expected += step;
}
REQUIRE_FALSE(gen.next());
}
SECTION("Slightly under end") {
const auto rangeStart = -1.;
const auto rangeEnd = .9;
const auto step = .3;
auto gen = range(rangeStart, rangeEnd, step);
auto expected = rangeStart;
while( (rangeEnd - expected) > step ) {
INFO( "Current expected value is " << expected )
REQUIRE(gen.get() == Approx(expected));
REQUIRE(gen.next());
expected += step;
}
REQUIRE_FALSE(gen.next());
}
}
}
SECTION("Negative manual step") {
SECTION("Integer") {
@ -259,6 +311,21 @@ TEST_CASE("GENERATE capture macros", "[generators][internals][approvals]") {
REQUIRE(value == value2);
}
TEST_CASE("#1809 - GENERATE_COPY and SingleValueGenerator does not compile", "[generators][compilation][approvals]") {
// Verify Issue #1809 fix, only needs to compile.
auto a = GENERATE_COPY(1, 2);
(void)a;
auto b = GENERATE_COPY(as<long>{}, 1, 2);
(void)b;
int i = 1;
int j = 2;
auto c = GENERATE_COPY(i, j);
(void)c;
auto d = GENERATE_COPY(as<long>{}, i, j);
(void)d;
SUCCEED();
}
TEST_CASE("Multiple random generators in one test case output different values", "[generators][internals][approvals]") {
SECTION("Integer") {
auto random1 = Catch::Generators::random(0, 1000);

View File

@ -4,39 +4,15 @@
#include <cstring>
namespace Catch {
// Implementation of test accessors
struct StringRefTestAccess {
static auto isOwned( StringRef const& stringRef ) -> bool {
return stringRef.isOwned();
}
static auto isSubstring( StringRef const& stringRef ) -> bool {
return stringRef.isSubstring();
}
};
namespace {
auto isOwned( StringRef const& stringRef ) -> bool {
return StringRefTestAccess::isOwned( stringRef );
}
auto isSubstring( StringRef const& stringRef ) -> bool {
return StringRefTestAccess::isSubstring( stringRef );
}
} // end anonymous namespace
} // namespace Catch
TEST_CASE( "StringRef", "[Strings][StringRef]" ) {
using Catch::StringRef;
using Catch::isOwned; using Catch::isSubstring;
SECTION( "Empty string" ) {
StringRef empty;
REQUIRE( empty.empty() );
REQUIRE( empty.size() == 0 );
REQUIRE( empty.isNullTerminated() );
REQUIRE( std::strcmp( empty.c_str(), "" ) == 0 );
}
@ -44,28 +20,22 @@ TEST_CASE( "StringRef", "[Strings][StringRef]" ) {
StringRef s = "hello";
REQUIRE( s.empty() == false );
REQUIRE( s.size() == 5 );
REQUIRE( isSubstring( s ) == false );
REQUIRE( s.isNullTerminated() );
auto rawChars = s.currentData();
auto rawChars = s.data();
REQUIRE( std::strcmp( rawChars, "hello" ) == 0 );
SECTION( "c_str() does not cause copy" ) {
REQUIRE( isOwned( s ) == false );
REQUIRE( s.c_str() == rawChars );
REQUIRE( isOwned( s ) == false );
}
REQUIRE_NOTHROW(s.c_str());
REQUIRE(s.c_str() == rawChars);
REQUIRE(s.data() == rawChars);
}
SECTION( "From sub-string" ) {
StringRef original = StringRef( "original string" ).substr(0, 8);
REQUIRE( original == "original" );
REQUIRE( isSubstring( original ) );
REQUIRE( isOwned( original ) == false );
original.c_str(); // Forces it to take ownership
REQUIRE( isOwned( original ) );
REQUIRE_FALSE(original.isNullTerminated());
REQUIRE_THROWS(original.c_str());
REQUIRE_NOTHROW(original.data());
}
@ -76,26 +46,9 @@ TEST_CASE( "StringRef", "[Strings][StringRef]" ) {
SECTION( "zero-based substring" ) {
REQUIRE( ss.empty() == false );
REQUIRE( ss.size() == 5 );
REQUIRE( std::strcmp( ss.c_str(), "hello" ) == 0 );
REQUIRE( std::strncmp( ss.data(), "hello", 5 ) == 0 );
REQUIRE( ss == "hello" );
}
SECTION( "c_str() causes copy" ) {
REQUIRE( isSubstring( ss ) );
REQUIRE( isOwned( ss ) == false );
auto rawChars = ss.currentData();
REQUIRE( rawChars == s.currentData() ); // same pointer value
REQUIRE( ss.c_str() != rawChars );
REQUIRE( isOwned( ss ) );
SECTION( "Self-assignment after substring" ) {
ss = *&ss; // the *& are there to suppress warnings (see: "Improvements to Clang's diagnostics" in https://rev.ng/gitlab/revng-bar-2019/clang/raw/master/docs/ReleaseNotes.rst)
REQUIRE( isOwned(ss) == false );
REQUIRE( ss == "hello" );
REQUIRE( rawChars == ss.currentData() ); // same pointer value
}
}
SECTION( "non-zero-based substring") {
ss = s.substr( 6, 6 );
@ -105,21 +58,32 @@ TEST_CASE( "StringRef", "[Strings][StringRef]" ) {
SECTION( "Pointer values of full refs should match" ) {
StringRef s2 = s;
REQUIRE( s.c_str() == s2.c_str() );
REQUIRE( s.data() == s2.data() );
}
SECTION( "Pointer values of substring refs should not match" ) {
REQUIRE( s.c_str() != ss.c_str() );
SECTION( "Pointer values of substring refs should also match" ) {
REQUIRE( s.data() == ss.data() );
}
SECTION("Past the end substring") {
REQUIRE(s.substr(s.size() + 1, 123).empty());
}
SECTION("Substring off the end are trimmed") {
ss = s.substr(6, 123);
REQUIRE(std::strcmp(ss.c_str(), "world!") == 0);
}
// TODO: substring into string + size is longer than end
}
SECTION( "Comparisons" ) {
REQUIRE( StringRef("hello") == StringRef("hello") );
REQUIRE( StringRef("hello") != StringRef("cello") );
SECTION( "Comparisons are deep" ) {
char buffer1[] = "Hello";
char buffer2[] = "Hello";
CHECK((char*)buffer1 != (char*)buffer2);
StringRef left(buffer1), right(buffer2);
REQUIRE( left == right );
REQUIRE(left != left.substr(0, 3));
}
SECTION( "from std::string" ) {
@ -159,3 +123,28 @@ TEST_CASE( "StringRef", "[Strings][StringRef]" ) {
}
}
}
TEST_CASE("StringRef at compilation time", "[Strings][StringRef][constexpr]") {
using Catch::StringRef;
SECTION("Simple constructors") {
STATIC_REQUIRE(StringRef{}.size() == 0);
STATIC_REQUIRE(StringRef{ "abc", 3 }.size() == 3);
STATIC_REQUIRE(StringRef{ "abc", 3 }.isNullTerminated());
STATIC_REQUIRE(StringRef{ "abc", 2 }.size() == 2);
STATIC_REQUIRE_FALSE(StringRef{ "abc", 2 }.isNullTerminated());
}
SECTION("UDL construction") {
constexpr auto sr1 = "abc"_catch_sr;
STATIC_REQUIRE_FALSE(sr1.empty());
STATIC_REQUIRE(sr1.size() == 3);
STATIC_REQUIRE(sr1.isNullTerminated());
using Catch::operator"" _sr;
constexpr auto sr2 = ""_sr;
STATIC_REQUIRE(sr2.empty());
STATIC_REQUIRE(sr2.size() == 0);
STATIC_REQUIRE(sr2.isNullTerminated());
}
}

View File

@ -40,10 +40,11 @@ TEST_CASE( "XmlEncode", "[XML]" ) {
}
// Thanks to Peter Bindels (dascandy) for some of the tests
TEST_CASE("XmlEncode: UTF-8", "[XML][UTF-8]") {
TEST_CASE("XmlEncode: UTF-8", "[XML][UTF-8][approvals]") {
#define ESC(lit) (char*)(lit)
SECTION("Valid utf-8 strings") {
CHECK(encode(u8"Here be 👾") == u8"Here be 👾");
CHECK(encode(u8"šš") == u8"šš");
CHECK(encode(ESC(u8"Here be 👾")) == ESC(u8"Here be 👾"));
CHECK(encode(ESC(u8"šš")) == ESC(u8"šš"));
CHECK(encode("\xDF\xBF") == "\xDF\xBF"); // 0x7FF
CHECK(encode("\xE0\xA0\x80") == "\xE0\xA0\x80"); // 0x800
@ -55,18 +56,18 @@ TEST_CASE("XmlEncode: UTF-8", "[XML][UTF-8]") {
}
SECTION("Invalid utf-8 strings") {
SECTION("Various broken strings") {
CHECK(encode("Here \xFF be 👾") == u8"Here \\xFF be 👾");
CHECK(encode(ESC("Here \xFF be \xF0\x9F\x91\xBE")) == ESC(u8"Here \\xFF be 👾"));
CHECK(encode("\xFF") == "\\xFF");
CHECK(encode("\xC5\xC5\xA0") == u8"\\xC5Š");
CHECK(encode("\xF4\x90\x80\x80") == u8"\\xF4\\x90\\x80\\x80"); // 0x110000 -- out of unicode range
CHECK(encode("\xC5\xC5\xA0") == ESC(u8"\\xC5Š"));
CHECK(encode("\xF4\x90\x80\x80") == ESC(u8"\\xF4\\x90\\x80\\x80")); // 0x110000 -- out of unicode range
}
SECTION("Overlong encodings") {
CHECK(encode("\xC0\x80") == u8"\\xC0\\x80"); // \0
CHECK(encode("\xF0\x80\x80\x80") == u8"\\xF0\\x80\\x80\\x80"); // Super-over-long \0
CHECK(encode("\xC1\xBF") == u8"\\xC1\\xBF"); // ASCII char as UTF-8 (0x7F)
CHECK(encode("\xE0\x9F\xBF") == u8"\\xE0\\x9F\\xBF"); // 0x7FF
CHECK(encode("\xF0\x8F\xBF\xBF") == u8"\\xF0\\x8F\\xBF\\xBF"); // 0xFFFF
CHECK(encode("\xC0\x80") == "\\xC0\\x80"); // \0
CHECK(encode("\xF0\x80\x80\x80") == "\\xF0\\x80\\x80\\x80"); // Super-over-long \0
CHECK(encode("\xC1\xBF") == "\\xC1\\xBF"); // ASCII char as UTF-8 (0x7F)
CHECK(encode("\xE0\x9F\xBF") == "\\xE0\\x9F\\xBF"); // 0x7FF
CHECK(encode("\xF0\x8F\xBF\xBF") == "\\xF0\\x8F\\xBF\\xBF"); // 0xFFFF
}
// Note that we actually don't modify surrogate pairs, as we do not do strict checking
@ -78,35 +79,36 @@ TEST_CASE("XmlEncode: UTF-8", "[XML][UTF-8]") {
}
SECTION("Invalid start byte") {
CHECK(encode("\x80") == u8"\\x80");
CHECK(encode("\x81") == u8"\\x81");
CHECK(encode("\xBC") == u8"\\xBC");
CHECK(encode("\xBF") == u8"\\xBF");
CHECK(encode("\x80") == "\\x80");
CHECK(encode("\x81") == "\\x81");
CHECK(encode("\xBC") == "\\xBC");
CHECK(encode("\xBF") == "\\xBF");
// Out of range
CHECK(encode("\xF5\x80\x80\x80") == u8"\\xF5\\x80\\x80\\x80");
CHECK(encode("\xF6\x80\x80\x80") == u8"\\xF6\\x80\\x80\\x80");
CHECK(encode("\xF7\x80\x80\x80") == u8"\\xF7\\x80\\x80\\x80");
CHECK(encode("\xF5\x80\x80\x80") == "\\xF5\\x80\\x80\\x80");
CHECK(encode("\xF6\x80\x80\x80") == "\\xF6\\x80\\x80\\x80");
CHECK(encode("\xF7\x80\x80\x80") == "\\xF7\\x80\\x80\\x80");
}
SECTION("Missing continuation byte(s)") {
// Missing first continuation byte
CHECK(encode("\xDE") == u8"\\xDE");
CHECK(encode("\xDF") == u8"\\xDF");
CHECK(encode("\xE0") == u8"\\xE0");
CHECK(encode("\xEF") == u8"\\xEF");
CHECK(encode("\xF0") == u8"\\xF0");
CHECK(encode("\xF4") == u8"\\xF4");
CHECK(encode("\xDE") == "\\xDE");
CHECK(encode("\xDF") == "\\xDF");
CHECK(encode("\xE0") == "\\xE0");
CHECK(encode("\xEF") == "\\xEF");
CHECK(encode("\xF0") == "\\xF0");
CHECK(encode("\xF4") == "\\xF4");
// Missing second continuation byte
CHECK(encode("\xE0\x80") == u8"\\xE0\\x80");
CHECK(encode("\xE0\xBF") == u8"\\xE0\\xBF");
CHECK(encode("\xE1\x80") == u8"\\xE1\\x80");
CHECK(encode("\xF0\x80") == u8"\\xF0\\x80");
CHECK(encode("\xF4\x80") == u8"\\xF4\\x80");
CHECK(encode("\xE0\x80") == "\\xE0\\x80");
CHECK(encode("\xE0\xBF") == "\\xE0\\xBF");
CHECK(encode("\xE1\x80") == "\\xE1\\x80");
CHECK(encode("\xF0\x80") == "\\xF0\\x80");
CHECK(encode("\xF4\x80") == "\\xF4\\x80");
// Missing third continuation byte
CHECK(encode("\xF0\x80\x80") == u8"\\xF0\\x80\\x80");
CHECK(encode("\xF4\x80\x80") == u8"\\xF4\\x80\\x80");
CHECK(encode("\xF0\x80\x80") == "\\xF0\\x80\\x80");
CHECK(encode("\xF4\x80\x80") == "\\xF4\\x80\\x80");
}
}
#undef ESC
}

View File

@ -13,6 +13,7 @@
#include "reporters/catch_reporter_teamcity.hpp"
#include "reporters/catch_reporter_tap.hpp"
#include "reporters/catch_reporter_automake.hpp"
#include "reporters/catch_reporter_sonarqube.hpp"
// Some example tag aliases

View File

@ -126,5 +126,19 @@ TEST_CASE("Benchmark containers", "[!benchmark]") {
REQUIRE(v[i] == generated);
}
}
SECTION("construct and destroy example") {
BENCHMARK_ADVANCED("construct")(Catch::Benchmark::Chronometer meter) {
std::vector<Catch::Benchmark::storage_for<std::string>> storage(meter.runs());
meter.measure([&](int i) { storage[i].construct("thing"); });
};
BENCHMARK_ADVANCED("destroy")(Catch::Benchmark::Chronometer meter) {
std::vector<Catch::Benchmark::destructable_object<std::string>> storage(meter.runs());
for(auto&& o : storage)
o.construct("thing");
meter.measure([&](int i) { storage[i].destruct(); });
};
}
}
#endif // CATCH_CONFIG_ENABLE_BENCHMARKING

View File

@ -58,9 +58,11 @@ struct AutoTestReg {
REGISTER_TEST_CASE( manuallyRegisteredTestFunction, "ManuallyRegistered" );
}
};
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
static AutoTestReg autoTestReg;
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
template<typename T>
struct Foo {

View File

@ -9,12 +9,12 @@
// We need 2 types with non-trivial copies/moves
struct MyType1 {
MyType1() = default;
MyType1(MyType1 const&) { throw 1; }
[[noreturn]] MyType1(MyType1 const&) { throw 1; }
MyType1& operator=(MyType1 const&) { throw 3; }
};
struct MyType2 {
MyType2() = default;
MyType2(MyType2 const&) { throw 2; }
[[noreturn]] MyType2(MyType2 const&) { throw 2; }
MyType2& operator=(MyType2 const&) { throw 4; }
};

View File

@ -390,12 +390,6 @@ TEST_CASE("Commas in various macros are allowed") {
}
}
TEST_CASE( "null deref", "[.][failing][!nonportable]" ) {
CHECK( false );
int *x = NULL;
*x = 1;
}
TEST_CASE( "non-copyable objects", "[.][failing]" ) {
// Thanks to Agustin Bergé (@k-ballo on the cpplang Slack) for raising this
std::type_info const& ti = typeid(int);

View File

@ -29,6 +29,7 @@ filelocParser = re.compile(r'''
lineNumberParser = re.compile(r' line="[0-9]*"')
hexParser = re.compile(r'\b(0[xX][0-9a-fA-F]+)\b')
durationsParser = re.compile(r' time="[0-9]*\.[0-9]*"')
sonarqubeDurationParser = re.compile(r' duration="[0-9]+"')
timestampsParser = re.compile(r'\d{4}-\d{2}-\d{2}T\d{2}\:\d{2}\:\d{2}Z')
versionParser = re.compile(r'Catch v[0-9]+\.[0-9]+\.[0-9]+(-develop\.[0-9]+)?')
nullParser = re.compile(r'\b(__null|nullptr)\b')
@ -138,6 +139,7 @@ def filterLine(line, isCompact):
# strip durations and timestamps
line = durationsParser.sub(' time="{duration}"', line)
line = sonarqubeDurationParser.sub(' duration="{duration}"', line)
line = timestampsParser.sub('{iso8601-timestamp}', line)
line = specialCaseParser.sub('file:\g<1>', line)
line = errnoParser.sub('errno', line)
@ -204,6 +206,8 @@ approve("junit.sw", ["~[!nonportable]~[!benchmark]~[approvals]", "-s", "-w", "No
approve("xml.sw", ["~[!nonportable]~[!benchmark]~[approvals]", "-s", "-w", "NoAssertions", "-r", "xml", "--order", "lex", "--rng-seed", "1"])
# compact reporter, include passes, warn about No Assertions
approve('compact.sw', ['~[!nonportable]~[!benchmark]~[approvals]', '-s', '-w', 'NoAssertions', '-r', 'compact', '--order', 'lex', "--rng-seed", "1"])
# sonarqube reporter, include passes, warn about No Assertions
approve("sonarqube.sw", ["~[!nonportable]~[!benchmark]~[approvals]", "-s", "-w", "NoAssertions", "-r", "sonarqube", "--order", "lex", "--rng-seed", "1"])
if overallResult != 0:
print("If these differences are expected, run approve.py to approve new baselines.")

View File

@ -23,6 +23,7 @@ def generate(v):
blankParser = re.compile( r'^\s*$')
seenHeaders = set([])
possibleHeaders = set([])
rootPath = os.path.join( catchPath, 'include/' )
outputPath = os.path.join( catchPath, 'single_include/catch2/catch.hpp' )
@ -52,8 +53,20 @@ def generate(v):
if globals['includeImpl'] or globals['implIfDefs'] == -1:
out.write( line )
def getDirsToSearch( ):
return [os.path.join( rootPath, s) for s in ['', 'internal', 'reporters', 'internal/benchmark', 'internal/benchmark/detail']]
def collectPossibleHeaders():
dirs = getDirsToSearch()
for dir in dirs:
hpps = glob(os.path.join(dir, '*.hpp'))
hs = glob(os.path.join(dir, '*.h'))
possibleHeaders.update( hpp.rpartition( os.sep )[2] for hpp in hpps )
possibleHeaders.update( h.rpartition( os.sep )[2] for h in hs )
def insertCpps():
dirs = [os.path.join( rootPath, s) for s in ['', 'internal', 'reporters', 'internal/benchmark', 'internal/benchmark/detail']]
dirs = getDirsToSearch()
cppFiles = []
for dir in dirs:
cppFiles += glob(os.path.join(dir, '*.cpp'))
@ -103,6 +116,13 @@ def generate(v):
write( line.rstrip() + "\n" )
write( u'// end {}\n'.format(filename) )
def warnUnparsedHeaders():
unparsedHeaders = possibleHeaders.difference( seenHeaders )
# These headers aren't packaged into the unified header, exclude them from any warning
whitelist = ['catch.hpp', 'catch_reporter_teamcity.hpp', 'catch_with_main.hpp', 'catch_reporter_automake.hpp', 'catch_reporter_tap.hpp', 'catch_reporter_sonarqube.hpp']
unparsedHeaders = unparsedHeaders.difference( whitelist )
if unparsedHeaders:
print( "WARNING: unparsed headers detected\n{0}\n".format( unparsedHeaders ) )
write( u"/*\n" )
write( u" * Catch v{0}\n".format( v.getVersionString() ) )
@ -117,11 +137,13 @@ def generate(v):
write( u"#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED\n" )
write( u"#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED\n" )
collectPossibleHeaders()
parseFile( rootPath, 'catch.hpp' )
warnUnparsedHeaders()
write( u"#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED\n\n" )
out.close()
print ("Generated single include for Catch v{0}\n".format( v.getVersionString() ) )
print( "Generated single include for Catch v{0}\n".format( v.getVersionString() ) )
if __name__ == '__main__':

View File

@ -156,7 +156,7 @@ def performUpdates(version):
# We probably should have some kind of convention to select which reporters need to be copied automagically,
# but this works for now
import shutil
for rep in ('automake', 'tap', 'teamcity'):
for rep in ('automake', 'tap', 'teamcity', 'sonarqube'):
sourceFile = os.path.join(catchPath, 'include/reporters/catch_reporter_{}.hpp'.format(rep))
destFile = os.path.join(catchPath, 'single_include', 'catch2', 'catch_reporter_{}.hpp'.format(rep))
shutil.copyfile(sourceFile, destFile)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,181 @@
/*
* Created by Daniel Garcia on 2018-12-04.
* Copyright Social Point SL. 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 CATCH_REPORTER_SONARQUBE_HPP_INCLUDED
#define CATCH_REPORTER_SONARQUBE_HPP_INCLUDED
// Don't #include any Catch headers here - we can assume they are already
// included before this header.
// This is not good practice in general but is necessary in this case so this
// file can be distributed as a single header that works with the main
// Catch single header.
#include <map>
namespace Catch {
struct SonarQubeReporter : CumulativeReporterBase<SonarQubeReporter> {
SonarQubeReporter(ReporterConfig const& config)
: CumulativeReporterBase(config)
, xml(config.stream()) {
m_reporterPrefs.shouldRedirectStdOut = true;
m_reporterPrefs.shouldReportAllAssertions = true;
}
~SonarQubeReporter() override;
static std::string getDescription() {
return "Reports test results in the Generic Test Data SonarQube XML format";
}
static std::set<Verbosity> getSupportedVerbosities() {
return { Verbosity::Normal };
}
void noMatchingTestCases(std::string const& /*spec*/) override {}
void testRunStarting(TestRunInfo const& testRunInfo) override {
CumulativeReporterBase::testRunStarting(testRunInfo);
xml.startElement("testExecutions");
xml.writeAttribute("version", "1");
}
void testGroupEnded(TestGroupStats const& testGroupStats) override {
CumulativeReporterBase::testGroupEnded(testGroupStats);
writeGroup(*m_testGroups.back());
}
void testRunEndedCumulative() override {
xml.endElement();
}
void writeGroup(TestGroupNode const& groupNode) {
std::map<std::string, TestGroupNode::ChildNodes> testsPerFile;
for(auto const& child : groupNode.children)
testsPerFile[child->value.testInfo.lineInfo.file].push_back(child);
for(auto const& kv : testsPerFile)
writeTestFile(kv.first.c_str(), kv.second);
}
void writeTestFile(const char* filename, TestGroupNode::ChildNodes const& testCaseNodes) {
XmlWriter::ScopedElement e = xml.scopedElement("file");
xml.writeAttribute("path", filename);
for(auto const& child : testCaseNodes)
writeTestCase(*child);
}
void writeTestCase(TestCaseNode const& testCaseNode) {
// All test cases have exactly one section - which represents the
// test case itself. That section may have 0-n nested sections
assert(testCaseNode.children.size() == 1);
SectionNode const& rootSection = *testCaseNode.children.front();
writeSection("", rootSection, testCaseNode.value.testInfo.okToFail());
}
void writeSection(std::string const& rootName, SectionNode const& sectionNode, bool okToFail) {
std::string name = trim(sectionNode.stats.sectionInfo.name);
if(!rootName.empty())
name = rootName + '/' + name;
if(!sectionNode.assertions.empty() || !sectionNode.stdOut.empty() || !sectionNode.stdErr.empty()) {
XmlWriter::ScopedElement e = xml.scopedElement("testCase");
xml.writeAttribute("name", name);
xml.writeAttribute("duration", static_cast<long>(sectionNode.stats.durationInSeconds * 1000));
writeAssertions(sectionNode, okToFail);
}
for(auto const& childNode : sectionNode.childSections)
writeSection(name, *childNode, okToFail);
}
void writeAssertions(SectionNode const& sectionNode, bool okToFail) {
for(auto const& assertion : sectionNode.assertions)
writeAssertion( assertion, okToFail);
}
void writeAssertion(AssertionStats const& stats, bool okToFail) {
AssertionResult const& result = stats.assertionResult;
if(!result.isOk()) {
std::string elementName;
if(okToFail) {
elementName = "skipped";
}
else {
switch(result.getResultType()) {
case ResultWas::ThrewException:
case ResultWas::FatalErrorCondition:
elementName = "error";
break;
case ResultWas::ExplicitFailure:
elementName = "failure";
break;
case ResultWas::ExpressionFailed:
elementName = "failure";
break;
case ResultWas::DidntThrowException:
elementName = "failure";
break;
// We should never see these here:
case ResultWas::Info:
case ResultWas::Warning:
case ResultWas::Ok:
case ResultWas::Unknown:
case ResultWas::FailureBit:
case ResultWas::Exception:
elementName = "internalError";
break;
}
}
XmlWriter::ScopedElement e = xml.scopedElement(elementName);
ReusableStringStream messageRss;
messageRss << result.getTestMacroName() << "(" << result.getExpression() << ")";
xml.writeAttribute("message", messageRss.str());
ReusableStringStream textRss;
if (stats.totals.assertions.total() > 0) {
textRss << "FAILED:\n";
if (result.hasExpression()) {
textRss << "\t" << result.getExpressionInMacro() << "\n";
}
if (result.hasExpandedExpression()) {
textRss << "with expansion:\n\t" << result.getExpandedExpression() << "\n";
}
}
if(!result.getMessage().empty())
textRss << result.getMessage() << "\n";
for(auto const& msg : stats.infoMessages)
if(msg.type == ResultWas::Info)
textRss << msg.message << "\n";
textRss << "at " << result.getSourceInfo();
xml.writeText(textRss.str(), XmlFormatting::Newline);
}
}
private:
XmlWriter xml;
};
#ifdef CATCH_IMPL
SonarQubeReporter::~SonarQubeReporter() {}
#endif
CATCH_REGISTER_REPORTER( "sonarqube", SonarQubeReporter )
} // end namespace Catch
#endif // CATCH_REPORTER_SONARQUBE_HPP_INCLUDED