diff --git a/.travis.yml b/.travis.yml index ac13003..21b95ed 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,12 @@ -language: generic -sudo: false -dist: trusty +language: cpp + +os: + - linux + - osx + +compiler: + - gcc + - clang addons: apt: @@ -21,4 +27,4 @@ script: notifications: email: on_success: never - on_failure: always + on_failure: never diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b271bb..27c0aee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,12 @@ if(OPENMP_FOUND) endif() # set compiler flags, see http://stackoverflow.com/questions/7724569/debug-vs-release-in-cmake -set(CMAKE_CXX_FLAGS "-fopenmp -Ofast -fno-signed-zeros -fno-trapping-math -frename-registers -Wall -Wno-format-extra-args -Wextra -Wformat-nonliteral -Wformat-security -Wformat=2") +if(OPENMP_FOUND) + set(CMAKE_CXX_FLAGS "-fopenmp -Ofast -fno-signed-zeros -fno-trapping-math -Wall -Wno-format-extra-args -Wextra -Wformat-nonliteral -Wformat-security -Wformat=2 -Wfatal-errors -Wextra -Wno-implicit-fallthrough -pedantic") +else() + message(WARNING "Configuring without OpenMP!") + set(CMAKE_CXX_FLAGS "-Ofast -fno-signed-zeros -fno-trapping-math -Wall -Wno-format-extra-args -Wextra -Wformat-nonliteral -Wformat-security -Wformat=2 -Wfatal-errors -Wextra -Wno-implicit-fallthrough -pedantic") +endif() set(CMAKE_CXX_FLAGS_DEBUG "-Og -g -DLOGLEVEL=3") set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS} -DLOGLEVEL=2") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} -DLOGLEVEL=2") diff --git a/src/cppgtfs b/src/cppgtfs index 3a462c3..eae77cf 160000 --- a/src/cppgtfs +++ b/src/cppgtfs @@ -1 +1 @@ -Subproject commit 3a462c37358da19f10e89f77fb7a277d69c6c4dc +Subproject commit eae77cfdfb7eb64f87ddd48204ea9b90d6d5cfd8 diff --git a/src/pfaedle/eval/Collector.h b/src/pfaedle/eval/Collector.h index 9fe7def..f08fc82 100644 --- a/src/pfaedle/eval/Collector.h +++ b/src/pfaedle/eval/Collector.h @@ -29,7 +29,6 @@ class Collector { public: Collector(const std::string& evalOutPath, const std::vector& dfBins) : _noOrigShp(0), - _noMatchShp(0), _fdSum(0), _unmatchedSegSum(0), _unmatchedSegLengthSum(0), @@ -70,7 +69,6 @@ class Collector { std::map > > _dACache; size_t _noOrigShp; - size_t _noMatchShp; double _fdSum; size_t _unmatchedSegSum; diff --git a/src/pfaedle/router/Router.cpp b/src/pfaedle/router/Router.cpp index 5cac1cb..56a6942 100644 --- a/src/pfaedle/router/Router.cpp +++ b/src/pfaedle/router/Router.cpp @@ -2,7 +2,13 @@ // Chair of Algorithms and Data Structures. // Authors: Patrick Brosi +#ifdef _OPENMP #include +#else +#define omp_get_thread_num() 0 +#define omp_get_num_procs() 1 +#endif + #include #include #include @@ -193,8 +199,7 @@ double CombCostFunc::operator()(const router::Edge* from, const router::Node* n, } // _____________________________________________________________________________ -Router::Router(const trgraph::Graph& g, size_t numThreads) - : _g(g), _cache(numThreads) { +Router::Router(size_t numThreads) : _cache(numThreads) { for (size_t i = 0; i < numThreads; i++) { _cache[i] = new Cache(); } diff --git a/src/pfaedle/router/Router.h b/src/pfaedle/router/Router.h index b93876a..ef682b2 100644 --- a/src/pfaedle/router/Router.h +++ b/src/pfaedle/router/Router.h @@ -135,7 +135,7 @@ struct CombCostFunc class Router { public: // Init this router with caches for numThreads threads - Router(const trgraph::Graph& g, size_t numThreads); + explicit Router(size_t numThreads); ~Router(); // Find the most likely path through the graph for a node candidate route. @@ -160,8 +160,6 @@ class Router { size_t getCacheNumber() const; private: - const trgraph::Graph& _g; - mutable std::vector _cache; HopBand getHopBand(const NodeCandGroup& a, const NodeCandGroup& b, const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, diff --git a/src/pfaedle/router/ShapeBuilder.cpp b/src/pfaedle/router/ShapeBuilder.cpp index ede3c64..068a26b 100644 --- a/src/pfaedle/router/ShapeBuilder.cpp +++ b/src/pfaedle/router/ShapeBuilder.cpp @@ -2,7 +2,13 @@ // Chair of Algorithms and Data Structures. // Authors: Patrick Brosi +#ifdef _OPENMP #include +#else +#define omp_get_thread_num() 0 +#define omp_get_num_procs() 1 +#endif + #include #include #include @@ -49,7 +55,7 @@ ShapeBuilder::ShapeBuilder(Feed* feed, MOTs mots, _motCfg(motCfg), _ecoll(ecoll), _cfg(cfg), - _crouter(_g, omp_get_num_procs()), + _crouter(omp_get_num_procs()), _curShpCnt(0) { _numThreads = _crouter.getCacheNumber(); writeMotStops(); diff --git a/src/util/tests/TestMain.cpp b/src/util/tests/TestMain.cpp index d5a9220..f253dac 100644 --- a/src/util/tests/TestMain.cpp +++ b/src/util/tests/TestMain.cpp @@ -23,6 +23,7 @@ using namespace util::graph; const lest::test specification[] = { // ___________________________________________________________________________ +{ CASE("atof") { EXPECT(util::atof("45.534215") == approx(45.534215)); EXPECT(util::atof("5.534") == approx(5.534)); @@ -33,9 +34,10 @@ CASE("atof") { // TODO: more test cases -}, +}}, // ___________________________________________________________________________ +{ CASE("dirgraph") { DirGraph g; @@ -73,10 +75,11 @@ CASE("dirgraph") { g.delEdg(a, a); EXPECT(a->getDeg() == (size_t)1); - // TODO: more test cases -}, + // TODO: more test cases +}}, // ___________________________________________________________________________ +{ CASE("unddirgraph") { UndirGraph g; @@ -118,9 +121,10 @@ CASE("unddirgraph") { // TODO: more test cases -}, +}}, // ___________________________________________________________________________ +{ CASE("grid") { Grid g(.5, .5, Box(Point(0, 0), Point(3, 3))); @@ -151,9 +155,10 @@ CASE("grid") { // TODO: more test cases -}, +}}, // ___________________________________________________________________________ +{ CASE("densify") { Line a; a.push_back(Point(1, 1)); @@ -179,9 +184,10 @@ CASE("densify") { dense = util::geo::simplify(dense, 0.1); EXPECT(dense.size() == (size_t)3); -}, +}}, // ___________________________________________________________________________ +{ CASE("summed frechet distance") { Line a; a.push_back(Point(1, 1)); @@ -203,9 +209,10 @@ CASE("summed frechet distance") { double fd = util::geo::accFrechetDistC(a, b, 0.1); EXPECT(fd == approx(2)); -}, +}}, // ___________________________________________________________________________ +{ CASE("frechet distance") { Line e; e.push_back(Point(1, 1)); @@ -268,9 +275,10 @@ CASE("frechet distance") { fd = util::geo::frechetDist(g, h, 0.1); EXPECT(fd == approx(1)); -}, +}}, // ___________________________________________________________________________ +{ CASE("geo box alignment") { Line a; a.push_back(Point(1, 1)); @@ -304,32 +312,36 @@ CASE("geo box alignment") { EXPECT(parallelity(box, ml) == approx(0)); ml = rotate(ml, 45); EXPECT(parallelity(box, ml) == approx(1)); -}, +}}, // ___________________________________________________________________________ +{ CASE("url decode") { EXPECT("zürich" == util::urlDecode("z%C3%BCrich")); EXPECT("!@$%^*()" == util::urlDecode("!%40%24%25%5E*()")); EXPECT("Løkken" == util::urlDecode("L%C3%B8kken")); EXPECT("á é" == util::urlDecode("%C3%A1%20%C3%A9")); EXPECT("á é" == util::urlDecode("%C3%A1+%C3%A9")); -}, +}}, // ___________________________________________________________________________ +{ CASE("json escape") { EXPECT("Hello\\\\Goodbye!" == util::jsonStringEscape("Hello\\Goodbye!")); EXPECT("\\\"Hello\\\"" == util::jsonStringEscape("\"Hello\"")); -}, +}}, // ___________________________________________________________________________ +{ CASE("split") { EXPECT(util::split("hello,again", ',').size() == (size_t)2); EXPECT(util::split("hello,,again", ',').size() == (size_t)3); EXPECT(util::split("hello", ',').size() == (size_t)1); EXPECT(util::split("", ',').size() == (size_t)0); -}, +}}, // ___________________________________________________________________________ +{ CASE("editdist") { EXPECT(util::editDist("hello", "mello") == (size_t)1); EXPECT(util::editDist("mello", "hello") == (size_t)1); @@ -338,15 +350,17 @@ CASE("editdist") { EXPECT(util::editDist("xabcd", "abcde") == (size_t)2); EXPECT(util::editDist("abcd", "abcdes") == (size_t)2); EXPECT(util::editDist("hello", "hello") == (size_t)0); -}, +}}, // ___________________________________________________________________________ +{ CASE("toString") { EXPECT(util::toString(34) == "34"); EXPECT(util::toString("34") == "34"); -}, +}}, // ___________________________________________________________________________ +{ CASE("replace") { std::string a("lorem ipsum ipsum lorem"); @@ -380,9 +394,10 @@ CASE("replace") { EXPECT(!util::replaceAll(b, "", "ee")); EXPECT(b == "loree aaaau aaaau loree"); -}, +}}, // ___________________________________________________________________________ +{ CASE("Edge-based Dijkstra directed, 1 to all") { DirGraph g; @@ -436,9 +451,10 @@ CASE("Edge-based Dijkstra directed, 1 to all") { int single = EDijkstra::shortestPath(u.first, eBC, cFunc); EXPECT(single == u.second); } -}, +}}, // ___________________________________________________________________________ +{ CASE("Edge-based Dijkstra undirected, edge 1 to 1") { UndirGraph g; @@ -494,9 +510,10 @@ CASE("Edge-based Dijkstra undirected, edge 1 to 1") { cost = EDijkstra::shortestPath(eAB, b, cFunc, &resE, &res); EXPECT(cost == 0); -}, +}}, // ___________________________________________________________________________ +{ CASE("Edge-based Dijkstra undirected, edge 1 to n") { UndirGraph g; @@ -542,9 +559,10 @@ CASE("Edge-based Dijkstra undirected, edge 1 to n") { EDijkstra::EList resE; int cost = EDijkstra::shortestPath(eAB, tos, cFunc, &resE, &res); EXPECT(cost == 0); -}, +}}, // ___________________________________________________________________________ +{ CASE("Edge-based Dijkstra undirected, 1 to n") { UndirGraph g; @@ -596,9 +614,10 @@ CASE("Edge-based Dijkstra undirected, 1 to n") { EXPECT(resE[eDC]->size() == (size_t)3); EXPECT(res[eED]->size() == (size_t)3); -}, +}}, // ___________________________________________________________________________ +{ CASE("Edge-based Dijkstra undirected") { UndirGraph g; @@ -668,9 +687,10 @@ CASE("Edge-based Dijkstra undirected") { cost = EDijkstra::shortestPath(a, d, cFunc, &res); EXPECT(cost == 2); -}, +}}, // ___________________________________________________________________________ +{ CASE("Edge-based Dijkstra") { DirGraph g; @@ -715,9 +735,10 @@ CASE("Edge-based Dijkstra") { cost = EDijkstra::shortestPath(a, d, cFunc, &res); EXPECT(cost == 2); -}, +}}, // ___________________________________________________________________________ +{ CASE("Dijkstra") { DirGraph g; @@ -779,9 +800,10 @@ CASE("Dijkstra") { EXPECT(costs[c] == 1); EXPECT(costs[d] == 2); EXPECT(costs[x] == 999); -}, +}}, // ___________________________________________________________________________ +{ CASE("nullable") { { util::Nullable nullable; @@ -840,9 +862,10 @@ CASE("nullable") { EXPECT_THROWS(nullable == voidnull); } -}, +}}, // ___________________________________________________________________________ +{ CASE("geometry") { geo::Point a(1, 2); geo::Point b(2, 3); @@ -1188,7 +1211,7 @@ CASE("geometry") { EXPECT(geo::contains(geo::Polygon({{0.0, 0.0}, {1.0, 1.0}, {1.5, 0.5}, {0.5, -0.5}}), geo::convexHull(obox))); } -}; +}}; // _____________________________________________________________________________ int main(int argc, char** argv) { diff --git a/src/util/tests/lest.h b/src/util/tests/lest.h index c68bbac..71db282 100644 --- a/src/util/tests/lest.h +++ b/src/util/tests/lest.h @@ -1,13 +1,13 @@ -// Copyright 2013, 2014 by Martin Moene +// Copyright 2013-2018 by Martin Moene // // lest is based on ideas by Kevlin Henney, see video at // http://skillsmatter.com/podcast/agile-testing/kevlin-henney-rethinking-unit-testing-in-c-plus-plus // // Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -#ifndef LEST_LEST_H_INCLUDED -#define LEST_LEST_H_INCLUDED +#ifndef LEST_LEST_HPP_INCLUDED +#define LEST_LEST_HPP_INCLUDED #include #include @@ -31,45 +31,93 @@ #include #include -#ifdef __clang__ -# pragma clang diagnostic ignored "-Wunused-comparison" -# pragma clang diagnostic ignored "-Wunused-value" -#elif defined __GNUC__ -# pragma GCC diagnostic ignored "-Wunused-value" -#endif - -#define lest_VERSION "1.22.0" +#define lest_VERSION "1.33.1" #ifndef lest_FEATURE_AUTO_REGISTER # define lest_FEATURE_AUTO_REGISTER 0 #endif #ifndef lest_FEATURE_COLOURISE -# define lest_FEATURE_COLOURISE 0 +# define lest_FEATURE_COLOURISE 0 #endif #ifndef lest_FEATURE_LITERAL_SUFFIX -# define lest_FEATURE_LITERAL_SUFFIX 0 +# define lest_FEATURE_LITERAL_SUFFIX 0 #endif #ifndef lest_FEATURE_REGEX_SEARCH -# define lest_FEATURE_REGEX_SEARCH 0 +# define lest_FEATURE_REGEX_SEARCH 0 #endif #ifndef lest_FEATURE_TIME_PRECISION #define lest_FEATURE_TIME_PRECISION 0 #endif +#ifndef lest_FEATURE_WSTRING +#define lest_FEATURE_WSTRING 1 +#endif + +#ifdef lest_FEATURE_RTTI +# define lest__cpp_rtti lest_FEATURE_RTTI +#elif defined(__cpp_rtti) +# define lest__cpp_rtti __cpp_rtti +#elif defined(__GXX_RTTI) || defined (_CPPRTTI) +# define lest__cpp_rtti 1 +#else +# define lest__cpp_rtti 0 +#endif + #if lest_FEATURE_REGEX_SEARCH # include #endif +// Compiler warning suppression: + +#ifdef __clang__ +# pragma clang diagnostic ignored "-Waggregate-return" +# pragma clang diagnostic ignored "-Woverloaded-shift-op-parentheses" +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunused-comparison" +#elif defined __GNUC__ +# pragma GCC diagnostic ignored "-Waggregate-return" +# pragma GCC diagnostic push +#endif + +// Suppress shadow and unused-value warning for sections: + +#if defined __clang__ +# define lest_SUPPRESS_WSHADOW _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wshadow\"" ) +# define lest_SUPPRESS_WUNUSED _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wunused-value\"" ) +# define lest_RESTORE_WARNINGS _Pragma( "clang diagnostic pop" ) + +#elif defined __GNUC__ +# define lest_SUPPRESS_WSHADOW _Pragma( "GCC diagnostic push" ) \ + _Pragma( "GCC diagnostic ignored \"-Wshadow\"" ) +# define lest_SUPPRESS_WUNUSED _Pragma( "GCC diagnostic push" ) \ + _Pragma( "GCC diagnostic ignored \"-Wunused-value\"" ) +# define lest_RESTORE_WARNINGS _Pragma( "GCC diagnostic pop" ) +#else +# define lest_SUPPRESS_WSHADOW /*empty*/ +# define lest_SUPPRESS_WUNUSED /*empty*/ +# define lest_RESTORE_WARNINGS /*empty*/ +#endif + +#ifdef _MSVC_LANG +# define lest_CPP17_OR_GREATER_MS ( _MSVC_LANG >= 201703L ) +#else +# define lest_CPP17_OR_GREATER_MS 0 +#endif +# define lest_CPP17_OR_GREATER ( __cplusplus >= 201703L || lest_CPP17_OR_GREATER_MS ) + #if ! defined( lest_NO_SHORT_MACRO_NAMES ) && ! defined( lest_NO_SHORT_ASSERTION_NAMES ) # define MODULE lest_MODULE # if ! lest_FEATURE_AUTO_REGISTER # define CASE lest_CASE -# define TEST lest_TEST +# define CASE_ON lest_CASE_ON +# define SCENARIO lest_SCENARIO # endif # define SETUP lest_SETUP @@ -81,7 +129,6 @@ # define EXPECT_THROWS lest_EXPECT_THROWS # define EXPECT_THROWS_AS lest_EXPECT_THROWS_AS -# define SCENARIO lest_SCENARIO # define GIVEN lest_GIVEN # define WHEN lest_WHEN # define THEN lest_THEN @@ -89,40 +136,48 @@ # define AND_THEN lest_AND_THEN #endif -#define lest_SCENARIO( sketch ) lest_CASE( "Scenario: " sketch ) -#define lest_GIVEN( context ) lest_SETUP( "Given: " context ) -#define lest_WHEN( story ) lest_SECTION( " When: " story ) -#define lest_THEN( story ) lest_SECTION( " Then: " story ) -#define lest_AND_WHEN( story ) lest_SECTION( " And: " story ) -#define lest_AND_THEN( story ) lest_SECTION( " And: " story ) - -#define lest_MODULE( specification, module ) \ - namespace { lest::add_module _( specification, module ); } - -#define lest_TEST \ - lest_CASE +#if lest_FEATURE_AUTO_REGISTER +#define lest_SCENARIO( specification, sketch ) lest_CASE( specification, lest::text("Scenario: ") + sketch ) +#else +#define lest_SCENARIO( sketch ) lest_CASE( lest::text("Scenario: ") + sketch ) +#endif +#define lest_GIVEN( context ) lest_SETUP( lest::text(" Given: ") + context ) +#define lest_WHEN( story ) lest_SECTION( lest::text(" When: ") + story ) +#define lest_THEN( story ) lest_SECTION( lest::text(" Then: ") + story ) +#define lest_AND_WHEN( story ) lest_SECTION( lest::text("And then: ") + story ) +#define lest_AND_THEN( story ) lest_SECTION( lest::text("And then: ") + story ) #if lest_FEATURE_AUTO_REGISTER # define lest_CASE( specification, proposition ) \ - void lest_FUNCTION( lest::env & ); \ + static void lest_FUNCTION( lest::env & ); \ namespace { lest::add_test lest_REGISTRAR( specification, lest::test( proposition, lest_FUNCTION ) ); } \ - void lest_FUNCTION( lest::env & $ ) + static void lest_FUNCTION( lest::env & lest_env ) -#else +#else // lest_FEATURE_AUTO_REGISTER -# define lest_CASE( proposition, ... ) \ - proposition, [__VA_ARGS__]( lest::env & $ ) +# define lest_CASE( proposition ) \ + proposition, []( lest::env & lest_env ) -#endif +# define lest_CASE_ON( proposition, ... ) \ + proposition, [__VA_ARGS__]( lest::env & lest_env ) + +# define lest_MODULE( specification, module ) \ + namespace { lest::add_module _( specification, module ); } + +#endif //lest_FEATURE_AUTO_REGISTER #define lest_SETUP( context ) \ - for ( int $section = 0, $count = 1; $section < $count; $count -= 0==$section++ ) + for ( int lest__section = 0, lest__count = 1; lest__section < lest__count; lest__count -= 0==lest__section++ ) \ + for ( lest::ctx lest__ctx_setup( lest_env, context ); lest__ctx_setup; ) #define lest_SECTION( proposition ) \ + lest_SUPPRESS_WSHADOW \ static int lest_UNIQUE( id ) = 0; \ - if ( lest::guard $run = lest::guard( lest_UNIQUE( id ), $section, $count ) ) \ - for ( int $section = 0, $count = 1; $section < $count; $count -= 0==$section++ ) + if ( lest::guard( lest_UNIQUE( id ), lest__section, lest__count ) ) \ + for ( int lest__section = 0, lest__count = 1; lest__section < lest__count; lest__count -= 0==lest__section++ ) \ + for ( lest::ctx lest__ctx_section( lest_env, proposition ); lest__ctx_section; ) \ + lest_RESTORE_WARNINGS #define lest_EXPECT( expr ) \ do { \ @@ -130,8 +185,8 @@ { \ if ( lest::result score = lest_DECOMPOSE( expr ) ) \ throw lest::failure{ lest_LOCATION, #expr, score.decomposition }; \ - else if ( $.pass ) \ - lest::report( $.os, lest::passing{ lest_LOCATION, #expr, score.decomposition }, $.testing ); \ + else if ( lest_env.pass() ) \ + lest::report( lest_env.os, lest::passing{ lest_LOCATION, #expr, score.decomposition }, lest_env.context() ); \ } \ catch(...) \ { \ @@ -145,8 +200,8 @@ { \ if ( lest::result score = lest_DECOMPOSE( expr ) ) \ { \ - if ( $.pass ) \ - lest::report( $.os, lest::passing{ lest_LOCATION, lest::not_expr( #expr ), lest::not_expr( score.decomposition ) }, $.testing ); \ + if ( lest_env.pass() ) \ + lest::report( lest_env.os, lest::passing{ lest_LOCATION, lest::not_expr( #expr ), lest::not_expr( score.decomposition ) }, lest_env.context() ); \ } \ else \ throw lest::failure{ lest_LOCATION, lest::not_expr( #expr ), lest::not_expr( score.decomposition ) }; \ @@ -162,14 +217,16 @@ { \ try \ { \ + lest_SUPPRESS_WUNUSED \ expr; \ + lest_RESTORE_WARNINGS \ } \ catch (...) \ { \ lest::inform( lest_LOCATION, #expr ); \ } \ - if ( $.pass ) \ - lest::report( $.os, lest::got_none( lest_LOCATION, #expr ), $.testing ); \ + if ( lest_env.pass() ) \ + lest::report( lest_env.os, lest::got_none( lest_LOCATION, #expr ), lest_env.context() ); \ } while ( lest::is_false() ) #define lest_EXPECT_THROWS( expr ) \ @@ -177,12 +234,14 @@ { \ try \ { \ + lest_SUPPRESS_WUNUSED \ expr; \ + lest_RESTORE_WARNINGS \ } \ catch (...) \ { \ - if ( $.pass ) \ - lest::report( $.os, lest::got{ lest_LOCATION, #expr }, $.testing ); \ + if ( lest_env.pass() ) \ + lest::report( lest_env.os, lest::got{ lest_LOCATION, #expr }, lest_env.context() ); \ break; \ } \ throw lest::expected{ lest_LOCATION, #expr }; \ @@ -194,12 +253,14 @@ { \ try \ { \ + lest_SUPPRESS_WUNUSED \ expr; \ + lest_RESTORE_WARNINGS \ } \ catch ( excpt & ) \ { \ - if ( $.pass ) \ - lest::report( $.os, lest::got{ lest_LOCATION, #expr, lest::of_type( #excpt ) }, $.testing ); \ + if ( lest_env.pass() ) \ + lest::report( lest_env.os, lest::got{ lest_LOCATION, #expr, lest::of_type( #excpt ) }, lest_env.context() ); \ break; \ } \ catch (...) {} \ @@ -211,7 +272,7 @@ #define lest_UNIQUE2( name, line ) lest_UNIQUE3( name, line ) #define lest_UNIQUE3( name, line ) name ## line -#define lest_DECOMPOSE( expr ) ( lest::expression_decomposer()->* expr ) +#define lest_DECOMPOSE( expr ) ( lest::expression_decomposer() << expr ) #define lest_FUNCTION lest_UNIQUE(__lest_function__ ) #define lest_REGISTRAR lest_UNIQUE(__lest_registrar__ ) @@ -220,6 +281,8 @@ namespace lest { +const int exit_max_value = 255; + using text = std::string; using texts = std::vector; @@ -231,8 +294,8 @@ struct test std::function behaviour; #if lest_FEATURE_AUTO_REGISTER - test( text name, std::function behaviour ) - : name( name ), behaviour( behaviour ) {} + test( text name_, std::function behaviour_ ) + : name( name_), behaviour( behaviour_) {} #endif }; @@ -252,7 +315,7 @@ struct add_test struct add_module { - template + template< std::size_t N > add_module( tests & specification, test const (&module)[N] ) { specification.insert( specification.end(), std::begin( module ), std::end( module ) ); @@ -266,6 +329,10 @@ struct result const bool passed; const text decomposition; + template< typename T > + result( T const & passed_, text decomposition_) + : passed( !!passed_), decomposition( decomposition_) {} + explicit operator bool() { return ! passed; } }; @@ -274,15 +341,15 @@ struct location const text file; const int line; - location( text file, int line ) - : file( file ), line( line ) {} + location( text file_, int line_) + : file( file_), line( line_) {} }; struct comment { const text info; - comment( text info ) : info( info ) {} + comment( text info_) : info( info_) {} explicit operator bool() { return ! info.empty(); } }; @@ -294,55 +361,55 @@ struct message : std::runtime_error ~message() throw() {} // GCC 4.6 - message( text kind, location where, text expr, text note = "" ) - : std::runtime_error( expr ), kind( kind ), where( where ), note( note ) {} + message( text kind_, location where_, text expr_, text note_ = "" ) + : std::runtime_error( expr_), kind( kind_), where( where_), note( note_) {} }; struct failure : message { - failure( location where, text expr, text decomposition ) - : message{ "failed", where, expr + " for " + decomposition } {} + failure( location where_, text expr_, text decomposition_) + : message{ "failed", where_, expr_ + " for " + decomposition_ } {} }; struct success : message { // using message::message; // VC is lagging here - success( text kind, location where, text expr, text note = "" ) - : message( kind, where, expr, note ) {} + success( text kind_, location where_, text expr_, text note_ = "" ) + : message( kind_, where_, expr_, note_ ) {} }; struct passing : success { - passing( location where, text expr, text decomposition ) - : success( "passed", where, expr + " for " + decomposition ) {} + passing( location where_, text expr_, text decomposition_ ) + : success( "passed", where_, expr_ + " for " + decomposition_) {} }; struct got_none : success { - got_none( location where, text expr ) - : success( "passed: got no exception", where, expr ) {} + got_none( location where_, text expr_ ) + : success( "passed: got no exception", where_, expr_ ) {} }; struct got : success { - got( location where, text expr ) - : success( "passed: got exception", where, expr ) {} + got( location where_, text expr_) + : success( "passed: got exception", where_, expr_) {} - got( location where, text expr, text excpt ) - : success( "passed: got exception " + excpt, where, expr ) {} + got( location where_, text expr_, text excpt_) + : success( "passed: got exception " + excpt_, where_, expr_) {} }; struct expected : message { - expected( location where, text expr, text excpt = "" ) - : message{ "failed: didn't get exception", where, expr, excpt } {} + expected( location where_, text expr_, text excpt_ = "" ) + : message{ "failed: didn't get exception", where_, expr_, excpt_ } {} }; struct unexpected : message { - unexpected( location where, text expr, text note = "" ) - : message{ "failed: got unexpected exception", where, expr, note } {} + unexpected( location where_, text expr_, text note_ = "" ) + : message{ "failed: got unexpected exception", where_, expr_, note_ } {} }; struct guard @@ -350,8 +417,8 @@ struct guard int & id; int const & section; - guard( int & id, int const & section, int & count ) - : id( id ), section( section ) + guard( int & id_, int const & section_, int & count ) + : id( id_), section( section_) { if ( section == 0 ) id = count++ - 1; @@ -371,12 +438,12 @@ public: static approx custom() { return approx( 0 ); } - approx operator()( double magnitude ) + approx operator()( double new_magnitude ) { - approx approx ( magnitude ); - approx.epsilon( epsilon_ ); - approx.scale ( scale_ ); - return approx; + approx appr( new_magnitude ); + appr.epsilon( epsilon_ ); + appr.scale ( scale_ ); + return appr; } double magnitude() const { return magnitude_; } @@ -387,13 +454,18 @@ public: friend bool operator == ( double lhs, approx const & rhs ) { // Thanks to Richard Harris for his help refining this formula. - return std::abs( lhs - rhs.magnitude_ ) < rhs.epsilon_ * ( rhs.scale_ + (std::max)( std::abs( lhs ), std::abs( rhs.magnitude_ ) ) ); + return std::abs( lhs - rhs.magnitude_ ) < rhs.epsilon_ * ( rhs.scale_ + (std::min)( std::abs( lhs ), std::abs( rhs.magnitude_ ) ) ); } friend bool operator == ( approx const & lhs, double rhs ) { return operator==( rhs, lhs ); } friend bool operator != ( double lhs, approx const & rhs ) { return !operator==( lhs, rhs ); } friend bool operator != ( approx const & lhs, double rhs ) { return !operator==( rhs, lhs ); } + friend bool operator <= ( double lhs, approx const & rhs ) { return lhs < rhs.magnitude_ || lhs == rhs; } + friend bool operator <= ( approx const & lhs, double rhs ) { return lhs.magnitude_ < rhs || lhs == rhs; } + friend bool operator >= ( double lhs, approx const & rhs ) { return lhs > rhs.magnitude_ || lhs == rhs; } + friend bool operator >= ( approx const & lhs, double rhs ) { return lhs.magnitude_ > rhs || lhs == rhs; } + private: double epsilon_; double scale_; @@ -440,132 +512,146 @@ inline void inform( location where, text expr ) // Expression decomposition: -template +template< typename T > auto make_value_string( T const & value ) -> std::string; -template +template< typename T > auto make_memory_string( T const & item ) -> std::string; #if lest_FEATURE_LITERAL_SUFFIX -inline char const * sfx( char const * text ) { return text; } +inline char const * sfx( char const * txt ) { return txt; } #else inline char const * sfx( char const * ) { return ""; } #endif inline std::string to_string( std::nullptr_t ) { return "nullptr"; } -inline std::string to_string( std::string const & text ) { return "\"" + text + "\"" ; } -inline std::string to_string( std::wstring const & text ) ; - -inline std::string to_string( char const * const text ) { return text ? to_string( std::string ( text ) ) : "{null string}"; } -inline std::string to_string( char * const text ) { return text ? to_string( std::string ( text ) ) : "{null string}"; } -inline std::string to_string( wchar_t const * const text ) { return text ? to_string( std::wstring( text ) ) : "{null string}"; } -inline std::string to_string( wchar_t * const text ) { return text ? to_string( std::wstring( text ) ) : "{null string}"; } - -inline std::string to_string( char text ) { return "\'" + std::string( 1, text ) + "\'" ; } -inline std::string to_string( signed char text ) { return "\'" + std::string( 1, text ) + "\'" ; } -inline std::string to_string( unsigned char text ) { return "\'" + std::string( 1, text ) + "\'" ; } - -inline std::string to_string( bool flag ) { return flag ? "true" : "false"; } - -inline std::string to_string( signed short value ) { return make_value_string( value ) ; } -inline std::string to_string( unsigned short value ) { return make_value_string( value ) + sfx("u" ); } -inline std::string to_string( signed int value ) { return make_value_string( value ) ; } -inline std::string to_string( unsigned int value ) { return make_value_string( value ) + sfx("u" ); } -inline std::string to_string( signed long value ) { return make_value_string( value ) + sfx("l" ); } -inline std::string to_string( unsigned long value ) { return make_value_string( value ) + sfx("ul" ); } -inline std::string to_string( signed long long value ) { return make_value_string( value ) + sfx("ll" ); } -inline std::string to_string( unsigned long long value ) { return make_value_string( value ) + sfx("ull"); } -inline std::string to_string( double value ) { return make_value_string( value ) ; } -inline std::string to_string( float value ) { return make_value_string( value ) + sfx("f" ); } - -template -struct is_streamable -{ - template - static auto test( int ) -> decltype( std::declval() << std::declval(), std::true_type() ); - - template - static auto test( ... ) -> std::false_type; - -#ifdef _MSC_VER - enum { value = std::is_same< decltype( test(0) ), std::true_type >::value }; -#else - static constexpr bool value = std::is_same< decltype( test(0) ), std::true_type >::value; +inline std::string to_string( std::string const & txt ) { return "\"" + txt + "\"" ; } +#if lest_FEATURE_WSTRING +inline std::string to_string( std::wstring const & txt ) ; #endif -}; -template -struct is_container -{ - template - static auto test( int ) -> decltype( std::declval().begin() == std::declval().end(), std::true_type() ); - - template - static auto test( ... ) -> std::false_type; - -#ifdef _MSC_VER - enum { value = std::is_same< decltype( test(0) ), std::true_type >::value }; -#else - static constexpr bool value = std::is_same< decltype( test(0) ), std::true_type >::value; +inline std::string to_string( char const * const txt ) { return txt ? to_string( std::string ( txt ) ) : "{null string}"; } +inline std::string to_string( char * const txt ) { return txt ? to_string( std::string ( txt ) ) : "{null string}"; } +#if lest_FEATURE_WSTRING +inline std::string to_string( wchar_t const * const txt ) { return txt ? to_string( std::wstring( txt ) ) : "{null string}"; } +inline std::string to_string( wchar_t * const txt ) { return txt ? to_string( std::wstring( txt ) ) : "{null string}"; } #endif -}; -template -using ForEnum = typename std::enable_if< std::is_enum::value, R>::type; +inline std::string to_string( bool flag ) { return flag ? "true" : "false"; } -template -using ForNonEnum = typename std::enable_if< ! std::is_enum::value, R>::type; +inline std::string to_string( signed short value ) { return make_value_string( value ) ; } +inline std::string to_string( unsigned short value ) { return make_value_string( value ) + sfx("u" ); } +inline std::string to_string( signed int value ) { return make_value_string( value ) ; } +inline std::string to_string( unsigned int value ) { return make_value_string( value ) + sfx("u" ); } +inline std::string to_string( signed long value ) { return make_value_string( value ) + sfx("l" ); } +inline std::string to_string( unsigned long value ) { return make_value_string( value ) + sfx("ul" ); } +inline std::string to_string( signed long long value ) { return make_value_string( value ) + sfx("ll" ); } +inline std::string to_string( unsigned long long value ) { return make_value_string( value ) + sfx("ull"); } +inline std::string to_string( double value ) { return make_value_string( value ) ; } +inline std::string to_string( float value ) { return make_value_string( value ) + sfx("f" ); } -template -using ForStreamable = typename std::enable_if< is_streamable::value, R>::type; +inline std::string to_string( signed char chr ) { return to_string( static_cast( chr ) ); } +inline std::string to_string( unsigned char chr ) { return to_string( static_cast( chr ) ); } -template -using ForNonStreamable = typename std::enable_if< ! is_streamable::value, R>::type; - -template -using ForContainer = typename std::enable_if< is_container::value, R>::type; - -template -using ForNonContainer = typename std::enable_if< ! is_container::value, R>::type; - -template -auto make_enum_string( T const & ) -> ForNonEnum +inline std::string to_string( char chr ) { - return text("[type: ") + typeid(T).name() + "]"; + struct Tr { char chr; char const * str; } table[] = + { + {'\r', "'\\r'" }, {'\f', "'\\f'" }, + {'\n', "'\\n'" }, {'\t', "'\\t'" }, + }; + + for ( auto tr : table ) + { + if ( chr == tr.chr ) + return tr.str; + } + + auto unprintable = [](char c){ return 0 <= c && c < ' '; }; + + return unprintable( chr ) + ? to_string( static_cast( chr ) ) + : "\'" + std::string( 1, chr ) + "\'" ; } -template +template< typename T > +struct is_streamable +{ + template< typename U > + static auto test( int ) -> decltype( std::declval() << std::declval(), std::true_type() ); + + template< typename > + static auto test( ... ) -> std::false_type; + +#ifdef _MSC_VER + enum { value = std::is_same< decltype( test(0) ), std::true_type >::value }; +#else + static constexpr bool value = std::is_same< decltype( test(0) ), std::true_type >::value; +#endif +}; + +template< typename T > +struct is_container +{ + template< typename U > + static auto test( int ) -> decltype( std::declval().begin() == std::declval().end(), std::true_type() ); + + template< typename > + static auto test( ... ) -> std::false_type; + +#ifdef _MSC_VER + enum { value = std::is_same< decltype( test(0) ), std::true_type >::value }; +#else + static constexpr bool value = std::is_same< decltype( test(0) ), std::true_type >::value; +#endif +}; + +template< typename T, typename R > +using ForEnum = typename std::enable_if< std::is_enum::value, R>::type; + +template< typename T, typename R > +using ForNonEnum = typename std::enable_if< ! std::is_enum::value, R>::type; + +template< typename T, typename R > +using ForStreamable = typename std::enable_if< is_streamable::value, R>::type; + +template< typename T, typename R > +using ForNonStreamable = typename std::enable_if< ! is_streamable::value, R>::type; + +template< typename T, typename R > +using ForContainer = typename std::enable_if< is_container::value, R>::type; + +template< typename T, typename R > +using ForNonContainerNonPointer = typename std::enable_if< ! (is_container::value || std::is_pointer::value), R>::type; + +template< typename T > +auto make_enum_string( T const & item ) -> ForNonEnum +{ +#if lest__cpp_rtti + return text("[type: ") + typeid(T).name() + "]: " + make_memory_string( item ); +#else + return text("[type: (no RTTI)]: ") + make_memory_string( item ); +#endif +} + +template< typename T > auto make_enum_string( T const & item ) -> ForEnum { return to_string( static_cast::type>( item ) ); } -template +template< typename T > auto make_string( T const & item ) -> ForNonStreamable { return make_enum_string( item ); } -template +template< typename T > auto make_string( T const & item ) -> ForStreamable { std::ostringstream os; os << item; return os.str(); } -template -auto make_string( T * p )-> std::string -{ - if ( p ) return make_memory_string( p ); - else return "NULL"; -} - -template -auto make_string( R C::* p ) -> std::string -{ - if ( p ) return make_memory_string( p ); - else return "NULL"; -} - template auto make_string( std::pair const & pair ) -> std::string { @@ -574,7 +660,7 @@ auto make_string( std::pair const & pair ) -> std::string return oss.str(); } -template +template< typename TU, std::size_t N > struct make_tuple_string { static std::string make( TU const & tuple ) @@ -585,25 +671,54 @@ struct make_tuple_string } }; -template +template< typename TU > struct make_tuple_string { static std::string make( TU const & ) { return ""; } }; -template +template< typename ...TS > auto make_string( std::tuple const & tuple ) -> std::string { return "{ " + make_tuple_string, sizeof...(TS)>::make( tuple ) + "}"; } -template -auto to_string( T const & item ) -> ForNonContainer +template< typename T > +inline std::string make_string( T const * ptr ) +{ + // Note showbase affects the behavior of /integer/ output; + std::ostringstream os; + os << std::internal << std::hex << std::showbase << std::setw( 2 + 2 * sizeof(T*) ) << std::setfill('0') << reinterpret_cast( ptr ); + return os.str(); +} + +template< typename C, typename R > +inline std::string make_string( R C::* ptr ) +{ + std::ostringstream os; + os << std::internal << std::hex << std::showbase << std::setw( 2 + 2 * sizeof(R C::* ) ) << std::setfill('0') << ptr; + return os.str(); +} + +template< typename T > +auto to_string( T const * ptr ) -> std::string +{ + return ! ptr ? "nullptr" : make_string( ptr ); +} + +template +auto to_string( R C::* ptr ) -> std::string +{ + return ! ptr ? "nullptr" : make_string( ptr ); +} + +template< typename T > +auto to_string( T const & item ) -> ForNonContainerNonPointer { return make_string( item ); } -template +template< typename C > auto to_string( C const & cont ) -> ForContainer { std::ostringstream os; @@ -616,19 +731,21 @@ auto to_string( C const & cont ) -> ForContainer return os.str(); } +#if lest_FEATURE_WSTRING inline -auto to_string( std::wstring const & text ) -> std::string +auto to_string( std::wstring const & txt ) -> std::string { - std::string result; result.reserve( text.size() ); + std::string result; result.reserve( txt.size() ); - for( auto & chr : text ) + for( auto & chr : txt ) { result += chr <= 0xff ? static_cast( chr ) : '?'; } return to_string( result ); } +#endif -template +template< typename T > auto make_value_string( T const & value ) -> std::string { std::ostringstream os; os << value; return os.str(); @@ -661,7 +778,7 @@ auto make_memory_string( void const * item, std::size_t size ) -> std::string return os.str(); } -template +template< typename T > auto make_memory_string( T const & item ) -> std::string { return make_memory_string( &item, sizeof item ); @@ -673,33 +790,33 @@ auto to_string( approx const & appr ) -> std::string return to_string( appr.magnitude() ); } -template +template< typename L, typename R > auto to_string( L const & lhs, std::string op, R const & rhs ) -> std::string { std::ostringstream os; os << to_string( lhs ) << " " << op << " " << to_string( rhs ); return os.str(); } -template +template< typename L > struct expression_lhs { const L lhs; - expression_lhs( L lhs ) : lhs( lhs ) {} + expression_lhs( L lhs_) : lhs( lhs_) {} - operator result() { return result{ lhs, to_string( lhs ) }; } + operator result() { return result{ !!lhs, to_string( lhs ) }; } - template result operator==( R const & rhs ) { return result{ lhs == rhs, to_string( lhs, "==", rhs ) }; } - template result operator!=( R const & rhs ) { return result{ lhs != rhs, to_string( lhs, "!=", rhs ) }; } - template result operator< ( R const & rhs ) { return result{ lhs < rhs, to_string( lhs, "<" , rhs ) }; } - template result operator<=( R const & rhs ) { return result{ lhs <= rhs, to_string( lhs, "<=", rhs ) }; } - template result operator> ( R const & rhs ) { return result{ lhs > rhs, to_string( lhs, ">" , rhs ) }; } - template result operator>=( R const & rhs ) { return result{ lhs >= rhs, to_string( lhs, ">=", rhs ) }; } + template< typename R > result operator==( R const & rhs ) { return result{ lhs == rhs, to_string( lhs, "==", rhs ) }; } + template< typename R > result operator!=( R const & rhs ) { return result{ lhs != rhs, to_string( lhs, "!=", rhs ) }; } + template< typename R > result operator< ( R const & rhs ) { return result{ lhs < rhs, to_string( lhs, "<" , rhs ) }; } + template< typename R > result operator<=( R const & rhs ) { return result{ lhs <= rhs, to_string( lhs, "<=", rhs ) }; } + template< typename R > result operator> ( R const & rhs ) { return result{ lhs > rhs, to_string( lhs, ">" , rhs ) }; } + template< typename R > result operator>=( R const & rhs ) { return result{ lhs >= rhs, to_string( lhs, ">=", rhs ) }; } }; struct expression_decomposer { template - expression_lhs operator->* ( L const & operand ) + expression_lhs operator<< ( L const & operand ) { return expression_lhs( operand ); } @@ -754,7 +871,7 @@ inline std::ostream & operator<<( std::ostream & os, colourise words ) { return inline text colourise( text words ) { return words; } #endif -inline text pluralise( int n, text word ) +inline text pluralise( text word, int n ) { return n == 1 ? word : word + "s"; } @@ -814,9 +931,9 @@ inline bool select( text name, texts include ) auto none = []( texts args ) { return args.size() == 0; }; #if lest_FEATURE_REGEX_SEARCH - auto hidden = []( text name ){ return match( { "\\[\\.\\]", "\\[hide\\]" }, name ); }; + auto hidden = []( text arg ){ return match( { "\\[\\..*", "\\[hide\\]" }, arg ); }; #else - auto hidden = []( text name ){ return match( { "[.]", "[hide]" }, name ); }; + auto hidden = []( text arg ){ return match( { "[.", "[hide]" }, arg ); }; #endif if ( none( include ) ) @@ -864,6 +981,7 @@ struct options bool pass = false; bool lexical = false; bool random = false; + bool verbose = false; bool version = false; int repeat = 1; seed_t seed = 0; @@ -872,24 +990,74 @@ struct options struct env { std::ostream & os; - bool pass; + options opt; text testing; + std::vector< text > ctx; - env( std::ostream & os, bool pass ) - : os( os ), pass( pass ) {} + env( std::ostream & out, options option ) + : os( out ), opt( option ), testing(), ctx() {} env & operator()( text test ) { testing = test; return *this; } + + bool abort() { return opt.abort; } + bool pass() { return opt.pass; } + + void pop() { ctx.pop_back(); } + void push( text proposition ) { ctx.emplace_back( proposition ); } + + text context() { return testing + sections(); } + + text sections() + { + if ( ! opt.verbose ) + return ""; + + text msg; + for( auto section : ctx ) + { + msg += "\n " + section; + } + return msg; + } +}; + +struct ctx +{ + env & environment; + bool once; + + ctx( env & environment_, text proposition_ ) + : environment( environment_), once( true ) + { + environment.push( proposition_); + } + + ~ctx() + { +#if lest_CPP17_OR_GREATER + if ( std::uncaught_exceptions() == 0 ) +#else + if ( ! std::uncaught_exception() ) +#endif + { + environment.pop(); + } + } + + explicit operator bool() { bool result = once; once = false; return result; } }; struct action { std::ostream & os; - action( action const & ) = delete; - action( std::ostream & os ) : os( os ) {} + action( std::ostream & out ) : os( out ) {} + + action( action const & ) = delete; + void operator=( action const & ) = delete; operator int() { return 0; } bool abort() { return false; } @@ -898,7 +1066,7 @@ struct action struct print : action { - print( std::ostream & os ) : action( os ) {} + print( std::ostream & out ) : action( out ) {} print & operator()( test testing ) { @@ -924,7 +1092,7 @@ struct ptags : action { std::set result; - ptags( std::ostream & os ) : action( os ) {} + ptags( std::ostream & out ) : action( out ), result() {} ptags & operator()( test testing ) { @@ -944,13 +1112,13 @@ struct count : action { int n = 0; - count( std::ostream & os ) : action( os ) {} + count( std::ostream & out ) : action( out ) {} count & operator()( test ) { ++n; return *this; } ~count() { - os << n << " selected " << pluralise(n, "test") << "\n"; + os << n << " selected " << pluralise("test", n) << "\n"; } }; @@ -962,28 +1130,27 @@ struct timer double elapsed_seconds() const { - return 1e-6 * std::chrono::duration_cast< std::chrono::microseconds >( time::now() - start ).count(); + return 1e-6 * static_cast( std::chrono::duration_cast< std::chrono::microseconds >( time::now() - start ).count() ); } }; struct times : action { env output; - options option; int selected = 0; int failures = 0; timer total; - times( std::ostream & os, options option ) - : action( os ), output( os, option.pass ), option( option ), total() + times( std::ostream & out, options option ) + : action( out ), output( out, option ), total() { os << std::setfill(' ') << std::fixed << std::setprecision( lest_FEATURE_TIME_PRECISION ); } operator int() { return failures; } - bool abort() { return option.abort && failures > 0; } + bool abort() { return output.abort() && failures > 0; } times & operator()( test testing ) { @@ -1012,16 +1179,15 @@ struct times : action struct confirm : action { env output; - options option; int selected = 0; int failures = 0; - confirm( std::ostream & os, options option ) - : action( os ), output( os, option.pass ), option( option ) {} + confirm( std::ostream & out, options option ) + : action( out ), output( out, option ) {} operator int() { return failures; } - bool abort() { return option.abort && failures > 0; } + bool abort() { return output.abort() && failures > 0; } confirm & operator()( test testing ) { @@ -1031,7 +1197,7 @@ struct confirm : action } catch( message const & e ) { - ++failures; report( os, e, testing.name ); + ++failures; report( os, e, output.context() ); } return *this; } @@ -1040,16 +1206,16 @@ struct confirm : action { if ( failures > 0 ) { - os << failures << " out of " << selected << " selected " << pluralise(selected, "test") << " " << colourise( "failed.\n" ); + os << failures << " out of " << selected << " selected " << pluralise("test", selected) << " " << colourise( "failed.\n" ); } - else if ( option.pass ) + else if ( output.pass() ) { - os << "All " << selected << " selected " << pluralise(selected, "test") << " " << colourise( "passed.\n" ); + os << "All " << selected << " selected " << pluralise("test", selected) << " " << colourise( "passed.\n" ); } } }; -template +template< typename Action > bool abort( Action & perform ) { return perform.abort(); @@ -1085,7 +1251,7 @@ inline void shuffle( tests & specification, options option ) inline int stoi( text num ) { - return std::strtol( num.c_str(), NULL, 10 ); + return static_cast( std::strtol( num.c_str(), nullptr, 10 ) ); } inline bool is_number( text arg ) @@ -1096,7 +1262,7 @@ inline bool is_number( text arg ) inline seed_t seed( text opt, text arg ) { if ( is_number( arg ) ) - return lest::stoi( arg ); + return static_cast( lest::stoi( arg ) ); if ( arg == "time" ) return static_cast( std::chrono::high_resolution_clock::now().time_since_epoch().count() ); @@ -1145,6 +1311,7 @@ inline auto split_arguments( texts args ) -> std::tuple else if ( opt == "-l" || "--list-tests" == opt ) { option.list = true; continue; } else if ( opt == "-t" || "--time" == opt ) { option.time = true; continue; } else if ( opt == "-p" || "--pass" == opt ) { option.pass = true; continue; } + else if ( opt == "-v" || "--verbose" == opt ) { option.verbose = true; continue; } else if ( "--version" == opt ) { option.version = true; continue; } else if ( opt == "--order" && "declared" == val ) { /* by definition */ ; continue; } else if ( opt == "--order" && "lexical" == val ) { option.lexical = true; continue; } @@ -1171,7 +1338,8 @@ inline int usage( std::ostream & os ) " -l, --list-tests list selected tests\n" " -p, --pass also report passing tests\n" " -t, --time list duration of selected tests\n" - " --order=declared use source code test order\n" + " -v, --verbose also report passing or failing sections\n" + " --order=declared use source code test order (default)\n" " --order=lexical use lexical sort test order\n" " --order=random use random test order\n" " --random-seed=n use n for random generator seed\n" @@ -1182,7 +1350,7 @@ inline int usage( std::ostream & os ) "\n" "Test specification:\n" " \"@\", \"*\" all tests, unless excluded\n" - " empty all tests, unless tagged [hide] or [.]\n" + " empty all tests, unless tagged [hide] or [.optional-name]\n" #if lest_FEATURE_REGEX_SEARCH " \"re\" select tests that match regular expression\n" " \"!re\" omit tests that match regular expression\n" @@ -1202,7 +1370,7 @@ inline text compiler() #elif defined (__GNUC__ ) os << "gcc " << __GNUC__ << "." << __GNUC_MINOR__ << "." << __GNUC_PATCHLEVEL__; #elif defined ( _MSC_VER ) - os << "MSVC " << (_MSC_VER / 100 - 6 ) << " (" << _MSC_VER << ")"; + os << "MSVC " << (_MSC_VER / 100 - 5 - (_MSC_VER < 1900)) << " (" << _MSC_VER << ")"; #else os << "[compiler]"; #endif @@ -1248,19 +1416,20 @@ inline int run( tests specification, int argc, char * argv[], std::ostream & os return run( specification, texts( argv + 1, argv + argc ), os ); } -template +template< std::size_t N > int run( test const (&specification)[N], texts arguments, std::ostream & os = std::cout ) { - return run( tests( specification, specification + N ), arguments, os ); + std::cout.sync_with_stdio( false ); + return (std::min)( run( tests( specification, specification + N ), arguments, os ), exit_max_value ); } -template +template< std::size_t N > int run( test const (&specification)[N], std::ostream & os = std::cout ) { return run( tests( specification, specification + N ), {}, os ); } -template +template< std::size_t N > int run( test const (&specification)[N], int argc, char * argv[], std::ostream & os = std::cout ) { return run( tests( specification, specification + N ), texts( argv + 1, argv + argc ), os ); @@ -1268,4 +1437,10 @@ int run( test const (&specification)[N], int argc, char * argv[], std::ostream & } // namespace lest -#endif // LEST_LEST_H_INCLUDED \ No newline at end of file +#ifdef __clang__ +# pragma clang diagnostic pop +#elif defined __GNUC__ +# pragma GCC diagnostic pop +#endif + +#endif // LEST_LEST_HPP_INCLUDED diff --git a/src/xml b/src/xml index b87c826..f8e187d 160000 --- a/src/xml +++ b/src/xml @@ -1 +1 @@ -Subproject commit b87c8262361f72fff05ca0b1320f8a1d9160b5cd +Subproject commit f8e187d07f9d8045bb41883d3e1c7d0ed6f241e4