try to compile with clang and on osx in travis

This commit is contained in:
Patrick Brosi 2018-07-23 02:46:31 +02:00
parent 1a9482a445
commit df2ab2df77
10 changed files with 485 additions and 269 deletions

View file

@ -1,6 +1,12 @@
language: generic language: cpp
sudo: false
dist: trusty os:
- linux
- osx
compiler:
- gcc
- clang
addons: addons:
apt: apt:
@ -21,4 +27,4 @@ script:
notifications: notifications:
email: email:
on_success: never on_success: never
on_failure: always on_failure: never

View file

@ -20,7 +20,12 @@ if(OPENMP_FOUND)
endif() endif()
# set compiler flags, see http://stackoverflow.com/questions/7724569/debug-vs-release-in-cmake # 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_DEBUG "-Og -g -DLOGLEVEL=3")
set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS} -DLOGLEVEL=2") set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS} -DLOGLEVEL=2")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} -DLOGLEVEL=2") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} -DLOGLEVEL=2")

@ -1 +1 @@
Subproject commit 3a462c37358da19f10e89f77fb7a277d69c6c4dc Subproject commit eae77cfdfb7eb64f87ddd48204ea9b90d6d5cfd8

View file

@ -29,7 +29,6 @@ class Collector {
public: public:
Collector(const std::string& evalOutPath, const std::vector<double>& dfBins) Collector(const std::string& evalOutPath, const std::vector<double>& dfBins)
: _noOrigShp(0), : _noOrigShp(0),
_noMatchShp(0),
_fdSum(0), _fdSum(0),
_unmatchedSegSum(0), _unmatchedSegSum(0),
_unmatchedSegLengthSum(0), _unmatchedSegLengthSum(0),
@ -70,7 +69,6 @@ class Collector {
std::map<const Shape*, std::map<const Shape*, std::pair<size_t, double> > > std::map<const Shape*, std::map<const Shape*, std::pair<size_t, double> > >
_dACache; _dACache;
size_t _noOrigShp; size_t _noOrigShp;
size_t _noMatchShp;
double _fdSum; double _fdSum;
size_t _unmatchedSegSum; size_t _unmatchedSegSum;

View file

@ -2,7 +2,13 @@
// Chair of Algorithms and Data Structures. // Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de> // Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifdef _OPENMP
#include <omp.h> #include <omp.h>
#else
#define omp_get_thread_num() 0
#define omp_get_num_procs() 1
#endif
#include <algorithm> #include <algorithm>
#include <fstream> #include <fstream>
#include <limits> #include <limits>
@ -193,8 +199,7 @@ double CombCostFunc::operator()(const router::Edge* from, const router::Node* n,
} }
// _____________________________________________________________________________ // _____________________________________________________________________________
Router::Router(const trgraph::Graph& g, size_t numThreads) Router::Router(size_t numThreads) : _cache(numThreads) {
: _g(g), _cache(numThreads) {
for (size_t i = 0; i < numThreads; i++) { for (size_t i = 0; i < numThreads; i++) {
_cache[i] = new Cache(); _cache[i] = new Cache();
} }

View file

@ -135,7 +135,7 @@ struct CombCostFunc
class Router { class Router {
public: public:
// Init this router with caches for numThreads threads // Init this router with caches for numThreads threads
Router(const trgraph::Graph& g, size_t numThreads); explicit Router(size_t numThreads);
~Router(); ~Router();
// Find the most likely path through the graph for a node candidate route. // Find the most likely path through the graph for a node candidate route.
@ -160,8 +160,6 @@ class Router {
size_t getCacheNumber() const; size_t getCacheNumber() const;
private: private:
const trgraph::Graph& _g;
mutable std::vector<Cache*> _cache; mutable std::vector<Cache*> _cache;
HopBand getHopBand(const NodeCandGroup& a, const NodeCandGroup& b, HopBand getHopBand(const NodeCandGroup& a, const NodeCandGroup& b,
const RoutingAttrs& rAttrs, const RoutingOpts& rOpts, const RoutingAttrs& rAttrs, const RoutingOpts& rOpts,

View file

@ -2,7 +2,13 @@
// Chair of Algorithms and Data Structures. // Chair of Algorithms and Data Structures.
// Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de> // Authors: Patrick Brosi <brosi@informatik.uni-freiburg.de>
#ifdef _OPENMP
#include <omp.h> #include <omp.h>
#else
#define omp_get_thread_num() 0
#define omp_get_num_procs() 1
#endif
#include <map> #include <map>
#include <mutex> #include <mutex>
#include <unordered_map> #include <unordered_map>
@ -49,7 +55,7 @@ ShapeBuilder::ShapeBuilder(Feed* feed, MOTs mots,
_motCfg(motCfg), _motCfg(motCfg),
_ecoll(ecoll), _ecoll(ecoll),
_cfg(cfg), _cfg(cfg),
_crouter(_g, omp_get_num_procs()), _crouter(omp_get_num_procs()),
_curShpCnt(0) { _curShpCnt(0) {
_numThreads = _crouter.getCacheNumber(); _numThreads = _crouter.getCacheNumber();
writeMotStops(); writeMotStops();

View file

@ -23,6 +23,7 @@ using namespace util::graph;
const lest::test specification[] = { const lest::test specification[] = {
// ___________________________________________________________________________ // ___________________________________________________________________________
{
CASE("atof") { CASE("atof") {
EXPECT(util::atof("45.534215") == approx(45.534215)); EXPECT(util::atof("45.534215") == approx(45.534215));
EXPECT(util::atof("5.534") == approx(5.534)); EXPECT(util::atof("5.534") == approx(5.534));
@ -33,9 +34,10 @@ CASE("atof") {
// TODO: more test cases // TODO: more test cases
}, }},
// ___________________________________________________________________________ // ___________________________________________________________________________
{
CASE("dirgraph") { CASE("dirgraph") {
DirGraph<int, int> g; DirGraph<int, int> g;
@ -74,9 +76,10 @@ CASE("dirgraph") {
EXPECT(a->getDeg() == (size_t)1); EXPECT(a->getDeg() == (size_t)1);
// TODO: more test cases // TODO: more test cases
}, }},
// ___________________________________________________________________________ // ___________________________________________________________________________
{
CASE("unddirgraph") { CASE("unddirgraph") {
UndirGraph<int, int> g; UndirGraph<int, int> g;
@ -118,9 +121,10 @@ CASE("unddirgraph") {
// TODO: more test cases // TODO: more test cases
}, }},
// ___________________________________________________________________________ // ___________________________________________________________________________
{
CASE("grid") { CASE("grid") {
Grid<int, Line, double> g(.5, .5, Box<double>(Point<double>(0, 0), Point<double>(3, 3))); Grid<int, Line, double> g(.5, .5, Box<double>(Point<double>(0, 0), Point<double>(3, 3)));
@ -151,9 +155,10 @@ CASE("grid") {
// TODO: more test cases // TODO: more test cases
}, }},
// ___________________________________________________________________________ // ___________________________________________________________________________
{
CASE("densify") { CASE("densify") {
Line<double> a; Line<double> a;
a.push_back(Point<double>(1, 1)); a.push_back(Point<double>(1, 1));
@ -179,9 +184,10 @@ CASE("densify") {
dense = util::geo::simplify(dense, 0.1); dense = util::geo::simplify(dense, 0.1);
EXPECT(dense.size() == (size_t)3); EXPECT(dense.size() == (size_t)3);
}, }},
// ___________________________________________________________________________ // ___________________________________________________________________________
{
CASE("summed frechet distance") { CASE("summed frechet distance") {
Line<double> a; Line<double> a;
a.push_back(Point<double>(1, 1)); a.push_back(Point<double>(1, 1));
@ -203,9 +209,10 @@ CASE("summed frechet distance") {
double fd = util::geo::accFrechetDistC(a, b, 0.1); double fd = util::geo::accFrechetDistC(a, b, 0.1);
EXPECT(fd == approx(2)); EXPECT(fd == approx(2));
}, }},
// ___________________________________________________________________________ // ___________________________________________________________________________
{
CASE("frechet distance") { CASE("frechet distance") {
Line<double> e; Line<double> e;
e.push_back(Point<double>(1, 1)); e.push_back(Point<double>(1, 1));
@ -268,9 +275,10 @@ CASE("frechet distance") {
fd = util::geo::frechetDist(g, h, 0.1); fd = util::geo::frechetDist(g, h, 0.1);
EXPECT(fd == approx(1)); EXPECT(fd == approx(1));
}, }},
// ___________________________________________________________________________ // ___________________________________________________________________________
{
CASE("geo box alignment") { CASE("geo box alignment") {
Line<double> a; Line<double> a;
a.push_back(Point<double>(1, 1)); a.push_back(Point<double>(1, 1));
@ -304,32 +312,36 @@ CASE("geo box alignment") {
EXPECT(parallelity(box, ml) == approx(0)); EXPECT(parallelity(box, ml) == approx(0));
ml = rotate(ml, 45); ml = rotate(ml, 45);
EXPECT(parallelity(box, ml) == approx(1)); EXPECT(parallelity(box, ml) == approx(1));
}, }},
// ___________________________________________________________________________ // ___________________________________________________________________________
{
CASE("url decode") { CASE("url decode") {
EXPECT("zürich" == util::urlDecode("z%C3%BCrich")); EXPECT("zürich" == util::urlDecode("z%C3%BCrich"));
EXPECT("!@$%^*()" == util::urlDecode("!%40%24%25%5E*()")); EXPECT("!@$%^*()" == util::urlDecode("!%40%24%25%5E*()"));
EXPECT("Løkken" == util::urlDecode("L%C3%B8kken")); EXPECT("Løkken" == util::urlDecode("L%C3%B8kken"));
EXPECT("á é" == util::urlDecode("%C3%A1%20%C3%A9")); EXPECT("á é" == util::urlDecode("%C3%A1%20%C3%A9"));
EXPECT("á é" == util::urlDecode("%C3%A1+%C3%A9")); EXPECT("á é" == util::urlDecode("%C3%A1+%C3%A9"));
}, }},
// ___________________________________________________________________________ // ___________________________________________________________________________
{
CASE("json escape") { CASE("json escape") {
EXPECT("Hello\\\\Goodbye!" == util::jsonStringEscape("Hello\\Goodbye!")); EXPECT("Hello\\\\Goodbye!" == util::jsonStringEscape("Hello\\Goodbye!"));
EXPECT("\\\"Hello\\\"" == util::jsonStringEscape("\"Hello\"")); EXPECT("\\\"Hello\\\"" == util::jsonStringEscape("\"Hello\""));
}, }},
// ___________________________________________________________________________ // ___________________________________________________________________________
{
CASE("split") { CASE("split") {
EXPECT(util::split("hello,again", ',').size() == (size_t)2); EXPECT(util::split("hello,again", ',').size() == (size_t)2);
EXPECT(util::split("hello,,again", ',').size() == (size_t)3); EXPECT(util::split("hello,,again", ',').size() == (size_t)3);
EXPECT(util::split("hello", ',').size() == (size_t)1); EXPECT(util::split("hello", ',').size() == (size_t)1);
EXPECT(util::split("", ',').size() == (size_t)0); EXPECT(util::split("", ',').size() == (size_t)0);
}, }},
// ___________________________________________________________________________ // ___________________________________________________________________________
{
CASE("editdist") { CASE("editdist") {
EXPECT(util::editDist("hello", "mello") == (size_t)1); EXPECT(util::editDist("hello", "mello") == (size_t)1);
EXPECT(util::editDist("mello", "hello") == (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("xabcd", "abcde") == (size_t)2);
EXPECT(util::editDist("abcd", "abcdes") == (size_t)2); EXPECT(util::editDist("abcd", "abcdes") == (size_t)2);
EXPECT(util::editDist("hello", "hello") == (size_t)0); EXPECT(util::editDist("hello", "hello") == (size_t)0);
}, }},
// ___________________________________________________________________________ // ___________________________________________________________________________
{
CASE("toString") { CASE("toString") {
EXPECT(util::toString(34) == "34"); EXPECT(util::toString(34) == "34");
EXPECT(util::toString("34") == "34"); EXPECT(util::toString("34") == "34");
}, }},
// ___________________________________________________________________________ // ___________________________________________________________________________
{
CASE("replace") { CASE("replace") {
std::string a("lorem ipsum ipsum lorem"); std::string a("lorem ipsum ipsum lorem");
@ -380,9 +394,10 @@ CASE("replace") {
EXPECT(!util::replaceAll(b, "", "ee")); EXPECT(!util::replaceAll(b, "", "ee"));
EXPECT(b == "loree aaaau aaaau loree"); EXPECT(b == "loree aaaau aaaau loree");
}, }},
// ___________________________________________________________________________ // ___________________________________________________________________________
{
CASE("Edge-based Dijkstra directed, 1 to all") { CASE("Edge-based Dijkstra directed, 1 to all") {
DirGraph<std::string, int> g; DirGraph<std::string, int> g;
@ -436,9 +451,10 @@ CASE("Edge-based Dijkstra directed, 1 to all") {
int single = EDijkstra::shortestPath(u.first, eBC, cFunc); int single = EDijkstra::shortestPath(u.first, eBC, cFunc);
EXPECT(single == u.second); EXPECT(single == u.second);
} }
}, }},
// ___________________________________________________________________________ // ___________________________________________________________________________
{
CASE("Edge-based Dijkstra undirected, edge 1 to 1") { CASE("Edge-based Dijkstra undirected, edge 1 to 1") {
UndirGraph<std::string, int> g; UndirGraph<std::string, int> g;
@ -494,9 +510,10 @@ CASE("Edge-based Dijkstra undirected, edge 1 to 1") {
cost = EDijkstra::shortestPath(eAB, b, cFunc, &resE, &res); cost = EDijkstra::shortestPath(eAB, b, cFunc, &resE, &res);
EXPECT(cost == 0); EXPECT(cost == 0);
}, }},
// ___________________________________________________________________________ // ___________________________________________________________________________
{
CASE("Edge-based Dijkstra undirected, edge 1 to n") { CASE("Edge-based Dijkstra undirected, edge 1 to n") {
UndirGraph<std::string, int> g; UndirGraph<std::string, int> g;
@ -542,9 +559,10 @@ CASE("Edge-based Dijkstra undirected, edge 1 to n") {
EDijkstra::EList<std::string, int> resE; EDijkstra::EList<std::string, int> resE;
int cost = EDijkstra::shortestPath(eAB, tos, cFunc, &resE, &res); int cost = EDijkstra::shortestPath(eAB, tos, cFunc, &resE, &res);
EXPECT(cost == 0); EXPECT(cost == 0);
}, }},
// ___________________________________________________________________________ // ___________________________________________________________________________
{
CASE("Edge-based Dijkstra undirected, 1 to n") { CASE("Edge-based Dijkstra undirected, 1 to n") {
UndirGraph<std::string, int> g; UndirGraph<std::string, int> g;
@ -596,9 +614,10 @@ CASE("Edge-based Dijkstra undirected, 1 to n") {
EXPECT(resE[eDC]->size() == (size_t)3); EXPECT(resE[eDC]->size() == (size_t)3);
EXPECT(res[eED]->size() == (size_t)3); EXPECT(res[eED]->size() == (size_t)3);
}, }},
// ___________________________________________________________________________ // ___________________________________________________________________________
{
CASE("Edge-based Dijkstra undirected") { CASE("Edge-based Dijkstra undirected") {
UndirGraph<std::string, int> g; UndirGraph<std::string, int> g;
@ -668,9 +687,10 @@ CASE("Edge-based Dijkstra undirected") {
cost = EDijkstra::shortestPath(a, d, cFunc, &res); cost = EDijkstra::shortestPath(a, d, cFunc, &res);
EXPECT(cost == 2); EXPECT(cost == 2);
}, }},
// ___________________________________________________________________________ // ___________________________________________________________________________
{
CASE("Edge-based Dijkstra") { CASE("Edge-based Dijkstra") {
DirGraph<int, int> g; DirGraph<int, int> g;
@ -715,9 +735,10 @@ CASE("Edge-based Dijkstra") {
cost = EDijkstra::shortestPath(a, d, cFunc, &res); cost = EDijkstra::shortestPath(a, d, cFunc, &res);
EXPECT(cost == 2); EXPECT(cost == 2);
}, }},
// ___________________________________________________________________________ // ___________________________________________________________________________
{
CASE("Dijkstra") { CASE("Dijkstra") {
DirGraph<int, int> g; DirGraph<int, int> g;
@ -779,9 +800,10 @@ CASE("Dijkstra") {
EXPECT(costs[c] == 1); EXPECT(costs[c] == 1);
EXPECT(costs[d] == 2); EXPECT(costs[d] == 2);
EXPECT(costs[x] == 999); EXPECT(costs[x] == 999);
}, }},
// ___________________________________________________________________________ // ___________________________________________________________________________
{
CASE("nullable") { CASE("nullable") {
{ {
util::Nullable<std::string> nullable; util::Nullable<std::string> nullable;
@ -840,9 +862,10 @@ CASE("nullable") {
EXPECT_THROWS(nullable == voidnull); EXPECT_THROWS(nullable == voidnull);
} }
}, }},
// ___________________________________________________________________________ // ___________________________________________________________________________
{
CASE("geometry") { CASE("geometry") {
geo::Point<double> a(1, 2); geo::Point<double> a(1, 2);
geo::Point<double> b(2, 3); geo::Point<double> b(2, 3);
@ -1188,7 +1211,7 @@ CASE("geometry") {
EXPECT(geo::contains(geo::Polygon<double>({{0.0, 0.0}, {1.0, 1.0}, {1.5, 0.5}, {0.5, -0.5}}), geo::convexHull(obox))); EXPECT(geo::contains(geo::Polygon<double>({{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) { int main(int argc, char** argv) {

View file

@ -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 // 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 // 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 // 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 #ifndef LEST_LEST_HPP_INCLUDED
#define LEST_LEST_H_INCLUDED #define LEST_LEST_HPP_INCLUDED
#include <algorithm> #include <algorithm>
#include <chrono> #include <chrono>
@ -31,14 +31,7 @@
#include <cmath> #include <cmath>
#include <cstddef> #include <cstddef>
#ifdef __clang__ #define lest_VERSION "1.33.1"
# 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"
#ifndef lest_FEATURE_AUTO_REGISTER #ifndef lest_FEATURE_AUTO_REGISTER
# define lest_FEATURE_AUTO_REGISTER 0 # define lest_FEATURE_AUTO_REGISTER 0
@ -60,16 +53,71 @@
#define lest_FEATURE_TIME_PRECISION 0 #define lest_FEATURE_TIME_PRECISION 0
#endif #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 #if lest_FEATURE_REGEX_SEARCH
# include <regex> # include <regex>
#endif #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 ) #if ! defined( lest_NO_SHORT_MACRO_NAMES ) && ! defined( lest_NO_SHORT_ASSERTION_NAMES )
# define MODULE lest_MODULE # define MODULE lest_MODULE
# if ! lest_FEATURE_AUTO_REGISTER # if ! lest_FEATURE_AUTO_REGISTER
# define CASE lest_CASE # define CASE lest_CASE
# define TEST lest_TEST # define CASE_ON lest_CASE_ON
# define SCENARIO lest_SCENARIO
# endif # endif
# define SETUP lest_SETUP # define SETUP lest_SETUP
@ -81,7 +129,6 @@
# define EXPECT_THROWS lest_EXPECT_THROWS # define EXPECT_THROWS lest_EXPECT_THROWS
# define EXPECT_THROWS_AS lest_EXPECT_THROWS_AS # define EXPECT_THROWS_AS lest_EXPECT_THROWS_AS
# define SCENARIO lest_SCENARIO
# define GIVEN lest_GIVEN # define GIVEN lest_GIVEN
# define WHEN lest_WHEN # define WHEN lest_WHEN
# define THEN lest_THEN # define THEN lest_THEN
@ -89,40 +136,48 @@
# define AND_THEN lest_AND_THEN # define AND_THEN lest_AND_THEN
#endif #endif
#define lest_SCENARIO( sketch ) lest_CASE( "Scenario: " sketch ) #if lest_FEATURE_AUTO_REGISTER
#define lest_GIVEN( context ) lest_SETUP( "Given: " context ) #define lest_SCENARIO( specification, sketch ) lest_CASE( specification, lest::text("Scenario: ") + sketch )
#define lest_WHEN( story ) lest_SECTION( " When: " story ) #else
#define lest_THEN( story ) lest_SECTION( " Then: " story ) #define lest_SCENARIO( sketch ) lest_CASE( lest::text("Scenario: ") + sketch )
#define lest_AND_WHEN( story ) lest_SECTION( " And: " story ) #endif
#define lest_AND_THEN( story ) lest_SECTION( " And: " story ) #define lest_GIVEN( context ) lest_SETUP( lest::text(" Given: ") + context )
#define lest_WHEN( story ) lest_SECTION( lest::text(" When: ") + story )
#define lest_MODULE( specification, module ) \ #define lest_THEN( story ) lest_SECTION( lest::text(" Then: ") + story )
namespace { lest::add_module _( specification, module ); } #define lest_AND_WHEN( story ) lest_SECTION( lest::text("And then: ") + story )
#define lest_AND_THEN( story ) lest_SECTION( lest::text("And then: ") + story )
#define lest_TEST \
lest_CASE
#if lest_FEATURE_AUTO_REGISTER #if lest_FEATURE_AUTO_REGISTER
# define lest_CASE( specification, proposition ) \ # 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 ) ); } \ 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, ... ) \ # define lest_CASE( proposition ) \
proposition, [__VA_ARGS__]( lest::env & $ ) 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 ) \ #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 ) \ #define lest_SECTION( proposition ) \
lest_SUPPRESS_WSHADOW \
static int lest_UNIQUE( id ) = 0; \ static int lest_UNIQUE( id ) = 0; \
if ( lest::guard $run = lest::guard( lest_UNIQUE( id ), $section, $count ) ) \ if ( lest::guard( lest_UNIQUE( id ), lest__section, lest__count ) ) \
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_section( lest_env, proposition ); lest__ctx_section; ) \
lest_RESTORE_WARNINGS
#define lest_EXPECT( expr ) \ #define lest_EXPECT( expr ) \
do { \ do { \
@ -130,8 +185,8 @@
{ \ { \
if ( lest::result score = lest_DECOMPOSE( expr ) ) \ if ( lest::result score = lest_DECOMPOSE( expr ) ) \
throw lest::failure{ lest_LOCATION, #expr, score.decomposition }; \ throw lest::failure{ lest_LOCATION, #expr, score.decomposition }; \
else if ( $.pass ) \ else if ( lest_env.pass() ) \
lest::report( $.os, lest::passing{ lest_LOCATION, #expr, score.decomposition }, $.testing ); \ lest::report( lest_env.os, lest::passing{ lest_LOCATION, #expr, score.decomposition }, lest_env.context() ); \
} \ } \
catch(...) \ catch(...) \
{ \ { \
@ -145,8 +200,8 @@
{ \ { \
if ( lest::result score = lest_DECOMPOSE( expr ) ) \ if ( lest::result score = lest_DECOMPOSE( expr ) ) \
{ \ { \
if ( $.pass ) \ if ( lest_env.pass() ) \
lest::report( $.os, lest::passing{ lest_LOCATION, lest::not_expr( #expr ), lest::not_expr( score.decomposition ) }, $.testing ); \ lest::report( lest_env.os, lest::passing{ lest_LOCATION, lest::not_expr( #expr ), lest::not_expr( score.decomposition ) }, lest_env.context() ); \
} \ } \
else \ else \
throw lest::failure{ lest_LOCATION, lest::not_expr( #expr ), lest::not_expr( score.decomposition ) }; \ throw lest::failure{ lest_LOCATION, lest::not_expr( #expr ), lest::not_expr( score.decomposition ) }; \
@ -162,14 +217,16 @@
{ \ { \
try \ try \
{ \ { \
lest_SUPPRESS_WUNUSED \
expr; \ expr; \
lest_RESTORE_WARNINGS \
} \ } \
catch (...) \ catch (...) \
{ \ { \
lest::inform( lest_LOCATION, #expr ); \ lest::inform( lest_LOCATION, #expr ); \
} \ } \
if ( $.pass ) \ if ( lest_env.pass() ) \
lest::report( $.os, lest::got_none( lest_LOCATION, #expr ), $.testing ); \ lest::report( lest_env.os, lest::got_none( lest_LOCATION, #expr ), lest_env.context() ); \
} while ( lest::is_false() ) } while ( lest::is_false() )
#define lest_EXPECT_THROWS( expr ) \ #define lest_EXPECT_THROWS( expr ) \
@ -177,12 +234,14 @@
{ \ { \
try \ try \
{ \ { \
lest_SUPPRESS_WUNUSED \
expr; \ expr; \
lest_RESTORE_WARNINGS \
} \ } \
catch (...) \ catch (...) \
{ \ { \
if ( $.pass ) \ if ( lest_env.pass() ) \
lest::report( $.os, lest::got{ lest_LOCATION, #expr }, $.testing ); \ lest::report( lest_env.os, lest::got{ lest_LOCATION, #expr }, lest_env.context() ); \
break; \ break; \
} \ } \
throw lest::expected{ lest_LOCATION, #expr }; \ throw lest::expected{ lest_LOCATION, #expr }; \
@ -194,12 +253,14 @@
{ \ { \
try \ try \
{ \ { \
lest_SUPPRESS_WUNUSED \
expr; \ expr; \
lest_RESTORE_WARNINGS \
} \ } \
catch ( excpt & ) \ catch ( excpt & ) \
{ \ { \
if ( $.pass ) \ if ( lest_env.pass() ) \
lest::report( $.os, lest::got{ lest_LOCATION, #expr, lest::of_type( #excpt ) }, $.testing ); \ lest::report( lest_env.os, lest::got{ lest_LOCATION, #expr, lest::of_type( #excpt ) }, lest_env.context() ); \
break; \ break; \
} \ } \
catch (...) {} \ catch (...) {} \
@ -211,7 +272,7 @@
#define lest_UNIQUE2( name, line ) lest_UNIQUE3( name, line ) #define lest_UNIQUE2( name, line ) lest_UNIQUE3( name, line )
#define lest_UNIQUE3( name, line ) 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_FUNCTION lest_UNIQUE(__lest_function__ )
#define lest_REGISTRAR lest_UNIQUE(__lest_registrar__ ) #define lest_REGISTRAR lest_UNIQUE(__lest_registrar__ )
@ -220,6 +281,8 @@
namespace lest { namespace lest {
const int exit_max_value = 255;
using text = std::string; using text = std::string;
using texts = std::vector<text>; using texts = std::vector<text>;
@ -231,8 +294,8 @@ struct test
std::function<void( env & )> behaviour; std::function<void( env & )> behaviour;
#if lest_FEATURE_AUTO_REGISTER #if lest_FEATURE_AUTO_REGISTER
test( text name, std::function<void( env & )> behaviour ) test( text name_, std::function<void( env & )> behaviour_ )
: name( name ), behaviour( behaviour ) {} : name( name_), behaviour( behaviour_) {}
#endif #endif
}; };
@ -266,6 +329,10 @@ struct result
const bool passed; const bool passed;
const text decomposition; const text decomposition;
template< typename T >
result( T const & passed_, text decomposition_)
: passed( !!passed_), decomposition( decomposition_) {}
explicit operator bool() { return ! passed; } explicit operator bool() { return ! passed; }
}; };
@ -274,15 +341,15 @@ struct location
const text file; const text file;
const int line; const int line;
location( text file, int line ) location( text file_, int line_)
: file( file ), line( line ) {} : file( file_), line( line_) {}
}; };
struct comment struct comment
{ {
const text info; const text info;
comment( text info ) : info( info ) {} comment( text info_) : info( info_) {}
explicit operator bool() { return ! info.empty(); } explicit operator bool() { return ! info.empty(); }
}; };
@ -294,55 +361,55 @@ struct message : std::runtime_error
~message() throw() {} // GCC 4.6 ~message() throw() {} // GCC 4.6
message( text kind, location where, text expr, text note = "" ) message( text kind_, location where_, text expr_, text note_ = "" )
: std::runtime_error( expr ), kind( kind ), where( where ), note( note ) {} : std::runtime_error( expr_), kind( kind_), where( where_), note( note_) {}
}; };
struct failure : message struct failure : message
{ {
failure( location where, text expr, text decomposition ) failure( location where_, text expr_, text decomposition_)
: message{ "failed", where, expr + " for " + decomposition } {} : message{ "failed", where_, expr_ + " for " + decomposition_ } {}
}; };
struct success : message struct success : message
{ {
// using message::message; // VC is lagging here // using message::message; // VC is lagging here
success( text kind, location where, text expr, text note = "" ) success( text kind_, location where_, text expr_, text note_ = "" )
: message( kind, where, expr, note ) {} : message( kind_, where_, expr_, note_ ) {}
}; };
struct passing : success struct passing : success
{ {
passing( location where, text expr, text decomposition ) passing( location where_, text expr_, text decomposition_ )
: success( "passed", where, expr + " for " + decomposition ) {} : success( "passed", where_, expr_ + " for " + decomposition_) {}
}; };
struct got_none : success struct got_none : success
{ {
got_none( location where, text expr ) got_none( location where_, text expr_ )
: success( "passed: got no exception", where, expr ) {} : success( "passed: got no exception", where_, expr_ ) {}
}; };
struct got : success struct got : success
{ {
got( location where, text expr ) got( location where_, text expr_)
: success( "passed: got exception", where, expr ) {} : success( "passed: got exception", where_, expr_) {}
got( location where, text expr, text excpt ) got( location where_, text expr_, text excpt_)
: success( "passed: got exception " + excpt, where, expr ) {} : success( "passed: got exception " + excpt_, where_, expr_) {}
}; };
struct expected : message struct expected : message
{ {
expected( location where, text expr, text excpt = "" ) expected( location where_, text expr_, text excpt_ = "" )
: message{ "failed: didn't get exception", where, expr, excpt } {} : message{ "failed: didn't get exception", where_, expr_, excpt_ } {}
}; };
struct unexpected : message struct unexpected : message
{ {
unexpected( location where, text expr, text note = "" ) unexpected( location where_, text expr_, text note_ = "" )
: message{ "failed: got unexpected exception", where, expr, note } {} : message{ "failed: got unexpected exception", where_, expr_, note_ } {}
}; };
struct guard struct guard
@ -350,8 +417,8 @@ struct guard
int & id; int & id;
int const & section; int const & section;
guard( int & id, int const & section, int & count ) guard( int & id_, int const & section_, int & count )
: id( id ), section( section ) : id( id_), section( section_)
{ {
if ( section == 0 ) if ( section == 0 )
id = count++ - 1; id = count++ - 1;
@ -371,12 +438,12 @@ public:
static approx custom() { return approx( 0 ); } static approx custom() { return approx( 0 ); }
approx operator()( double magnitude ) approx operator()( double new_magnitude )
{ {
approx approx ( magnitude ); approx appr( new_magnitude );
approx.epsilon( epsilon_ ); appr.epsilon( epsilon_ );
approx.scale ( scale_ ); appr.scale ( scale_ );
return approx; return appr;
} }
double magnitude() const { return magnitude_; } double magnitude() const { return magnitude_; }
@ -387,13 +454,18 @@ public:
friend bool operator == ( double lhs, approx const & rhs ) friend bool operator == ( double lhs, approx const & rhs )
{ {
// Thanks to Richard Harris for his help refining this formula. // Thanks to Richard Harris for his help refining this formula.
return std::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 == ( approx const & lhs, double rhs ) { return operator==( rhs, lhs ); }
friend bool operator != ( double lhs, approx const & rhs ) { return !operator==( lhs, rhs ); } 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 != ( 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: private:
double epsilon_; double epsilon_;
double scale_; double scale_;
@ -447,23 +519,23 @@ template<typename T>
auto make_memory_string( T const & item ) -> std::string; auto make_memory_string( T const & item ) -> std::string;
#if lest_FEATURE_LITERAL_SUFFIX #if lest_FEATURE_LITERAL_SUFFIX
inline char const * sfx( char const * text ) { return text; } inline char const * sfx( char const * txt ) { return txt; }
#else #else
inline char const * sfx( char const * ) { return ""; } inline char const * sfx( char const * ) { return ""; }
#endif #endif
inline std::string to_string( std::nullptr_t ) { return "nullptr"; } 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::string const & txt ) { return "\"" + txt + "\"" ; }
inline std::string to_string( std::wstring const & text ) ; #if lest_FEATURE_WSTRING
inline std::string to_string( std::wstring const & txt ) ;
#endif
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 * const txt ) { return txt ? to_string( std::string ( txt ) ) : "{null string}"; }
inline std::string to_string( char * const text ) { return text ? to_string( std::string ( text ) ) : "{null string}"; } inline std::string to_string( char * const txt ) { return txt ? to_string( std::string ( txt ) ) : "{null string}"; }
inline std::string to_string( wchar_t const * const text ) { return text ? to_string( std::wstring( text ) ) : "{null string}"; } #if lest_FEATURE_WSTRING
inline std::string to_string( wchar_t * const text ) { return text ? to_string( std::wstring( text ) ) : "{null string}"; } 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}"; }
inline std::string to_string( char text ) { return "\'" + std::string( 1, text ) + "\'" ; } #endif
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( bool flag ) { return flag ? "true" : "false"; }
@ -478,6 +550,30 @@ inline std::string to_string( unsigned long long value ) { return make_value
inline std::string to_string( double value ) { return make_value_string( value ) ; } 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" ); } inline std::string to_string( float value ) { return make_value_string( value ) + sfx("f" ); }
inline std::string to_string( signed char chr ) { return to_string( static_cast<char>( chr ) ); }
inline std::string to_string( unsigned char chr ) { return to_string( static_cast<char>( chr ) ); }
inline std::string to_string( char chr )
{
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<unsigned int>( chr ) )
: "\'" + std::string( 1, chr ) + "\'" ;
}
template< typename T > template< typename T >
struct is_streamable struct is_streamable
{ {
@ -526,12 +622,16 @@ template <typename T, typename R>
using ForContainer = typename std::enable_if< is_container<T>::value, R>::type; using ForContainer = typename std::enable_if< is_container<T>::value, R>::type;
template< typename T, typename R > template< typename T, typename R >
using ForNonContainer = typename std::enable_if< ! is_container<T>::value, R>::type; using ForNonContainerNonPointer = typename std::enable_if< ! (is_container<T>::value || std::is_pointer<T>::value), R>::type;
template< typename T > template< typename T >
auto make_enum_string( T const & ) -> ForNonEnum<T, std::string> auto make_enum_string( T const & item ) -> ForNonEnum<T, std::string>
{ {
return text("[type: ") + typeid(T).name() + "]"; #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 > template< typename T >
@ -552,20 +652,6 @@ auto make_string( T const & item ) -> ForStreamable<T, std::string>
std::ostringstream os; os << item; return os.str(); std::ostringstream os; os << item; return os.str();
} }
template<typename T>
auto make_string( T * p )-> std::string
{
if ( p ) return make_memory_string( p );
else return "NULL";
}
template<typename C, typename R>
auto make_string( R C::* p ) -> std::string
{
if ( p ) return make_memory_string( p );
else return "NULL";
}
template<typename T1, typename T2> template<typename T1, typename T2>
auto make_string( std::pair<T1,T2> const & pair ) -> std::string auto make_string( std::pair<T1,T2> const & pair ) -> std::string
{ {
@ -598,7 +684,36 @@ auto make_string( std::tuple<TS...> const & tuple ) -> std::string
} }
template< typename T > template< typename T >
auto to_string( T const & item ) -> ForNonContainer<T, std::string> 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<std::ptrdiff_t>( 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<typename C, typename R>
auto to_string( R C::* ptr ) -> std::string
{
return ! ptr ? "nullptr" : make_string( ptr );
}
template< typename T >
auto to_string( T const & item ) -> ForNonContainerNonPointer<T, std::string>
{ {
return make_string( item ); return make_string( item );
} }
@ -616,17 +731,19 @@ auto to_string( C const & cont ) -> ForContainer<C, std::string>
return os.str(); return os.str();
} }
#if lest_FEATURE_WSTRING
inline 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<char>( chr ) : '?'; result += chr <= 0xff ? static_cast<char>( chr ) : '?';
} }
return to_string( result ); return to_string( result );
} }
#endif
template< typename T > template< typename T >
auto make_value_string( T const & value ) -> std::string auto make_value_string( T const & value ) -> std::string
@ -684,9 +801,9 @@ struct expression_lhs
{ {
const L 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< 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 ) }; }
@ -699,7 +816,7 @@ struct expression_lhs
struct expression_decomposer struct expression_decomposer
{ {
template <typename L> template <typename L>
expression_lhs<L const &> operator->* ( L const & operand ) expression_lhs<L const &> operator<< ( L const & operand )
{ {
return expression_lhs<L const &>( operand ); return expression_lhs<L const &>( operand );
} }
@ -754,7 +871,7 @@ inline std::ostream & operator<<( std::ostream & os, colourise words ) { return
inline text colourise( text words ) { return words; } inline text colourise( text words ) { return words; }
#endif #endif
inline text pluralise( int n, text word ) inline text pluralise( text word, int n )
{ {
return n == 1 ? word : word + "s"; 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; }; auto none = []( texts args ) { return args.size() == 0; };
#if lest_FEATURE_REGEX_SEARCH #if lest_FEATURE_REGEX_SEARCH
auto hidden = []( text name ){ return match( { "\\[\\.\\]", "\\[hide\\]" }, name ); }; auto hidden = []( text arg ){ return match( { "\\[\\..*", "\\[hide\\]" }, arg ); };
#else #else
auto hidden = []( text name ){ return match( { "[.]", "[hide]" }, name ); }; auto hidden = []( text arg ){ return match( { "[.", "[hide]" }, arg ); };
#endif #endif
if ( none( include ) ) if ( none( include ) )
@ -864,6 +981,7 @@ struct options
bool pass = false; bool pass = false;
bool lexical = false; bool lexical = false;
bool random = false; bool random = false;
bool verbose = false;
bool version = false; bool version = false;
int repeat = 1; int repeat = 1;
seed_t seed = 0; seed_t seed = 0;
@ -872,24 +990,74 @@ struct options
struct env struct env
{ {
std::ostream & os; std::ostream & os;
bool pass; options opt;
text testing; text testing;
std::vector< text > ctx;
env( std::ostream & os, bool pass ) env( std::ostream & out, options option )
: os( os ), pass( pass ) {} : os( out ), opt( option ), testing(), ctx() {}
env & operator()( text test ) env & operator()( text test )
{ {
testing = test; return *this; 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 struct action
{ {
std::ostream & os; std::ostream & os;
action( std::ostream & out ) : os( out ) {}
action( action const & ) = delete; action( action const & ) = delete;
action( std::ostream & os ) : os( os ) {} void operator=( action const & ) = delete;
operator int() { return 0; } operator int() { return 0; }
bool abort() { return false; } bool abort() { return false; }
@ -898,7 +1066,7 @@ struct action
struct print : action struct print : action
{ {
print( std::ostream & os ) : action( os ) {} print( std::ostream & out ) : action( out ) {}
print & operator()( test testing ) print & operator()( test testing )
{ {
@ -924,7 +1092,7 @@ struct ptags : action
{ {
std::set<text> result; std::set<text> result;
ptags( std::ostream & os ) : action( os ) {} ptags( std::ostream & out ) : action( out ), result() {}
ptags & operator()( test testing ) ptags & operator()( test testing )
{ {
@ -944,13 +1112,13 @@ struct count : action
{ {
int n = 0; int n = 0;
count( std::ostream & os ) : action( os ) {} count( std::ostream & out ) : action( out ) {}
count & operator()( test ) { ++n; return *this; } count & operator()( test ) { ++n; return *this; }
~count() ~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 double elapsed_seconds() const
{ {
return 1e-6 * std::chrono::duration_cast< std::chrono::microseconds >( time::now() - start ).count(); return 1e-6 * static_cast<double>( std::chrono::duration_cast< std::chrono::microseconds >( time::now() - start ).count() );
} }
}; };
struct times : action struct times : action
{ {
env output; env output;
options option;
int selected = 0; int selected = 0;
int failures = 0; int failures = 0;
timer total; timer total;
times( std::ostream & os, options option ) times( std::ostream & out, options option )
: action( os ), output( os, option.pass ), option( option ), total() : action( out ), output( out, option ), total()
{ {
os << std::setfill(' ') << std::fixed << std::setprecision( lest_FEATURE_TIME_PRECISION ); os << std::setfill(' ') << std::fixed << std::setprecision( lest_FEATURE_TIME_PRECISION );
} }
operator int() { return failures; } operator int() { return failures; }
bool abort() { return option.abort && failures > 0; } bool abort() { return output.abort() && failures > 0; }
times & operator()( test testing ) times & operator()( test testing )
{ {
@ -1012,16 +1179,15 @@ struct times : action
struct confirm : action struct confirm : action
{ {
env output; env output;
options option;
int selected = 0; int selected = 0;
int failures = 0; int failures = 0;
confirm( std::ostream & os, options option ) confirm( std::ostream & out, options option )
: action( os ), output( os, option.pass ), option( option ) {} : action( out ), output( out, option ) {}
operator int() { return failures; } operator int() { return failures; }
bool abort() { return option.abort && failures > 0; } bool abort() { return output.abort() && failures > 0; }
confirm & operator()( test testing ) confirm & operator()( test testing )
{ {
@ -1031,7 +1197,7 @@ struct confirm : action
} }
catch( message const & e ) catch( message const & e )
{ {
++failures; report( os, e, testing.name ); ++failures; report( os, e, output.context() );
} }
return *this; return *this;
} }
@ -1040,11 +1206,11 @@ struct confirm : action
{ {
if ( failures > 0 ) 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" );
} }
} }
}; };
@ -1085,7 +1251,7 @@ inline void shuffle( tests & specification, options option )
inline int stoi( text num ) inline int stoi( text num )
{ {
return std::strtol( num.c_str(), NULL, 10 ); return static_cast<int>( std::strtol( num.c_str(), nullptr, 10 ) );
} }
inline bool is_number( text arg ) inline bool is_number( text arg )
@ -1096,7 +1262,7 @@ inline bool is_number( text arg )
inline seed_t seed( text opt, text arg ) inline seed_t seed( text opt, text arg )
{ {
if ( is_number( arg ) ) if ( is_number( arg ) )
return lest::stoi( arg ); return static_cast<seed_t>( lest::stoi( arg ) );
if ( arg == "time" ) if ( arg == "time" )
return static_cast<seed_t>( std::chrono::high_resolution_clock::now().time_since_epoch().count() ); return static_cast<seed_t>( std::chrono::high_resolution_clock::now().time_since_epoch().count() );
@ -1145,6 +1311,7 @@ inline auto split_arguments( texts args ) -> std::tuple<options, texts>
else if ( opt == "-l" || "--list-tests" == opt ) { option.list = true; continue; } else if ( opt == "-l" || "--list-tests" == opt ) { option.list = true; continue; }
else if ( opt == "-t" || "--time" == opt ) { option.time = 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 == "-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 ( "--version" == opt ) { option.version = true; continue; }
else if ( opt == "--order" && "declared" == val ) { /* by definition */ ; continue; } else if ( opt == "--order" && "declared" == val ) { /* by definition */ ; continue; }
else if ( opt == "--order" && "lexical" == val ) { option.lexical = true; 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" " -l, --list-tests list selected tests\n"
" -p, --pass also report passing tests\n" " -p, --pass also report passing tests\n"
" -t, --time list duration of selected 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=lexical use lexical sort test order\n"
" --order=random use random test order\n" " --order=random use random test order\n"
" --random-seed=n use n for random generator seed\n" " --random-seed=n use n for random generator seed\n"
@ -1182,7 +1350,7 @@ inline int usage( std::ostream & os )
"\n" "\n"
"Test specification:\n" "Test specification:\n"
" \"@\", \"*\" all tests, unless excluded\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 #if lest_FEATURE_REGEX_SEARCH
" \"re\" select tests that match regular expression\n" " \"re\" select tests that match regular expression\n"
" \"!re\" omit tests that match regular expression\n" " \"!re\" omit tests that match regular expression\n"
@ -1202,7 +1370,7 @@ inline text compiler()
#elif defined (__GNUC__ ) #elif defined (__GNUC__ )
os << "gcc " << __GNUC__ << "." << __GNUC_MINOR__ << "." << __GNUC_PATCHLEVEL__; os << "gcc " << __GNUC__ << "." << __GNUC_MINOR__ << "." << __GNUC_PATCHLEVEL__;
#elif defined ( _MSC_VER ) #elif defined ( _MSC_VER )
os << "MSVC " << (_MSC_VER / 100 - 6 ) << " (" << _MSC_VER << ")"; os << "MSVC " << (_MSC_VER / 100 - 5 - (_MSC_VER < 1900)) << " (" << _MSC_VER << ")";
#else #else
os << "[compiler]"; os << "[compiler]";
#endif #endif
@ -1251,7 +1419,8 @@ inline int run( tests specification, int argc, char * argv[], std::ostream & os
template< std::size_t N > template< std::size_t N >
int run( test const (&specification)[N], texts arguments, std::ostream & os = std::cout ) 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< std::size_t N > template< std::size_t N >
@ -1268,4 +1437,10 @@ int run( test const (&specification)[N], int argc, char * argv[], std::ostream &
} // namespace lest } // namespace lest
#endif // LEST_LEST_H_INCLUDED #ifdef __clang__
# pragma clang diagnostic pop
#elif defined __GNUC__
# pragma GCC diagnostic pop
#endif
#endif // LEST_LEST_HPP_INCLUDED

@ -1 +1 @@
Subproject commit b87c8262361f72fff05ca0b1320f8a1d9160b5cd Subproject commit f8e187d07f9d8045bb41883d3e1c7d0ed6f241e4