Unverified Commit f9fe0193 by Niels Lohmann

Merge branch 'develop' into coverity_scan

parents 03f06e19 758c4add
...@@ -64,7 +64,7 @@ Please understand that I cannot accept pull requests changing only file `src/jso ...@@ -64,7 +64,7 @@ Please understand that I cannot accept pull requests changing only file `src/jso
- Specifically, I am aware of compilation problems with **Microsoft Visual Studio** (there even is an [issue label](https://github.com/nlohmann/json/issues?utf8=✓&q=label%3A%22visual+studio%22+) for these kind of bugs). I understand that even in 2016, complete C++11 support isn't there yet. But please also understand that I do not want to drop features or uglify the code just to make Microsoft's sub-standard compiler happy. The past has shown that there are ways to express the functionality such that the code compiles with the most recent MSVC - unfortunately, this is not the main objective of the project. - Specifically, I am aware of compilation problems with **Microsoft Visual Studio** (there even is an [issue label](https://github.com/nlohmann/json/issues?utf8=✓&q=label%3A%22visual+studio%22+) for these kind of bugs). I understand that even in 2016, complete C++11 support isn't there yet. But please also understand that I do not want to drop features or uglify the code just to make Microsoft's sub-standard compiler happy. The past has shown that there are ways to express the functionality such that the code compiles with the most recent MSVC - unfortunately, this is not the main objective of the project.
- Please refrain from proposing changes that would **break [JSON](http://json.org) conformance**. If you propose a conformant extension of JSON to be supported by the library, please motivate this extension. - Please refrain from proposing changes that would **break [JSON](http://json.org) conformance**. If you propose a conformant extension of JSON to be supported by the library, please motivate this extension.
- We shall not extend the library to **support comments**. There is quite some [controversy](https://www.reddit.com/r/programming/comments/4v6chu/why_json_doesnt_support_comments_douglas_crockford/) around this topic, and there were quite some [issues](https://github.com/nlohmann/json/issues/376) on this. We believe that JSON is fine without comments. - We shall not extend the library to **support comments**. There is quite some [controversy](https://www.reddit.com/r/programming/comments/4v6chu/why_json_doesnt_support_comments_douglas_crockford/) around this topic, and there were quite some [issues](https://github.com/nlohmann/json/issues/376) on this. We believe that JSON is fine without comments.
- We do not preserve the **insertion order of object elements**. The [JSON standard](https://tools.ietf.org/html/rfc7159.html) defines objects as "an unordered collection of zero or more name/value pairs". To this end, this library does not preserve insertion order of name/value pairs. (In fact, keys will be traversed in alphabetical order as `std::map` with `std::less` is used by default.) Note this behavior conforms to the standard, and we shall not it to any other order. - We do not preserve the **insertion order of object elements**. The [JSON standard](https://tools.ietf.org/html/rfc7159.html) defines objects as "an unordered collection of zero or more name/value pairs". To this end, this library does not preserve insertion order of name/value pairs. (In fact, keys will be traversed in alphabetical order as `std::map` with `std::less` is used by default.) Note this behavior conforms to the standard, and we shall not change it to any other order. If you do want to preserve the insertion order, you can specialize the object type with containers like [`tsl::ordered_map`](https://github.com/Tessil/ordered-map) or [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map).
- Please do not open pull requests that address **multiple issues**. - Please do not open pull requests that address **multiple issues**.
......
...@@ -49,7 +49,6 @@ doctest: ...@@ -49,7 +49,6 @@ doctest:
# -Wno-documentation-unknown-command: code uses user-defined commands like @complexity # -Wno-documentation-unknown-command: code uses user-defined commands like @complexity
# -Wno-exit-time-destructors: warning in Catch code # -Wno-exit-time-destructors: warning in Catch code
# -Wno-keyword-macro: unit-tests use "#define private public" # -Wno-keyword-macro: unit-tests use "#define private public"
# -Wno-deprecated-declarations: some functions are deprecated until 3.0.0
# -Wno-range-loop-analysis: iterator_wrapper tests tests "for(const auto i...)" # -Wno-range-loop-analysis: iterator_wrapper tests tests "for(const auto i...)"
pedantic: pedantic:
$(MAKE) json_unit CXXFLAGS="\ $(MAKE) json_unit CXXFLAGS="\
...@@ -59,7 +58,6 @@ pedantic: ...@@ -59,7 +58,6 @@ pedantic:
-Wno-documentation-unknown-command \ -Wno-documentation-unknown-command \
-Wno-exit-time-destructors \ -Wno-exit-time-destructors \
-Wno-keyword-macro \ -Wno-keyword-macro \
-Wno-deprecated-declarations \
-Wno-range-loop-analysis" -Wno-range-loop-analysis"
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
[![Coverage Status](https://img.shields.io/coveralls/nlohmann/json.svg)](https://coveralls.io/r/nlohmann/json) [![Coverage Status](https://img.shields.io/coveralls/nlohmann/json.svg)](https://coveralls.io/r/nlohmann/json)
[![Coverity Scan Build Status](https://scan.coverity.com/projects/5550/badge.svg)](https://scan.coverity.com/projects/nlohmann-json) [![Coverity Scan Build Status](https://scan.coverity.com/projects/5550/badge.svg)](https://scan.coverity.com/projects/nlohmann-json)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/f3732b3327e34358a0e9d1fe9f661f08)](https://www.codacy.com/app/nlohmann/json?utm_source=github.com&utm_medium=referral&utm_content=nlohmann/json&utm_campaign=Badge_Grade) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/f3732b3327e34358a0e9d1fe9f661f08)](https://www.codacy.com/app/nlohmann/json?utm_source=github.com&utm_medium=referral&utm_content=nlohmann/json&utm_campaign=Badge_Grade)
[![Try online](https://img.shields.io/badge/try-online-blue.svg)](http://melpon.org/wandbox/permlink/4NEU6ZZMoM9lpIex) [![Try online](https://img.shields.io/badge/try-online-blue.svg)](http://melpon.org/wandbox/permlink/nv9fOg0XVVhWmFFy)
[![Documentation](https://img.shields.io/badge/docs-doxygen-blue.svg)](http://nlohmann.github.io/json) [![Documentation](https://img.shields.io/badge/docs-doxygen-blue.svg)](http://nlohmann.github.io/json)
[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/nlohmann/json/master/LICENSE.MIT) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/nlohmann/json/master/LICENSE.MIT)
[![Github Releases](https://img.shields.io/github/release/nlohmann/json.svg)](https://github.com/nlohmann/json/releases) [![Github Releases](https://img.shields.io/github/release/nlohmann/json.svg)](https://github.com/nlohmann/json/releases)
...@@ -65,6 +65,8 @@ to the files you want to use JSON objects. That's it. Do not forget to set the n ...@@ -65,6 +65,8 @@ to the files you want to use JSON objects. That's it. Do not forget to set the n
:beer: If you are using OS X and [Homebrew](http://brew.sh), just type `brew tap nlohmann/json` and `brew install nlohmann_json` and you're set. If you want the bleeding edge rather than the latest release, use `brew install nlohmann_json --HEAD`. :beer: If you are using OS X and [Homebrew](http://brew.sh), just type `brew tap nlohmann/json` and `brew install nlohmann_json` and you're set. If you want the bleeding edge rather than the latest release, use `brew install nlohmann_json --HEAD`.
:warning: [Version 3.0.0](https://github.com/nlohmann/json/wiki/Road-toward-3.0.0) is currently under development. Branch `develop` is used for the ongoing work and is probably **unstable**. Please use the `master` branch for the last stable version 2.1.1.
## Examples ## Examples
...@@ -823,9 +825,10 @@ I deeply appreciate the help of the following people. ...@@ -823,9 +825,10 @@ I deeply appreciate the help of the following people.
- [EnricoBilla](https://github.com/EnricoBilla) noted a typo in an example. - [EnricoBilla](https://github.com/EnricoBilla) noted a typo in an example.
- [Martin Hořeňovský](https://github.com/horenmar) found a way for a 2x speedup for the compilation time of the test suite. - [Martin Hořeňovský](https://github.com/horenmar) found a way for a 2x speedup for the compilation time of the test suite.
- [ukhegg](https://github.com/ukhegg) found proposed an improvement for the examples section. - [ukhegg](https://github.com/ukhegg) found proposed an improvement for the examples section.
- [rswanson-ihi](https://github.com/rswanson-ihi) noted a type in the README. - [rswanson-ihi](https://github.com/rswanson-ihi) noted a typo in the README.
- [Mihai Stan](https://github.com/stanmihai4) fixed a bug in the comparison with `nullptr`s. - [Mihai Stan](https://github.com/stanmihai4) fixed a bug in the comparison with `nullptr`s.
- [Tushar Maheshwari](https://github.com/tusharpm) added [cotire](https://github.com/sakra/cotire) support to speed up the compilation. - [Tushar Maheshwari](https://github.com/tusharpm) added [cotire](https://github.com/sakra/cotire) support to speed up the compilation.
- [TedLyngmo](https://github.com/TedLyngmo) noted a typo in the README.
Thanks a lot for helping out! Please [let me know](mailto:mail@nlohmann.me) if I forgot someone. Thanks a lot for helping out! Please [let me know](mailto:mail@nlohmann.me) if I forgot someone.
...@@ -876,6 +879,7 @@ The library is currently used in Apple macOS Sierra and iOS 10. I am not sure wh ...@@ -876,6 +879,7 @@ The library is currently used in Apple macOS Sierra and iOS 10. I am not sure wh
- The strings stored in the library are UTF-8 encoded. When using the default string type (`std::string`), note that its length/size functions return the number of stored bytes rather than the number of characters or glyphs. - The strings stored in the library are UTF-8 encoded. When using the default string type (`std::string`), note that its length/size functions return the number of stored bytes rather than the number of characters or glyphs.
- The code can be compiled without C++ **runtime type identification** features; that is, you can use the `-fno-rtti` compiler flag. - The code can be compiled without C++ **runtime type identification** features; that is, you can use the `-fno-rtti` compiler flag.
- **Exceptions** are used widly within the library. They can, however, be switched off with either using the compiler flag `-fno-exceptions` or by defining the symbol `JSON_NOEXCEPTION`. In this case, exceptions are replaced by an `abort()` call. - **Exceptions** are used widly within the library. They can, however, be switched off with either using the compiler flag `-fno-exceptions` or by defining the symbol `JSON_NOEXCEPTION`. In this case, exceptions are replaced by an `abort()` call.
- By default, the library does not preserve the **insertion order of object elements**. This is standards-compliant, as the [JSON standard](https://tools.ietf.org/html/rfc7159.html) defines objects as "an unordered collection of zero or more name/value pairs". If you do want to preserve the insertion order, you can specialize the object type with containers like [`tsl::ordered_map`](https://github.com/Tessil/ordered-map) or [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map).
## Execute unit tests ## Execute unit tests
...@@ -884,7 +888,7 @@ To compile and run the tests, you need to execute ...@@ -884,7 +888,7 @@ To compile and run the tests, you need to execute
```sh ```sh
$ make json_unit -Ctest $ make json_unit -Ctest
$ ./test/json_unit "*"" $ ./test/json_unit "*"
=============================================================================== ===============================================================================
All tests passed (11202597 assertions in 47 test cases) All tests passed (11202597 assertions in 47 test cases)
......
<a target="_blank" href="http://melpon.org/wandbox/permlink/4NEU6ZZMoM9lpIex"><b>online</b></a> <a target="_blank" href="http://melpon.org/wandbox/permlink/nv9fOg0XVVhWmFFy"><b>online</b></a>
\ No newline at end of file \ No newline at end of file
#include <json.hpp>
using json = nlohmann::json;
int main()
{
// a JSON text
auto text = R"(
{
"Image": {
"Width": 800,
"Height": 600,
"Title": "View from 15th Floor",
"Thumbnail": {
"Url": "http://www.example.com/image/481989943",
"Height": 125,
"Width": 100
},
"Animated" : false,
"IDs": [116, 943, 234, 38793]
}
}
)";
// fill a stream with JSON text
std::stringstream ss;
ss << text;
// create JSON from stream
json j_complete(ss); // deprecated!
// shall be replaced by: json j_complete = json::parse(ss);
std::cout << std::setw(4) << j_complete << "\n\n";
// define parser callback
json::parser_callback_t cb = [](int depth, json::parse_event_t event, json & parsed)
{
// skip object elements with key "Thumbnail"
if (event == json::parse_event_t::key and parsed == json("Thumbnail"))
{
return false;
}
else
{
return true;
}
};
// fill a stream with JSON text
ss.clear();
ss << text;
// create JSON from stream (with callback)
json j_filtered(ss, cb);
// shall be replaced by: json j_filtered = json::parse(ss, cb);
std::cout << std::setw(4) << j_filtered << '\n';
}
\ No newline at end of file
<a target="_blank" href="http://melpon.org/wandbox/permlink/R6dzpKXlxrttShf7"><b>online</b></a>
\ No newline at end of file
{
"Image": {
"Animated": false,
"Height": 600,
"IDs": [
116,
943,
234,
38793
],
"Thumbnail": {
"Height": 125,
"Url": "http://www.example.com/image/481989943",
"Width": 100
},
"Title": "View from 15th Floor",
"Width": 800
}
}
{
"Image": {
"Animated": false,
"Height": 600,
"IDs": [
116,
943,
234,
38793
],
"Title": "View from 15th Floor",
"Width": 800
}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -699,6 +699,15 @@ TEST_CASE("constructors") ...@@ -699,6 +699,15 @@ TEST_CASE("constructors")
json j(n); json j(n);
CHECK(j.type() == json::value_t::number_float); CHECK(j.type() == json::value_t::number_float);
} }
SECTION("infinity")
{
// infinity is stored as null
// should change in the future: https://github.com/nlohmann/json/issues/388
json::number_float_t n(std::numeric_limits<json::number_float_t>::infinity());
json j(n);
CHECK(j.type() == json::value_t::null);
}
} }
SECTION("create a floating-point number (implicit)") SECTION("create a floating-point number (implicit)")
...@@ -1272,40 +1281,4 @@ TEST_CASE("constructors") ...@@ -1272,40 +1281,4 @@ TEST_CASE("constructors")
} }
} }
} }
SECTION("create a JSON value from an input stream")
{
SECTION("std::stringstream")
{
std::stringstream ss;
ss << "[\"foo\",1,2,3,false,{\"one\":1}]";
json j(ss);
CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}}));
}
SECTION("with callback function")
{
std::stringstream ss;
ss << "[\"foo\",1,2,3,false,{\"one\":1}]";
json j(ss, [](int, json::parse_event_t, const json & val)
{
// filter all number(2) elements
if (val == json(2))
{
return false;
}
else
{
return true;
}
});
CHECK(j == json({"foo", 1, 3, false, {{"one", 1}}}));
}
SECTION("std::ifstream")
{
std::ifstream f("test/data/json_tests/pass1.json");
json j(f);
}
}
} }
...@@ -49,44 +49,53 @@ TEST_CASE("convenience functions") ...@@ -49,44 +49,53 @@ TEST_CASE("convenience functions")
SECTION("string escape") SECTION("string escape")
{ {
CHECK(json::escape_string("\"") == "\\\""); const auto check_escaped = [](const char* original,
CHECK(json::escape_string("\\") == "\\\\"); const char* escaped)
CHECK(json::escape_string("\b") == "\\b"); {
CHECK(json::escape_string("\f") == "\\f"); std::stringstream ss;
CHECK(json::escape_string("\n") == "\\n"); json::serializer s(ss);
CHECK(json::escape_string("\r") == "\\r"); s.dump_escaped(original);
CHECK(json::escape_string("\t") == "\\t"); CHECK(ss.str() == escaped);
};
check_escaped("\"", "\\\"");
check_escaped("\\", "\\\\");
check_escaped("\b", "\\b");
check_escaped("\f", "\\f");
check_escaped("\n", "\\n");
check_escaped("\r", "\\r");
check_escaped("\t", "\\t");
CHECK(json::escape_string("\x01") == "\\u0001"); check_escaped("\x01", "\\u0001");
CHECK(json::escape_string("\x02") == "\\u0002"); check_escaped("\x02", "\\u0002");
CHECK(json::escape_string("\x03") == "\\u0003"); check_escaped("\x03", "\\u0003");
CHECK(json::escape_string("\x04") == "\\u0004"); check_escaped("\x04", "\\u0004");
CHECK(json::escape_string("\x05") == "\\u0005"); check_escaped("\x05", "\\u0005");
CHECK(json::escape_string("\x06") == "\\u0006"); check_escaped("\x06", "\\u0006");
CHECK(json::escape_string("\x07") == "\\u0007"); check_escaped("\x07", "\\u0007");
CHECK(json::escape_string("\x08") == "\\b"); check_escaped("\x08", "\\b");
CHECK(json::escape_string("\x09") == "\\t"); check_escaped("\x09", "\\t");
CHECK(json::escape_string("\x0a") == "\\n"); check_escaped("\x0a", "\\n");
CHECK(json::escape_string("\x0b") == "\\u000b"); check_escaped("\x0b", "\\u000b");
CHECK(json::escape_string("\x0c") == "\\f"); check_escaped("\x0c", "\\f");
CHECK(json::escape_string("\x0d") == "\\r"); check_escaped("\x0d", "\\r");
CHECK(json::escape_string("\x0e") == "\\u000e"); check_escaped("\x0e", "\\u000e");
CHECK(json::escape_string("\x0f") == "\\u000f"); check_escaped("\x0f", "\\u000f");
CHECK(json::escape_string("\x10") == "\\u0010"); check_escaped("\x10", "\\u0010");
CHECK(json::escape_string("\x11") == "\\u0011"); check_escaped("\x11", "\\u0011");
CHECK(json::escape_string("\x12") == "\\u0012"); check_escaped("\x12", "\\u0012");
CHECK(json::escape_string("\x13") == "\\u0013"); check_escaped("\x13", "\\u0013");
CHECK(json::escape_string("\x14") == "\\u0014"); check_escaped("\x14", "\\u0014");
CHECK(json::escape_string("\x15") == "\\u0015"); check_escaped("\x15", "\\u0015");
CHECK(json::escape_string("\x16") == "\\u0016"); check_escaped("\x16", "\\u0016");
CHECK(json::escape_string("\x17") == "\\u0017"); check_escaped("\x17", "\\u0017");
CHECK(json::escape_string("\x18") == "\\u0018"); check_escaped("\x18", "\\u0018");
CHECK(json::escape_string("\x19") == "\\u0019"); check_escaped("\x19", "\\u0019");
CHECK(json::escape_string("\x1a") == "\\u001a"); check_escaped("\x1a", "\\u001a");
CHECK(json::escape_string("\x1b") == "\\u001b"); check_escaped("\x1b", "\\u001b");
CHECK(json::escape_string("\x1c") == "\\u001c"); check_escaped("\x1c", "\\u001c");
CHECK(json::escape_string("\x1d") == "\\u001d"); check_escaped("\x1d", "\\u001d");
CHECK(json::escape_string("\x1e") == "\\u001e"); check_escaped("\x1e", "\\u001e");
CHECK(json::escape_string("\x1f") == "\\u001f"); check_escaped("\x1f", "\\u001f");
} }
} }
...@@ -182,6 +182,10 @@ TEST_CASE("value conversion") ...@@ -182,6 +182,10 @@ TEST_CASE("value conversion")
std::vector<float> v; std::vector<float> v;
CHECK_THROWS_AS(nlohmann::from_json(j, v), std::logic_error); CHECK_THROWS_AS(nlohmann::from_json(j, v), std::logic_error);
CHECK(v.capacity() == j.size()); CHECK(v.capacity() == j.size());
// make sure all values are properly copied
std::vector<int> v2 = json({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
CHECK(v2.size() == 10);
} }
#endif #endif
} }
......
...@@ -213,6 +213,18 @@ TEST_CASE("object inspection") ...@@ -213,6 +213,18 @@ TEST_CASE("object inspection")
"{\n \"array\": [\n 1,\n 2,\n 3,\n 4\n ],\n \"boolean\": false,\n \"null\": null,\n \"number\": 42,\n \"object\": {},\n \"string\": \"Hello world\"\n}"); "{\n \"array\": [\n 1,\n 2,\n 3,\n 4\n ],\n \"boolean\": false,\n \"null\": null,\n \"number\": 42,\n \"object\": {},\n \"string\": \"Hello world\"\n}");
} }
SECTION("indent=x")
{
CHECK(j.dump().size() == 94);
CHECK(j.dump(1).size() == 127);
CHECK(j.dump(2).size() == 142);
CHECK(j.dump(512).size() == 7792);
// important test, because it yields a resize of the indent_string
// inside the dump() function
CHECK(j.dump(1024).size() == 15472);
}
SECTION("dump and floating-point numbers") SECTION("dump and floating-point numbers")
{ {
auto s = json(42.23).dump(); auto s = json(42.23).dump();
......
...@@ -33,10 +33,14 @@ using nlohmann::json; ...@@ -33,10 +33,14 @@ using nlohmann::json;
TEST_CASE("version information") TEST_CASE("version information")
{ {
SECTION("version()") SECTION("meta()")
{ {
CHECK(json::meta()["name"] == "JSON for Modern C++"); json j = json::meta();
CHECK(json::meta()["version"] == json(
CHECK(j["name"] == "JSON for Modern C++");
CHECK(j["copyright"] == "(C) 2013-2017 Niels Lohmann");
CHECK(j["url"] == "https://github.com/nlohmann/json");
CHECK(j["version"] == json(
{ {
{"string", "2.1.1"}, {"string", "2.1.1"},
{"major", 2}, {"major", 2},
......
...@@ -583,10 +583,11 @@ TEST_CASE("modifiers") ...@@ -583,10 +583,11 @@ TEST_CASE("modifiers")
SECTION("insert nothing (count = 0)") SECTION("insert nothing (count = 0)")
{ {
auto pos = j_array.end();
auto it = j_array.insert(j_array.end(), 0, 5); auto it = j_array.insert(j_array.end(), 0, 5);
CHECK(j_array.size() == 4); CHECK(j_array.size() == 4);
CHECK(it == pos); // the returned iterator points to the first inserted element;
// there were 4 elements, so it should point to the 5th
CHECK(it == j_array.begin() + 4);
CHECK(j_array == json({1, 2, 3, 4})); CHECK(j_array == json({1, 2, 3, 4}));
} }
} }
......
...@@ -63,7 +63,7 @@ TEST_CASE("regression tests") ...@@ -63,7 +63,7 @@ TEST_CASE("regression tests")
SECTION("pull request #71 - handle enum type") SECTION("pull request #71 - handle enum type")
{ {
enum { t = 0, u = 1}; enum { t = 0, u = 102};
json j = json::array(); json j = json::array();
j.push_back(t); j.push_back(t);
...@@ -73,6 +73,9 @@ TEST_CASE("regression tests") ...@@ -73,6 +73,9 @@ TEST_CASE("regression tests")
auto anon_enum_value = j2.get<decltype(u)>(); auto anon_enum_value = j2.get<decltype(u)>();
CHECK(u == anon_enum_value); CHECK(u == anon_enum_value);
// check if the actual value was stored
CHECK(j2 == 102);
static_assert(std::is_same<decltype(anon_enum_value), decltype(u)>::value, ""); static_assert(std::is_same<decltype(anon_enum_value), decltype(u)>::value, "");
j.push_back(json::object( j.push_back(json::object(
......
/* /*
* Catch v1.7.2 * Catch v1.8.1
* Generated: 2017-02-13 15:57:33.350226 * Generated: 2017-03-01 16:04:19.016511
* ---------------------------------------------------------- * ----------------------------------------------------------
* This file has been merged from multiple headers. Please don't edit it directly * This file has been merged from multiple headers. Please don't edit it directly
* Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved.
...@@ -40,6 +40,12 @@ ...@@ -40,6 +40,12 @@
#elif defined __GNUC__ #elif defined __GNUC__
# pragma GCC diagnostic ignored "-Wvariadic-macros" # pragma GCC diagnostic ignored "-Wvariadic-macros"
# pragma GCC diagnostic ignored "-Wunused-variable" # pragma GCC diagnostic ignored "-Wunused-variable"
// For newer version we can use __Pragma to disable the warnings locally
# if __GNUC__ == 4 && __GNUC_MINOR__ >= 4 && __GNUC_MINOR__ <= 7
# pragma GCC diagnostic ignored "-Wparentheses"
# endif
# pragma GCC diagnostic push # pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wpadded" # pragma GCC diagnostic ignored "-Wpadded"
#endif #endif
...@@ -82,6 +88,7 @@ ...@@ -82,6 +88,7 @@
// CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported? // CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported?
// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? // CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported?
// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? // CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported?
// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported?
// **************** // ****************
// Note to maintainers: if new toggles are added please document them // Note to maintainers: if new toggles are added please document them
// in configuration.md, too // in configuration.md, too
...@@ -117,12 +124,30 @@ ...@@ -117,12 +124,30 @@
# endif # endif
# if defined(CATCH_CPP11_OR_GREATER) # if defined(CATCH_CPP11_OR_GREATER)
# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) # 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" )
# endif # endif
#endif // __clang__ #endif // __clang__
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Cygwin
#ifdef __CYGWIN__
# if !defined(CATCH_CONFIG_POSIX_SIGNALS)
# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS
# endif
// Required for some versions of Cygwin to declare gettimeofday
// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin
# define _BSD_SOURCE
#endif // __CYGWIN__
////////////////////////////////////////////////////////////////////////////////
// Borland // Borland
#ifdef __BORLANDC__ #ifdef __BORLANDC__
...@@ -144,12 +169,20 @@ ...@@ -144,12 +169,20 @@
// GCC // GCC
#ifdef __GNUC__ #ifdef __GNUC__
# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)
# define CATCH_GCC_HAS_NEW_PRAGMA
# endif
# if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) # if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__)
# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR # define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
# endif # endif
# if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) && defined(CATCH_CPP11_OR_GREATER) # if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) && defined(CATCH_GCC_HAS_NEW_PRAGMA)
# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS _Pragma( "GCC diagnostic ignored \"-Wparentheses\"" ) # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
_Pragma( "GCC diagnostic push" ) \
_Pragma( "GCC diagnostic ignored \"-Wparentheses\"" )
# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
_Pragma( "GCC diagnostic pop" )
# endif # endif
// - otherwise more recent versions define __cplusplus >= 201103L // - otherwise more recent versions define __cplusplus >= 201103L
...@@ -290,9 +323,14 @@ ...@@ -290,9 +323,14 @@
#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) #if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH)
# define CATCH_CONFIG_WINDOWS_SEH # define CATCH_CONFIG_WINDOWS_SEH
#endif #endif
// This is set by default, because we assume that unix compilers are posix-signal-compatible by default.
#if !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS)
# define CATCH_CONFIG_POSIX_SIGNALS
#endif
#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) #if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS)
# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS
# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS
#endif #endif
// noexcept support: // noexcept support:
...@@ -871,6 +909,9 @@ namespace Catch { ...@@ -871,6 +909,9 @@ namespace Catch {
template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator % ( T const& ); template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator % ( T const& );
template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( T const& ); template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( T const& );
template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( T const& ); template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( T const& );
private:
DecomposedExpression& operator = (DecomposedExpression const&);
}; };
struct AssertionInfo struct AssertionInfo
...@@ -967,313 +1008,153 @@ namespace Catch { ...@@ -967,313 +1008,153 @@ namespace Catch {
namespace Matchers { namespace Matchers {
namespace Impl { namespace Impl {
namespace Generic { template<typename ArgT> struct MatchAllOf;
template<typename ExpressionT> class AllOf; template<typename ArgT> struct MatchAnyOf;
template<typename ExpressionT> class AnyOf; template<typename ArgT> struct MatchNotOf;
template<typename ExpressionT> class Not;
}
template<typename ExpressionT>
struct Matcher : SharedImpl<IShared>
{
typedef ExpressionT ExpressionType;
virtual ~Matcher() {}
virtual Ptr<Matcher> clone() const = 0;
virtual bool match( ExpressionT const& expr ) const = 0;
virtual std::string toString() const = 0;
Generic::AllOf<ExpressionT> operator && ( Matcher<ExpressionT> const& other ) const;
Generic::AnyOf<ExpressionT> operator || ( Matcher<ExpressionT> const& other ) const;
Generic::Not<ExpressionT> operator ! () const;
};
template<typename DerivedT, typename ExpressionT> class MatcherUntypedBase {
struct MatcherImpl : Matcher<ExpressionT> {
virtual Ptr<Matcher<ExpressionT> > clone() const {
return Ptr<Matcher<ExpressionT> >( new DerivedT( static_cast<DerivedT const&>( *this ) ) );
}
};
namespace Generic {
template<typename ExpressionT>
class Not : public MatcherImpl<Not<ExpressionT>, ExpressionT> {
public: public:
explicit Not( Matcher<ExpressionT> const& matcher ) : m_matcher(matcher.clone()) {} std::string toString() const {
Not( Not const& other ) : m_matcher( other.m_matcher ) {} if( m_cachedToString.empty() )
m_cachedToString = describe();
virtual bool match( ExpressionT const& expr ) const CATCH_OVERRIDE { return m_cachedToString;
return !m_matcher->match( expr );
} }
virtual std::string toString() const CATCH_OVERRIDE { protected:
return "not " + m_matcher->toString(); virtual std::string describe() const = 0;
} mutable std::string m_cachedToString;
private: private:
Ptr< Matcher<ExpressionT> > m_matcher; MatcherUntypedBase& operator = ( MatcherUntypedBase const& );
}; };
template<typename ExpressionT> template<typename ObjectT, typename ComparatorT = ObjectT>
class AllOf : public MatcherImpl<AllOf<ExpressionT>, ExpressionT> { struct MatcherBase : MatcherUntypedBase {
public:
AllOf() {} virtual bool match( ObjectT const& arg ) const = 0;
AllOf( AllOf const& other ) : m_matchers( other.m_matchers ) {}
AllOf& add( Matcher<ExpressionT> const& matcher ) { MatchAllOf<ComparatorT> operator && ( MatcherBase const& other ) const;
m_matchers.push_back( matcher.clone() ); MatchAnyOf<ComparatorT> operator || ( MatcherBase const& other ) const;
return *this; MatchNotOf<ComparatorT> operator ! () const;
} };
virtual bool match( ExpressionT const& expr ) const
{ template<typename ArgT>
for( std::size_t i = 0; i < m_matchers.size(); ++i ) struct MatchAllOf : MatcherBase<ArgT> {
if( !m_matchers[i]->match( expr ) ) virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE {
for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
if (!m_matchers[i]->match(arg))
return false; return false;
}
return true; return true;
} }
virtual std::string toString() const { virtual std::string describe() const CATCH_OVERRIDE {
std::ostringstream oss; std::string description;
oss << "( "; description.reserve( 4 + m_matchers.size()*32 );
description += "( ";
for( std::size_t i = 0; i < m_matchers.size(); ++i ) { for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
if( i != 0 ) if( i != 0 )
oss << " and "; description += " and ";
oss << m_matchers[i]->toString(); description += m_matchers[i]->toString();
} }
oss << " )"; description += " )";
return oss.str(); return description;
} }
AllOf operator && ( Matcher<ExpressionT> const& other ) const { MatchAllOf<ArgT>& operator && ( MatcherBase<ArgT> const& other ) {
AllOf allOfExpr( *this ); m_matchers.push_back( &other );
allOfExpr.add( other ); return *this;
return allOfExpr;
} }
private: std::vector<MatcherBase<ArgT> const*> m_matchers;
std::vector<Ptr<Matcher<ExpressionT> > > m_matchers;
}; };
template<typename ArgT>
struct MatchAnyOf : MatcherBase<ArgT> {
template<typename ExpressionT> virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE {
class AnyOf : public MatcherImpl<AnyOf<ExpressionT>, ExpressionT> { for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
public: if (m_matchers[i]->match(arg))
AnyOf() {}
AnyOf( AnyOf const& other ) : m_matchers( other.m_matchers ) {}
AnyOf& add( Matcher<ExpressionT> const& matcher ) {
m_matchers.push_back( matcher.clone() );
return *this;
}
virtual bool match( ExpressionT const& expr ) const
{
for( std::size_t i = 0; i < m_matchers.size(); ++i )
if( m_matchers[i]->match( expr ) )
return true; return true;
}
return false; return false;
} }
virtual std::string toString() const { virtual std::string describe() const CATCH_OVERRIDE {
std::ostringstream oss; std::string description;
oss << "( "; description.reserve( 4 + m_matchers.size()*32 );
description += "( ";
for( std::size_t i = 0; i < m_matchers.size(); ++i ) { for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
if( i != 0 ) if( i != 0 )
oss << " or "; description += " or ";
oss << m_matchers[i]->toString(); description += m_matchers[i]->toString();
}
oss << " )";
return oss.str();
}
AnyOf operator || ( Matcher<ExpressionT> const& other ) const {
AnyOf anyOfExpr( *this );
anyOfExpr.add( other );
return anyOfExpr;
}
private:
std::vector<Ptr<Matcher<ExpressionT> > > m_matchers;
};
} // namespace Generic
template<typename ExpressionT>
Generic::AllOf<ExpressionT> Matcher<ExpressionT>::operator && ( Matcher<ExpressionT> const& other ) const {
Generic::AllOf<ExpressionT> allOfExpr;
allOfExpr.add( *this );
allOfExpr.add( other );
return allOfExpr;
} }
description += " )";
template<typename ExpressionT> return description;
Generic::AnyOf<ExpressionT> Matcher<ExpressionT>::operator || ( Matcher<ExpressionT> const& other ) const {
Generic::AnyOf<ExpressionT> anyOfExpr;
anyOfExpr.add( *this );
anyOfExpr.add( other );
return anyOfExpr;
} }
template<typename ExpressionT> MatchAnyOf<ArgT>& operator || ( MatcherBase<ArgT> const& other ) {
Generic::Not<ExpressionT> Matcher<ExpressionT>::operator ! () const { m_matchers.push_back( &other );
return Generic::Not<ExpressionT>( *this ); return *this;
} }
namespace StdString { std::vector<MatcherBase<ArgT> const*> m_matchers;
inline std::string makeString( std::string const& str ) { return str; }
inline std::string makeString( const char* str ) { return str ? std::string( str ) : std::string(); }
struct CasedString
{
CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity )
: m_caseSensitivity( caseSensitivity ),
m_str( adjustString( str ) )
{}
std::string adjustString( std::string const& str ) const {
return m_caseSensitivity == CaseSensitive::No
? toLower( str )
: str;
}
std::string toStringSuffix() const
{
return m_caseSensitivity == CaseSensitive::No
? " (case insensitive)"
: std::string();
}
CaseSensitive::Choice m_caseSensitivity;
std::string m_str;
}; };
struct Equals : MatcherImpl<Equals, std::string> { template<typename ArgT>
Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) struct MatchNotOf : MatcherBase<ArgT> {
: m_data( str, caseSensitivity )
{}
Equals( Equals const& other ) : m_data( other.m_data ){}
virtual ~Equals(); MatchNotOf( MatcherBase<ArgT> const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {}
virtual bool match( std::string const& expr ) const { virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE {
return m_data.m_str == m_data.adjustString( expr );; return !m_underlyingMatcher.match( arg );
}
virtual std::string toString() const {
return "equals: \"" + m_data.m_str + '"' + m_data.toStringSuffix();
} }
CasedString m_data; virtual std::string describe() const CATCH_OVERRIDE {
}; return "not " + m_underlyingMatcher.toString();
struct Contains : MatcherImpl<Contains, std::string> {
Contains( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes )
: m_data( substr, caseSensitivity ){}
Contains( Contains const& other ) : m_data( other.m_data ){}
virtual ~Contains();
virtual bool match( std::string const& expr ) const {
return m_data.adjustString( expr ).find( m_data.m_str ) != std::string::npos;
} }
virtual std::string toString() const { MatcherBase<ArgT> const& m_underlyingMatcher;
return "contains: \"" + m_data.m_str + '"' + m_data.toStringSuffix();
}
CasedString m_data;
}; };
struct StartsWith : MatcherImpl<StartsWith, std::string> { template<typename ObjectT, typename ComparatorT>
StartsWith( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) MatchAllOf<ComparatorT> MatcherBase<ObjectT, ComparatorT>::operator && ( MatcherBase const& other ) const {
: m_data( substr, caseSensitivity ){} return MatchAllOf<ComparatorT>() && *this && other;
StartsWith( StartsWith const& other ) : m_data( other.m_data ){}
virtual ~StartsWith();
virtual bool match( std::string const& expr ) const {
return startsWith( m_data.adjustString( expr ), m_data.m_str );
}
virtual std::string toString() const {
return "starts with: \"" + m_data.m_str + '"' + m_data.toStringSuffix();
} }
template<typename ObjectT, typename ComparatorT>
CasedString m_data; MatchAnyOf<ComparatorT> MatcherBase<ObjectT, ComparatorT>::operator || ( MatcherBase const& other ) const {
}; return MatchAnyOf<ComparatorT>() || *this || other;
struct EndsWith : MatcherImpl<EndsWith, std::string> {
EndsWith( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes )
: m_data( substr, caseSensitivity ){}
EndsWith( EndsWith const& other ) : m_data( other.m_data ){}
virtual ~EndsWith();
virtual bool match( std::string const& expr ) const {
return endsWith( m_data.adjustString( expr ), m_data.m_str );
} }
virtual std::string toString() const { template<typename ObjectT, typename ComparatorT>
return "ends with: \"" + m_data.m_str + '"' + m_data.toStringSuffix(); MatchNotOf<ComparatorT> MatcherBase<ObjectT, ComparatorT>::operator ! () const {
return MatchNotOf<ComparatorT>( *this );
} }
CasedString m_data;
};
} // namespace StdString
} // namespace Impl } // namespace Impl
// The following functions create the actual matcher objects. // The following functions create the actual matcher objects.
// This allows the types to be inferred // This allows the types to be inferred
template<typename ExpressionT> // - deprecated: prefer ||, && and !
inline Impl::Generic::Not<ExpressionT> Not( Impl::Matcher<ExpressionT> const& m ) { template<typename T>
return Impl::Generic::Not<ExpressionT>( m ); inline Impl::MatchNotOf<T> Not( Impl::MatcherBase<T> const& underlyingMatcher ) {
} return Impl::MatchNotOf<T>( underlyingMatcher );
template<typename ExpressionT>
inline Impl::Generic::AllOf<ExpressionT> AllOf( Impl::Matcher<ExpressionT> const& m1,
Impl::Matcher<ExpressionT> const& m2 ) {
return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 );
}
template<typename ExpressionT>
inline Impl::Generic::AllOf<ExpressionT> AllOf( Impl::Matcher<ExpressionT> const& m1,
Impl::Matcher<ExpressionT> const& m2,
Impl::Matcher<ExpressionT> const& m3 ) {
return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 );
}
template<typename ExpressionT>
inline Impl::Generic::AnyOf<ExpressionT> AnyOf( Impl::Matcher<ExpressionT> const& m1,
Impl::Matcher<ExpressionT> const& m2 ) {
return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 );
}
template<typename ExpressionT>
inline Impl::Generic::AnyOf<ExpressionT> AnyOf( Impl::Matcher<ExpressionT> const& m1,
Impl::Matcher<ExpressionT> const& m2,
Impl::Matcher<ExpressionT> const& m3 ) {
return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 );
}
inline Impl::StdString::Equals Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) {
return Impl::StdString::Equals( str, caseSensitivity );
}
inline Impl::StdString::Equals Equals( const char* str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) {
return Impl::StdString::Equals( Impl::StdString::makeString( str ), caseSensitivity );
}
inline Impl::StdString::Contains Contains( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) {
return Impl::StdString::Contains( substr, caseSensitivity );
}
inline Impl::StdString::Contains Contains( const char* substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) {
return Impl::StdString::Contains( Impl::StdString::makeString( substr ), caseSensitivity );
} }
inline Impl::StdString::StartsWith StartsWith( std::string const& substr ) { template<typename T>
return Impl::StdString::StartsWith( substr ); inline Impl::MatchAllOf<T> AllOf( Impl::MatcherBase<T> const& m1, Impl::MatcherBase<T> const& m2 ) {
return Impl::MatchAllOf<T>() && m1 && m2;
} }
inline Impl::StdString::StartsWith StartsWith( const char* substr ) { template<typename T>
return Impl::StdString::StartsWith( Impl::StdString::makeString( substr ) ); inline Impl::MatchAllOf<T> AllOf( Impl::MatcherBase<T> const& m1, Impl::MatcherBase<T> const& m2, Impl::MatcherBase<T> const& m3 ) {
return Impl::MatchAllOf<T>() && m1 && m2 && m3;
} }
inline Impl::StdString::EndsWith EndsWith( std::string const& substr ) { template<typename T>
return Impl::StdString::EndsWith( substr ); inline Impl::MatchAnyOf<T> AnyOf( Impl::MatcherBase<T> const& m1, Impl::MatcherBase<T> const& m2 ) {
return Impl::MatchAnyOf<T>() || m1 || m2;
} }
inline Impl::StdString::EndsWith EndsWith( const char* substr ) { template<typename T>
return Impl::StdString::EndsWith( Impl::StdString::makeString( substr ) ); inline Impl::MatchAnyOf<T> AnyOf( Impl::MatcherBase<T> const& m1, Impl::MatcherBase<T> const& m2, Impl::MatcherBase<T> const& m3 ) {
return Impl::MatchAnyOf<T>() || m1 || m2 || m3;
} }
} // namespace Matchers } // namespace Matchers
using namespace Matchers; using namespace Matchers;
using Matchers::Impl::MatcherBase;
} // namespace Catch } // namespace Catch
...@@ -1328,7 +1209,7 @@ namespace Catch { ...@@ -1328,7 +1209,7 @@ namespace Catch {
void captureResult( ResultWas::OfType resultType ); void captureResult( ResultWas::OfType resultType );
void captureExpression(); void captureExpression();
void captureExpectedException( std::string const& expectedMessage ); void captureExpectedException( std::string const& expectedMessage );
void captureExpectedException( Matchers::Impl::Matcher<std::string> const& matcher ); void captureExpectedException( Matchers::Impl::MatcherBase<std::string> const& matcher );
void handleResult( AssertionResult const& result ); void handleResult( AssertionResult const& result );
void react(); void react();
bool shouldDebugBreak() const; bool shouldDebugBreak() const;
...@@ -1358,6 +1239,7 @@ namespace Catch { ...@@ -1358,6 +1239,7 @@ namespace Catch {
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma warning(push) #pragma warning(push)
#pragma warning(disable:4389) // '==' : signed/unsigned mismatch #pragma warning(disable:4389) // '==' : signed/unsigned mismatch
#pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform)
#endif #endif
#include <cstddef> #include <cstddef>
...@@ -1874,6 +1756,8 @@ class ExpressionLhs : public DecomposedExpression { ...@@ -1874,6 +1756,8 @@ class ExpressionLhs : public DecomposedExpression {
public: public:
ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ), m_truthy(false) {} ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ), m_truthy(false) {}
ExpressionLhs& operator = ( const ExpressionLhs& );
template<typename RhsT> template<typename RhsT>
BinaryExpression<T, Internal::IsEqualTo, RhsT const&> BinaryExpression<T, Internal::IsEqualTo, RhsT const&>
operator == ( RhsT const& rhs ) { operator == ( RhsT const& rhs ) {
...@@ -1952,6 +1836,8 @@ public: ...@@ -1952,6 +1836,8 @@ public:
BinaryExpression( ResultBuilder& rb, LhsT lhs, RhsT rhs ) BinaryExpression( ResultBuilder& rb, LhsT lhs, RhsT rhs )
: m_rb( rb ), m_lhs( lhs ), m_rhs( rhs ) {} : m_rb( rb ), m_lhs( lhs ), m_rhs( rhs ) {}
BinaryExpression& operator = ( BinaryExpression& );
void endExpression() const { void endExpression() const {
m_rb m_rb
.setResultType( Internal::compare<Op>( m_lhs, m_rhs ) ) .setResultType( Internal::compare<Op>( m_lhs, m_rhs ) )
...@@ -2246,6 +2132,14 @@ namespace Catch { ...@@ -2246,6 +2132,14 @@ namespace Catch {
} }
#if defined(CATCH_CONFIG_FAST_COMPILE)
///////////////////////////////////////////////////////////////////////////////
// We can speedup compilation significantly by breaking into debugger lower in
// the callstack, because then we don't have to expand CATCH_BREAK_INTO_DEBUGGER
// macro in each assertion
#define INTERNAL_CATCH_REACT( resultBuilder ) \
resultBuilder.react();
#else
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// In the event of a failure works out if the debugger needs to be invoked // In the event of a failure works out if the debugger needs to be invoked
// and/or an exception thrown and takes appropriate action. // and/or an exception thrown and takes appropriate action.
...@@ -2254,6 +2148,7 @@ namespace Catch { ...@@ -2254,6 +2148,7 @@ namespace Catch {
#define INTERNAL_CATCH_REACT( resultBuilder ) \ #define INTERNAL_CATCH_REACT( resultBuilder ) \
if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \ if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \
resultBuilder.react(); resultBuilder.react();
#endif
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ) \ #define INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ) \
...@@ -2262,6 +2157,7 @@ namespace Catch { ...@@ -2262,6 +2157,7 @@ namespace Catch {
try { \ try { \
CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
( __catchResult <= expr ).endExpression(); \ ( __catchResult <= expr ).endExpression(); \
CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
} \ } \
catch( ... ) { \ catch( ... ) { \
__catchResult.useActiveException( resultDisposition ); \ __catchResult.useActiveException( resultDisposition ); \
...@@ -2825,12 +2721,14 @@ namespace Detail { ...@@ -2825,12 +2721,14 @@ namespace Detail {
public: public:
explicit Approx ( double value ) explicit Approx ( double value )
: m_epsilon( std::numeric_limits<float>::epsilon()*100 ), : m_epsilon( std::numeric_limits<float>::epsilon()*100 ),
m_margin( 0.0 ),
m_scale( 1.0 ), m_scale( 1.0 ),
m_value( value ) m_value( value )
{} {}
Approx( Approx const& other ) Approx( Approx const& other )
: m_epsilon( other.m_epsilon ), : m_epsilon( other.m_epsilon ),
m_margin( other.m_margin ),
m_scale( other.m_scale ), m_scale( other.m_scale ),
m_value( other.m_value ) m_value( other.m_value )
{} {}
...@@ -2842,6 +2740,7 @@ namespace Detail { ...@@ -2842,6 +2740,7 @@ namespace Detail {
Approx operator()( double value ) { Approx operator()( double value ) {
Approx approx( value ); Approx approx( value );
approx.epsilon( m_epsilon ); approx.epsilon( m_epsilon );
approx.margin( m_margin );
approx.scale( m_scale ); approx.scale( m_scale );
return approx; return approx;
} }
...@@ -2851,7 +2750,11 @@ namespace Detail { ...@@ -2851,7 +2750,11 @@ namespace Detail {
friend bool operator == ( const T& lhs, Approx const& rhs ) { friend bool operator == ( const T& lhs, Approx const& rhs ) {
// Thanks to Richard Harris for his help refining this formula // Thanks to Richard Harris for his help refining this formula
auto lhs_v = double(lhs); auto lhs_v = double(lhs);
return std::fabs( lhs_v - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( std::fabs(lhs_v), std::fabs(rhs.m_value) ) ); bool relativeOK = std::fabs(lhs_v - rhs.m_value) < rhs.m_epsilon * (rhs.m_scale + (std::max)(std::fabs(lhs_v), std::fabs(rhs.m_value)));
if (relativeOK) {
return true;
}
return std::fabs(lhs_v - rhs.m_value) < rhs.m_margin;
} }
template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
...@@ -2895,7 +2798,11 @@ namespace Detail { ...@@ -2895,7 +2798,11 @@ namespace Detail {
#else #else
friend bool operator == ( double lhs, Approx const& rhs ) { friend bool operator == ( double lhs, Approx const& rhs ) {
// Thanks to Richard Harris for his help refining this formula // Thanks to Richard Harris for his help refining this formula
return std::fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( std::fabs(lhs), std::fabs(rhs.m_value) ) ); bool relativeOK = std::fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( std::fabs(lhs), std::fabs(rhs.m_value) ) );
if (relativeOK) {
return true;
}
return std::fabs(lhs - rhs.m_value) < rhs.m_margin;
} }
friend bool operator == ( Approx const& lhs, double rhs ) { friend bool operator == ( Approx const& lhs, double rhs ) {
...@@ -2936,6 +2843,11 @@ namespace Detail { ...@@ -2936,6 +2843,11 @@ namespace Detail {
return *this; return *this;
} }
Approx& margin( double newMargin ) {
m_margin = newMargin;
return *this;
}
Approx& scale( double newScale ) { Approx& scale( double newScale ) {
m_scale = newScale; m_scale = newScale;
return *this; return *this;
...@@ -2949,6 +2861,7 @@ namespace Detail { ...@@ -2949,6 +2861,7 @@ namespace Detail {
private: private:
double m_epsilon; double m_epsilon;
double m_margin;
double m_scale; double m_scale;
double m_value; double m_value;
}; };
...@@ -2961,6 +2874,153 @@ inline std::string toString<Detail::Approx>( Detail::Approx const& value ) { ...@@ -2961,6 +2874,153 @@ inline std::string toString<Detail::Approx>( Detail::Approx const& value ) {
} // end namespace Catch } // end namespace Catch
// #included from: internal/catch_matchers_string.h
#define TWOBLUECUBES_CATCH_MATCHERS_STRING_H_INCLUDED
namespace Catch {
namespace Matchers {
namespace StdString {
struct CasedString
{
CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity );
std::string adjustString( std::string const& str ) const;
std::string caseSensitivitySuffix() const;
CaseSensitive::Choice m_caseSensitivity;
std::string m_str;
};
struct StringMatcherBase : MatcherBase<std::string> {
StringMatcherBase( std::string operation, CasedString const& comparator );
virtual std::string describe() const CATCH_OVERRIDE;
CasedString m_comparator;
std::string m_operation;
};
struct EqualsMatcher : StringMatcherBase {
EqualsMatcher( CasedString const& comparator );
virtual bool match( std::string const& source ) const CATCH_OVERRIDE;
};
struct ContainsMatcher : StringMatcherBase {
ContainsMatcher( CasedString const& comparator );
virtual bool match( std::string const& source ) const CATCH_OVERRIDE;
};
struct StartsWithMatcher : StringMatcherBase {
StartsWithMatcher( CasedString const& comparator );
virtual bool match( std::string const& source ) const CATCH_OVERRIDE;
};
struct EndsWithMatcher : StringMatcherBase {
EndsWithMatcher( CasedString const& comparator );
virtual bool match( std::string const& source ) const CATCH_OVERRIDE;
};
} // namespace StdString
// The following functions create the actual matcher objects.
// This allows the types to be inferred
StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
} // namespace Matchers
} // namespace Catch
// #included from: internal/catch_matchers_vector.h
#define TWOBLUECUBES_CATCH_MATCHERS_VECTOR_H_INCLUDED
namespace Catch {
namespace Matchers {
namespace Vector {
template<typename T>
struct ContainsElementMatcher : MatcherBase<std::vector<T>, T> {
ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {}
bool match(std::vector<T> const &v) const CATCH_OVERRIDE {
return std::find(v.begin(), v.end(), m_comparator) != v.end();
}
virtual std::string describe() const CATCH_OVERRIDE {
return "Contains: " + Catch::toString( m_comparator );
}
T const& m_comparator;
};
template<typename T>
struct ContainsMatcher : MatcherBase<std::vector<T>, std::vector<T> > {
ContainsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {}
bool match(std::vector<T> const &v) const CATCH_OVERRIDE {
// !TBD: see note in EqualsMatcher
if (m_comparator.size() > v.size())
return false;
for (size_t i = 0; i < m_comparator.size(); ++i)
if (std::find(v.begin(), v.end(), m_comparator[i]) == v.end())
return false;
return true;
}
virtual std::string describe() const CATCH_OVERRIDE {
return "Contains: " + Catch::toString( m_comparator );
}
std::vector<T> const& m_comparator;
};
template<typename T>
struct EqualsMatcher : MatcherBase<std::vector<T>, std::vector<T> > {
EqualsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {}
bool match(std::vector<T> const &v) const CATCH_OVERRIDE {
// !TBD: This currently works if all elements can be compared using !=
// - a more general approach would be via a compare template that defaults
// to using !=. but could be specialised for, e.g. std::vector<T> etc
// - then just call that directly
if (m_comparator.size() != v.size())
return false;
for (size_t i = 0; i < v.size(); ++i)
if (m_comparator[i] != v[i])
return false;
return true;
}
virtual std::string describe() const CATCH_OVERRIDE {
return "Equals: " + Catch::toString( m_comparator );
}
std::vector<T> const& m_comparator;
};
} // namespace Vector
// The following functions create the actual matcher objects.
// This allows the types to be inferred
template<typename T>
Vector::ContainsMatcher<T> Contains( std::vector<T> const& comparator ) {
return Vector::ContainsMatcher<T>( comparator );
}
template<typename T>
Vector::ContainsElementMatcher<T> VectorContains( T const& comparator ) {
return Vector::ContainsElementMatcher<T>( comparator );
}
template<typename T>
Vector::EqualsMatcher<T> Equals( std::vector<T> const& comparator ) {
return Vector::EqualsMatcher<T>( comparator );
}
} // namespace Matchers
} // namespace Catch
// #included from: internal/catch_interfaces_tag_alias_registry.h // #included from: internal/catch_interfaces_tag_alias_registry.h
#define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED #define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED
...@@ -3342,6 +3402,29 @@ return @ desc; \ ...@@ -3342,6 +3402,29 @@ return @ desc; \
#endif #endif
#ifdef CATCH_IMPL #ifdef CATCH_IMPL
// !TBD: Move the leak detector code into a separate header
#ifdef CATCH_CONFIG_WINDOWS_CRTDBG
#include <crtdbg.h>
class LeakDetector {
public:
LeakDetector() {
int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
flag |= _CRTDBG_LEAK_CHECK_DF;
flag |= _CRTDBG_ALLOC_MEM_DF;
_CrtSetDbgFlag(flag);
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
// Change this to leaking allocation's number to break there
_CrtSetBreakAlloc(-1);
}
};
#else
class LeakDetector {};
#endif
LeakDetector leakDetector;
// #included from: internal/catch_impl.hpp // #included from: internal/catch_impl.hpp
#define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED #define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED
...@@ -3601,7 +3684,7 @@ namespace Catch { ...@@ -3601,7 +3684,7 @@ namespace Catch {
void addPattern() { void addPattern() {
std::string token = subString(); std::string token = subString();
for( size_t i = 0; i < m_escapeChars.size(); ++i ) for( size_t i = 0; i < m_escapeChars.size(); ++i )
token = token.substr( 0, m_escapeChars[i]-i ) + token.substr( m_escapeChars[i]+1-i ); token = token.substr( 0, m_escapeChars[i]-m_start-i ) + token.substr( m_escapeChars[i]-m_start-i+1 );
m_escapeChars.clear(); m_escapeChars.clear();
if( startsWith( token, "exclude:" ) ) { if( startsWith( token, "exclude:" ) ) {
m_exclusion = true; m_exclusion = true;
...@@ -6259,6 +6342,16 @@ namespace Catch { ...@@ -6259,6 +6342,16 @@ namespace Catch {
#else // Not Windows - assumed to be POSIX compatible ////////////////////////// #else // Not Windows - assumed to be POSIX compatible //////////////////////////
# if !defined(CATCH_CONFIG_POSIX_SIGNALS)
namespace Catch {
struct FatalConditionHandler {
void reset() {}
};
}
# else // CATCH_CONFIG_POSIX_SIGNALS is defined
#include <signal.h> #include <signal.h>
namespace Catch { namespace Catch {
...@@ -6337,6 +6430,8 @@ namespace Catch { ...@@ -6337,6 +6430,8 @@ namespace Catch {
} // namespace Catch } // namespace Catch
# endif // CATCH_CONFIG_POSIX_SIGNALS
#endif // not Windows #endif // not Windows
#include <set> #include <set>
...@@ -8043,7 +8138,7 @@ namespace Catch { ...@@ -8043,7 +8138,7 @@ namespace Catch {
return os; return os;
} }
Version libraryVersion( 1, 7, 2, "", 0 ); Version libraryVersion( 1, 8, 1, "", 0 );
} }
...@@ -8214,8 +8309,11 @@ namespace Catch ...@@ -8214,8 +8309,11 @@ namespace Catch
#endif #endif
#ifdef CATCH_PLATFORM_WINDOWS #ifdef CATCH_PLATFORM_WINDOWS
#else #else
#include <sys/time.h> #include <sys/time.h>
#endif #endif
namespace Catch { namespace Catch {
...@@ -8768,12 +8866,12 @@ namespace Catch { ...@@ -8768,12 +8866,12 @@ namespace Catch {
void ResultBuilder::captureExpectedException( std::string const& expectedMessage ) { void ResultBuilder::captureExpectedException( std::string const& expectedMessage ) {
if( expectedMessage.empty() ) if( expectedMessage.empty() )
captureExpectedException( Matchers::Impl::Generic::AllOf<std::string>() ); captureExpectedException( Matchers::Impl::MatchAllOf<std::string>() );
else else
captureExpectedException( Matchers::Equals( expectedMessage ) ); captureExpectedException( Matchers::Equals( expectedMessage ) );
} }
void ResultBuilder::captureExpectedException( Matchers::Impl::Matcher<std::string> const& matcher ) { void ResultBuilder::captureExpectedException( Matchers::Impl::MatcherBase<std::string> const& matcher ) {
assert( !isFalseTest( m_assertionInfo.resultDisposition ) ); assert( !isFalseTest( m_assertionInfo.resultDisposition ) );
AssertionResultData data = m_data; AssertionResultData data = m_data;
...@@ -8807,6 +8905,15 @@ namespace Catch { ...@@ -8807,6 +8905,15 @@ namespace Catch {
} }
void ResultBuilder::react() { void ResultBuilder::react() {
#if defined(CATCH_CONFIG_FAST_COMPILE)
if (m_shouldDebugBreak) {
///////////////////////////////////////////////////////////////////
// To inspect the state during test, you need to go one level up the callstack
// To go back to the test and change execution, jump over the throw statement
///////////////////////////////////////////////////////////////////
CATCH_BREAK_INTO_DEBUGGER();
}
#endif
if( m_shouldThrow ) if( m_shouldThrow )
throw Catch::TestFailureException(); throw Catch::TestFailureException();
} }
...@@ -8935,6 +9042,86 @@ namespace Catch { ...@@ -8935,6 +9042,86 @@ namespace Catch {
} // end namespace Catch } // end namespace Catch
// #included from: catch_matchers_string.hpp
namespace Catch {
namespace Matchers {
namespace StdString {
CasedString::CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity )
: m_caseSensitivity( caseSensitivity ),
m_str( adjustString( str ) )
{}
std::string CasedString::adjustString( std::string const& str ) const {
return m_caseSensitivity == CaseSensitive::No
? toLower( str )
: str;
}
std::string CasedString::caseSensitivitySuffix() const {
return m_caseSensitivity == CaseSensitive::No
? " (case insensitive)"
: std::string();
}
StringMatcherBase::StringMatcherBase( std::string operation, CasedString const& comparator )
: m_comparator( comparator ),
m_operation( operation ) {
}
std::string StringMatcherBase::describe() const {
std::string description;
description.reserve(5 + m_operation.size() + m_comparator.m_str.size() +
m_comparator.caseSensitivitySuffix().size());
description += m_operation;
description += ": \"";
description += m_comparator.m_str;
description += "\"";
description += m_comparator.caseSensitivitySuffix();
return description;
}
EqualsMatcher::EqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals", comparator ) {}
bool EqualsMatcher::match( std::string const& source ) const {
return m_comparator.adjustString( source ) == m_comparator.m_str;
}
ContainsMatcher::ContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains", comparator ) {}
bool ContainsMatcher::match( std::string const& source ) const {
return contains( m_comparator.adjustString( source ), m_comparator.m_str );
}
StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with", comparator ) {}
bool StartsWithMatcher::match( std::string const& source ) const {
return startsWith( m_comparator.adjustString( source ), m_comparator.m_str );
}
EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with", comparator ) {}
bool EndsWithMatcher::match( std::string const& source ) const {
return endsWith( m_comparator.adjustString( source ), m_comparator.m_str );
}
} // namespace StdString
StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
return StdString::EqualsMatcher( StdString::CasedString( str, caseSensitivity) );
}
StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
return StdString::ContainsMatcher( StdString::CasedString( str, caseSensitivity) );
}
StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
return StdString::EndsWithMatcher( StdString::CasedString( str, caseSensitivity) );
}
StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
return StdString::StartsWithMatcher( StdString::CasedString( str, caseSensitivity) );
}
} // namespace Matchers
} // namespace Catch
// #included from: ../reporters/catch_reporter_multi.hpp // #included from: ../reporters/catch_reporter_multi.hpp
#define TWOBLUECUBES_CATCH_REPORTER_MULTI_HPP_INCLUDED #define TWOBLUECUBES_CATCH_REPORTER_MULTI_HPP_INCLUDED
...@@ -9078,6 +9265,7 @@ Ptr<IStreamingReporter> addReporter( Ptr<IStreamingReporter> const& existingRepo ...@@ -9078,6 +9265,7 @@ Ptr<IStreamingReporter> addReporter( Ptr<IStreamingReporter> const& existingRepo
#define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED #define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED
#include <cstring> #include <cstring>
#include <assert.h>
namespace Catch { namespace Catch {
...@@ -9677,6 +9865,12 @@ namespace Catch { ...@@ -9677,6 +9865,12 @@ namespace Catch {
return std::string(); return std::string();
} }
void writeSourceInfo( SourceLineInfo const& sourceInfo ) {
m_xml
.writeAttribute( "filename", sourceInfo.file )
.writeAttribute( "line", sourceInfo.line );
}
public: // StreamingReporterBase public: // StreamingReporterBase
virtual void noMatchingTestCases( std::string const& s ) CATCH_OVERRIDE { virtual void noMatchingTestCases( std::string const& s ) CATCH_OVERRIDE {
...@@ -9706,6 +9900,8 @@ namespace Catch { ...@@ -9706,6 +9900,8 @@ namespace Catch {
.writeAttribute( "description", testInfo.description ) .writeAttribute( "description", testInfo.description )
.writeAttribute( "tags", testInfo.tagsAsString ); .writeAttribute( "tags", testInfo.tagsAsString );
writeSourceInfo( testInfo.lineInfo );
if ( m_config->showDurations() == ShowDurations::Always ) if ( m_config->showDurations() == ShowDurations::Always )
m_testCaseTimer.start(); m_testCaseTimer.start();
m_xml.ensureTagClosed(); m_xml.ensureTagClosed();
...@@ -9717,6 +9913,7 @@ namespace Catch { ...@@ -9717,6 +9913,7 @@ namespace Catch {
m_xml.startElement( "Section" ) m_xml.startElement( "Section" )
.writeAttribute( "name", trim( sectionInfo.name ) ) .writeAttribute( "name", trim( sectionInfo.name ) )
.writeAttribute( "description", sectionInfo.description ); .writeAttribute( "description", sectionInfo.description );
writeSourceInfo( sectionInfo.lineInfo );
m_xml.ensureTagClosed(); m_xml.ensureTagClosed();
} }
} }
...@@ -9749,9 +9946,9 @@ namespace Catch { ...@@ -9749,9 +9946,9 @@ namespace Catch {
if( assertionResult.hasExpression() ) { if( assertionResult.hasExpression() ) {
m_xml.startElement( "Expression" ) m_xml.startElement( "Expression" )
.writeAttribute( "success", assertionResult.succeeded() ) .writeAttribute( "success", assertionResult.succeeded() )
.writeAttribute( "type", assertionResult.getTestMacroName() ) .writeAttribute( "type", assertionResult.getTestMacroName() );
.writeAttribute( "filename", assertionResult.getSourceInfo().file )
.writeAttribute( "line", assertionResult.getSourceInfo().line ); writeSourceInfo( assertionResult.getSourceInfo() );
m_xml.scopedElement( "Original" ) m_xml.scopedElement( "Original" )
.writeText( assertionResult.getExpression() ); .writeText( assertionResult.getExpression() );
...@@ -9762,16 +9959,16 @@ namespace Catch { ...@@ -9762,16 +9959,16 @@ namespace Catch {
// And... Print a result applicable to each result type. // And... Print a result applicable to each result type.
switch( assertionResult.getResultType() ) { switch( assertionResult.getResultType() ) {
case ResultWas::ThrewException: case ResultWas::ThrewException:
m_xml.scopedElement( "Exception" ) m_xml.startElement( "Exception" );
.writeAttribute( "filename", assertionResult.getSourceInfo().file ) writeSourceInfo( assertionResult.getSourceInfo() );
.writeAttribute( "line", assertionResult.getSourceInfo().line ) m_xml.writeText( assertionResult.getMessage() );
.writeText( assertionResult.getMessage() ); m_xml.endElement();
break; break;
case ResultWas::FatalErrorCondition: case ResultWas::FatalErrorCondition:
m_xml.scopedElement( "FatalErrorCondition" ) m_xml.startElement( "FatalErrorCondition" );
.writeAttribute( "filename", assertionResult.getSourceInfo().file ) writeSourceInfo( assertionResult.getSourceInfo() );
.writeAttribute( "line", assertionResult.getSourceInfo().line ) m_xml.writeText( assertionResult.getMessage() );
.writeText( assertionResult.getMessage() ); m_xml.endElement();
break; break;
case ResultWas::Info: case ResultWas::Info:
m_xml.scopedElement( "Info" ) m_xml.scopedElement( "Info" )
...@@ -9781,8 +9978,10 @@ namespace Catch { ...@@ -9781,8 +9978,10 @@ namespace Catch {
// Warning will already have been written // Warning will already have been written
break; break;
case ResultWas::ExplicitFailure: case ResultWas::ExplicitFailure:
m_xml.scopedElement( "Failure" ) m_xml.startElement( "Failure" );
.writeText( assertionResult.getMessage() ); writeSourceInfo( assertionResult.getSourceInfo() );
m_xml.writeText( assertionResult.getMessage() );
m_xml.endElement();
break; break;
default: default:
break; break;
...@@ -10095,8 +10294,30 @@ namespace Catch { ...@@ -10095,8 +10294,30 @@ namespace Catch {
// #included from: ../reporters/catch_reporter_console.hpp // #included from: ../reporters/catch_reporter_console.hpp
#define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED #define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED
#include <cfloat>
#include <cstdio>
namespace Catch { namespace Catch {
namespace {
// Because formatting using c++ streams is stateful, drop down to C is required
// Alternatively we could use stringstream, but its performance is... not good.
std::string getFormattedDuration( double duration ) {
// Max exponent + 1 is required to represent the whole part
// + 1 for decimal point
// + 3 for the 3 decimal places
// + 1 for null terminator
const size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1;
char buffer[maxDoubleSize];
#ifdef _MSC_VER
sprintf_s(buffer, "%.3f", duration);
#else
sprintf(buffer, "%.3f", duration);
#endif
return std::string(buffer);
}
}
struct ConsoleReporter : StreamingReporterBase { struct ConsoleReporter : StreamingReporterBase {
ConsoleReporter( ReporterConfig const& _config ) ConsoleReporter( ReporterConfig const& _config )
: StreamingReporterBase( _config ), : StreamingReporterBase( _config ),
...@@ -10149,15 +10370,12 @@ namespace Catch { ...@@ -10149,15 +10370,12 @@ namespace Catch {
stream << "\nNo assertions in test case"; stream << "\nNo assertions in test case";
stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl;
} }
if( m_config->showDurations() == ShowDurations::Always ) {
stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl;
}
if( m_headerPrinted ) { if( m_headerPrinted ) {
if( m_config->showDurations() == ShowDurations::Always )
stream << "Completed in " << _sectionStats.durationInSeconds << 's' << std::endl;
m_headerPrinted = false; m_headerPrinted = false;
} }
else {
if( m_config->showDurations() == ShowDurations::Always )
stream << _sectionStats.sectionInfo.name << " completed in " << _sectionStats.durationInSeconds << 's' << std::endl;
}
StreamingReporterBase::sectionEnded( _sectionStats ); StreamingReporterBase::sectionEnded( _sectionStats );
} }
...@@ -10860,11 +11078,6 @@ namespace Catch { ...@@ -10860,11 +11078,6 @@ namespace Catch {
TestSpec::TagPattern::~TagPattern() {} TestSpec::TagPattern::~TagPattern() {}
TestSpec::ExcludedPattern::~ExcludedPattern() {} TestSpec::ExcludedPattern::~ExcludedPattern() {}
Matchers::Impl::StdString::Equals::~Equals() {}
Matchers::Impl::StdString::Contains::~Contains() {}
Matchers::Impl::StdString::StartsWith::~StartsWith() {}
Matchers::Impl::StdString::EndsWith::~EndsWith() {}
void Config::dummy() {} void Config::dummy() {}
namespace TestCaseTracking { namespace TestCaseTracking {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment