mirror of
https://github.com/catchorg/Catch2.git
synced 2024-11-26 07:16:10 +01:00
v2.2.2
This commit is contained in:
parent
ab30621138
commit
d2d8455b57
@ -6,7 +6,7 @@ if(NOT DEFINED PROJECT_NAME)
|
|||||||
set(NOT_SUBPROJECT ON)
|
set(NOT_SUBPROJECT ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
project(Catch2 LANGUAGES CXX VERSION 2.2.1)
|
project(Catch2 LANGUAGES CXX VERSION 2.2.2)
|
||||||
|
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
|
|
||||||
|
@ -5,9 +5,9 @@
|
|||||||
[![Build Status](https://travis-ci.org/catchorg/Catch2.svg?branch=master)](https://travis-ci.org/catchorg/Catch2)
|
[![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)
|
[![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)
|
[![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/zDKMK3eGMC9IP2jy)
|
[![Try online](https://img.shields.io/badge/try-online-blue.svg)](https://wandbox.org/permlink/fRDMfYjUnrbOFwLn)
|
||||||
|
|
||||||
<a href="https://github.com/catchorg/Catch2/releases/download/v2.2.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.2.2/catch.hpp">The latest version of the single header can be downloaded directly using this link</a>
|
||||||
|
|
||||||
## Catch2 is released!
|
## Catch2 is released!
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ from conans import ConanFile
|
|||||||
|
|
||||||
class CatchConan(ConanFile):
|
class CatchConan(ConanFile):
|
||||||
name = "Catch"
|
name = "Catch"
|
||||||
version = "2.2.1"
|
version = "2.2.2"
|
||||||
description = "A modern, C++-native, header-only, framework for unit-tests, TDD and BDD"
|
description = "A modern, C++-native, header-only, framework for unit-tests, TDD and BDD"
|
||||||
author = "philsquared"
|
author = "philsquared"
|
||||||
generators = "cmake"
|
generators = "cmake"
|
||||||
|
@ -1,5 +1,30 @@
|
|||||||
<a id="top"></a>
|
<a id="top"></a>
|
||||||
|
|
||||||
|
# 2.2.2
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
* Fixed bug in `WithinAbs::match()` failing spuriously (#1228)
|
||||||
|
* Fixed clang-tidy diagnostic about virtual call in destructor (#1226)
|
||||||
|
* Reduced the number of GCC warnings suppression leaking out of the header (#1090, #1091)
|
||||||
|
* Only `-Wparentheses` should be leaking now
|
||||||
|
* Added upper bound on the time benchmark timer calibration is allowed to take (#1237)
|
||||||
|
* On platforms where `std::chrono::high_resolution_clock`'s resolution is low, the calibration would appear stuck
|
||||||
|
* Fixed compilation error when stringifying static arrays of `unsigned char`s (#1238)
|
||||||
|
|
||||||
|
## Improvements
|
||||||
|
* XML encoder now hex-encodes invalid UTF-8 sequences (#1207)
|
||||||
|
* This affects xml and junit reporters
|
||||||
|
* Some invalid UTF-8 parts are left as is, e.g. surrogate pairs. This is because certain extensions of UTF-8 allow them, such as WTF-8.
|
||||||
|
* CLR objects (`T^`) can now be stringified (#1216)
|
||||||
|
* This affects code compiled as C++/CLI
|
||||||
|
* Added `PredicateMatcher`, a matcher that takes an arbitrary predicate function (#1236)
|
||||||
|
* See [documentation for details](https://github.com/catchorg/Catch2/blob/master/docs/matchers.md)
|
||||||
|
|
||||||
|
## Others
|
||||||
|
* Modified CMake-installed pkg-config to allow `#include <catch.hpp>`(#1239)
|
||||||
|
* The plans to standardize on `#include <catch2/catch.hpp>` are still in effect
|
||||||
|
|
||||||
|
|
||||||
# 2.2.1
|
# 2.2.1
|
||||||
|
|
||||||
## Fixes
|
## Fixes
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
#define CATCH_VERSION_MAJOR 2
|
#define CATCH_VERSION_MAJOR 2
|
||||||
#define CATCH_VERSION_MINOR 2
|
#define CATCH_VERSION_MINOR 2
|
||||||
#define CATCH_VERSION_PATCH 1
|
#define CATCH_VERSION_PATCH 2
|
||||||
|
|
||||||
#ifdef __clang__
|
#ifdef __clang__
|
||||||
# pragma clang system_header
|
# pragma clang system_header
|
||||||
|
@ -37,7 +37,7 @@ namespace Catch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Version const& libraryVersion() {
|
Version const& libraryVersion() {
|
||||||
static Version version( 2, 2, 1, "", 0 );
|
static Version version( 2, 2, 2, "", 0 );
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Catch v2.2.1
|
* Catch v2.2.2
|
||||||
* Generated: 2018-03-11 12:01:31.654719
|
* Generated: 2018-04-06 12:05:03.186665
|
||||||
* ----------------------------------------------------------
|
* ----------------------------------------------------------
|
||||||
* 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) 2018 Two Blue Cubes Ltd. All rights reserved.
|
* Copyright (c) 2018 Two Blue Cubes Ltd. All rights reserved.
|
||||||
@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
#define CATCH_VERSION_MAJOR 2
|
#define CATCH_VERSION_MAJOR 2
|
||||||
#define CATCH_VERSION_MINOR 2
|
#define CATCH_VERSION_MINOR 2
|
||||||
#define CATCH_VERSION_PATCH 1
|
#define CATCH_VERSION_PATCH 2
|
||||||
|
|
||||||
#ifdef __clang__
|
#ifdef __clang__
|
||||||
# pragma clang system_header
|
# pragma clang system_header
|
||||||
@ -37,9 +37,9 @@
|
|||||||
# pragma clang diagnostic ignored "-Wcovered-switch-default"
|
# pragma clang diagnostic ignored "-Wcovered-switch-default"
|
||||||
# endif
|
# endif
|
||||||
#elif defined __GNUC__
|
#elif defined __GNUC__
|
||||||
# pragma GCC diagnostic ignored "-Wunused-variable"
|
|
||||||
# pragma GCC diagnostic ignored "-Wparentheses"
|
# pragma GCC diagnostic ignored "-Wparentheses"
|
||||||
# pragma GCC diagnostic push
|
# pragma GCC diagnostic push
|
||||||
|
# pragma GCC diagnostic ignored "-Wunused-variable"
|
||||||
# pragma GCC diagnostic ignored "-Wpadded"
|
# pragma GCC diagnostic ignored "-Wpadded"
|
||||||
#endif
|
#endif
|
||||||
// end catch_suppress_warnings.h
|
// end catch_suppress_warnings.h
|
||||||
@ -783,6 +783,18 @@ namespace Catch {
|
|||||||
return convertUnknownEnumToString( value );
|
return convertUnknownEnumToString( value );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(_MANAGED)
|
||||||
|
//! Convert a CLR string to a utf8 std::string
|
||||||
|
template<typename T>
|
||||||
|
std::string clrReferenceToString( T^ ref ) {
|
||||||
|
if (ref == nullptr)
|
||||||
|
return std::string("null");
|
||||||
|
auto bytes = System::Text::Encoding::UTF8->GetBytes(ref->ToString());
|
||||||
|
cli::pin_ptr<System::Byte> p = &bytes[0];
|
||||||
|
return std::string(reinterpret_cast<char const *>(p), bytes->Length);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace Detail
|
} // namespace Detail
|
||||||
|
|
||||||
// If we decide for C++14, change these to enable_if_ts
|
// If we decide for C++14, change these to enable_if_ts
|
||||||
@ -819,6 +831,13 @@ namespace Catch {
|
|||||||
return ::Catch::Detail::stringify(static_cast<typename std::underlying_type<E>::type>(e));
|
return ::Catch::Detail::stringify(static_cast<typename std::underlying_type<E>::type>(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(_MANAGED)
|
||||||
|
template <typename T>
|
||||||
|
std::string stringify( T^ e ) {
|
||||||
|
return ::Catch::StringMaker<T^>::convert(e);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace Detail
|
} // namespace Detail
|
||||||
|
|
||||||
// Some predefined specializations
|
// Some predefined specializations
|
||||||
@ -842,6 +861,7 @@ namespace Catch {
|
|||||||
struct StringMaker<char *> {
|
struct StringMaker<char *> {
|
||||||
static std::string convert(char * str);
|
static std::string convert(char * str);
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CATCH_CONFIG_WCHAR
|
#ifdef CATCH_CONFIG_WCHAR
|
||||||
template<>
|
template<>
|
||||||
struct StringMaker<wchar_t const *> {
|
struct StringMaker<wchar_t const *> {
|
||||||
@ -853,22 +873,24 @@ namespace Catch {
|
|||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// TBD: Should we use `strnlen` to ensure that we don't go out of the buffer,
|
||||||
|
// while keeping string semantics?
|
||||||
template<int SZ>
|
template<int SZ>
|
||||||
struct StringMaker<char[SZ]> {
|
struct StringMaker<char[SZ]> {
|
||||||
static std::string convert(const char* str) {
|
static std::string convert(char const* str) {
|
||||||
return ::Catch::Detail::stringify(std::string{ str });
|
return ::Catch::Detail::stringify(std::string{ str });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
template<int SZ>
|
template<int SZ>
|
||||||
struct StringMaker<signed char[SZ]> {
|
struct StringMaker<signed char[SZ]> {
|
||||||
static std::string convert(const char* str) {
|
static std::string convert(signed char const* str) {
|
||||||
return ::Catch::Detail::stringify(std::string{ str });
|
return ::Catch::Detail::stringify(std::string{ reinterpret_cast<char const *>(str) });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
template<int SZ>
|
template<int SZ>
|
||||||
struct StringMaker<unsigned char[SZ]> {
|
struct StringMaker<unsigned char[SZ]> {
|
||||||
static std::string convert(const char* str) {
|
static std::string convert(unsigned char const* str) {
|
||||||
return ::Catch::Detail::stringify(std::string{ str });
|
return ::Catch::Detail::stringify(std::string{ reinterpret_cast<char const *>(str) });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -952,6 +974,15 @@ namespace Catch {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if defined(_MANAGED)
|
||||||
|
template <typename T>
|
||||||
|
struct StringMaker<T^> {
|
||||||
|
static std::string convert( T^ ref ) {
|
||||||
|
return ::Catch::Detail::clrReferenceToString(ref);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace Detail {
|
namespace Detail {
|
||||||
template<typename InputIterator>
|
template<typename InputIterator>
|
||||||
std::string rangeToString(InputIterator first, InputIterator last) {
|
std::string rangeToString(InputIterator first, InputIterator last) {
|
||||||
@ -1080,6 +1111,13 @@ namespace Catch {
|
|||||||
!std::is_same<decltype(end(std::declval<T>())), not_this_one>::value;
|
!std::is_same<decltype(end(std::declval<T>())), not_this_one>::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if defined(_MANAGED) // Managed types are never ranges
|
||||||
|
template <typename T>
|
||||||
|
struct is_range<T^> {
|
||||||
|
static const bool value = false;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
template<typename Range>
|
template<typename Range>
|
||||||
std::string rangeToString( Range const& range ) {
|
std::string rangeToString( Range const& range ) {
|
||||||
return ::Catch::Detail::rangeToString( begin( range ), end( range ) );
|
return ::Catch::Detail::rangeToString( begin( range ), end( range ) );
|
||||||
@ -2357,6 +2395,54 @@ namespace Matchers {
|
|||||||
} // namespace Catch
|
} // namespace Catch
|
||||||
|
|
||||||
// end catch_matchers_floating.h
|
// end catch_matchers_floating.h
|
||||||
|
// start catch_matchers_generic.hpp
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace Catch {
|
||||||
|
namespace Matchers {
|
||||||
|
namespace Generic {
|
||||||
|
|
||||||
|
namespace Detail {
|
||||||
|
std::string finalizeDescription(const std::string& desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class PredicateMatcher : public MatcherBase<T> {
|
||||||
|
std::function<bool(T const&)> m_predicate;
|
||||||
|
std::string m_description;
|
||||||
|
public:
|
||||||
|
|
||||||
|
PredicateMatcher(std::function<bool(T const&)> const& elem, std::string const& descr)
|
||||||
|
:m_predicate(std::move(elem)),
|
||||||
|
m_description(Detail::finalizeDescription(descr))
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool match( T const& item ) const override {
|
||||||
|
return m_predicate(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string describe() const override {
|
||||||
|
return m_description;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Generic
|
||||||
|
|
||||||
|
// The following functions create the actual matcher objects.
|
||||||
|
// The user has to explicitly specify type to the function, because
|
||||||
|
// infering std::function<bool(T const&)> is hard (but possible) and
|
||||||
|
// requires a lot of TMP.
|
||||||
|
template<typename T>
|
||||||
|
Generic::PredicateMatcher<T> Predicate(std::function<bool(T const&)> const& predicate, std::string const& description = "") {
|
||||||
|
return Generic::PredicateMatcher<T>(predicate, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Matchers
|
||||||
|
} // namespace Catch
|
||||||
|
|
||||||
|
// end catch_matchers_generic.hpp
|
||||||
// start catch_matchers_string.h
|
// start catch_matchers_string.h
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -4830,7 +4916,7 @@ namespace Catch {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
// !TBD We need to do this another way!
|
// !TBD We need to do this another way!
|
||||||
bool aborting() const override;
|
bool aborting() const final;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
@ -7880,7 +7966,7 @@ namespace Floating {
|
|||||||
// Performs equivalent check of std::fabs(lhs - rhs) <= margin
|
// Performs equivalent check of std::fabs(lhs - rhs) <= margin
|
||||||
// But without the subtraction to allow for INFINITY in comparison
|
// But without the subtraction to allow for INFINITY in comparison
|
||||||
bool WithinAbsMatcher::match(double const& matchee) const {
|
bool WithinAbsMatcher::match(double const& matchee) const {
|
||||||
return (matchee + m_margin >= m_target) && (m_target + m_margin >= m_margin);
|
return (matchee + m_margin >= m_target) && (m_target + m_margin >= matchee);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string WithinAbsMatcher::describe() const {
|
std::string WithinAbsMatcher::describe() const {
|
||||||
@ -7927,6 +8013,16 @@ Floating::WithinAbsMatcher WithinAbs(double target, double margin) {
|
|||||||
} // namespace Catch
|
} // namespace Catch
|
||||||
|
|
||||||
// end catch_matchers_floating.cpp
|
// end catch_matchers_floating.cpp
|
||||||
|
// start catch_matchers_generic.cpp
|
||||||
|
|
||||||
|
std::string Catch::Matchers::Generic::Detail::finalizeDescription(const std::string& desc) {
|
||||||
|
if (desc.empty()) {
|
||||||
|
return "matches undescribed predicate";
|
||||||
|
} else {
|
||||||
|
return "matches predicate: \"" + desc + '"';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// end catch_matchers_generic.cpp
|
||||||
// start catch_matchers_string.cpp
|
// start catch_matchers_string.cpp
|
||||||
|
|
||||||
#include <regex>
|
#include <regex>
|
||||||
@ -10425,6 +10521,8 @@ namespace Catch {
|
|||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
|
static const uint64_t nanosecondsInSecond = 1000000000;
|
||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
|
|
||||||
auto getCurrentNanosecondsSinceEpoch() -> uint64_t {
|
auto getCurrentNanosecondsSinceEpoch() -> uint64_t {
|
||||||
@ -10435,17 +10533,25 @@ namespace Catch {
|
|||||||
uint64_t sum = 0;
|
uint64_t sum = 0;
|
||||||
static const uint64_t iterations = 1000000;
|
static const uint64_t iterations = 1000000;
|
||||||
|
|
||||||
|
auto startTime = getCurrentNanosecondsSinceEpoch();
|
||||||
|
|
||||||
for( std::size_t i = 0; i < iterations; ++i ) {
|
for( std::size_t i = 0; i < iterations; ++i ) {
|
||||||
|
|
||||||
uint64_t ticks;
|
uint64_t ticks;
|
||||||
uint64_t baseTicks = getCurrentNanosecondsSinceEpoch();
|
uint64_t baseTicks = getCurrentNanosecondsSinceEpoch();
|
||||||
do {
|
do {
|
||||||
ticks = getCurrentNanosecondsSinceEpoch();
|
ticks = getCurrentNanosecondsSinceEpoch();
|
||||||
}
|
} while( ticks == baseTicks );
|
||||||
while( ticks == baseTicks );
|
|
||||||
|
|
||||||
auto delta = ticks - baseTicks;
|
auto delta = ticks - baseTicks;
|
||||||
sum += delta;
|
sum += delta;
|
||||||
|
|
||||||
|
// If we have been calibrating for over 3 seconds -- the clock
|
||||||
|
// is terrible and we should move on.
|
||||||
|
// TBD: How to signal that the measured resolution is probably wrong?
|
||||||
|
if (ticks > startTime + 3 * nanosecondsInSecond) {
|
||||||
|
return sum / i;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We're just taking the mean, here. To do better we could take the std. dev and exclude outliers
|
// We're just taking the mean, here. To do better we could take the std. dev and exclude outliers
|
||||||
@ -10807,7 +10913,7 @@ namespace Catch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Version const& libraryVersion() {
|
Version const& libraryVersion() {
|
||||||
static Version version( 2, 2, 1, "", 0 );
|
static Version version( 2, 2, 2, "", 0 );
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -10858,49 +10964,141 @@ namespace Catch {
|
|||||||
|
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
|
||||||
|
using uchar = unsigned char;
|
||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
size_t trailingBytes(unsigned char c) {
|
||||||
|
if ((c & 0xE0) == 0xC0) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
if ((c & 0xF0) == 0xE0) {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
if ((c & 0xF8) == 0xF0) {
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered");
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t headerValue(unsigned char c) {
|
||||||
|
if ((c & 0xE0) == 0xC0) {
|
||||||
|
return c & 0x1F;
|
||||||
|
}
|
||||||
|
if ((c & 0xF0) == 0xE0) {
|
||||||
|
return c & 0x0F;
|
||||||
|
}
|
||||||
|
if ((c & 0xF8) == 0xF0) {
|
||||||
|
return c & 0x07;
|
||||||
|
}
|
||||||
|
CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered");
|
||||||
|
}
|
||||||
|
|
||||||
|
void hexEscapeChar(std::ostream& os, unsigned char c) {
|
||||||
|
os << "\\x"
|
||||||
|
<< std::uppercase << std::hex << std::setfill('0') << std::setw(2)
|
||||||
|
<< static_cast<int>(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat )
|
XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat )
|
||||||
: m_str( str ),
|
: m_str( str ),
|
||||||
m_forWhat( forWhat )
|
m_forWhat( forWhat )
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void XmlEncode::encodeTo( std::ostream& os ) const {
|
void XmlEncode::encodeTo( std::ostream& os ) const {
|
||||||
|
|
||||||
// Apostrophe escaping not necessary if we always use " to write attributes
|
// Apostrophe escaping not necessary if we always use " to write attributes
|
||||||
// (see: http://www.w3.org/TR/xml/#syntax)
|
// (see: http://www.w3.org/TR/xml/#syntax)
|
||||||
|
|
||||||
for( std::size_t i = 0; i < m_str.size(); ++ i ) {
|
for( std::size_t idx = 0; idx < m_str.size(); ++ idx ) {
|
||||||
char c = m_str[i];
|
uchar c = m_str[idx];
|
||||||
switch( c ) {
|
switch (c) {
|
||||||
case '<': os << "<"; break;
|
case '<': os << "<"; break;
|
||||||
case '&': os << "&"; break;
|
case '&': os << "&"; break;
|
||||||
|
|
||||||
case '>':
|
case '>':
|
||||||
// See: http://www.w3.org/TR/xml/#syntax
|
// See: http://www.w3.org/TR/xml/#syntax
|
||||||
if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' )
|
if (idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']')
|
||||||
os << ">";
|
os << ">";
|
||||||
else
|
else
|
||||||
os << c;
|
os << c;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '\"':
|
||||||
|
if (m_forWhat == ForAttributes)
|
||||||
|
os << """;
|
||||||
|
else
|
||||||
|
os << c;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Check for control characters and invalid utf-8
|
||||||
|
|
||||||
|
// Escape control characters in standard ascii
|
||||||
|
// see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0
|
||||||
|
if (c < 0x09 || (c > 0x0D && c < 0x20) || c == 0x7F) {
|
||||||
|
hexEscapeChar(os, c);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case '\"':
|
// Plain ASCII: Write it to stream
|
||||||
if( m_forWhat == ForAttributes )
|
if (c < 0x7F) {
|
||||||
os << """;
|
os << c;
|
||||||
else
|
|
||||||
os << c;
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
// UTF-8 territory
|
||||||
// Escape control chars - based on contribution by @espenalb in PR #465 and
|
// Check if the encoding is valid and if it is not, hex escape bytes.
|
||||||
// by @mrpi PR #588
|
// Important: We do not check the exact decoded values for validity, only the encoding format
|
||||||
if ( ( c >= 0 && c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) {
|
// First check that this bytes is a valid lead byte:
|
||||||
// see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0
|
// This means that it is not encoded as 1111 1XXX
|
||||||
os << "\\x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2)
|
// Or as 10XX XXXX
|
||||||
<< static_cast<int>( c );
|
if (c < 0xC0 ||
|
||||||
}
|
c >= 0xF8) {
|
||||||
else
|
hexEscapeChar(os, c);
|
||||||
os << c;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto encBytes = trailingBytes(c);
|
||||||
|
// Are there enough bytes left to avoid accessing out-of-bounds memory?
|
||||||
|
if (idx + encBytes - 1 >= m_str.size()) {
|
||||||
|
hexEscapeChar(os, c);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// The header is valid, check data
|
||||||
|
// The next encBytes bytes must together be a valid utf-8
|
||||||
|
// This means: bitpattern 10XX XXXX and the extracted value is sane (ish)
|
||||||
|
bool valid = true;
|
||||||
|
uint32_t value = headerValue(c);
|
||||||
|
for (std::size_t n = 1; n < encBytes; ++n) {
|
||||||
|
uchar nc = m_str[idx + n];
|
||||||
|
valid &= ((nc & 0xC0) == 0x80);
|
||||||
|
value = (value << 6) | (nc & 0x3F);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
// Wrong bit pattern of following bytes
|
||||||
|
(!valid) ||
|
||||||
|
// Overlong encodings
|
||||||
|
(value < 0x80) ||
|
||||||
|
(0x80 <= value && value < 0x800 && encBytes > 2) ||
|
||||||
|
(0x800 < value && value < 0x10000 && encBytes > 3) ||
|
||||||
|
// Encoded value out of range
|
||||||
|
(value >= 0x110000)
|
||||||
|
) {
|
||||||
|
hexEscapeChar(os, c);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we got here, this is in fact a valid(ish) utf-8 sequence
|
||||||
|
for (std::size_t n = 0; n < encBytes; ++n) {
|
||||||
|
os << m_str[idx + n];
|
||||||
|
}
|
||||||
|
idx += encBytes - 1;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ class CatchConanTest(ConanFile):
|
|||||||
settings = "os", "compiler", "arch", "build_type"
|
settings = "os", "compiler", "arch", "build_type"
|
||||||
username = getenv("CONAN_USERNAME", "philsquared")
|
username = getenv("CONAN_USERNAME", "philsquared")
|
||||||
channel = getenv("CONAN_CHANNEL", "testing")
|
channel = getenv("CONAN_CHANNEL", "testing")
|
||||||
requires = "Catch/2.2.1@%s/%s" % (username, channel)
|
requires = "Catch/2.2.2@%s/%s" % (username, channel)
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
cmake = CMake(self)
|
cmake = CMake(self)
|
||||||
|
Loading…
Reference in New Issue
Block a user