forked from catchorg/Catch2
Compare commits
19 Commits
v2.0.0-dev
...
v2.0.0-dev
Author | SHA1 | Date | |
---|---|---|---|
|
355ab78f4a | ||
|
927f520a97 | ||
|
cc0b093c20 | ||
|
17cdf20968 | ||
|
4899d891d3 | ||
|
760a25e813 | ||
|
79b405fd3f | ||
|
f972732737 | ||
|
61280e6d0a | ||
|
7e9b53e40c | ||
|
b80c5134f0 | ||
|
70e0d48978 | ||
|
11918b76d0 | ||
|
5fe19f73e7 | ||
|
c1416d55cb | ||
|
2a1f8ae684 | ||
|
9541e89e6a | ||
|
80bbce8424 | ||
|
3d49d83128 |
@@ -68,6 +68,7 @@ set(TEST_SOURCES
|
|||||||
${SELF_TEST_DIR}/PartTrackerTests.cpp
|
${SELF_TEST_DIR}/PartTrackerTests.cpp
|
||||||
${SELF_TEST_DIR}/TagAliasTests.cpp
|
${SELF_TEST_DIR}/TagAliasTests.cpp
|
||||||
${SELF_TEST_DIR}/TestMain.cpp
|
${SELF_TEST_DIR}/TestMain.cpp
|
||||||
|
${SELF_TEST_DIR}/ToStringChrono.cpp
|
||||||
${SELF_TEST_DIR}/ToStringGeneralTests.cpp
|
${SELF_TEST_DIR}/ToStringGeneralTests.cpp
|
||||||
${SELF_TEST_DIR}/ToStringPair.cpp
|
${SELF_TEST_DIR}/ToStringPair.cpp
|
||||||
${SELF_TEST_DIR}/ToStringTuple.cpp
|
${SELF_TEST_DIR}/ToStringTuple.cpp
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
[](https://travis-ci.org/philsquared/Catch?branch=catch2)
|
[](https://travis-ci.org/philsquared/Catch?branch=catch2)
|
||||||
[](https://ci.appveyor.com/project/philsquared/catch/branch/catch2)
|
[](https://ci.appveyor.com/project/philsquared/catch/branch/catch2)
|
||||||
|
|
||||||
<a href="https://github.com/philsquared/Catch/releases/download/v2.0.0-develop.1/catch.hpp">The latest version of the single header can be downloaded directly using this link</a>
|
<a href="https://github.com/philsquared/Catch/releases/download/v2.0.0-develop.4/catch.hpp">The latest version of the single header can be downloaded directly using this link</a>
|
||||||
|
|
||||||
## What's the Catch?
|
## What's the Catch?
|
||||||
|
|
||||||
|
@@ -4,7 +4,7 @@ from conans import ConanFile
|
|||||||
|
|
||||||
class CatchConan(ConanFile):
|
class CatchConan(ConanFile):
|
||||||
name = "Catch"
|
name = "Catch"
|
||||||
version = "2.0.0-develop.4"
|
version = "2.0.0-develop.5"
|
||||||
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"
|
||||||
|
@@ -9,6 +9,7 @@
|
|||||||
[stdout](#stdout)
|
[stdout](#stdout)
|
||||||
[Other toggles](#other-toggles)
|
[Other toggles](#other-toggles)
|
||||||
[Windows header clutter](#windows-header-clutter)
|
[Windows header clutter](#windows-header-clutter)
|
||||||
|
[Enabling stringification](#enabling-stringification)
|
||||||
|
|
||||||
Catch is designed to "just work" as much as possible. For most people the only configuration needed is telling Catch which source file should host all the implementation code (```CATCH_CONFIG_MAIN```).
|
Catch is designed to "just work" as much as possible. For most people the only configuration needed is telling Catch which source file should host all the implementation code (```CATCH_CONFIG_MAIN```).
|
||||||
|
|
||||||
@@ -121,6 +122,17 @@ On Windows Catch includes `windows.h`. To minimize global namespace clutter in t
|
|||||||
CATCH_CONFIG_NO_NOMINMAX // Stops Catch from using NOMINMAX macro
|
CATCH_CONFIG_NO_NOMINMAX // Stops Catch from using NOMINMAX macro
|
||||||
CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN // Stops Catch from using WIN32_LEAN_AND_MEAN macro
|
CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN // Stops Catch from using WIN32_LEAN_AND_MEAN macro
|
||||||
|
|
||||||
|
|
||||||
|
## Enabling stringification
|
||||||
|
|
||||||
|
By default, Catch does not stringify some types from the standard library. This is done to avoid dragging in various standard library headers by default. However, Catch does contain these and can be configured to provide them, using these macros:
|
||||||
|
|
||||||
|
CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER // Provide StringMaker specialization for std::pair
|
||||||
|
CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER // Provide StringMaker specialization for std::tuple
|
||||||
|
CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER // Provide StringMaker specialization for std::chrono::duration, std::chrono::timepoint
|
||||||
|
CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS // Defines all of the above
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
[Home](Readme.md#top)
|
[Home](Readme.md#top)
|
||||||
|
@@ -19,8 +19,12 @@
|
|||||||
* This is most noticeable in `CHECK(throws())`, which would previously report failure, properly stringify the exception and continue. Now it will report failure and stop executing current section.
|
* This is most noticeable in `CHECK(throws())`, which would previously report failure, properly stringify the exception and continue. Now it will report failure and stop executing current section.
|
||||||
* Removed deprecated matcher utility functions `Not`, `AllOf` and `AnyOf`.
|
* Removed deprecated matcher utility functions `Not`, `AllOf` and `AnyOf`.
|
||||||
* They are superseded by operators `!`, `&&` and `||`, which are natural and do not have limited arity
|
* They are superseded by operators `!`, `&&` and `||`, which are natural and do not have limited arity
|
||||||
* No longer accept non-const comparison operators
|
* Removed support for non-const comparison operators
|
||||||
|
* Non-const comparison operators are an abomination that should not exist
|
||||||
|
* They were breaking support for comparing function to function pointer
|
||||||
|
* `std::pair` and `std::tuple` are no longer stringified by default
|
||||||
|
* This is done to avoid dragging in `<tuple>` and `<utility>` headers in common path
|
||||||
|
* Their stringification can be enabled per-file via new configuration macros
|
||||||
|
|
||||||
## Improvements
|
## Improvements
|
||||||
* Reporters and Listeners can be defined in files different from the main file
|
* Reporters and Listeners can be defined in files different from the main file
|
||||||
@@ -48,13 +52,21 @@
|
|||||||
* Exception translators are not registered
|
* Exception translators are not registered
|
||||||
* Reporters are not registered
|
* Reporters are not registered
|
||||||
* Listeners are not registered
|
* Listeners are not registered
|
||||||
* More warnings are silenced
|
* Reporters/Listeners are now notified of fatal errors
|
||||||
* Don't use console colour if running in XCode
|
* This means specific signals or structured exceptions
|
||||||
|
* The Reporter/Listener interface provides default, empty, implementation to preserve backward compatibility
|
||||||
|
* Stringification of `std::chrono::duration` and `std::chrono::time_point` is now supported
|
||||||
|
* Needs to be enabled by a per-file compile time configuration option
|
||||||
|
|
||||||
## Fixes
|
## Fixes
|
||||||
|
* Don't use console colour if running in XCode
|
||||||
* Explicit constructor in reporter base class
|
* Explicit constructor in reporter base class
|
||||||
* Many fixes for building in Objective-C context
|
* Swept out `-Wweak-vtables`, `-Wexit-time-destructors`, `-Wglobal-constructors` warnings
|
||||||
* Do not use SEH and console api under UWP
|
* Compilation for Universal Windows Platform (UWP) is supported
|
||||||
|
* SEH handling and colorized output are disabled when compiling for UWP
|
||||||
|
* Implemented a workaround for `std::uncaught_exception` issues in libcxxrt
|
||||||
|
* These issues caused incorrect section traversals
|
||||||
|
* The workaround is only partial, user's test can still trigger the issue by using `throw;` to rethrow an exception
|
||||||
|
|
||||||
## Internal changes
|
## Internal changes
|
||||||
* The development version now uses .cpp files instead of header files containing implementation.
|
* The development version now uses .cpp files instead of header files containing implementation.
|
||||||
|
129
include/external/clara.hpp
vendored
129
include/external/clara.hpp
vendored
@@ -4,11 +4,13 @@
|
|||||||
#ifndef CATCH_CLARA_HPP_INCLUDED
|
#ifndef CATCH_CLARA_HPP_INCLUDED
|
||||||
#define CATCH_CLARA_HPP_INCLUDED
|
#define CATCH_CLARA_HPP_INCLUDED
|
||||||
|
|
||||||
|
|
||||||
#ifndef CATCH_CLARA_CONFIG_CONSOLE_WIDTH
|
#ifndef CATCH_CLARA_CONFIG_CONSOLE_WIDTH
|
||||||
#define CATCH_CLARA_CONFIG_CONSOLE_WIDTH 80
|
#define CATCH_CLARA_CONFIG_CONSOLE_WIDTH 80
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH
|
||||||
|
#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CLARA_CONFIG_CONSOLE_WIDTH
|
||||||
|
#endif
|
||||||
|
|
||||||
// ----------- #included from clara_textflow.hpp -----------
|
// ----------- #included from clara_textflow.hpp -----------
|
||||||
|
|
||||||
@@ -24,7 +26,6 @@
|
|||||||
#ifndef CATCH_CLARA_TEXTFLOW_HPP_INCLUDED
|
#ifndef CATCH_CLARA_TEXTFLOW_HPP_INCLUDED
|
||||||
#define CATCH_CLARA_TEXTFLOW_HPP_INCLUDED
|
#define CATCH_CLARA_TEXTFLOW_HPP_INCLUDED
|
||||||
|
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@@ -350,9 +351,8 @@ namespace Catch { namespace clara { namespace TextFlow {
|
|||||||
#include <set>
|
#include <set>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#if !defined(CLARA_PLATFORM_WINDOWS) && ( defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) )
|
#if !defined(CATCH_PLATFORM_WINDOWS) && ( defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) )
|
||||||
#define CLARA_PLATFORM_WINDOWS
|
#define CATCH_PLATFORM_WINDOWS
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace Catch { namespace clara {
|
namespace Catch { namespace clara {
|
||||||
@@ -549,25 +549,19 @@ namespace detail {
|
|||||||
template<typename U>
|
template<typename U>
|
||||||
explicit BasicResult( BasicResult<U> const &other )
|
explicit BasicResult( BasicResult<U> const &other )
|
||||||
: ResultValueBase<T>( other.type() ),
|
: ResultValueBase<T>( other.type() ),
|
||||||
m_errorMessage(other.errorMessage()) {
|
m_errorMessage( other.errorMessage() )
|
||||||
|
{
|
||||||
assert( type() != ResultBase::Ok );
|
assert( type() != ResultBase::Ok );
|
||||||
}
|
}
|
||||||
|
|
||||||
static auto ok() -> BasicResult { return {ResultBase::Ok}; }
|
|
||||||
|
|
||||||
template<typename U>
|
template<typename U>
|
||||||
static auto ok( U const &value ) -> BasicResult { return { ResultBase::Ok, value }; }
|
static auto ok( U const &value ) -> BasicResult { return { ResultBase::Ok, value }; }
|
||||||
|
static auto ok() -> BasicResult { return { ResultBase::Ok }; }
|
||||||
static auto logicError( std::string const &message ) -> BasicResult { return { ResultBase::LogicError, message }; }
|
static auto logicError( std::string const &message ) -> BasicResult { return { ResultBase::LogicError, message }; }
|
||||||
|
static auto runtimeError( std::string const &message ) -> BasicResult { return { ResultBase::RuntimeError, message }; }
|
||||||
static auto runtimeError(std::string const &message) -> BasicResult {
|
|
||||||
return {ResultBase::RuntimeError, message};
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit operator bool() const { return m_type == ResultBase::Ok; }
|
explicit operator bool() const { return m_type == ResultBase::Ok; }
|
||||||
|
|
||||||
auto type() const -> ResultBase::Type { return m_type; }
|
auto type() const -> ResultBase::Type { return m_type; }
|
||||||
|
|
||||||
auto errorMessage() const -> std::string { return m_errorMessage; }
|
auto errorMessage() const -> std::string { return m_errorMessage; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -587,7 +581,8 @@ namespace detail {
|
|||||||
|
|
||||||
BasicResult( ResultBase::Type type, std::string const &message )
|
BasicResult( ResultBase::Type type, std::string const &message )
|
||||||
: ResultValueBase<T>(type),
|
: ResultValueBase<T>(type),
|
||||||
m_errorMessage(message) {
|
m_errorMessage(message)
|
||||||
|
{
|
||||||
assert( m_type != ResultBase::Ok );
|
assert( m_type != ResultBase::Ok );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -604,10 +599,10 @@ namespace detail {
|
|||||||
|
|
||||||
ParseState( ParseResultType type, TokenStream const &remainingTokens )
|
ParseState( ParseResultType type, TokenStream const &remainingTokens )
|
||||||
: m_type(type),
|
: m_type(type),
|
||||||
m_remainingTokens(remainingTokens) {}
|
m_remainingTokens( remainingTokens )
|
||||||
|
{}
|
||||||
|
|
||||||
auto type() const -> ParseResultType { return m_type; }
|
auto type() const -> ParseResultType { return m_type; }
|
||||||
|
|
||||||
auto remainingTokens() const -> TokenStream { return m_remainingTokens; }
|
auto remainingTokens() const -> TokenStream { return m_remainingTokens; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -652,23 +647,16 @@ namespace detail {
|
|||||||
|
|
||||||
struct BoundRefBase {
|
struct BoundRefBase {
|
||||||
BoundRefBase() = default;
|
BoundRefBase() = default;
|
||||||
|
|
||||||
BoundRefBase( BoundRefBase const & ) = delete;
|
BoundRefBase( BoundRefBase const & ) = delete;
|
||||||
|
|
||||||
BoundRefBase( BoundRefBase && ) = delete;
|
BoundRefBase( BoundRefBase && ) = delete;
|
||||||
|
|
||||||
BoundRefBase &operator=( BoundRefBase const & ) = delete;
|
BoundRefBase &operator=( BoundRefBase const & ) = delete;
|
||||||
|
|
||||||
BoundRefBase &operator=( BoundRefBase && ) = delete;
|
BoundRefBase &operator=( BoundRefBase && ) = delete;
|
||||||
|
|
||||||
virtual ~BoundRefBase() = default;
|
virtual ~BoundRefBase() = default;
|
||||||
|
|
||||||
virtual auto isFlag() const -> bool = 0;
|
virtual auto isFlag() const -> bool = 0;
|
||||||
|
|
||||||
virtual auto isContainer() const -> bool { return false; }
|
virtual auto isContainer() const -> bool { return false; }
|
||||||
|
|
||||||
virtual auto setValue( std::string const &arg ) -> ParserResult = 0;
|
virtual auto setValue( std::string const &arg ) -> ParserResult = 0;
|
||||||
|
|
||||||
virtual auto setFlag( bool flag ) -> ParserResult = 0;
|
virtual auto setFlag( bool flag ) -> ParserResult = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -786,9 +774,7 @@ namespace detail {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class Optionality {
|
enum class Optionality { Optional, Required };
|
||||||
Optional, Required
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Parser;
|
struct Parser;
|
||||||
|
|
||||||
@@ -808,7 +794,7 @@ namespace detail {
|
|||||||
class ComposableParserImpl : public ParserBase {
|
class ComposableParserImpl : public ParserBase {
|
||||||
public:
|
public:
|
||||||
template<typename T>
|
template<typename T>
|
||||||
auto operator+(T const &other) const -> Parser;
|
auto operator|( T const &other ) const -> Parser;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Common code and state for Args and Opts
|
// Common code and state for Args and Opts
|
||||||
@@ -824,11 +810,16 @@ namespace detail {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
template<typename T>
|
template<typename T>
|
||||||
ParserRefImpl(T &ref, std::string const &hint) : m_ref(std::make_shared<BoundRef<T>>(ref)), m_hint(hint) {}
|
ParserRefImpl( T &ref, std::string const &hint )
|
||||||
|
: m_ref( std::make_shared<BoundRef<T>>( ref ) ),
|
||||||
|
m_hint( hint )
|
||||||
|
{}
|
||||||
|
|
||||||
template<typename LambdaT>
|
template<typename LambdaT>
|
||||||
ParserRefImpl(LambdaT const &ref, std::string const &hint) : m_ref(std::make_shared<BoundLambda<LambdaT>>(ref)),
|
ParserRefImpl( LambdaT const &ref, std::string const &hint )
|
||||||
m_hint(hint) {}
|
: m_ref( std::make_shared<BoundLambda<LambdaT>>( ref ) ),
|
||||||
|
m_hint(hint)
|
||||||
|
{}
|
||||||
|
|
||||||
auto operator()( std::string const &description ) -> DerivedT & {
|
auto operator()( std::string const &description ) -> DerivedT & {
|
||||||
m_description = description;
|
m_description = description;
|
||||||
@@ -967,7 +958,7 @@ namespace detail {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto isMatch( std::string const &optToken ) const -> bool {
|
auto isMatch( std::string const &optToken ) const -> bool {
|
||||||
#ifdef CLARA_PLATFORM_WINDOWS
|
#ifdef CATCH_PLATFORM_WINDOWS
|
||||||
auto normalisedToken = normaliseOpt( optToken );
|
auto normalisedToken = normaliseOpt( optToken );
|
||||||
#else
|
#else
|
||||||
auto const &normalisedToken = optToken;
|
auto const &normalisedToken = optToken;
|
||||||
@@ -1049,30 +1040,30 @@ namespace detail {
|
|||||||
std::vector<Opt> m_options;
|
std::vector<Opt> m_options;
|
||||||
std::vector<Arg> m_args;
|
std::vector<Arg> m_args;
|
||||||
|
|
||||||
auto operator+=(ExeName const &exeName) -> Parser & {
|
auto operator|=( ExeName const &exeName ) -> Parser & {
|
||||||
m_exeName = exeName;
|
m_exeName = exeName;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto operator+=(Arg const &arg) -> Parser & {
|
auto operator|=( Arg const &arg ) -> Parser & {
|
||||||
m_args.push_back(arg);
|
m_args.push_back(arg);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto operator+=(Opt const &opt) -> Parser & {
|
auto operator|=( Opt const &opt ) -> Parser & {
|
||||||
m_options.push_back(opt);
|
m_options.push_back(opt);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto operator+=(Parser const &other) -> Parser & {
|
auto operator|=( Parser const &other ) -> Parser & {
|
||||||
m_options.insert(m_options.end(), other.m_options.begin(), other.m_options.end());
|
m_options.insert(m_options.end(), other.m_options.begin(), other.m_options.end());
|
||||||
m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end());
|
m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end());
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
auto operator+(T const &other) const -> Parser {
|
auto operator|( T const &other ) const -> Parser {
|
||||||
return Parser(*this) += other;
|
return Parser( *this ) |= other;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto getHelpColumns() const -> std::vector<HelpColumns> {
|
auto getHelpColumns() const -> std::vector<HelpColumns> {
|
||||||
@@ -1145,46 +1136,46 @@ namespace detail {
|
|||||||
using ParserBase::parse;
|
using ParserBase::parse;
|
||||||
|
|
||||||
auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override {
|
auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override {
|
||||||
std::vector<ParserBase const *> allParsers;
|
|
||||||
allParsers.reserve(m_args.size() + m_options.size());
|
|
||||||
std::set<ParserBase const *> requiredParsers;
|
|
||||||
|
|
||||||
for (auto const &opt : m_options) {
|
struct ParserInfo {
|
||||||
allParsers.push_back(&opt);
|
ParserBase const* parser = nullptr;
|
||||||
if (!opt.isOptional())
|
size_t count = 0;
|
||||||
requiredParsers.insert(&opt);
|
};
|
||||||
}
|
const size_t totalParsers = m_options.size() + m_args.size();
|
||||||
|
assert( totalParsers < 512 );
|
||||||
|
// ParserInfo parseInfos[totalParsers]; // <-- this is what we really want to do
|
||||||
|
ParserInfo parseInfos[512];
|
||||||
|
|
||||||
size_t optionalArgs = 0;
|
{
|
||||||
for (auto const &arg : m_args) {
|
size_t i = 0;
|
||||||
allParsers.push_back(&arg);
|
for (auto const &opt : m_options) parseInfos[i++].parser = &opt;
|
||||||
if (!arg.isOptional()) {
|
for (auto const &arg : m_args) parseInfos[i++].parser = &arg;
|
||||||
if (optionalArgs > 0)
|
|
||||||
return InternalParseResult::logicError(
|
|
||||||
"Required arguments must preceed any optional arguments");
|
|
||||||
else
|
|
||||||
++optionalArgs;
|
|
||||||
requiredParsers.insert(&arg);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_exeName.set( exeName );
|
m_exeName.set( exeName );
|
||||||
|
|
||||||
auto result = InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) );
|
auto result = InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) );
|
||||||
while( result.value().remainingTokens() ) {
|
while( result.value().remainingTokens() ) {
|
||||||
auto remainingTokenCount = result.value().remainingTokens().count();
|
bool tokenParsed = false;
|
||||||
for (auto parser : allParsers) {
|
|
||||||
result = parser->parse( exeName, result.value().remainingTokens() );
|
for( size_t i = 0; i < totalParsers; ++i ) {
|
||||||
if (!result || result.value().type() != ParseResultType::NoMatch) {
|
auto& parseInfo = parseInfos[i];
|
||||||
if (parser->cardinality() == 1)
|
if( parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality() ) {
|
||||||
allParsers.erase(std::remove(allParsers.begin(), allParsers.end(), parser),
|
result = parseInfo.parser->parse(exeName, result.value().remainingTokens());
|
||||||
allParsers.end());
|
if (!result)
|
||||||
requiredParsers.erase(parser);
|
return result;
|
||||||
|
if (result.value().type() != ParseResultType::NoMatch) {
|
||||||
|
tokenParsed = true;
|
||||||
|
++parseInfo.count;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!result || remainingTokenCount == result.value().remainingTokens().count())
|
}
|
||||||
|
|
||||||
|
if( result.value().type() == ParseResultType::ShortCircuitAll )
|
||||||
return result;
|
return result;
|
||||||
|
if( !tokenParsed )
|
||||||
|
return InternalParseResult::runtimeError( "Unrecognised token: " + result.value().remainingTokens()->token );
|
||||||
}
|
}
|
||||||
// !TBD Check missing required options
|
// !TBD Check missing required options
|
||||||
return result;
|
return result;
|
||||||
@@ -1193,8 +1184,8 @@ namespace detail {
|
|||||||
|
|
||||||
template<typename DerivedT>
|
template<typename DerivedT>
|
||||||
template<typename T>
|
template<typename T>
|
||||||
auto ComposableParserImpl<DerivedT>::operator+(T const &other) const -> Parser {
|
auto ComposableParserImpl<DerivedT>::operator|( T const &other ) const -> Parser {
|
||||||
return Parser() + static_cast<DerivedT const &>( *this ) + other;
|
return Parser() | static_cast<DerivedT const &>( *this ) | other;
|
||||||
}
|
}
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
|
@@ -98,84 +98,84 @@ namespace Catch {
|
|||||||
|
|
||||||
auto cli
|
auto cli
|
||||||
= ExeName( config.processName )
|
= ExeName( config.processName )
|
||||||
+ Help( config.showHelp )
|
| Help( config.showHelp )
|
||||||
+ Opt( config.listTests )
|
| Opt( config.listTests )
|
||||||
["-l"]["--list-tests"]
|
["-l"]["--list-tests"]
|
||||||
( "list all/matching test cases" )
|
( "list all/matching test cases" )
|
||||||
+ Opt( config.listTags )
|
| Opt( config.listTags )
|
||||||
["-t"]["--list-tags"]
|
["-t"]["--list-tags"]
|
||||||
( "list all/matching tags" )
|
( "list all/matching tags" )
|
||||||
+ Opt( config.showSuccessfulTests )
|
| Opt( config.showSuccessfulTests )
|
||||||
["-s"]["--success"]
|
["-s"]["--success"]
|
||||||
( "include successful tests in output" )
|
( "include successful tests in output" )
|
||||||
+ Opt( config.shouldDebugBreak )
|
| Opt( config.shouldDebugBreak )
|
||||||
["-b"]["--break"]
|
["-b"]["--break"]
|
||||||
( "break into debugger on failure" )
|
( "break into debugger on failure" )
|
||||||
+ Opt( config.noThrow )
|
| Opt( config.noThrow )
|
||||||
["-e"]["--nothrow"]
|
["-e"]["--nothrow"]
|
||||||
( "skip exception tests" )
|
( "skip exception tests" )
|
||||||
+ Opt( config.showInvisibles )
|
| Opt( config.showInvisibles )
|
||||||
["-i"]["--invisibles"]
|
["-i"]["--invisibles"]
|
||||||
( "show invisibles (tabs, newlines)" )
|
( "show invisibles (tabs, newlines)" )
|
||||||
+ Opt( config.outputFilename, "filename" )
|
| Opt( config.outputFilename, "filename" )
|
||||||
["-o"]["--out"]
|
["-o"]["--out"]
|
||||||
( "output filename" )
|
( "output filename" )
|
||||||
+ Opt( config.reporterNames, "name" )
|
| Opt( config.reporterNames, "name" )
|
||||||
["-r"]["--reporter"]
|
["-r"]["--reporter"]
|
||||||
( "reporter to use (defaults to console)" )
|
( "reporter to use (defaults to console)" )
|
||||||
+ Opt( config.name, "name" )
|
| Opt( config.name, "name" )
|
||||||
["-n"]["--name"]
|
["-n"]["--name"]
|
||||||
( "suite name" )
|
( "suite name" )
|
||||||
+ Opt( [&]( bool ){ config.abortAfter = 1; } )
|
| Opt( [&]( bool ){ config.abortAfter = 1; } )
|
||||||
["-a"]["--abort"]
|
["-a"]["--abort"]
|
||||||
( "abort at first failure" )
|
( "abort at first failure" )
|
||||||
+ Opt( [&]( int x ){ config.abortAfter = x; }, "no. failures" )
|
| Opt( [&]( int x ){ config.abortAfter = x; }, "no. failures" )
|
||||||
["-x"]["--abortx"]
|
["-x"]["--abortx"]
|
||||||
( "abort after x failures" )
|
( "abort after x failures" )
|
||||||
+ Opt( setWarning, "warning name" )
|
| Opt( setWarning, "warning name" )
|
||||||
["-w"]["--warn"]
|
["-w"]["--warn"]
|
||||||
( "enable warnings" )
|
( "enable warnings" )
|
||||||
+ Opt( [&]( bool flag ) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, "yes|no" )
|
| Opt( [&]( bool flag ) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, "yes|no" )
|
||||||
["-d"]["--durations"]
|
["-d"]["--durations"]
|
||||||
( "show test durations" )
|
( "show test durations" )
|
||||||
+ Opt( loadTestNamesFromFile, "filename" )
|
| Opt( loadTestNamesFromFile, "filename" )
|
||||||
["-f"]["--input-file"]
|
["-f"]["--input-file"]
|
||||||
( "load test names to run from a file" )
|
( "load test names to run from a file" )
|
||||||
+ Opt( config.filenamesAsTags )
|
| Opt( config.filenamesAsTags )
|
||||||
["-#"]["--filenames-as-tags"]
|
["-#"]["--filenames-as-tags"]
|
||||||
( "adds a tag for the filename" )
|
( "adds a tag for the filename" )
|
||||||
+ Opt( config.sectionsToRun, "section name" )
|
| Opt( config.sectionsToRun, "section name" )
|
||||||
["-c"]["--section"]
|
["-c"]["--section"]
|
||||||
( "specify section to run" )
|
( "specify section to run" )
|
||||||
+ Opt( setVerbosity, "quiet|normal|high" )
|
| Opt( setVerbosity, "quiet|normal|high" )
|
||||||
["-v"]["--verbosity"]
|
["-v"]["--verbosity"]
|
||||||
( "set output verbosity" )
|
( "set output verbosity" )
|
||||||
+ Opt( config.listTestNamesOnly )
|
| Opt( config.listTestNamesOnly )
|
||||||
["--list-test-names-only"]
|
["--list-test-names-only"]
|
||||||
( "list all/matching test cases names only" )
|
( "list all/matching test cases names only" )
|
||||||
+ Opt( config.listReporters )
|
| Opt( config.listReporters )
|
||||||
["--list-reporters"]
|
["--list-reporters"]
|
||||||
( "list all reporters" )
|
( "list all reporters" )
|
||||||
+ Opt( setTestOrder, "decl|lex|rand" )
|
| Opt( setTestOrder, "decl|lex|rand" )
|
||||||
["--order"]
|
["--order"]
|
||||||
( "test case order (defaults to decl)" )
|
( "test case order (defaults to decl)" )
|
||||||
+ Opt( setRngSeed, "'time'|number" )
|
| Opt( setRngSeed, "'time'|number" )
|
||||||
["--rng-seed"]
|
["--rng-seed"]
|
||||||
( "set a specific seed for random numbers" )
|
( "set a specific seed for random numbers" )
|
||||||
+ Opt( setColourUsage, "yes|no" )
|
| Opt( setColourUsage, "yes|no" )
|
||||||
["--use-colour"]
|
["--use-colour"]
|
||||||
( "should output be colourised" )
|
( "should output be colourised" )
|
||||||
+ Opt( config.libIdentify )
|
| Opt( config.libIdentify )
|
||||||
["--libidentify"]
|
["--libidentify"]
|
||||||
( "report name and version according to libidentify standard" )
|
( "report name and version according to libidentify standard" )
|
||||||
+ Opt( setWaitForKeypress, "start|exit|both" )
|
| Opt( setWaitForKeypress, "start|exit|both" )
|
||||||
["--wait-for-keypress"]
|
["--wait-for-keypress"]
|
||||||
( "waits for a keypress before exiting" )
|
( "waits for a keypress before exiting" )
|
||||||
+ Opt( config.benchmarkResolutionMultiple, "multiplier" )
|
| Opt( config.benchmarkResolutionMultiple, "multiplier" )
|
||||||
["--benchmark-resolution-multiple"]
|
["--benchmark-resolution-multiple"]
|
||||||
( "multiple of clock resolution to run benchmarks" )
|
( "multiple of clock resolution to run benchmarks" )
|
||||||
|
|
||||||
+ Arg( config.testsOrTags, "test name|pattern|tags" )
|
| Arg( config.testsOrTags, "test name|pattern|tags" )
|
||||||
( "which test or tests to use" );
|
( "which test or tests to use" );
|
||||||
|
|
||||||
return cli;
|
return cli;
|
||||||
|
@@ -21,6 +21,7 @@
|
|||||||
#include "catch_context.h"
|
#include "catch_context.h"
|
||||||
#include "catch_platform.h"
|
#include "catch_platform.h"
|
||||||
#include "catch_debugger.h"
|
#include "catch_debugger.h"
|
||||||
|
#include "catch_windows_h_proxy.h"
|
||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
namespace {
|
namespace {
|
||||||
@@ -53,8 +54,6 @@ namespace Catch {
|
|||||||
|
|
||||||
#if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) /////////////////////////////////////////
|
#if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) /////////////////////////////////////////
|
||||||
|
|
||||||
#include "catch_windows_h_proxy.h"
|
|
||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
@@ -37,7 +37,7 @@ namespace Catch {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
catch( TestFailureException& ) {
|
catch( TestFailureException& ) {
|
||||||
throw;
|
std::rethrow_exception(std::current_exception());
|
||||||
}
|
}
|
||||||
catch( std::exception& ex ) {
|
catch( std::exception& ex ) {
|
||||||
return ex.what();
|
return ex.what();
|
||||||
@@ -55,7 +55,7 @@ namespace Catch {
|
|||||||
|
|
||||||
std::string ExceptionTranslatorRegistry::tryTranslators() const {
|
std::string ExceptionTranslatorRegistry::tryTranslators() const {
|
||||||
if( m_translators.empty() )
|
if( m_translators.empty() )
|
||||||
throw;
|
std::rethrow_exception(std::current_exception());
|
||||||
else
|
else
|
||||||
return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() );
|
return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() );
|
||||||
}
|
}
|
||||||
|
@@ -15,6 +15,7 @@
|
|||||||
static std::string translatorName( signature )
|
static std::string translatorName( signature )
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <exception>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -47,7 +48,7 @@ namespace Catch {
|
|||||||
std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const override {
|
std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const override {
|
||||||
try {
|
try {
|
||||||
if( it == itEnd )
|
if( it == itEnd )
|
||||||
throw;
|
std::rethrow_exception(std::current_exception());
|
||||||
else
|
else
|
||||||
return (*it)->translate( it+1, itEnd );
|
return (*it)->translate( it+1, itEnd );
|
||||||
}
|
}
|
||||||
|
@@ -12,7 +12,6 @@
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <tuple>
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@@ -241,7 +240,53 @@ namespace Catch {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// === Pair ===
|
template<typename T>
|
||||||
|
struct EnumStringMaker {
|
||||||
|
static std::string convert(const T& t) {
|
||||||
|
return ::Catch::Detail::stringify(static_cast<typename std::underlying_type<T>::type>(t));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef __OBJC__
|
||||||
|
template<>
|
||||||
|
struct StringMaker<NSString*> {
|
||||||
|
static std::string convert(NSString * nsstring) {
|
||||||
|
if (!nsstring)
|
||||||
|
return "nil";
|
||||||
|
return std::string("@") + [nsstring UTF8String];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template<>
|
||||||
|
struct StringMaker<NSObject*> {
|
||||||
|
static std::string convert(NSObject* nsObject) {
|
||||||
|
return ::Catch::Detail::stringify([nsObject description]);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
namespace Detail {
|
||||||
|
inline std::string stringify( NSString* nsstring ) {
|
||||||
|
return StringMaker<NSString*>::convert( nsstring );
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Detail
|
||||||
|
#endif // __OBJC__
|
||||||
|
|
||||||
|
} // namespace Catch
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////
|
||||||
|
// Separate std-lib types stringification, so it can be selectively enabled
|
||||||
|
// This means that we do not bring in
|
||||||
|
|
||||||
|
#if defined(CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS)
|
||||||
|
# define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER
|
||||||
|
# define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
|
||||||
|
# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Separate std::pair specialization
|
||||||
|
#if defined(CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER)
|
||||||
|
#include <utility>
|
||||||
|
namespace Catch {
|
||||||
template<typename T1, typename T2>
|
template<typename T1, typename T2>
|
||||||
struct StringMaker<std::pair<T1, T2> > {
|
struct StringMaker<std::pair<T1, T2> > {
|
||||||
static std::string convert(const std::pair<T1, T2>& pair) {
|
static std::string convert(const std::pair<T1, T2>& pair) {
|
||||||
@@ -254,9 +299,13 @@ namespace Catch {
|
|||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
#endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER
|
||||||
|
|
||||||
|
// Separate std::tuple specialization
|
||||||
|
#if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER)
|
||||||
|
#include <tuple>
|
||||||
|
namespace Catch {
|
||||||
namespace Detail {
|
namespace Detail {
|
||||||
template<
|
template<
|
||||||
typename Tuple,
|
typename Tuple,
|
||||||
@@ -292,40 +341,126 @@ namespace Catch {
|
|||||||
return os.str();
|
return os.str();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct EnumStringMaker {
|
|
||||||
static std::string convert(const T& t) {
|
|
||||||
return ::Catch::Detail::stringify(static_cast<typename std::underlying_type<T>::type>(t));
|
|
||||||
}
|
}
|
||||||
|
#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
|
||||||
|
|
||||||
|
|
||||||
|
// Separate std::chrono::duration specialization
|
||||||
|
#if defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER)
|
||||||
|
#include <ctime>
|
||||||
|
#include <ratio>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
template <class Ratio>
|
||||||
|
struct ratio_string {
|
||||||
|
static std::string symbol();
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef __OBJC__
|
template <class Ratio>
|
||||||
template<>
|
std::string ratio_string<Ratio>::symbol() {
|
||||||
struct StringMaker<NSString*> {
|
std::ostringstream oss;
|
||||||
static std::string convert(NSString * nsstring) {
|
oss << '[' << Ratio::num << '/'
|
||||||
if (!nsstring)
|
<< Ratio::den << ']';
|
||||||
return "nil";
|
return oss.str();
|
||||||
return std::string("@") + [nsstring UTF8String];
|
|
||||||
}
|
}
|
||||||
|
template <>
|
||||||
|
struct ratio_string<std::atto> {
|
||||||
|
static std::string symbol() { return "a"; }
|
||||||
};
|
};
|
||||||
template <>
|
template <>
|
||||||
struct StringMaker<NSObject*> {
|
struct ratio_string<std::femto> {
|
||||||
static std::string convert(NSObject* nsObject) {
|
static std::string symbol() { return "f"; }
|
||||||
return ::Catch::Detail::stringify([nsObject description]);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
namespace Detail {
|
template <>
|
||||||
inline std::string stringify( NSString* nsstring ) {
|
struct ratio_string<std::pico> {
|
||||||
return StringMaker<NSString*>::convert( nsstring );
|
static std::string symbol() { return "p"; }
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct ratio_string<std::nano> {
|
||||||
|
static std::string symbol() { return "n"; }
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct ratio_string<std::micro> {
|
||||||
|
static std::string symbol() { return "u"; }
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct ratio_string<std::milli> {
|
||||||
|
static std::string symbol() { return "m"; }
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace Catch {
|
||||||
|
////////////
|
||||||
|
// std::chrono::duration specializations
|
||||||
|
template<typename Value, typename Ratio>
|
||||||
|
struct StringMaker<std::chrono::duration<Value, Ratio>> {
|
||||||
|
static std::string convert(std::chrono::duration<Value, Ratio> const& duration) {
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << duration.count() << ' ' << ratio_string<Ratio>::symbol() << 's';
|
||||||
|
return oss.str();
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
template<typename Value>
|
||||||
|
struct StringMaker<std::chrono::duration<Value, std::ratio<1>>> {
|
||||||
|
static std::string convert(std::chrono::duration<Value, std::ratio<1>> const& duration) {
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << duration.count() << " s";
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template<typename Value>
|
||||||
|
struct StringMaker<std::chrono::duration<Value, std::ratio<60>>> {
|
||||||
|
static std::string convert(std::chrono::duration<Value, std::ratio<60>> const& duration) {
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << duration.count() << " m";
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template<typename Value>
|
||||||
|
struct StringMaker<std::chrono::duration<Value, std::ratio<3600>>> {
|
||||||
|
static std::string convert(std::chrono::duration<Value, std::ratio<3600>> const& duration) {
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << duration.count() << " h";
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace Detail
|
////////////
|
||||||
#endif // __OBJC__
|
// std::chrono::time_point specialization
|
||||||
|
// Generic time_point cannot be specialized, only std::chrono::time_point<system_clock>
|
||||||
|
template<typename Clock, typename Duration>
|
||||||
|
struct StringMaker<std::chrono::time_point<Clock, Duration>> {
|
||||||
|
static std::string convert(std::chrono::time_point<Clock, Duration> const& time_point) {
|
||||||
|
return ::Catch::Detail::stringify(time_point.time_since_epoch()) + " since epoch";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// std::chrono::time_point<system_clock> specialization
|
||||||
|
template<typename Duration>
|
||||||
|
struct StringMaker<std::chrono::time_point<std::chrono::system_clock, Duration>> {
|
||||||
|
static std::string convert(std::chrono::time_point<std::chrono::system_clock, Duration> const& time_point) {
|
||||||
|
auto converted = std::chrono::system_clock::to_time_t(time_point);
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
std::tm timeInfo = {};
|
||||||
|
gmtime_s(&timeInfo, &converted);
|
||||||
|
#else
|
||||||
|
std::tm* timeInfo = std::gmtime(&converted);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto const timeStampSize = sizeof("2017-01-16T17:06:45Z");
|
||||||
|
char timeStamp[timeStampSize];
|
||||||
|
const char * const fmt = "%Y-%m-%dT%H:%M:%SZ";
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
std::strftime(timeStamp, timeStampSize, fmt, &timeInfo);
|
||||||
|
#else
|
||||||
|
std::strftime(timeStamp, timeStampSize, fmt, timeInfo);
|
||||||
|
#endif
|
||||||
|
return std::string(timeStamp);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif // CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
|
||||||
|
|
||||||
} // namespace Catch
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#pragma warning(pop)
|
#pragma warning(pop)
|
||||||
|
@@ -37,7 +37,7 @@ namespace Catch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Version const& libraryVersion() {
|
Version const& libraryVersion() {
|
||||||
static Version version( 2, 0, 0, "develop", 4 );
|
static Version version( 2, 0, 0, "develop", 5 );
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -11,6 +11,7 @@
|
|||||||
#include "catch_platform.h"
|
#include "catch_platform.h"
|
||||||
|
|
||||||
#if defined(CATCH_PLATFORM_WINDOWS)
|
#if defined(CATCH_PLATFORM_WINDOWS)
|
||||||
|
|
||||||
#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX)
|
#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX)
|
||||||
# define CATCH_DEFINED_NOMINMAX
|
# define CATCH_DEFINED_NOMINMAX
|
||||||
# define NOMINMAX
|
# define NOMINMAX
|
||||||
@@ -19,7 +20,6 @@
|
|||||||
# define CATCH_DEFINED_WIN32_LEAN_AND_MEAN
|
# define CATCH_DEFINED_WIN32_LEAN_AND_MEAN
|
||||||
# define WIN32_LEAN_AND_MEAN
|
# define WIN32_LEAN_AND_MEAN
|
||||||
#endif
|
#endif
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __AFXDLL
|
#ifdef __AFXDLL
|
||||||
#include <AfxWin.h>
|
#include <AfxWin.h>
|
||||||
@@ -34,5 +34,6 @@
|
|||||||
# undef WIN32_LEAN_AND_MEAN
|
# undef WIN32_LEAN_AND_MEAN
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#endif // defined(CATCH_PLATFORM_WINDOWS)
|
||||||
|
|
||||||
#endif // TWOBLUECUBES_CATCH_WINDOWS_H_PROXY_H_INCLUDED
|
#endif // TWOBLUECUBES_CATCH_WINDOWS_H_PROXY_H_INCLUDED
|
||||||
|
@@ -28,6 +28,14 @@ namespace Catch {
|
|||||||
reporter->noMatchingTestCases( spec );
|
reporter->noMatchingTestCases( spec );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MultipleReporters::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) {
|
||||||
|
for( auto const& reporter : m_reporters )
|
||||||
|
reporter->benchmarkStarting( benchmarkInfo );
|
||||||
|
}
|
||||||
|
void MultipleReporters::benchmarkEnded( BenchmarkStats const& benchmarkStats ) {
|
||||||
|
for( auto const& reporter : m_reporters )
|
||||||
|
reporter->benchmarkEnded( benchmarkStats );
|
||||||
|
}
|
||||||
|
|
||||||
void MultipleReporters::testRunStarting( TestRunInfo const& testRunInfo ) {
|
void MultipleReporters::testRunStarting( TestRunInfo const& testRunInfo ) {
|
||||||
for( auto const& reporter : m_reporters )
|
for( auto const& reporter : m_reporters )
|
||||||
|
@@ -26,6 +26,9 @@ namespace Catch {
|
|||||||
|
|
||||||
static std::set<Verbosity> getSupportedVerbosities();
|
static std::set<Verbosity> getSupportedVerbosities();
|
||||||
|
|
||||||
|
void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) override;
|
||||||
|
void benchmarkEnded( BenchmarkStats const& benchmarkStats ) override;
|
||||||
|
|
||||||
void testRunStarting( TestRunInfo const& testRunInfo ) override;
|
void testRunStarting( TestRunInfo const& testRunInfo ) override;
|
||||||
void testGroupStarting( GroupInfo const& groupInfo ) override;
|
void testGroupStarting( GroupInfo const& groupInfo ) override;
|
||||||
void testCaseStarting( TestCaseInfo const& testInfo ) override;
|
void testCaseStarting( TestCaseInfo const& testInfo ) override;
|
||||||
|
@@ -1003,6 +1003,6 @@ with expansion:
|
|||||||
"{?}" == "1"
|
"{?}" == "1"
|
||||||
|
|
||||||
===============================================================================
|
===============================================================================
|
||||||
test cases: 176 | 125 passed | 47 failed | 4 failed as expected
|
test cases: 179 | 128 passed | 47 failed | 4 failed as expected
|
||||||
assertions: 878 | 761 passed | 96 failed | 21 failed as expected
|
assertions: 884 | 767 passed | 96 failed | 21 failed as expected
|
||||||
|
|
||||||
|
@@ -3336,7 +3336,7 @@ TrickyTests.cpp:<line number>:
|
|||||||
PASSED:
|
PASSED:
|
||||||
REQUIRE( (std::pair<int, int>( 1, 2 )) == aNicePair )
|
REQUIRE( (std::pair<int, int>( 1, 2 )) == aNicePair )
|
||||||
with expansion:
|
with expansion:
|
||||||
{ 1, 2 } == { 1, 2 }
|
{?} == {?}
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
Pointers can be compared to null
|
Pointers can be compared to null
|
||||||
@@ -4613,6 +4613,62 @@ PASSED:
|
|||||||
with expansion:
|
with expansion:
|
||||||
11 == 11
|
11 == 11
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Stringifying std::chrono::duration helpers
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
ToStringChrono.cpp:<line number>
|
||||||
|
...............................................................................
|
||||||
|
|
||||||
|
ToStringChrono.cpp:<line number>:
|
||||||
|
PASSED:
|
||||||
|
REQUIRE( minute == seconds )
|
||||||
|
with expansion:
|
||||||
|
1 m == 60 s
|
||||||
|
|
||||||
|
ToStringChrono.cpp:<line number>:
|
||||||
|
PASSED:
|
||||||
|
REQUIRE( hour != seconds )
|
||||||
|
with expansion:
|
||||||
|
1 h != 60 s
|
||||||
|
|
||||||
|
ToStringChrono.cpp:<line number>:
|
||||||
|
PASSED:
|
||||||
|
REQUIRE( micro != milli )
|
||||||
|
with expansion:
|
||||||
|
1 us != 1 ms
|
||||||
|
|
||||||
|
ToStringChrono.cpp:<line number>:
|
||||||
|
PASSED:
|
||||||
|
REQUIRE( nano != micro )
|
||||||
|
with expansion:
|
||||||
|
1 ns != 1 us
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Stringifying std::chrono::duration with weird ratios
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
ToStringChrono.cpp:<line number>
|
||||||
|
...............................................................................
|
||||||
|
|
||||||
|
ToStringChrono.cpp:<line number>:
|
||||||
|
PASSED:
|
||||||
|
REQUIRE( half_minute != femto_second )
|
||||||
|
with expansion:
|
||||||
|
1 [30/1]s != 1 fs
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Stringifying std::chrono::time_point<system_clock>
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
ToStringChrono.cpp:<line number>
|
||||||
|
...............................................................................
|
||||||
|
|
||||||
|
ToStringChrono.cpp:<line number>:
|
||||||
|
PASSED:
|
||||||
|
REQUIRE( now != later )
|
||||||
|
with expansion:
|
||||||
|
{iso8601-timestamp}
|
||||||
|
!=
|
||||||
|
{iso8601-timestamp}
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
Tabs and newlines show in output
|
Tabs and newlines show in output
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
@@ -7434,6 +7490,6 @@ MiscTests.cpp:<line number>:
|
|||||||
PASSED:
|
PASSED:
|
||||||
|
|
||||||
===============================================================================
|
===============================================================================
|
||||||
test cases: 176 | 123 passed | 49 failed | 4 failed as expected
|
test cases: 179 | 126 passed | 49 failed | 4 failed as expected
|
||||||
assertions: 877 | 757 passed | 99 failed | 21 failed as expected
|
assertions: 883 | 763 passed | 99 failed | 21 failed as expected
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<testsuitesloose text artifact
|
<testsuitesloose text artifact
|
||||||
>
|
>
|
||||||
<testsuite name="<exe-name>" errors="15" failures="85" tests="878" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
|
<testsuite name="<exe-name>" errors="15" failures="85" tests="884" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
|
||||||
<testcase classname="<exe-name>.global" name="# A test name that starts with a #" time="{duration}"/>
|
<testcase classname="<exe-name>.global" name="# A test name that starts with a #" time="{duration}"/>
|
||||||
<testcase classname="<exe-name>.global" name="#1005: Comparing pointer to int and long (NULL can be either on various systems)" time="{duration}"/>
|
<testcase classname="<exe-name>.global" name="#1005: Comparing pointer to int and long (NULL can be either on various systems)" time="{duration}"/>
|
||||||
<testcase classname="<exe-name>.global" name="#748 - captures with unexpected exceptions/outside assertions" time="{duration}">
|
<testcase classname="<exe-name>.global" name="#748 - captures with unexpected exceptions/outside assertions" time="{duration}">
|
||||||
@@ -519,6 +519,9 @@ StringRef.tests.cpp:<line number>
|
|||||||
<testcase classname="<exe-name>.global" name="StringRef/to std::string/implicitly constructed" time="{duration}"/>
|
<testcase classname="<exe-name>.global" name="StringRef/to std::string/implicitly constructed" time="{duration}"/>
|
||||||
<testcase classname="<exe-name>.global" name="StringRef/to std::string/explicitly constructed" time="{duration}"/>
|
<testcase classname="<exe-name>.global" name="StringRef/to std::string/explicitly constructed" time="{duration}"/>
|
||||||
<testcase classname="<exe-name>.global" name="StringRef/to std::string/assigned" time="{duration}"/>
|
<testcase classname="<exe-name>.global" name="StringRef/to std::string/assigned" time="{duration}"/>
|
||||||
|
<testcase classname="<exe-name>.global" name="Stringifying std::chrono::duration helpers" time="{duration}"/>
|
||||||
|
<testcase classname="<exe-name>.global" name="Stringifying std::chrono::duration with weird ratios" time="{duration}"/>
|
||||||
|
<testcase classname="<exe-name>.global" name="Stringifying std::chrono::time_point<system_clock>" time="{duration}"/>
|
||||||
<testcase classname="<exe-name>.global" name="Tabs and newlines show in output" time="{duration}">
|
<testcase classname="<exe-name>.global" name="Tabs and newlines show in output" time="{duration}">
|
||||||
<failure message=""if ($b == 10) {
|
<failure message=""if ($b == 10) {
|
||||||
$a = 20;
|
$a = 20;
|
||||||
|
@@ -3853,7 +3853,7 @@
|
|||||||
(std::pair<int, int>( 1, 2 )) == aNicePair
|
(std::pair<int, int>( 1, 2 )) == aNicePair
|
||||||
</Original>
|
</Original>
|
||||||
<Expanded>
|
<Expanded>
|
||||||
{ 1, 2 } == { 1, 2 }
|
{?} == {?}
|
||||||
</Expanded>
|
</Expanded>
|
||||||
</Expression>
|
</Expression>
|
||||||
<OverallResult success="true"/>
|
<OverallResult success="true"/>
|
||||||
@@ -5250,6 +5250,65 @@ Message from section two
|
|||||||
</Section>
|
</Section>
|
||||||
<OverallResult success="false"/>
|
<OverallResult success="false"/>
|
||||||
</TestCase>
|
</TestCase>
|
||||||
|
<TestCase name="Stringifying std::chrono::duration helpers" tags="[chrono][toString]" filename="projects/<exe-name>/ToStringChrono.cpp" >
|
||||||
|
<Expression success="true" type="REQUIRE" filename="projects/<exe-name>/ToStringChrono.cpp" >
|
||||||
|
<Original>
|
||||||
|
minute == seconds
|
||||||
|
</Original>
|
||||||
|
<Expanded>
|
||||||
|
1 m == 60 s
|
||||||
|
</Expanded>
|
||||||
|
</Expression>
|
||||||
|
<Expression success="true" type="REQUIRE" filename="projects/<exe-name>/ToStringChrono.cpp" >
|
||||||
|
<Original>
|
||||||
|
hour != seconds
|
||||||
|
</Original>
|
||||||
|
<Expanded>
|
||||||
|
1 h != 60 s
|
||||||
|
</Expanded>
|
||||||
|
</Expression>
|
||||||
|
<Expression success="true" type="REQUIRE" filename="projects/<exe-name>/ToStringChrono.cpp" >
|
||||||
|
<Original>
|
||||||
|
micro != milli
|
||||||
|
</Original>
|
||||||
|
<Expanded>
|
||||||
|
1 us != 1 ms
|
||||||
|
</Expanded>
|
||||||
|
</Expression>
|
||||||
|
<Expression success="true" type="REQUIRE" filename="projects/<exe-name>/ToStringChrono.cpp" >
|
||||||
|
<Original>
|
||||||
|
nano != micro
|
||||||
|
</Original>
|
||||||
|
<Expanded>
|
||||||
|
1 ns != 1 us
|
||||||
|
</Expanded>
|
||||||
|
</Expression>
|
||||||
|
<OverallResult success="true"/>
|
||||||
|
</TestCase>
|
||||||
|
<TestCase name="Stringifying std::chrono::duration with weird ratios" tags="[chrono][toString]" filename="projects/<exe-name>/ToStringChrono.cpp" >
|
||||||
|
<Expression success="true" type="REQUIRE" filename="projects/<exe-name>/ToStringChrono.cpp" >
|
||||||
|
<Original>
|
||||||
|
half_minute != femto_second
|
||||||
|
</Original>
|
||||||
|
<Expanded>
|
||||||
|
1 [30/1]s != 1 fs
|
||||||
|
</Expanded>
|
||||||
|
</Expression>
|
||||||
|
<OverallResult success="true"/>
|
||||||
|
</TestCase>
|
||||||
|
<TestCase name="Stringifying std::chrono::time_point<system_clock>" tags="[chrono][toString]" filename="projects/<exe-name>/ToStringChrono.cpp" >
|
||||||
|
<Expression success="true" type="REQUIRE" filename="projects/<exe-name>/ToStringChrono.cpp" >
|
||||||
|
<Original>
|
||||||
|
now != later
|
||||||
|
</Original>
|
||||||
|
<Expanded>
|
||||||
|
{iso8601-timestamp}
|
||||||
|
!=
|
||||||
|
{iso8601-timestamp}
|
||||||
|
</Expanded>
|
||||||
|
</Expression>
|
||||||
|
<OverallResult success="true"/>
|
||||||
|
</TestCase>
|
||||||
<TestCase name="Tabs and newlines show in output" tags="[.][failing][whitespace]" filename="projects/<exe-name>/MiscTests.cpp" >
|
<TestCase name="Tabs and newlines show in output" tags="[.][failing][whitespace]" filename="projects/<exe-name>/MiscTests.cpp" >
|
||||||
<Expression success="false" type="CHECK" filename="projects/<exe-name>/MiscTests.cpp" >
|
<Expression success="false" type="CHECK" filename="projects/<exe-name>/MiscTests.cpp" >
|
||||||
<Original>
|
<Original>
|
||||||
@@ -8209,7 +8268,7 @@ loose text artifact
|
|||||||
</Section>
|
</Section>
|
||||||
<OverallResult success="true"/>
|
<OverallResult success="true"/>
|
||||||
</TestCase>
|
</TestCase>
|
||||||
<OverallResults successes="757" failures="100" expectedFailures="21"/>
|
<OverallResults successes="763" failures="100" expectedFailures="21"/>
|
||||||
</Group>
|
</Group>
|
||||||
<OverallResults successes="757" failures="99" expectedFailures="21"/>
|
<OverallResults successes="763" failures="99" expectedFailures="21"/>
|
||||||
</Catch>
|
</Catch>
|
||||||
|
41
projects/SelfTest/ToStringChrono.cpp
Normal file
41
projects/SelfTest/ToStringChrono.cpp
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
|
||||||
|
#include "catch.hpp"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
TEST_CASE("Stringifying std::chrono::duration helpers", "[toString][chrono]") {
|
||||||
|
// No literals because we still support c++11
|
||||||
|
auto hour = std::chrono::hours(1);
|
||||||
|
auto minute = std::chrono::minutes(1);
|
||||||
|
auto seconds = std::chrono::seconds(60);
|
||||||
|
auto micro = std::chrono::microseconds(1);
|
||||||
|
auto milli = std::chrono::milliseconds(1);
|
||||||
|
auto nano = std::chrono::nanoseconds(1);
|
||||||
|
REQUIRE(minute == seconds);
|
||||||
|
REQUIRE(hour != seconds);
|
||||||
|
REQUIRE(micro != milli);
|
||||||
|
REQUIRE(nano != micro);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Stringifying std::chrono::duration with weird ratios", "[toString][chrono]") {
|
||||||
|
std::chrono::duration<int64_t, std::ratio<30>> half_minute(1);
|
||||||
|
std::chrono::duration<int64_t, std::ratio<1, 1000000000000000>> femto_second(1);
|
||||||
|
REQUIRE(half_minute != femto_second);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Stringifying std::chrono::time_point<system_clock>", "[toString][chrono]") {
|
||||||
|
auto now = std::chrono::system_clock::now();
|
||||||
|
auto later = now + std::chrono::minutes(2);
|
||||||
|
REQUIRE(now != later);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Stringifying std::chrono::time_point<Clock>", "[toString][chrono][!nonportable]") {
|
||||||
|
auto now = std::chrono::high_resolution_clock::now();
|
||||||
|
auto later = now + std::chrono::minutes(2);
|
||||||
|
REQUIRE(now != later);
|
||||||
|
|
||||||
|
auto now2 = std::chrono::steady_clock::now();
|
||||||
|
auto later2 = now2 + std::chrono::minutes(2);
|
||||||
|
REQUIRE(now2 != later2);
|
||||||
|
}
|
@@ -1,3 +1,4 @@
|
|||||||
|
#define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER
|
||||||
#include "catch.hpp"
|
#include "catch.hpp"
|
||||||
|
|
||||||
TEST_CASE( "std::pair<int,std::string> -> toString", "[toString][pair]" ) {
|
TEST_CASE( "std::pair<int,std::string> -> toString", "[toString][pair]" ) {
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
#define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
|
||||||
#include "catch.hpp"
|
#include "catch.hpp"
|
||||||
|
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
@@ -24,7 +24,7 @@ filelocParser = re.compile(r'''
|
|||||||
lineNumberParser = re.compile(r' line="[0-9]*"')
|
lineNumberParser = re.compile(r' line="[0-9]*"')
|
||||||
hexParser = re.compile(r'\b(0[xX][0-9a-fA-F]+)\b')
|
hexParser = re.compile(r'\b(0[xX][0-9a-fA-F]+)\b')
|
||||||
durationsParser = re.compile(r' time="[0-9]*\.[0-9]*"')
|
durationsParser = re.compile(r' time="[0-9]*\.[0-9]*"')
|
||||||
timestampsParser = re.compile(r' timestamp="\d{4}-\d{2}-\d{2}T\d{2}\:\d{2}\:\d{2}Z"')
|
timestampsParser = re.compile(r'\d{4}-\d{2}-\d{2}T\d{2}\:\d{2}\:\d{2}Z')
|
||||||
versionParser = re.compile(r'Catch v[0-9]+\.[0-9]+\.[0-9]+(-develop\.[0-9]+)?')
|
versionParser = re.compile(r'Catch v[0-9]+\.[0-9]+\.[0-9]+(-develop\.[0-9]+)?')
|
||||||
nullParser = re.compile(r'\b(__null|nullptr)\b')
|
nullParser = re.compile(r'\b(__null|nullptr)\b')
|
||||||
exeNameParser = re.compile(r'''
|
exeNameParser = re.compile(r'''
|
||||||
@@ -44,6 +44,7 @@ errnoParser = re.compile(r'''
|
|||||||
|
|
|
|
||||||
\(\*_errno\(\)\)
|
\(\*_errno\(\)\)
|
||||||
''', re.VERBOSE)
|
''', re.VERBOSE)
|
||||||
|
sinceEpochParser = re.compile(r'\d+ .+ since epoch')
|
||||||
|
|
||||||
if len(sys.argv) == 2:
|
if len(sys.argv) == 2:
|
||||||
cmdPath = sys.argv[1]
|
cmdPath = sys.argv[1]
|
||||||
@@ -97,9 +98,10 @@ def filterLine(line):
|
|||||||
|
|
||||||
# strip durations and timestamps
|
# strip durations and timestamps
|
||||||
line = durationsParser.sub(' time="{duration}"', line)
|
line = durationsParser.sub(' time="{duration}"', line)
|
||||||
line = timestampsParser.sub(' timestamp="{iso8601-timestamp}"', line)
|
line = timestampsParser.sub('{iso8601-timestamp}', line)
|
||||||
line = specialCaseParser.sub('file:\g<1>', line)
|
line = specialCaseParser.sub('file:\g<1>', line)
|
||||||
line = errnoParser.sub('errno', line)
|
line = errnoParser.sub('errno', line)
|
||||||
|
line = sinceEpochParser.sub('{since-epoch-report}', line)
|
||||||
return line
|
return line
|
||||||
|
|
||||||
|
|
||||||
|
@@ -22,27 +22,16 @@ class LineMapper:
|
|||||||
self.idMap = idMap
|
self.idMap = idMap
|
||||||
self.outerNamespace = outerNamespace
|
self.outerNamespace = outerNamespace
|
||||||
|
|
||||||
def replaceId( self, lineNo, id ):
|
|
||||||
if not self.idMap.has_key( id ):
|
|
||||||
raise ValueError( "Unrecognised macro identifier: '{0}' on line: {1}".format( id, lineNo ) )
|
|
||||||
subst = self.idMap[id]
|
|
||||||
if subst == "":
|
|
||||||
return id
|
|
||||||
else:
|
|
||||||
return subst
|
|
||||||
|
|
||||||
# TBD:
|
# TBD:
|
||||||
# #if, #ifdef, comments after #else
|
# #if, #ifdef, comments after #else
|
||||||
def mapLine( self, lineNo, line ):
|
def mapLine( self, lineNo, line ):
|
||||||
m = ifndefRe.match( line )
|
for idFrom, idTo in self.idMap.iteritems():
|
||||||
|
r = re.compile("(.*)" + idFrom + "(.*)")
|
||||||
|
|
||||||
|
m = r.match( line )
|
||||||
if m:
|
if m:
|
||||||
return "#ifndef " + self.replaceId( lineNo, m.group(1)) + "\n"
|
line = m.group(1) + idTo + m.group(2) + "\n"
|
||||||
m = defineRe.match( line )
|
|
||||||
if m:
|
|
||||||
return "#define {0}{1}{2}\n".format( self.replaceId( lineNo, m.group(1)), m.group(2), m.group(3) )
|
|
||||||
m = endifRe.match( line )
|
|
||||||
if m:
|
|
||||||
return "#endif // " + self.replaceId( lineNo, m.group(1)) + "\n"
|
|
||||||
m = nsCloseRe.match( line )
|
m = nsCloseRe.match( line )
|
||||||
if m:
|
if m:
|
||||||
originalNs = m.group(3)
|
originalNs = m.group(3)
|
||||||
@@ -61,6 +50,7 @@ class LineMapper:
|
|||||||
if self.outerNamespace.has_key(originalNs):
|
if self.outerNamespace.has_key(originalNs):
|
||||||
outerNs, innerNs = self.outerNamespace[originalNs]
|
outerNs, innerNs = self.outerNamespace[originalNs]
|
||||||
return "{0}{1} {{ namespace {2}{3}{4}\n".format( m.group(1), outerNs, innerNs, m.group(3), m.group(4) )
|
return "{0}{1} {{ namespace {2}{3}{4}\n".format( m.group(1), outerNs, innerNs, m.group(3), m.group(4) )
|
||||||
|
|
||||||
return line
|
return line
|
||||||
|
|
||||||
def mapFile(self, filenameIn, filenameOut ):
|
def mapFile(self, filenameIn, filenameOut ):
|
||||||
|
@@ -15,7 +15,7 @@ idMap = {
|
|||||||
"CLARA_CONFIG_CONSOLE_WIDTH": "CATCH_CLARA_CONFIG_CONSOLE_WIDTH",
|
"CLARA_CONFIG_CONSOLE_WIDTH": "CATCH_CLARA_CONFIG_CONSOLE_WIDTH",
|
||||||
"CLARA_TEXTFLOW_HPP_INCLUDED": "CATCH_CLARA_TEXTFLOW_HPP_INCLUDED",
|
"CLARA_TEXTFLOW_HPP_INCLUDED": "CATCH_CLARA_TEXTFLOW_HPP_INCLUDED",
|
||||||
"CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH": "CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH",
|
"CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH": "CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH",
|
||||||
"CLARA_PLATFORM_WINDOWS": ""
|
"CLARA_PLATFORM_WINDOWS": "CATCH_PLATFORM_WINDOWS"
|
||||||
}
|
}
|
||||||
|
|
||||||
# outer namespace to add
|
# outer namespace to add
|
||||||
|
446
scripts/updateDocumentToC.py
Normal file
446
scripts/updateDocumentToC.py
Normal file
@@ -0,0 +1,446 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
#
|
||||||
|
# updateDocumentToC.py
|
||||||
|
#
|
||||||
|
# Insert table of contents at top of Catch markdown documents.
|
||||||
|
#
|
||||||
|
# This script is distributed under the GNU General Public License v3.0
|
||||||
|
#
|
||||||
|
# It is based on markdown-toclify version 1.7.1 by Sebastian Raschka,
|
||||||
|
# https://github.com/rasbt/markdown-toclify
|
||||||
|
#
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
from scriptCommon import catchPath
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Configuration:
|
||||||
|
|
||||||
|
minTocEntries = 4
|
||||||
|
|
||||||
|
headingExcludeDefault = [1,3,4,5] # use level 2 headers for at default
|
||||||
|
headingExcludeRelease = [2,3,4,5] # use level 1 headers for release-notes.md
|
||||||
|
|
||||||
|
documentsDefault = os.path.join(os.path.relpath(catchPath), 'docs/*.md')
|
||||||
|
releaseNotesName = 'release-notes.md'
|
||||||
|
|
||||||
|
contentTitle = '**Contents** '
|
||||||
|
contentLineNo = 4
|
||||||
|
contentLineNdx = contentLineNo - 1
|
||||||
|
|
||||||
|
# End configuration
|
||||||
|
|
||||||
|
VALIDS = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-&'
|
||||||
|
|
||||||
|
def readLines(in_file):
|
||||||
|
"""Returns a list of lines from a input markdown file."""
|
||||||
|
|
||||||
|
with open(in_file, 'r') as inf:
|
||||||
|
in_contents = inf.read().split('\n')
|
||||||
|
return in_contents
|
||||||
|
|
||||||
|
def removeLines(lines, remove=('[[back to top]', '<a class="mk-toclify"')):
|
||||||
|
"""Removes existing [back to top] links and <a id> tags."""
|
||||||
|
|
||||||
|
if not remove:
|
||||||
|
return lines[:]
|
||||||
|
|
||||||
|
out = []
|
||||||
|
for l in lines:
|
||||||
|
if l.startswith(remove):
|
||||||
|
continue
|
||||||
|
out.append(l)
|
||||||
|
return out
|
||||||
|
|
||||||
|
def removeToC(lines):
|
||||||
|
"""Removes existing table of contents starting at index contentLineNdx."""
|
||||||
|
if not lines[contentLineNdx ].startswith(contentTitle):
|
||||||
|
return lines[:]
|
||||||
|
|
||||||
|
result_top = lines[:contentLineNdx]
|
||||||
|
|
||||||
|
pos = contentLineNdx + 1
|
||||||
|
while lines[pos].startswith('['):
|
||||||
|
pos = pos + 1
|
||||||
|
|
||||||
|
result_bottom = lines[pos + 1:]
|
||||||
|
|
||||||
|
return result_top + result_bottom
|
||||||
|
|
||||||
|
def dashifyHeadline(line):
|
||||||
|
"""
|
||||||
|
Takes a header line from a Markdown document and
|
||||||
|
returns a tuple of the
|
||||||
|
'#'-stripped version of the head line,
|
||||||
|
a string version for <a id=''></a> anchor tags,
|
||||||
|
and the level of the headline as integer.
|
||||||
|
E.g.,
|
||||||
|
>>> dashifyHeadline('### some header lvl3')
|
||||||
|
('Some header lvl3', 'some-header-lvl3', 3)
|
||||||
|
|
||||||
|
"""
|
||||||
|
stripped_right = line.rstrip('#')
|
||||||
|
stripped_both = stripped_right.lstrip('#')
|
||||||
|
level = len(stripped_right) - len(stripped_both)
|
||||||
|
stripped_wspace = stripped_both.strip()
|
||||||
|
|
||||||
|
# character replacements
|
||||||
|
replaced_colon = stripped_wspace.replace('.', '')
|
||||||
|
replaced_slash = replaced_colon.replace('/', '')
|
||||||
|
rem_nonvalids = ''.join([c if c in VALIDS
|
||||||
|
else '-' for c in replaced_slash])
|
||||||
|
|
||||||
|
lowered = rem_nonvalids.lower()
|
||||||
|
dashified = re.sub(r'(-)\1+', r'\1', lowered) # remove duplicate dashes
|
||||||
|
dashified = dashified.strip('-') # strip dashes from start and end
|
||||||
|
|
||||||
|
# exception '&' (double-dash in github)
|
||||||
|
dashified = dashified.replace('-&-', '--')
|
||||||
|
|
||||||
|
return [stripped_wspace, dashified, level]
|
||||||
|
|
||||||
|
def tagAndCollect(lines, id_tag=True, back_links=False, exclude_h=None):
|
||||||
|
"""
|
||||||
|
Gets headlines from the markdown document and creates anchor tags.
|
||||||
|
|
||||||
|
Keyword arguments:
|
||||||
|
lines: a list of sublists where every sublist
|
||||||
|
represents a line from a Markdown document.
|
||||||
|
id_tag: if true, creates inserts a the <a id> tags (not req. by GitHub)
|
||||||
|
back_links: if true, adds "back to top" links below each headline
|
||||||
|
exclude_h: header levels to exclude. E.g., [2, 3]
|
||||||
|
excludes level 2 and 3 headings.
|
||||||
|
|
||||||
|
Returns a tuple of 2 lists:
|
||||||
|
1st list:
|
||||||
|
A modified version of the input list where
|
||||||
|
<a id="some-header"></a> anchor tags where inserted
|
||||||
|
above the header lines (if github is False).
|
||||||
|
|
||||||
|
2nd list:
|
||||||
|
A list of 3-value sublists, where the first value
|
||||||
|
represents the heading, the second value the string
|
||||||
|
that was inserted assigned to the IDs in the anchor tags,
|
||||||
|
and the third value is an integer that reprents the headline level.
|
||||||
|
E.g.,
|
||||||
|
[['some header lvl3', 'some-header-lvl3', 3], ...]
|
||||||
|
|
||||||
|
"""
|
||||||
|
out_contents = []
|
||||||
|
headlines = []
|
||||||
|
for l in lines:
|
||||||
|
saw_headline = False
|
||||||
|
|
||||||
|
orig_len = len(l)
|
||||||
|
l_stripped = l.lstrip()
|
||||||
|
|
||||||
|
if l_stripped.startswith(('# ', '## ', '### ', '#### ', '##### ', '###### ')):
|
||||||
|
|
||||||
|
# comply with new markdown standards
|
||||||
|
|
||||||
|
# not a headline if '#' not followed by whitespace '##no-header':
|
||||||
|
if not l.lstrip('#').startswith(' '):
|
||||||
|
continue
|
||||||
|
# not a headline if more than 6 '#':
|
||||||
|
if len(l) - len(l.lstrip('#')) > 6:
|
||||||
|
continue
|
||||||
|
# headers can be indented by at most 3 spaces:
|
||||||
|
if orig_len - len(l_stripped) > 3:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# ignore empty headers
|
||||||
|
if not set(l) - {'#', ' '}:
|
||||||
|
continue
|
||||||
|
|
||||||
|
saw_headline = True
|
||||||
|
dashified = dashifyHeadline(l)
|
||||||
|
|
||||||
|
if not exclude_h or not dashified[-1] in exclude_h:
|
||||||
|
if id_tag:
|
||||||
|
id_tag = '<a class="mk-toclify" id="%s"></a>'\
|
||||||
|
% (dashified[1])
|
||||||
|
out_contents.append(id_tag)
|
||||||
|
headlines.append(dashified)
|
||||||
|
|
||||||
|
out_contents.append(l)
|
||||||
|
if back_links and saw_headline:
|
||||||
|
out_contents.append('[[back to top](#table-of-contents)]')
|
||||||
|
return out_contents, headlines
|
||||||
|
|
||||||
|
def positioningHeadlines(headlines):
|
||||||
|
"""
|
||||||
|
Strips unnecessary whitespaces/tabs if first header is not left-aligned
|
||||||
|
"""
|
||||||
|
left_just = False
|
||||||
|
for row in headlines:
|
||||||
|
if row[-1] == 1:
|
||||||
|
left_just = True
|
||||||
|
break
|
||||||
|
if not left_just:
|
||||||
|
for row in headlines:
|
||||||
|
row[-1] -= 1
|
||||||
|
return headlines
|
||||||
|
|
||||||
|
def createToc(headlines, hyperlink=True, top_link=False, no_toc_header=False):
|
||||||
|
"""
|
||||||
|
Creates the table of contents from the headline list
|
||||||
|
that was returned by the tagAndCollect function.
|
||||||
|
|
||||||
|
Keyword Arguments:
|
||||||
|
headlines: list of lists
|
||||||
|
e.g., ['Some header lvl3', 'some-header-lvl3', 3]
|
||||||
|
hyperlink: Creates hyperlinks in Markdown format if True,
|
||||||
|
e.g., '- [Some header lvl1](#some-header-lvl1)'
|
||||||
|
top_link: if True, add a id tag for linking the table
|
||||||
|
of contents itself (for the back-to-top-links)
|
||||||
|
no_toc_header: suppresses TOC header if True.
|
||||||
|
|
||||||
|
Returns a list of headlines for a table of contents
|
||||||
|
in Markdown format,
|
||||||
|
e.g., [' - [Some header lvl3](#some-header-lvl3)', ...]
|
||||||
|
|
||||||
|
"""
|
||||||
|
processed = []
|
||||||
|
if not no_toc_header:
|
||||||
|
if top_link:
|
||||||
|
processed.append('<a class="mk-toclify" id="table-of-contents"></a>\n')
|
||||||
|
processed.append(contentTitle)
|
||||||
|
|
||||||
|
for line in headlines:
|
||||||
|
if hyperlink:
|
||||||
|
item = '[%s](#%s) ' % (line[0], line[1])
|
||||||
|
else:
|
||||||
|
item = '%s- %s' % ((line[2]-1)*' ', line[0])
|
||||||
|
processed.append(item)
|
||||||
|
processed.append('\n')
|
||||||
|
return processed
|
||||||
|
|
||||||
|
def buildMarkdown(toc_headlines, body, spacer=0, placeholder=None):
|
||||||
|
"""
|
||||||
|
Returns a string with the Markdown output contents incl.
|
||||||
|
the table of contents.
|
||||||
|
|
||||||
|
Keyword arguments:
|
||||||
|
toc_headlines: lines for the table of contents
|
||||||
|
as created by the createToc function.
|
||||||
|
body: contents of the Markdown file including
|
||||||
|
ID-anchor tags as returned by the
|
||||||
|
tagAndCollect function.
|
||||||
|
spacer: Adds vertical space after the table
|
||||||
|
of contents. Height in pixels.
|
||||||
|
placeholder: If a placeholder string is provided, the placeholder
|
||||||
|
will be replaced by the TOC instead of inserting the TOC at
|
||||||
|
the top of the document
|
||||||
|
|
||||||
|
"""
|
||||||
|
if spacer:
|
||||||
|
spacer_line = ['\n<div style="height:%spx;"></div>\n' % (spacer)]
|
||||||
|
toc_markdown = "\n".join(toc_headlines + spacer_line)
|
||||||
|
else:
|
||||||
|
toc_markdown = "\n".join(toc_headlines)
|
||||||
|
|
||||||
|
if placeholder:
|
||||||
|
body_markdown = "\n".join(body)
|
||||||
|
markdown = body_markdown.replace(placeholder, toc_markdown)
|
||||||
|
else:
|
||||||
|
body_markdown_p1 = "\n".join(body[:contentLineNdx ]) + '\n'
|
||||||
|
body_markdown_p2 = "\n".join(body[ contentLineNdx:])
|
||||||
|
markdown = body_markdown_p1 + toc_markdown + body_markdown_p2
|
||||||
|
|
||||||
|
return markdown
|
||||||
|
|
||||||
|
def outputMarkdown(markdown_cont, output_file):
|
||||||
|
"""
|
||||||
|
Writes to an output file if `outfile` is a valid path.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if output_file:
|
||||||
|
with open(output_file, 'w') as out:
|
||||||
|
out.write(markdown_cont)
|
||||||
|
|
||||||
|
def markdownToclify(
|
||||||
|
input_file,
|
||||||
|
output_file=None,
|
||||||
|
min_toc_len=2,
|
||||||
|
github=False,
|
||||||
|
back_to_top=False,
|
||||||
|
nolink=False,
|
||||||
|
no_toc_header=False,
|
||||||
|
spacer=0,
|
||||||
|
placeholder=None,
|
||||||
|
exclude_h=None):
|
||||||
|
""" Function to add table of contents to markdown files.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
-----------
|
||||||
|
input_file: str
|
||||||
|
Path to the markdown input file.
|
||||||
|
|
||||||
|
output_file: str (defaul: None)
|
||||||
|
Path to the markdown output file.
|
||||||
|
|
||||||
|
min_toc_len: int (default: 2)
|
||||||
|
Miniumum number of entries to create a table of contents for.
|
||||||
|
|
||||||
|
github: bool (default: False)
|
||||||
|
Uses GitHub TOC syntax if True.
|
||||||
|
|
||||||
|
back_to_top: bool (default: False)
|
||||||
|
Inserts back-to-top links below headings if True.
|
||||||
|
|
||||||
|
nolink: bool (default: False)
|
||||||
|
Creates the table of contents without internal links if True.
|
||||||
|
|
||||||
|
no_toc_header: bool (default: False)
|
||||||
|
Suppresses the Table of Contents header if True
|
||||||
|
|
||||||
|
spacer: int (default: 0)
|
||||||
|
Inserts horizontal space (in pixels) after the table of contents.
|
||||||
|
|
||||||
|
placeholder: str (default: None)
|
||||||
|
Inserts the TOC at the placeholder string instead
|
||||||
|
of inserting the TOC at the top of the document.
|
||||||
|
|
||||||
|
exclude_h: list (default None)
|
||||||
|
Excludes header levels, e.g., if [2, 3], ignores header
|
||||||
|
levels 2 and 3 in the TOC.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-----------
|
||||||
|
changed: Boolean
|
||||||
|
True if the file has been updated, False otherwise.
|
||||||
|
|
||||||
|
"""
|
||||||
|
cleaned_contents = removeLines(
|
||||||
|
removeToC(readLines(input_file)),
|
||||||
|
remove=('[[back to top]', '<a class="mk-toclify"'))
|
||||||
|
|
||||||
|
processed_contents, raw_headlines = tagAndCollect(
|
||||||
|
cleaned_contents,
|
||||||
|
id_tag=not github,
|
||||||
|
back_links=back_to_top,
|
||||||
|
exclude_h=exclude_h)
|
||||||
|
|
||||||
|
# add table of contents?
|
||||||
|
if len(raw_headlines) < min_toc_len:
|
||||||
|
processed_headlines = []
|
||||||
|
else:
|
||||||
|
leftjustified_headlines = positioningHeadlines(raw_headlines)
|
||||||
|
|
||||||
|
processed_headlines = createToc(
|
||||||
|
leftjustified_headlines,
|
||||||
|
hyperlink=not nolink,
|
||||||
|
top_link=not nolink and not github,
|
||||||
|
no_toc_header=no_toc_header)
|
||||||
|
|
||||||
|
if nolink:
|
||||||
|
processed_contents = cleaned_contents
|
||||||
|
|
||||||
|
cont = buildMarkdown(
|
||||||
|
toc_headlines=processed_headlines,
|
||||||
|
body=processed_contents,
|
||||||
|
spacer=spacer,
|
||||||
|
placeholder=placeholder)
|
||||||
|
|
||||||
|
if output_file:
|
||||||
|
outputMarkdown(cont, output_file)
|
||||||
|
|
||||||
|
def isReleaseNotes(f):
|
||||||
|
return os.path.basename(f) == releaseNotesName
|
||||||
|
|
||||||
|
def excludeHeadingsFor(f):
|
||||||
|
return headingExcludeRelease if isReleaseNotes(f) else headingExcludeDefault
|
||||||
|
|
||||||
|
def updateSingleDocumentToC(input_file, min_toc_len, verbose=False):
|
||||||
|
"""Add or update table of contents in specified file. Return 1 if file changed, 0 otherwise."""
|
||||||
|
if verbose :
|
||||||
|
print( 'file: {}'.format(input_file))
|
||||||
|
|
||||||
|
output_file = input_file + '.tmp'
|
||||||
|
|
||||||
|
markdownToclify(
|
||||||
|
input_file=input_file,
|
||||||
|
output_file=output_file,
|
||||||
|
min_toc_len=min_toc_len,
|
||||||
|
github=True,
|
||||||
|
back_to_top=False,
|
||||||
|
nolink=False,
|
||||||
|
no_toc_header=False,
|
||||||
|
spacer=False,
|
||||||
|
placeholder=False,
|
||||||
|
exclude_h=excludeHeadingsFor(input_file))
|
||||||
|
|
||||||
|
# prevent race-condition (Python 3.3):
|
||||||
|
if sys.version_info >= (3, 3):
|
||||||
|
os.replace(output_file, input_file)
|
||||||
|
else:
|
||||||
|
os.remove(input_file)
|
||||||
|
os.rename(output_file, input_file)
|
||||||
|
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def updateDocumentToC(paths, min_toc_len, verbose):
|
||||||
|
"""Add or update table of contents to specified paths. Return number of changed files"""
|
||||||
|
n = 0
|
||||||
|
for g in paths:
|
||||||
|
for f in glob.glob(g):
|
||||||
|
if os.path.isfile(f):
|
||||||
|
n = n + updateSingleDocumentToC(input_file=f, min_toc_len=min_toc_len, verbose=verbose)
|
||||||
|
return n
|
||||||
|
|
||||||
|
def updateDocumentToCMain():
|
||||||
|
"""Add or update table of contents to specified paths."""
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description='Add or update table of contents in markdown documents.',
|
||||||
|
epilog="""""",
|
||||||
|
formatter_class=argparse.RawTextHelpFormatter)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'Input',
|
||||||
|
metavar='file',
|
||||||
|
type=str,
|
||||||
|
nargs=argparse.REMAINDER,
|
||||||
|
help='files to process, at default: docs/*.md')
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'-v', '--verbose',
|
||||||
|
action='store_true',
|
||||||
|
help='report the name of the file being processed')
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--min-toc-entries',
|
||||||
|
dest='minTocEntries',
|
||||||
|
default=minTocEntries,
|
||||||
|
type=int,
|
||||||
|
metavar='N',
|
||||||
|
help='the minimum number of entries to create a table of contents for [{deflt}]'.format(deflt=minTocEntries))
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--remove-toc',
|
||||||
|
action='store_const',
|
||||||
|
dest='minTocEntries',
|
||||||
|
const=99,
|
||||||
|
help='remove all tables of contents')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
paths = args.Input if len(args.Input) > 0 else [documentsDefault]
|
||||||
|
|
||||||
|
changedFiles = updateDocumentToC(paths=paths, min_toc_len=args.minTocEntries, verbose=args.verbose)
|
||||||
|
|
||||||
|
if changedFiles > 0:
|
||||||
|
print( "Processed table of contents in " + str(changedFiles) + " file(s)" )
|
||||||
|
else:
|
||||||
|
print( "No table of contents added or updated" )
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
updateDocumentToCMain()
|
||||||
|
|
||||||
|
# end of file
|
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Catch v2.0.0-develop.4
|
* Catch v2.0.0-develop.5
|
||||||
* Generated: 2017-09-19 17:37:34.480115
|
* Generated: 2017-10-12 13:05:08.135067
|
||||||
* ----------------------------------------------------------
|
* ----------------------------------------------------------
|
||||||
* 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) 2017 Two Blue Cubes Ltd. All rights reserved.
|
* Copyright (c) 2017 Two Blue Cubes Ltd. All rights reserved.
|
||||||
@@ -480,7 +480,6 @@ struct AutoReg : NonCopyable {
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <tuple>
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@@ -749,7 +748,53 @@ namespace Catch {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// === Pair ===
|
template<typename T>
|
||||||
|
struct EnumStringMaker {
|
||||||
|
static std::string convert(const T& t) {
|
||||||
|
return ::Catch::Detail::stringify(static_cast<typename std::underlying_type<T>::type>(t));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef __OBJC__
|
||||||
|
template<>
|
||||||
|
struct StringMaker<NSString*> {
|
||||||
|
static std::string convert(NSString * nsstring) {
|
||||||
|
if (!nsstring)
|
||||||
|
return "nil";
|
||||||
|
return std::string("@") + [nsstring UTF8String];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template<>
|
||||||
|
struct StringMaker<NSObject*> {
|
||||||
|
static std::string convert(NSObject* nsObject) {
|
||||||
|
return ::Catch::Detail::stringify([nsObject description]);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
namespace Detail {
|
||||||
|
inline std::string stringify( NSString* nsstring ) {
|
||||||
|
return StringMaker<NSString*>::convert( nsstring );
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Detail
|
||||||
|
#endif // __OBJC__
|
||||||
|
|
||||||
|
} // namespace Catch
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////
|
||||||
|
// Separate std-lib types stringification, so it can be selectively enabled
|
||||||
|
// This means that we do not bring in
|
||||||
|
|
||||||
|
#if defined(CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS)
|
||||||
|
# define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER
|
||||||
|
# define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
|
||||||
|
# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Separate std::pair specialization
|
||||||
|
#if defined(CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER)
|
||||||
|
#include <utility>
|
||||||
|
namespace Catch {
|
||||||
template<typename T1, typename T2>
|
template<typename T1, typename T2>
|
||||||
struct StringMaker<std::pair<T1, T2> > {
|
struct StringMaker<std::pair<T1, T2> > {
|
||||||
static std::string convert(const std::pair<T1, T2>& pair) {
|
static std::string convert(const std::pair<T1, T2>& pair) {
|
||||||
@@ -762,7 +807,13 @@ namespace Catch {
|
|||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
#endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER
|
||||||
|
|
||||||
|
// Separate std::tuple specialization
|
||||||
|
#if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER)
|
||||||
|
#include <tuple>
|
||||||
|
namespace Catch {
|
||||||
namespace Detail {
|
namespace Detail {
|
||||||
template<
|
template<
|
||||||
typename Tuple,
|
typename Tuple,
|
||||||
@@ -797,39 +848,124 @@ namespace Catch {
|
|||||||
return os.str();
|
return os.str();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct EnumStringMaker {
|
|
||||||
static std::string convert(const T& t) {
|
|
||||||
return ::Catch::Detail::stringify(static_cast<typename std::underlying_type<T>::type>(t));
|
|
||||||
}
|
}
|
||||||
|
#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
|
||||||
|
|
||||||
|
// Separate std::chrono::duration specialization
|
||||||
|
#if defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER)
|
||||||
|
#include <ctime>
|
||||||
|
#include <ratio>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
template <class Ratio>
|
||||||
|
struct ratio_string {
|
||||||
|
static std::string symbol();
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef __OBJC__
|
template <class Ratio>
|
||||||
template<>
|
std::string ratio_string<Ratio>::symbol() {
|
||||||
struct StringMaker<NSString*> {
|
std::ostringstream oss;
|
||||||
static std::string convert(NSString * nsstring) {
|
oss << '[' << Ratio::num << '/'
|
||||||
if (!nsstring)
|
<< Ratio::den << ']';
|
||||||
return "nil";
|
return oss.str();
|
||||||
return std::string("@") + [nsstring UTF8String];
|
|
||||||
}
|
}
|
||||||
|
template <>
|
||||||
|
struct ratio_string<std::atto> {
|
||||||
|
static std::string symbol() { return "a"; }
|
||||||
};
|
};
|
||||||
template <>
|
template <>
|
||||||
struct StringMaker<NSObject*> {
|
struct ratio_string<std::femto> {
|
||||||
static std::string convert(NSObject* nsObject) {
|
static std::string symbol() { return "f"; }
|
||||||
return ::Catch::Detail::stringify([nsObject description]);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
namespace Detail {
|
template <>
|
||||||
inline std::string stringify( NSString* nsstring ) {
|
struct ratio_string<std::pico> {
|
||||||
return StringMaker<NSString*>::convert( nsstring );
|
static std::string symbol() { return "p"; }
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct ratio_string<std::nano> {
|
||||||
|
static std::string symbol() { return "n"; }
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct ratio_string<std::micro> {
|
||||||
|
static std::string symbol() { return "u"; }
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct ratio_string<std::milli> {
|
||||||
|
static std::string symbol() { return "m"; }
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace Catch {
|
||||||
|
////////////
|
||||||
|
// std::chrono::duration specializations
|
||||||
|
template<typename Value, typename Ratio>
|
||||||
|
struct StringMaker<std::chrono::duration<Value, Ratio>> {
|
||||||
|
static std::string convert(std::chrono::duration<Value, Ratio> const& duration) {
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << duration.count() << ' ' << ratio_string<Ratio>::symbol() << 's';
|
||||||
|
return oss.str();
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
template<typename Value>
|
||||||
|
struct StringMaker<std::chrono::duration<Value, std::ratio<1>>> {
|
||||||
|
static std::string convert(std::chrono::duration<Value, std::ratio<1>> const& duration) {
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << duration.count() << " s";
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template<typename Value>
|
||||||
|
struct StringMaker<std::chrono::duration<Value, std::ratio<60>>> {
|
||||||
|
static std::string convert(std::chrono::duration<Value, std::ratio<60>> const& duration) {
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << duration.count() << " m";
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template<typename Value>
|
||||||
|
struct StringMaker<std::chrono::duration<Value, std::ratio<3600>>> {
|
||||||
|
static std::string convert(std::chrono::duration<Value, std::ratio<3600>> const& duration) {
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << duration.count() << " h";
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace Detail
|
////////////
|
||||||
#endif // __OBJC__
|
// std::chrono::time_point specialization
|
||||||
|
// Generic time_point cannot be specialized, only std::chrono::time_point<system_clock>
|
||||||
|
template<typename Clock, typename Duration>
|
||||||
|
struct StringMaker<std::chrono::time_point<Clock, Duration>> {
|
||||||
|
static std::string convert(std::chrono::time_point<Clock, Duration> const& time_point) {
|
||||||
|
return ::Catch::Detail::stringify(time_point.time_since_epoch()) + " since epoch";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// std::chrono::time_point<system_clock> specialization
|
||||||
|
template<typename Duration>
|
||||||
|
struct StringMaker<std::chrono::time_point<std::chrono::system_clock, Duration>> {
|
||||||
|
static std::string convert(std::chrono::time_point<std::chrono::system_clock, Duration> const& time_point) {
|
||||||
|
auto converted = std::chrono::system_clock::to_time_t(time_point);
|
||||||
|
|
||||||
} // namespace Catch
|
#ifdef _MSC_VER
|
||||||
|
std::tm timeInfo = {};
|
||||||
|
gmtime_s(&timeInfo, &converted);
|
||||||
|
#else
|
||||||
|
std::tm* timeInfo = std::gmtime(&converted);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto const timeStampSize = sizeof("2017-01-16T17:06:45Z");
|
||||||
|
char timeStamp[timeStampSize];
|
||||||
|
const char * const fmt = "%Y-%m-%dT%H:%M:%SZ";
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
std::strftime(timeStamp, timeStampSize, fmt, &timeInfo);
|
||||||
|
#else
|
||||||
|
std::strftime(timeStamp, timeStampSize, fmt, timeInfo);
|
||||||
|
#endif
|
||||||
|
return std::string(timeStamp);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif // CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#pragma warning(pop)
|
#pragma warning(pop)
|
||||||
@@ -1614,6 +1750,7 @@ namespace Catch {
|
|||||||
static std::string translatorName( signature )
|
static std::string translatorName( signature )
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <exception>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -1646,7 +1783,7 @@ namespace Catch {
|
|||||||
std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const override {
|
std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const override {
|
||||||
try {
|
try {
|
||||||
if( it == itEnd )
|
if( it == itEnd )
|
||||||
throw;
|
std::rethrow_exception(std::current_exception());
|
||||||
else
|
else
|
||||||
return (*it)->translate( it+1, itEnd );
|
return (*it)->translate( it+1, itEnd );
|
||||||
}
|
}
|
||||||
@@ -4205,6 +4342,10 @@ namespace Catch {
|
|||||||
#define CATCH_CLARA_CONFIG_CONSOLE_WIDTH 80
|
#define CATCH_CLARA_CONFIG_CONSOLE_WIDTH 80
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH
|
||||||
|
#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CLARA_CONFIG_CONSOLE_WIDTH
|
||||||
|
#endif
|
||||||
|
|
||||||
// ----------- #included from clara_textflow.hpp -----------
|
// ----------- #included from clara_textflow.hpp -----------
|
||||||
|
|
||||||
// TextFlowCpp
|
// TextFlowCpp
|
||||||
@@ -4538,9 +4679,8 @@ namespace Catch { namespace clara { namespace TextFlow {
|
|||||||
#include <set>
|
#include <set>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#if !defined(CLARA_PLATFORM_WINDOWS) && ( defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) )
|
#if !defined(CATCH_PLATFORM_WINDOWS) && ( defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) )
|
||||||
#define CLARA_PLATFORM_WINDOWS
|
#define CATCH_PLATFORM_WINDOWS
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace Catch { namespace clara {
|
namespace Catch { namespace clara {
|
||||||
@@ -4736,25 +4876,19 @@ namespace detail {
|
|||||||
template<typename U>
|
template<typename U>
|
||||||
explicit BasicResult( BasicResult<U> const &other )
|
explicit BasicResult( BasicResult<U> const &other )
|
||||||
: ResultValueBase<T>( other.type() ),
|
: ResultValueBase<T>( other.type() ),
|
||||||
m_errorMessage(other.errorMessage()) {
|
m_errorMessage( other.errorMessage() )
|
||||||
|
{
|
||||||
assert( type() != ResultBase::Ok );
|
assert( type() != ResultBase::Ok );
|
||||||
}
|
}
|
||||||
|
|
||||||
static auto ok() -> BasicResult { return {ResultBase::Ok}; }
|
|
||||||
|
|
||||||
template<typename U>
|
template<typename U>
|
||||||
static auto ok( U const &value ) -> BasicResult { return { ResultBase::Ok, value }; }
|
static auto ok( U const &value ) -> BasicResult { return { ResultBase::Ok, value }; }
|
||||||
|
static auto ok() -> BasicResult { return { ResultBase::Ok }; }
|
||||||
static auto logicError( std::string const &message ) -> BasicResult { return { ResultBase::LogicError, message }; }
|
static auto logicError( std::string const &message ) -> BasicResult { return { ResultBase::LogicError, message }; }
|
||||||
|
static auto runtimeError( std::string const &message ) -> BasicResult { return { ResultBase::RuntimeError, message }; }
|
||||||
static auto runtimeError(std::string const &message) -> BasicResult {
|
|
||||||
return {ResultBase::RuntimeError, message};
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit operator bool() const { return m_type == ResultBase::Ok; }
|
explicit operator bool() const { return m_type == ResultBase::Ok; }
|
||||||
|
|
||||||
auto type() const -> ResultBase::Type { return m_type; }
|
auto type() const -> ResultBase::Type { return m_type; }
|
||||||
|
|
||||||
auto errorMessage() const -> std::string { return m_errorMessage; }
|
auto errorMessage() const -> std::string { return m_errorMessage; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -4774,7 +4908,8 @@ namespace detail {
|
|||||||
|
|
||||||
BasicResult( ResultBase::Type type, std::string const &message )
|
BasicResult( ResultBase::Type type, std::string const &message )
|
||||||
: ResultValueBase<T>(type),
|
: ResultValueBase<T>(type),
|
||||||
m_errorMessage(message) {
|
m_errorMessage(message)
|
||||||
|
{
|
||||||
assert( m_type != ResultBase::Ok );
|
assert( m_type != ResultBase::Ok );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4791,10 +4926,10 @@ namespace detail {
|
|||||||
|
|
||||||
ParseState( ParseResultType type, TokenStream const &remainingTokens )
|
ParseState( ParseResultType type, TokenStream const &remainingTokens )
|
||||||
: m_type(type),
|
: m_type(type),
|
||||||
m_remainingTokens(remainingTokens) {}
|
m_remainingTokens( remainingTokens )
|
||||||
|
{}
|
||||||
|
|
||||||
auto type() const -> ParseResultType { return m_type; }
|
auto type() const -> ParseResultType { return m_type; }
|
||||||
|
|
||||||
auto remainingTokens() const -> TokenStream { return m_remainingTokens; }
|
auto remainingTokens() const -> TokenStream { return m_remainingTokens; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -4839,23 +4974,16 @@ namespace detail {
|
|||||||
|
|
||||||
struct BoundRefBase {
|
struct BoundRefBase {
|
||||||
BoundRefBase() = default;
|
BoundRefBase() = default;
|
||||||
|
|
||||||
BoundRefBase( BoundRefBase const & ) = delete;
|
BoundRefBase( BoundRefBase const & ) = delete;
|
||||||
|
|
||||||
BoundRefBase( BoundRefBase && ) = delete;
|
BoundRefBase( BoundRefBase && ) = delete;
|
||||||
|
|
||||||
BoundRefBase &operator=( BoundRefBase const & ) = delete;
|
BoundRefBase &operator=( BoundRefBase const & ) = delete;
|
||||||
|
|
||||||
BoundRefBase &operator=( BoundRefBase && ) = delete;
|
BoundRefBase &operator=( BoundRefBase && ) = delete;
|
||||||
|
|
||||||
virtual ~BoundRefBase() = default;
|
virtual ~BoundRefBase() = default;
|
||||||
|
|
||||||
virtual auto isFlag() const -> bool = 0;
|
virtual auto isFlag() const -> bool = 0;
|
||||||
|
|
||||||
virtual auto isContainer() const -> bool { return false; }
|
virtual auto isContainer() const -> bool { return false; }
|
||||||
|
|
||||||
virtual auto setValue( std::string const &arg ) -> ParserResult = 0;
|
virtual auto setValue( std::string const &arg ) -> ParserResult = 0;
|
||||||
|
|
||||||
virtual auto setFlag( bool flag ) -> ParserResult = 0;
|
virtual auto setFlag( bool flag ) -> ParserResult = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -4972,9 +5100,7 @@ namespace detail {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class Optionality {
|
enum class Optionality { Optional, Required };
|
||||||
Optional, Required
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Parser;
|
struct Parser;
|
||||||
|
|
||||||
@@ -4994,7 +5120,7 @@ namespace detail {
|
|||||||
class ComposableParserImpl : public ParserBase {
|
class ComposableParserImpl : public ParserBase {
|
||||||
public:
|
public:
|
||||||
template<typename T>
|
template<typename T>
|
||||||
auto operator+(T const &other) const -> Parser;
|
auto operator|( T const &other ) const -> Parser;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Common code and state for Args and Opts
|
// Common code and state for Args and Opts
|
||||||
@@ -5010,11 +5136,16 @@ namespace detail {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
template<typename T>
|
template<typename T>
|
||||||
ParserRefImpl(T &ref, std::string const &hint) : m_ref(std::make_shared<BoundRef<T>>(ref)), m_hint(hint) {}
|
ParserRefImpl( T &ref, std::string const &hint )
|
||||||
|
: m_ref( std::make_shared<BoundRef<T>>( ref ) ),
|
||||||
|
m_hint( hint )
|
||||||
|
{}
|
||||||
|
|
||||||
template<typename LambdaT>
|
template<typename LambdaT>
|
||||||
ParserRefImpl(LambdaT const &ref, std::string const &hint) : m_ref(std::make_shared<BoundLambda<LambdaT>>(ref)),
|
ParserRefImpl( LambdaT const &ref, std::string const &hint )
|
||||||
m_hint(hint) {}
|
: m_ref( std::make_shared<BoundLambda<LambdaT>>( ref ) ),
|
||||||
|
m_hint(hint)
|
||||||
|
{}
|
||||||
|
|
||||||
auto operator()( std::string const &description ) -> DerivedT & {
|
auto operator()( std::string const &description ) -> DerivedT & {
|
||||||
m_description = description;
|
m_description = description;
|
||||||
@@ -5153,7 +5284,7 @@ namespace detail {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto isMatch( std::string const &optToken ) const -> bool {
|
auto isMatch( std::string const &optToken ) const -> bool {
|
||||||
#ifdef CLARA_PLATFORM_WINDOWS
|
#ifdef CATCH_PLATFORM_WINDOWS
|
||||||
auto normalisedToken = normaliseOpt( optToken );
|
auto normalisedToken = normaliseOpt( optToken );
|
||||||
#else
|
#else
|
||||||
auto const &normalisedToken = optToken;
|
auto const &normalisedToken = optToken;
|
||||||
@@ -5234,30 +5365,30 @@ namespace detail {
|
|||||||
std::vector<Opt> m_options;
|
std::vector<Opt> m_options;
|
||||||
std::vector<Arg> m_args;
|
std::vector<Arg> m_args;
|
||||||
|
|
||||||
auto operator+=(ExeName const &exeName) -> Parser & {
|
auto operator|=( ExeName const &exeName ) -> Parser & {
|
||||||
m_exeName = exeName;
|
m_exeName = exeName;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto operator+=(Arg const &arg) -> Parser & {
|
auto operator|=( Arg const &arg ) -> Parser & {
|
||||||
m_args.push_back(arg);
|
m_args.push_back(arg);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto operator+=(Opt const &opt) -> Parser & {
|
auto operator|=( Opt const &opt ) -> Parser & {
|
||||||
m_options.push_back(opt);
|
m_options.push_back(opt);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto operator+=(Parser const &other) -> Parser & {
|
auto operator|=( Parser const &other ) -> Parser & {
|
||||||
m_options.insert(m_options.end(), other.m_options.begin(), other.m_options.end());
|
m_options.insert(m_options.end(), other.m_options.begin(), other.m_options.end());
|
||||||
m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end());
|
m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end());
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
auto operator+(T const &other) const -> Parser {
|
auto operator|( T const &other ) const -> Parser {
|
||||||
return Parser(*this) += other;
|
return Parser( *this ) |= other;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto getHelpColumns() const -> std::vector<HelpColumns> {
|
auto getHelpColumns() const -> std::vector<HelpColumns> {
|
||||||
@@ -5330,46 +5461,46 @@ namespace detail {
|
|||||||
using ParserBase::parse;
|
using ParserBase::parse;
|
||||||
|
|
||||||
auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override {
|
auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override {
|
||||||
std::vector<ParserBase const *> allParsers;
|
|
||||||
allParsers.reserve(m_args.size() + m_options.size());
|
|
||||||
std::set<ParserBase const *> requiredParsers;
|
|
||||||
|
|
||||||
for (auto const &opt : m_options) {
|
struct ParserInfo {
|
||||||
allParsers.push_back(&opt);
|
ParserBase const* parser = nullptr;
|
||||||
if (!opt.isOptional())
|
size_t count = 0;
|
||||||
requiredParsers.insert(&opt);
|
};
|
||||||
}
|
const size_t totalParsers = m_options.size() + m_args.size();
|
||||||
|
assert( totalParsers < 512 );
|
||||||
|
// ParserInfo parseInfos[totalParsers]; // <-- this is what we really want to do
|
||||||
|
ParserInfo parseInfos[512];
|
||||||
|
|
||||||
size_t optionalArgs = 0;
|
{
|
||||||
for (auto const &arg : m_args) {
|
size_t i = 0;
|
||||||
allParsers.push_back(&arg);
|
for (auto const &opt : m_options) parseInfos[i++].parser = &opt;
|
||||||
if (!arg.isOptional()) {
|
for (auto const &arg : m_args) parseInfos[i++].parser = &arg;
|
||||||
if (optionalArgs > 0)
|
|
||||||
return InternalParseResult::logicError(
|
|
||||||
"Required arguments must preceed any optional arguments");
|
|
||||||
else
|
|
||||||
++optionalArgs;
|
|
||||||
requiredParsers.insert(&arg);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_exeName.set( exeName );
|
m_exeName.set( exeName );
|
||||||
|
|
||||||
auto result = InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) );
|
auto result = InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) );
|
||||||
while( result.value().remainingTokens() ) {
|
while( result.value().remainingTokens() ) {
|
||||||
auto remainingTokenCount = result.value().remainingTokens().count();
|
bool tokenParsed = false;
|
||||||
for (auto parser : allParsers) {
|
|
||||||
result = parser->parse( exeName, result.value().remainingTokens() );
|
for( size_t i = 0; i < totalParsers; ++i ) {
|
||||||
if (!result || result.value().type() != ParseResultType::NoMatch) {
|
auto& parseInfo = parseInfos[i];
|
||||||
if (parser->cardinality() == 1)
|
if( parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality() ) {
|
||||||
allParsers.erase(std::remove(allParsers.begin(), allParsers.end(), parser),
|
result = parseInfo.parser->parse(exeName, result.value().remainingTokens());
|
||||||
allParsers.end());
|
if (!result)
|
||||||
requiredParsers.erase(parser);
|
return result;
|
||||||
|
if (result.value().type() != ParseResultType::NoMatch) {
|
||||||
|
tokenParsed = true;
|
||||||
|
++parseInfo.count;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!result || remainingTokenCount == result.value().remainingTokens().count())
|
}
|
||||||
|
|
||||||
|
if( result.value().type() == ParseResultType::ShortCircuitAll )
|
||||||
return result;
|
return result;
|
||||||
|
if( !tokenParsed )
|
||||||
|
return InternalParseResult::runtimeError( "Unrecognised token: " + result.value().remainingTokens()->token );
|
||||||
}
|
}
|
||||||
// !TBD Check missing required options
|
// !TBD Check missing required options
|
||||||
return result;
|
return result;
|
||||||
@@ -5378,8 +5509,8 @@ namespace detail {
|
|||||||
|
|
||||||
template<typename DerivedT>
|
template<typename DerivedT>
|
||||||
template<typename T>
|
template<typename T>
|
||||||
auto ComposableParserImpl<DerivedT>::operator+(T const &other) const -> Parser {
|
auto ComposableParserImpl<DerivedT>::operator|( T const &other ) const -> Parser {
|
||||||
return Parser() + static_cast<DerivedT const &>( *this ) + other;
|
return Parser() | static_cast<DerivedT const &>( *this ) | other;
|
||||||
}
|
}
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
@@ -5516,84 +5647,84 @@ namespace Catch {
|
|||||||
|
|
||||||
auto cli
|
auto cli
|
||||||
= ExeName( config.processName )
|
= ExeName( config.processName )
|
||||||
+ Help( config.showHelp )
|
| Help( config.showHelp )
|
||||||
+ Opt( config.listTests )
|
| Opt( config.listTests )
|
||||||
["-l"]["--list-tests"]
|
["-l"]["--list-tests"]
|
||||||
( "list all/matching test cases" )
|
( "list all/matching test cases" )
|
||||||
+ Opt( config.listTags )
|
| Opt( config.listTags )
|
||||||
["-t"]["--list-tags"]
|
["-t"]["--list-tags"]
|
||||||
( "list all/matching tags" )
|
( "list all/matching tags" )
|
||||||
+ Opt( config.showSuccessfulTests )
|
| Opt( config.showSuccessfulTests )
|
||||||
["-s"]["--success"]
|
["-s"]["--success"]
|
||||||
( "include successful tests in output" )
|
( "include successful tests in output" )
|
||||||
+ Opt( config.shouldDebugBreak )
|
| Opt( config.shouldDebugBreak )
|
||||||
["-b"]["--break"]
|
["-b"]["--break"]
|
||||||
( "break into debugger on failure" )
|
( "break into debugger on failure" )
|
||||||
+ Opt( config.noThrow )
|
| Opt( config.noThrow )
|
||||||
["-e"]["--nothrow"]
|
["-e"]["--nothrow"]
|
||||||
( "skip exception tests" )
|
( "skip exception tests" )
|
||||||
+ Opt( config.showInvisibles )
|
| Opt( config.showInvisibles )
|
||||||
["-i"]["--invisibles"]
|
["-i"]["--invisibles"]
|
||||||
( "show invisibles (tabs, newlines)" )
|
( "show invisibles (tabs, newlines)" )
|
||||||
+ Opt( config.outputFilename, "filename" )
|
| Opt( config.outputFilename, "filename" )
|
||||||
["-o"]["--out"]
|
["-o"]["--out"]
|
||||||
( "output filename" )
|
( "output filename" )
|
||||||
+ Opt( config.reporterNames, "name" )
|
| Opt( config.reporterNames, "name" )
|
||||||
["-r"]["--reporter"]
|
["-r"]["--reporter"]
|
||||||
( "reporter to use (defaults to console)" )
|
( "reporter to use (defaults to console)" )
|
||||||
+ Opt( config.name, "name" )
|
| Opt( config.name, "name" )
|
||||||
["-n"]["--name"]
|
["-n"]["--name"]
|
||||||
( "suite name" )
|
( "suite name" )
|
||||||
+ Opt( [&]( bool ){ config.abortAfter = 1; } )
|
| Opt( [&]( bool ){ config.abortAfter = 1; } )
|
||||||
["-a"]["--abort"]
|
["-a"]["--abort"]
|
||||||
( "abort at first failure" )
|
( "abort at first failure" )
|
||||||
+ Opt( [&]( int x ){ config.abortAfter = x; }, "no. failures" )
|
| Opt( [&]( int x ){ config.abortAfter = x; }, "no. failures" )
|
||||||
["-x"]["--abortx"]
|
["-x"]["--abortx"]
|
||||||
( "abort after x failures" )
|
( "abort after x failures" )
|
||||||
+ Opt( setWarning, "warning name" )
|
| Opt( setWarning, "warning name" )
|
||||||
["-w"]["--warn"]
|
["-w"]["--warn"]
|
||||||
( "enable warnings" )
|
( "enable warnings" )
|
||||||
+ Opt( [&]( bool flag ) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, "yes|no" )
|
| Opt( [&]( bool flag ) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, "yes|no" )
|
||||||
["-d"]["--durations"]
|
["-d"]["--durations"]
|
||||||
( "show test durations" )
|
( "show test durations" )
|
||||||
+ Opt( loadTestNamesFromFile, "filename" )
|
| Opt( loadTestNamesFromFile, "filename" )
|
||||||
["-f"]["--input-file"]
|
["-f"]["--input-file"]
|
||||||
( "load test names to run from a file" )
|
( "load test names to run from a file" )
|
||||||
+ Opt( config.filenamesAsTags )
|
| Opt( config.filenamesAsTags )
|
||||||
["-#"]["--filenames-as-tags"]
|
["-#"]["--filenames-as-tags"]
|
||||||
( "adds a tag for the filename" )
|
( "adds a tag for the filename" )
|
||||||
+ Opt( config.sectionsToRun, "section name" )
|
| Opt( config.sectionsToRun, "section name" )
|
||||||
["-c"]["--section"]
|
["-c"]["--section"]
|
||||||
( "specify section to run" )
|
( "specify section to run" )
|
||||||
+ Opt( setVerbosity, "quiet|normal|high" )
|
| Opt( setVerbosity, "quiet|normal|high" )
|
||||||
["-v"]["--verbosity"]
|
["-v"]["--verbosity"]
|
||||||
( "set output verbosity" )
|
( "set output verbosity" )
|
||||||
+ Opt( config.listTestNamesOnly )
|
| Opt( config.listTestNamesOnly )
|
||||||
["--list-test-names-only"]
|
["--list-test-names-only"]
|
||||||
( "list all/matching test cases names only" )
|
( "list all/matching test cases names only" )
|
||||||
+ Opt( config.listReporters )
|
| Opt( config.listReporters )
|
||||||
["--list-reporters"]
|
["--list-reporters"]
|
||||||
( "list all reporters" )
|
( "list all reporters" )
|
||||||
+ Opt( setTestOrder, "decl|lex|rand" )
|
| Opt( setTestOrder, "decl|lex|rand" )
|
||||||
["--order"]
|
["--order"]
|
||||||
( "test case order (defaults to decl)" )
|
( "test case order (defaults to decl)" )
|
||||||
+ Opt( setRngSeed, "'time'|number" )
|
| Opt( setRngSeed, "'time'|number" )
|
||||||
["--rng-seed"]
|
["--rng-seed"]
|
||||||
( "set a specific seed for random numbers" )
|
( "set a specific seed for random numbers" )
|
||||||
+ Opt( setColourUsage, "yes|no" )
|
| Opt( setColourUsage, "yes|no" )
|
||||||
["--use-colour"]
|
["--use-colour"]
|
||||||
( "should output be colourised" )
|
( "should output be colourised" )
|
||||||
+ Opt( config.libIdentify )
|
| Opt( config.libIdentify )
|
||||||
["--libidentify"]
|
["--libidentify"]
|
||||||
( "report name and version according to libidentify standard" )
|
( "report name and version according to libidentify standard" )
|
||||||
+ Opt( setWaitForKeypress, "start|exit|both" )
|
| Opt( setWaitForKeypress, "start|exit|both" )
|
||||||
["--wait-for-keypress"]
|
["--wait-for-keypress"]
|
||||||
( "waits for a keypress before exiting" )
|
( "waits for a keypress before exiting" )
|
||||||
+ Opt( config.benchmarkResolutionMultiple, "multiplier" )
|
| Opt( config.benchmarkResolutionMultiple, "multiplier" )
|
||||||
["--benchmark-resolution-multiple"]
|
["--benchmark-resolution-multiple"]
|
||||||
( "multiple of clock resolution to run benchmarks" )
|
( "multiple of clock resolution to run benchmarks" )
|
||||||
|
|
||||||
+ Arg( config.testsOrTags, "test name|pattern|tags" )
|
| Arg( config.testsOrTags, "test name|pattern|tags" )
|
||||||
( "which test or tests to use" );
|
( "which test or tests to use" );
|
||||||
|
|
||||||
return cli;
|
return cli;
|
||||||
@@ -5731,6 +5862,36 @@ namespace Catch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// end catch_errno_guard.h
|
// end catch_errno_guard.h
|
||||||
|
// start catch_windows_h_proxy.h
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(CATCH_PLATFORM_WINDOWS)
|
||||||
|
|
||||||
|
#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX)
|
||||||
|
# define CATCH_DEFINED_NOMINMAX
|
||||||
|
# define NOMINMAX
|
||||||
|
#endif
|
||||||
|
#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN)
|
||||||
|
# define CATCH_DEFINED_WIN32_LEAN_AND_MEAN
|
||||||
|
# define WIN32_LEAN_AND_MEAN
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __AFXDLL
|
||||||
|
#include <AfxWin.h>
|
||||||
|
#else
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CATCH_DEFINED_NOMINMAX
|
||||||
|
# undef NOMINMAX
|
||||||
|
#endif
|
||||||
|
#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN
|
||||||
|
# undef WIN32_LEAN_AND_MEAN
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // defined(CATCH_PLATFORM_WINDOWS)
|
||||||
|
|
||||||
|
// end catch_windows_h_proxy.h
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@@ -5761,34 +5922,6 @@ namespace Catch {
|
|||||||
|
|
||||||
#if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) /////////////////////////////////////////
|
#if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) /////////////////////////////////////////
|
||||||
|
|
||||||
// start catch_windows_h_proxy.h
|
|
||||||
|
|
||||||
|
|
||||||
#if defined(CATCH_PLATFORM_WINDOWS)
|
|
||||||
# if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX)
|
|
||||||
# define CATCH_DEFINED_NOMINMAX
|
|
||||||
# define NOMINMAX
|
|
||||||
# endif
|
|
||||||
# if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN)
|
|
||||||
# define CATCH_DEFINED_WIN32_LEAN_AND_MEAN
|
|
||||||
# define WIN32_LEAN_AND_MEAN
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __AFXDLL
|
|
||||||
#include <AfxWin.h>
|
|
||||||
#else
|
|
||||||
#include <windows.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CATCH_DEFINED_NOMINMAX
|
|
||||||
# undef NOMINMAX
|
|
||||||
#endif
|
|
||||||
#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN
|
|
||||||
# undef WIN32_LEAN_AND_MEAN
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// end catch_windows_h_proxy.h
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@@ -6220,7 +6353,7 @@ namespace Catch {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
catch( TestFailureException& ) {
|
catch( TestFailureException& ) {
|
||||||
throw;
|
std::rethrow_exception(std::current_exception());
|
||||||
}
|
}
|
||||||
catch( std::exception& ex ) {
|
catch( std::exception& ex ) {
|
||||||
return ex.what();
|
return ex.what();
|
||||||
@@ -6238,7 +6371,7 @@ namespace Catch {
|
|||||||
|
|
||||||
std::string ExceptionTranslatorRegistry::tryTranslators() const {
|
std::string ExceptionTranslatorRegistry::tryTranslators() const {
|
||||||
if( m_translators.empty() )
|
if( m_translators.empty() )
|
||||||
throw;
|
std::rethrow_exception(std::current_exception());
|
||||||
else
|
else
|
||||||
return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() );
|
return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() );
|
||||||
}
|
}
|
||||||
@@ -6523,6 +6656,9 @@ namespace Catch {
|
|||||||
|
|
||||||
static std::set<Verbosity> getSupportedVerbosities();
|
static std::set<Verbosity> getSupportedVerbosities();
|
||||||
|
|
||||||
|
void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) override;
|
||||||
|
void benchmarkEnded( BenchmarkStats const& benchmarkStats ) override;
|
||||||
|
|
||||||
void testRunStarting( TestRunInfo const& testRunInfo ) override;
|
void testRunStarting( TestRunInfo const& testRunInfo ) override;
|
||||||
void testGroupStarting( GroupInfo const& groupInfo ) override;
|
void testGroupStarting( GroupInfo const& groupInfo ) override;
|
||||||
void testCaseStarting( TestCaseInfo const& testInfo ) override;
|
void testCaseStarting( TestCaseInfo const& testInfo ) override;
|
||||||
@@ -9649,7 +9785,7 @@ namespace Catch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Version const& libraryVersion() {
|
Version const& libraryVersion() {
|
||||||
static Version version( 2, 0, 0, "develop", 4 );
|
static Version version( 2, 0, 0, "develop", 5 );
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11204,6 +11340,15 @@ namespace Catch {
|
|||||||
reporter->noMatchingTestCases( spec );
|
reporter->noMatchingTestCases( spec );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MultipleReporters::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) {
|
||||||
|
for( auto const& reporter : m_reporters )
|
||||||
|
reporter->benchmarkStarting( benchmarkInfo );
|
||||||
|
}
|
||||||
|
void MultipleReporters::benchmarkEnded( BenchmarkStats const& benchmarkStats ) {
|
||||||
|
for( auto const& reporter : m_reporters )
|
||||||
|
reporter->benchmarkEnded( benchmarkStats );
|
||||||
|
}
|
||||||
|
|
||||||
void MultipleReporters::testRunStarting( TestRunInfo const& testRunInfo ) {
|
void MultipleReporters::testRunStarting( TestRunInfo const& testRunInfo ) {
|
||||||
for( auto const& reporter : m_reporters )
|
for( auto const& reporter : m_reporters )
|
||||||
reporter->testRunStarting( testRunInfo );
|
reporter->testRunStarting( testRunInfo );
|
||||||
|
@@ -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.0.0-develop.4@%s/%s" % (username, channel)
|
requires = "Catch/2.0.0-develop.5@%s/%s" % (username, channel)
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
cmake = CMake(self)
|
cmake = CMake(self)
|
||||||
|
120
third_party/clara.hpp
vendored
120
third_party/clara.hpp
vendored
@@ -8,6 +8,9 @@
|
|||||||
#define CLARA_CONFIG_CONSOLE_WIDTH 80
|
#define CLARA_CONFIG_CONSOLE_WIDTH 80
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH
|
||||||
|
#define CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH
|
||||||
|
#endif
|
||||||
|
|
||||||
// ----------- #included from clara_textflow.hpp -----------
|
// ----------- #included from clara_textflow.hpp -----------
|
||||||
|
|
||||||
@@ -133,7 +136,7 @@ namespace clara { namespace TextFlow {
|
|||||||
|
|
||||||
auto operator *() const -> std::string {
|
auto operator *() const -> std::string {
|
||||||
assert( m_stringIndex < m_column.m_strings.size() );
|
assert( m_stringIndex < m_column.m_strings.size() );
|
||||||
assert( m_pos < m_end );
|
assert( m_pos <= m_end );
|
||||||
if( m_pos + m_column.m_width < m_end )
|
if( m_pos + m_column.m_width < m_end )
|
||||||
return addIndentAndSuffix(line().substr(m_pos, m_len));
|
return addIndentAndSuffix(line().substr(m_pos, m_len));
|
||||||
else
|
else
|
||||||
@@ -546,25 +549,19 @@ namespace detail {
|
|||||||
template<typename U>
|
template<typename U>
|
||||||
explicit BasicResult( BasicResult<U> const &other )
|
explicit BasicResult( BasicResult<U> const &other )
|
||||||
: ResultValueBase<T>( other.type() ),
|
: ResultValueBase<T>( other.type() ),
|
||||||
m_errorMessage(other.errorMessage()) {
|
m_errorMessage( other.errorMessage() )
|
||||||
|
{
|
||||||
assert( type() != ResultBase::Ok );
|
assert( type() != ResultBase::Ok );
|
||||||
}
|
}
|
||||||
|
|
||||||
static auto ok() -> BasicResult { return {ResultBase::Ok}; }
|
|
||||||
|
|
||||||
template<typename U>
|
template<typename U>
|
||||||
static auto ok( U const &value ) -> BasicResult { return { ResultBase::Ok, value }; }
|
static auto ok( U const &value ) -> BasicResult { return { ResultBase::Ok, value }; }
|
||||||
|
static auto ok() -> BasicResult { return { ResultBase::Ok }; }
|
||||||
static auto logicError( std::string const &message ) -> BasicResult { return { ResultBase::LogicError, message }; }
|
static auto logicError( std::string const &message ) -> BasicResult { return { ResultBase::LogicError, message }; }
|
||||||
|
static auto runtimeError( std::string const &message ) -> BasicResult { return { ResultBase::RuntimeError, message }; }
|
||||||
static auto runtimeError(std::string const &message) -> BasicResult {
|
|
||||||
return {ResultBase::RuntimeError, message};
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit operator bool() const { return m_type == ResultBase::Ok; }
|
explicit operator bool() const { return m_type == ResultBase::Ok; }
|
||||||
|
|
||||||
auto type() const -> ResultBase::Type { return m_type; }
|
auto type() const -> ResultBase::Type { return m_type; }
|
||||||
|
|
||||||
auto errorMessage() const -> std::string { return m_errorMessage; }
|
auto errorMessage() const -> std::string { return m_errorMessage; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -584,7 +581,8 @@ namespace detail {
|
|||||||
|
|
||||||
BasicResult( ResultBase::Type type, std::string const &message )
|
BasicResult( ResultBase::Type type, std::string const &message )
|
||||||
: ResultValueBase<T>(type),
|
: ResultValueBase<T>(type),
|
||||||
m_errorMessage(message) {
|
m_errorMessage(message)
|
||||||
|
{
|
||||||
assert( m_type != ResultBase::Ok );
|
assert( m_type != ResultBase::Ok );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -601,10 +599,10 @@ namespace detail {
|
|||||||
|
|
||||||
ParseState( ParseResultType type, TokenStream const &remainingTokens )
|
ParseState( ParseResultType type, TokenStream const &remainingTokens )
|
||||||
: m_type(type),
|
: m_type(type),
|
||||||
m_remainingTokens(remainingTokens) {}
|
m_remainingTokens( remainingTokens )
|
||||||
|
{}
|
||||||
|
|
||||||
auto type() const -> ParseResultType { return m_type; }
|
auto type() const -> ParseResultType { return m_type; }
|
||||||
|
|
||||||
auto remainingTokens() const -> TokenStream { return m_remainingTokens; }
|
auto remainingTokens() const -> TokenStream { return m_remainingTokens; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -649,23 +647,16 @@ namespace detail {
|
|||||||
|
|
||||||
struct BoundRefBase {
|
struct BoundRefBase {
|
||||||
BoundRefBase() = default;
|
BoundRefBase() = default;
|
||||||
|
|
||||||
BoundRefBase( BoundRefBase const & ) = delete;
|
BoundRefBase( BoundRefBase const & ) = delete;
|
||||||
|
|
||||||
BoundRefBase( BoundRefBase && ) = delete;
|
BoundRefBase( BoundRefBase && ) = delete;
|
||||||
|
|
||||||
BoundRefBase &operator=( BoundRefBase const & ) = delete;
|
BoundRefBase &operator=( BoundRefBase const & ) = delete;
|
||||||
|
|
||||||
BoundRefBase &operator=( BoundRefBase && ) = delete;
|
BoundRefBase &operator=( BoundRefBase && ) = delete;
|
||||||
|
|
||||||
virtual ~BoundRefBase() = default;
|
virtual ~BoundRefBase() = default;
|
||||||
|
|
||||||
virtual auto isFlag() const -> bool = 0;
|
virtual auto isFlag() const -> bool = 0;
|
||||||
|
|
||||||
virtual auto isContainer() const -> bool { return false; }
|
virtual auto isContainer() const -> bool { return false; }
|
||||||
|
|
||||||
virtual auto setValue( std::string const &arg ) -> ParserResult = 0;
|
virtual auto setValue( std::string const &arg ) -> ParserResult = 0;
|
||||||
|
|
||||||
virtual auto setFlag( bool flag ) -> ParserResult = 0;
|
virtual auto setFlag( bool flag ) -> ParserResult = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -783,9 +774,7 @@ namespace detail {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class Optionality {
|
enum class Optionality { Optional, Required };
|
||||||
Optional, Required
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Parser;
|
struct Parser;
|
||||||
|
|
||||||
@@ -805,7 +794,7 @@ namespace detail {
|
|||||||
class ComposableParserImpl : public ParserBase {
|
class ComposableParserImpl : public ParserBase {
|
||||||
public:
|
public:
|
||||||
template<typename T>
|
template<typename T>
|
||||||
auto operator+(T const &other) const -> Parser;
|
auto operator|( T const &other ) const -> Parser;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Common code and state for Args and Opts
|
// Common code and state for Args and Opts
|
||||||
@@ -821,11 +810,16 @@ namespace detail {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
template<typename T>
|
template<typename T>
|
||||||
ParserRefImpl(T &ref, std::string const &hint) : m_ref(std::make_shared<BoundRef<T>>(ref)), m_hint(hint) {}
|
ParserRefImpl( T &ref, std::string const &hint )
|
||||||
|
: m_ref( std::make_shared<BoundRef<T>>( ref ) ),
|
||||||
|
m_hint( hint )
|
||||||
|
{}
|
||||||
|
|
||||||
template<typename LambdaT>
|
template<typename LambdaT>
|
||||||
ParserRefImpl(LambdaT const &ref, std::string const &hint) : m_ref(std::make_shared<BoundLambda<LambdaT>>(ref)),
|
ParserRefImpl( LambdaT const &ref, std::string const &hint )
|
||||||
m_hint(hint) {}
|
: m_ref( std::make_shared<BoundLambda<LambdaT>>( ref ) ),
|
||||||
|
m_hint(hint)
|
||||||
|
{}
|
||||||
|
|
||||||
auto operator()( std::string const &description ) -> DerivedT & {
|
auto operator()( std::string const &description ) -> DerivedT & {
|
||||||
m_description = description;
|
m_description = description;
|
||||||
@@ -1046,30 +1040,30 @@ namespace detail {
|
|||||||
std::vector<Opt> m_options;
|
std::vector<Opt> m_options;
|
||||||
std::vector<Arg> m_args;
|
std::vector<Arg> m_args;
|
||||||
|
|
||||||
auto operator+=(ExeName const &exeName) -> Parser & {
|
auto operator|=( ExeName const &exeName ) -> Parser & {
|
||||||
m_exeName = exeName;
|
m_exeName = exeName;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto operator+=(Arg const &arg) -> Parser & {
|
auto operator|=( Arg const &arg ) -> Parser & {
|
||||||
m_args.push_back(arg);
|
m_args.push_back(arg);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto operator+=(Opt const &opt) -> Parser & {
|
auto operator|=( Opt const &opt ) -> Parser & {
|
||||||
m_options.push_back(opt);
|
m_options.push_back(opt);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto operator+=(Parser const &other) -> Parser & {
|
auto operator|=( Parser const &other ) -> Parser & {
|
||||||
m_options.insert(m_options.end(), other.m_options.begin(), other.m_options.end());
|
m_options.insert(m_options.end(), other.m_options.begin(), other.m_options.end());
|
||||||
m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end());
|
m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end());
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
auto operator+(T const &other) const -> Parser {
|
auto operator|( T const &other ) const -> Parser {
|
||||||
return Parser(*this) += other;
|
return Parser( *this ) |= other;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto getHelpColumns() const -> std::vector<HelpColumns> {
|
auto getHelpColumns() const -> std::vector<HelpColumns> {
|
||||||
@@ -1142,46 +1136,40 @@ namespace detail {
|
|||||||
using ParserBase::parse;
|
using ParserBase::parse;
|
||||||
|
|
||||||
auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override {
|
auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override {
|
||||||
std::vector<ParserBase const *> allParsers;
|
|
||||||
allParsers.reserve(m_args.size() + m_options.size());
|
|
||||||
std::set<ParserBase const *> requiredParsers;
|
|
||||||
|
|
||||||
for (auto const &opt : m_options) {
|
struct ParserInfo {
|
||||||
allParsers.push_back(&opt);
|
ParserBase const* parser = nullptr;
|
||||||
if (!opt.isOptional())
|
size_t count = 0;
|
||||||
requiredParsers.insert(&opt);
|
};
|
||||||
}
|
const size_t totalParsers = m_options.size() + m_args.size();
|
||||||
|
ParserInfo parseInfos[totalParsers];
|
||||||
size_t optionalArgs = 0;
|
size_t i = 0;
|
||||||
for (auto const &arg : m_args) {
|
for( auto const& opt : m_options ) parseInfos[i++].parser = &opt;
|
||||||
allParsers.push_back(&arg);
|
for( auto const& arg : m_args ) parseInfos[i++].parser = &arg;
|
||||||
if (!arg.isOptional()) {
|
|
||||||
if (optionalArgs > 0)
|
|
||||||
return InternalParseResult::logicError(
|
|
||||||
"Required arguments must preceed any optional arguments");
|
|
||||||
else
|
|
||||||
++optionalArgs;
|
|
||||||
requiredParsers.insert(&arg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_exeName.set( exeName );
|
m_exeName.set( exeName );
|
||||||
|
|
||||||
auto result = InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) );
|
auto result = InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) );
|
||||||
while( result.value().remainingTokens() ) {
|
while( result.value().remainingTokens() ) {
|
||||||
auto remainingTokenCount = result.value().remainingTokens().count();
|
bool tokenParsed = false;
|
||||||
for (auto parser : allParsers) {
|
|
||||||
result = parser->parse( exeName, result.value().remainingTokens() );
|
for( auto& parseInfo : parseInfos ) {
|
||||||
if (!result || result.value().type() != ParseResultType::NoMatch) {
|
if( parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality() ) {
|
||||||
if (parser->cardinality() == 1)
|
result = parseInfo.parser->parse(exeName, result.value().remainingTokens());
|
||||||
allParsers.erase(std::remove(allParsers.begin(), allParsers.end(), parser),
|
if (!result)
|
||||||
allParsers.end());
|
return result;
|
||||||
requiredParsers.erase(parser);
|
if (result.value().type() != ParseResultType::NoMatch) {
|
||||||
|
tokenParsed = true;
|
||||||
|
++parseInfo.count;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!result || remainingTokenCount == result.value().remainingTokens().count())
|
}
|
||||||
|
|
||||||
|
if( result.value().type() == ParseResultType::ShortCircuitAll )
|
||||||
return result;
|
return result;
|
||||||
|
if( !tokenParsed )
|
||||||
|
return InternalParseResult::runtimeError( "Unrecognised token: " + result.value().remainingTokens()->token );
|
||||||
}
|
}
|
||||||
// !TBD Check missing required options
|
// !TBD Check missing required options
|
||||||
return result;
|
return result;
|
||||||
@@ -1190,8 +1178,8 @@ namespace detail {
|
|||||||
|
|
||||||
template<typename DerivedT>
|
template<typename DerivedT>
|
||||||
template<typename T>
|
template<typename T>
|
||||||
auto ComposableParserImpl<DerivedT>::operator+(T const &other) const -> Parser {
|
auto ComposableParserImpl<DerivedT>::operator|( T const &other ) const -> Parser {
|
||||||
return Parser() + static_cast<DerivedT const &>( *this ) + other;
|
return Parser() | static_cast<DerivedT const &>( *this ) | other;
|
||||||
}
|
}
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user