From 46654531e4e5c17d2ebe1933f3e768f908540f03 Mon Sep 17 00:00:00 2001 From: Geoyeob Kim Date: Mon, 15 May 2017 23:26:35 +0900 Subject: [PATCH 01/99] Fix 'dtRandomPointInConvexPoly()' returning a garbage value if s == 1.0f. (#271) --- Detour/Source/DetourCommon.cpp | 4 +-- RecastDemo/Source/NavMeshTesterTool.cpp | 2 +- RecastDemo/premake5.lua | 2 ++ Tests/Detour/Tests_Detour.cpp | 33 +++++++++++++++++++++++++ 4 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 Tests/Detour/Tests_Detour.cpp diff --git a/Detour/Source/DetourCommon.cpp b/Detour/Source/DetourCommon.cpp index 26fe65c1..41d0d7bd 100644 --- a/Detour/Source/DetourCommon.cpp +++ b/Detour/Source/DetourCommon.cpp @@ -342,8 +342,8 @@ void dtRandomPointInConvexPoly(const float* pts, const int npts, float* areas, // Find sub triangle weighted by area. const float thr = s*areasum; float acc = 0.0f; - float u = 0.0f; - int tri = 0; + float u = 1.0f; + int tri = npts - 1; for (int i = 2; i < npts; i++) { const float dacc = areas[i]; if (thr >= acc && thr < (acc+dacc)) diff --git a/RecastDemo/Source/NavMeshTesterTool.cpp b/RecastDemo/Source/NavMeshTesterTool.cpp index 99d6c521..dbd41d24 100644 --- a/RecastDemo/Source/NavMeshTesterTool.cpp +++ b/RecastDemo/Source/NavMeshTesterTool.cpp @@ -45,7 +45,7 @@ // Uncomment this to dump all the requests in stdout. #define DUMP_REQS -// Returns a random number [0..1) +// Returns a random number [0..1] static float frand() { // return ((float)(rand() & 0xffff)/(float)0xffff); diff --git a/RecastDemo/premake5.lua b/RecastDemo/premake5.lua index 7f6e00dc..04334b41 100644 --- a/RecastDemo/premake5.lua +++ b/RecastDemo/premake5.lua @@ -202,6 +202,8 @@ project "Tests" "../Tests/*.cpp", "../Tests/Recast/*.h", "../Tests/Recast/*.cpp", + "../Tests/Detour/*.h", + "../Tests/Detour/*.cpp", } -- project dependencies diff --git a/Tests/Detour/Tests_Detour.cpp b/Tests/Detour/Tests_Detour.cpp new file mode 100644 index 00000000..dd2a6ebf --- /dev/null +++ b/Tests/Detour/Tests_Detour.cpp @@ -0,0 +1,33 @@ +#include "catch.hpp" + +#include "DetourCommon.h" + +TEST_CASE("dtRandomPointInConvexPoly") +{ + SECTION("Properly works when the argument 's' is 1.0f") + { + const float pts[] = { + 0, 0, 0, + 0, 0, 1, + 1, 0, 0, + }; + const int npts = 3; + float areas[6]; + float out[3]; + + dtRandomPointInConvexPoly(pts, npts, areas, 0.0f, 1.0f, out); + REQUIRE(out[0] == 0); + REQUIRE(out[1] == 0); + REQUIRE(out[2] == 1); + + dtRandomPointInConvexPoly(pts, npts, areas, 0.5f, 1.0f, out); + REQUIRE(out[0] == 1.0f / 2); + REQUIRE(out[1] == 0); + REQUIRE(out[2] == 1.0f / 2); + + dtRandomPointInConvexPoly(pts, npts, areas, 1.0f, 1.0f, out); + REQUIRE(out[0] == 1); + REQUIRE(out[1] == 0); + REQUIRE(out[2] == 0); + } +} From 840c100639f125e503448d307b29c92f7eafccb2 Mon Sep 17 00:00:00 2001 From: aymarfisherman Date: Tue, 25 Jul 2017 08:35:23 -0300 Subject: [PATCH 02/99] Added option to add rotated Box obstacle to TileCache. (#278) Very useful when you have a obstacle with a big difference in x to z ratio and that could be rotated (not aligned), like a wall. --- DetourTileCache/Include/DetourTileCache.h | 17 +++++- .../Include/DetourTileCacheBuilder.h | 3 + DetourTileCache/Source/DetourTileCache.cpp | 58 ++++++++++++++++++- .../Source/DetourTileCacheBuilder.cpp | 54 +++++++++++++++++ 4 files changed, 130 insertions(+), 2 deletions(-) diff --git a/DetourTileCache/Include/DetourTileCache.h b/DetourTileCache/Include/DetourTileCache.h index c45910cd..aba0c138 100644 --- a/DetourTileCache/Include/DetourTileCache.h +++ b/DetourTileCache/Include/DetourTileCache.h @@ -38,7 +38,8 @@ enum ObstacleState enum ObstacleType { DT_OBSTACLE_CYLINDER, - DT_OBSTACLE_BOX, + DT_OBSTACLE_BOX, // AABB + DT_OBSTACLE_ORIENTED_BOX, // OBB }; struct dtObstacleCylinder @@ -54,6 +55,13 @@ struct dtObstacleBox float bmax[ 3 ]; }; +struct dtObstacleOrientedBox +{ + float center[ 3 ]; + float extents[ 3 ]; + float rotAux[ 2 ]; //{ cos(0.5f*angle)*sin(-0.5f*angle); cos(0.5f*angle)*cos(0.5f*angle) - 0.5 } +}; + static const int DT_MAX_TOUCHED_TILES = 8; struct dtTileCacheObstacle { @@ -61,6 +69,7 @@ struct dtTileCacheObstacle { dtObstacleCylinder cylinder; dtObstacleBox box; + dtObstacleOrientedBox orientedBox; }; dtCompressedTileRef touched[DT_MAX_TOUCHED_TILES]; @@ -130,8 +139,14 @@ public: dtStatus removeTile(dtCompressedTileRef ref, unsigned char** data, int* dataSize); + // Cylinder obstacle. dtStatus addObstacle(const float* pos, const float radius, const float height, dtObstacleRef* result); + + // Aabb obstacle. dtStatus addBoxObstacle(const float* bmin, const float* bmax, dtObstacleRef* result); + + // Box obstacle: can be rotated in Y. + dtStatus addBoxObstacle(const float* center, const float* extents, const float yRadians, dtObstacleRef* result); dtStatus removeObstacle(const dtObstacleRef ref); diff --git a/DetourTileCache/Include/DetourTileCacheBuilder.h b/DetourTileCache/Include/DetourTileCacheBuilder.h index d1ef80e1..7b95abc1 100644 --- a/DetourTileCache/Include/DetourTileCacheBuilder.h +++ b/DetourTileCache/Include/DetourTileCacheBuilder.h @@ -130,6 +130,9 @@ dtStatus dtMarkCylinderArea(dtTileCacheLayer& layer, const float* orig, const fl dtStatus dtMarkBoxArea(dtTileCacheLayer& layer, const float* orig, const float cs, const float ch, const float* bmin, const float* bmax, const unsigned char areaId); +dtStatus dtMarkBoxArea(dtTileCacheLayer& layer, const float* orig, const float cs, const float ch, + const float* center, const float* extents, const float* rotAux, const unsigned char areaId); + dtStatus dtBuildTileCacheRegions(dtTileCacheAlloc* alloc, dtTileCacheLayer& layer, const int walkableClimb); diff --git a/DetourTileCache/Source/DetourTileCache.cpp b/DetourTileCache/Source/DetourTileCache.cpp index cf575046..1b152dfe 100644 --- a/DetourTileCache/Source/DetourTileCache.cpp +++ b/DetourTileCache/Source/DetourTileCache.cpp @@ -420,6 +420,45 @@ dtStatus dtTileCache::addBoxObstacle(const float* bmin, const float* bmax, dtObs return DT_SUCCESS; } +dtStatus dtTileCache::addBoxObstacle(const float* center, const float* extents, const float yRadians, dtObstacleRef* result) +{ + if (m_nreqs >= MAX_REQUESTS) + return DT_FAILURE | DT_BUFFER_TOO_SMALL; + + dtTileCacheObstacle* ob = 0; + if (m_nextFreeObstacle) + { + ob = m_nextFreeObstacle; + m_nextFreeObstacle = ob->next; + ob->next = 0; + } + if (!ob) + return DT_FAILURE | DT_OUT_OF_MEMORY; + + unsigned short salt = ob->salt; + memset(ob, 0, sizeof(dtTileCacheObstacle)); + ob->salt = salt; + ob->state = DT_OBSTACLE_PROCESSING; + ob->type = DT_OBSTACLE_ORIENTED_BOX; + dtVcopy(ob->orientedBox.center, center); + dtVcopy(ob->orientedBox.extents, extents); + + float coshalf= cosf(0.5f*yRadians); + float sinhalf = sinf(-0.5f*yRadians); + ob->orientedBox.rotAux[0] = coshalf*sinhalf; + ob->orientedBox.rotAux[1] = coshalf*coshalf - 0.5f; + + ObstacleRequest* req = &m_reqs[m_nreqs++]; + memset(req, 0, sizeof(ObstacleRequest)); + req->action = REQUEST_ADD; + req->ref = getObstacleRef(ob); + + if (result) + *result = req->ref; + + return DT_SUCCESS; +} + dtStatus dtTileCache::removeObstacle(const dtObstacleRef ref) { if (!ref) @@ -650,7 +689,12 @@ dtStatus dtTileCache::buildNavMeshTile(const dtCompressedTileRef ref, dtNavMesh* else if (ob->type == DT_OBSTACLE_BOX) { dtMarkBoxArea(*bc.layer, tile->header->bmin, m_params.cs, m_params.ch, - ob->box.bmin, ob->box.bmax, 0); + ob->box.bmin, ob->box.bmax, 0); + } + else if (ob->type == DT_OBSTACLE_ORIENTED_BOX) + { + dtMarkBoxArea(*bc.layer, tile->header->bmin, m_params.cs, m_params.ch, + ob->orientedBox.center, ob->orientedBox.extents, ob->orientedBox.rotAux, 0); } } } @@ -761,4 +805,16 @@ void dtTileCache::getObstacleBounds(const struct dtTileCacheObstacle* ob, float* dtVcopy(bmin, ob->box.bmin); dtVcopy(bmax, ob->box.bmax); } + else if (ob->type == DT_OBSTACLE_ORIENTED_BOX) + { + const dtObstacleOrientedBox &orientedBox = ob->orientedBox; + + float maxr = 1.41f*dtMax(orientedBox.extents[0], orientedBox.extents[2]); + bmin[0] = orientedBox.center[0] - maxr; + bmax[0] = orientedBox.center[0] + maxr; + bmin[1] = orientedBox.center[1] - orientedBox.extents[1]; + bmax[1] = orientedBox.center[1] + orientedBox.extents[1]; + bmin[2] = orientedBox.center[2] - maxr; + bmax[2] = orientedBox.center[2] + maxr; + } } diff --git a/DetourTileCache/Source/DetourTileCacheBuilder.cpp b/DetourTileCache/Source/DetourTileCacheBuilder.cpp index a81a7b43..56ee1341 100644 --- a/DetourTileCache/Source/DetourTileCacheBuilder.cpp +++ b/DetourTileCache/Source/DetourTileCacheBuilder.cpp @@ -2041,6 +2041,60 @@ dtStatus dtMarkBoxArea(dtTileCacheLayer& layer, const float* orig, const float c return DT_SUCCESS; } +dtStatus dtMarkBoxArea(dtTileCacheLayer& layer, const float* orig, const float cs, const float ch, + const float* center, const float* extents, const float* rotAux, const unsigned char areaId) +{ + const int w = (int)layer.header->width; + const int h = (int)layer.header->height; + const float ics = 1.0f/cs; + const float ich = 1.0f/ch; + + float cx = (center[0] - orig[0])*ics; + float cz = (center[2] - orig[2])*ics; + + float maxr = 1.41f*dtMax(extents[0], extents[2]); + int minx = (int)floorf(cx - maxr*ics); + int maxx = (int)floorf(cx + maxr*ics); + int minz = (int)floorf(cz - maxr*ics); + int maxz = (int)floorf(cz + maxr*ics); + int miny = (int)floorf((center[1]-extents[1]-orig[1])*ich); + int maxy = (int)floorf((center[1]+extents[1]-orig[1])*ich); + + if (maxx < 0) return DT_SUCCESS; + if (minx >= w) return DT_SUCCESS; + if (maxz < 0) return DT_SUCCESS; + if (minz >= h) return DT_SUCCESS; + + if (minx < 0) minx = 0; + if (maxx >= w) maxx = w-1; + if (minz < 0) minz = 0; + if (maxz >= h) maxz = h-1; + + float xhalf = extents[0]*ics + 0.5f; + float zhalf = extents[2]*ics + 0.5f; + + for (int z = minz; z <= maxz; ++z) + { + for (int x = minx; x <= maxx; ++x) + { + float x2 = 2.0f*(float(x) - cx); + float z2 = 2.0f*(float(z) - cz); + float xrot = rotAux[1]*x2 + rotAux[0]*z2; + if (xrot > xhalf || xrot < -xhalf) + continue; + float zrot = rotAux[1]*z2 - rotAux[0]*x2; + if (zrot > zhalf || zrot < -zhalf) + continue; + const int y = layer.heights[x+z*w]; + if (y < miny || y > maxy) + continue; + layer.areas[x+z*w] = areaId; + } + } + + return DT_SUCCESS; +} + dtStatus dtBuildTileCacheLayer(dtTileCacheCompressor* comp, dtTileCacheLayerHeader* header, const unsigned char* heights, From 7cca61dc417ea2e7dacad07f313eede08ad711d2 Mon Sep 17 00:00:00 2001 From: aymarfisherman Date: Sun, 20 Aug 2017 12:05:51 -0300 Subject: [PATCH 03/99] Renamed all instances of 'extents' to 'halfExtents' (#279) --- Detour/Include/DetourNavMesh.h | 2 +- Detour/Include/DetourNavMeshQuery.h | 12 ++++++------ Detour/Source/DetourNavMesh.cpp | 14 +++++++------- Detour/Source/DetourNavMeshQuery.cpp | 18 +++++++++--------- DetourCrowd/Include/DetourCrowd.h | 12 ++++++++---- DetourCrowd/Source/DetourCrowd.cpp | 9 +++++---- DetourCrowd/Source/DetourPathCorridor.cpp | 4 ++-- DetourTileCache/Include/DetourTileCache.h | 4 ++-- .../Include/DetourTileCacheBuilder.h | 2 +- DetourTileCache/Source/DetourTileCache.cpp | 12 ++++++------ .../Source/DetourTileCacheBuilder.cpp | 12 ++++++------ RecastDemo/Include/Sample_Debug.h | 2 +- RecastDemo/Source/ConvexVolumeTool.cpp | 2 +- RecastDemo/Source/CrowdTool.cpp | 8 ++++---- RecastDemo/Source/NavMeshPruneTool.cpp | 4 ++-- RecastDemo/Source/Sample_Debug.cpp | 10 +++++----- 16 files changed, 66 insertions(+), 61 deletions(-) diff --git a/Detour/Include/DetourNavMesh.h b/Detour/Include/DetourNavMesh.h index 8ecd57e4..02ee5e78 100644 --- a/Detour/Include/DetourNavMesh.h +++ b/Detour/Include/DetourNavMesh.h @@ -635,7 +635,7 @@ private: dtPolyRef* polys, const int maxPolys) const; /// Find nearest polygon within a tile. dtPolyRef findNearestPolyInTile(const dtMeshTile* tile, const float* center, - const float* extents, float* nearestPt) const; + const float* halfExtents, float* nearestPt) const; /// Returns closest point on polygon. void closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest, bool* posOverPoly) const; diff --git a/Detour/Include/DetourNavMeshQuery.h b/Detour/Include/DetourNavMeshQuery.h index 61541e83..1c23e485 100644 --- a/Detour/Include/DetourNavMeshQuery.h +++ b/Detour/Include/DetourNavMeshQuery.h @@ -316,33 +316,33 @@ public: /// Finds the polygon nearest to the specified center point. /// @param[in] center The center of the search box. [(x, y, z)] - /// @param[in] extents The search distance along each axis. [(x, y, z)] + /// @param[in] halfExtents The search distance along each axis. [(x, y, z)] /// @param[in] filter The polygon filter to apply to the query. /// @param[out] nearestRef The reference id of the nearest polygon. /// @param[out] nearestPt The nearest point on the polygon. [opt] [(x, y, z)] /// @returns The status flags for the query. - dtStatus findNearestPoly(const float* center, const float* extents, + dtStatus findNearestPoly(const float* center, const float* halfExtents, const dtQueryFilter* filter, dtPolyRef* nearestRef, float* nearestPt) const; /// Finds polygons that overlap the search box. /// @param[in] center The center of the search box. [(x, y, z)] - /// @param[in] extents The search distance along each axis. [(x, y, z)] + /// @param[in] halfExtents The search distance along each axis. [(x, y, z)] /// @param[in] filter The polygon filter to apply to the query. /// @param[out] polys The reference ids of the polygons that overlap the query box. /// @param[out] polyCount The number of polygons in the search result. /// @param[in] maxPolys The maximum number of polygons the search result can hold. /// @returns The status flags for the query. - dtStatus queryPolygons(const float* center, const float* extents, + dtStatus queryPolygons(const float* center, const float* halfExtents, const dtQueryFilter* filter, dtPolyRef* polys, int* polyCount, const int maxPolys) const; /// Finds polygons that overlap the search box. /// @param[in] center The center of the search box. [(x, y, z)] - /// @param[in] extents The search distance along each axis. [(x, y, z)] + /// @param[in] halfExtents The search distance along each axis. [(x, y, z)] /// @param[in] filter The polygon filter to apply to the query. /// @param[in] query The query. Polygons found will be batched together and passed to this query. - dtStatus queryPolygons(const float* center, const float* extents, + dtStatus queryPolygons(const float* center, const float* halfExtents, const dtQueryFilter* filter, dtPolyQuery* query) const; /// Finds the non-overlapping navigation polygons in the local neighbourhood around the center position. diff --git a/Detour/Source/DetourNavMesh.cpp b/Detour/Source/DetourNavMesh.cpp index a6557f9e..b81a2567 100644 --- a/Detour/Source/DetourNavMesh.cpp +++ b/Detour/Source/DetourNavMesh.cpp @@ -470,12 +470,12 @@ void dtNavMesh::connectExtOffMeshLinks(dtMeshTile* tile, dtMeshTile* target, int if (targetPoly->firstLink == DT_NULL_LINK) continue; - const float ext[3] = { targetCon->rad, target->header->walkableClimb, targetCon->rad }; + const float halfExtents[3] = { targetCon->rad, target->header->walkableClimb, targetCon->rad }; // Find polygon to connect to. const float* p = &targetCon->pos[3]; float nearestPt[3]; - dtPolyRef ref = findNearestPolyInTile(tile, p, ext, nearestPt); + dtPolyRef ref = findNearestPolyInTile(tile, p, halfExtents, nearestPt); if (!ref) continue; // findNearestPoly may return too optimistic results, further check to make sure. @@ -570,12 +570,12 @@ void dtNavMesh::baseOffMeshLinks(dtMeshTile* tile) dtOffMeshConnection* con = &tile->offMeshCons[i]; dtPoly* poly = &tile->polys[con->poly]; - const float ext[3] = { con->rad, tile->header->walkableClimb, con->rad }; + const float halfExtents[3] = { con->rad, tile->header->walkableClimb, con->rad }; // Find polygon to connect to. const float* p = &con->pos[0]; // First vertex float nearestPt[3]; - dtPolyRef ref = findNearestPolyInTile(tile, p, ext, nearestPt); + dtPolyRef ref = findNearestPolyInTile(tile, p, halfExtents, nearestPt); if (!ref) continue; // findNearestPoly may return too optimistic results, further check to make sure. if (dtSqr(nearestPt[0]-p[0])+dtSqr(nearestPt[2]-p[2]) > dtSqr(con->rad)) @@ -696,12 +696,12 @@ void dtNavMesh::closestPointOnPoly(dtPolyRef ref, const float* pos, float* close } dtPolyRef dtNavMesh::findNearestPolyInTile(const dtMeshTile* tile, - const float* center, const float* extents, + const float* center, const float* halfExtents, float* nearestPt) const { float bmin[3], bmax[3]; - dtVsub(bmin, center, extents); - dtVadd(bmax, center, extents); + dtVsub(bmin, center, halfExtents); + dtVadd(bmax, center, halfExtents); // Get nearby polygons from proximity grid. dtPolyRef polys[128]; diff --git a/Detour/Source/DetourNavMeshQuery.cpp b/Detour/Source/DetourNavMeshQuery.cpp index 024d4a1f..90999f2f 100644 --- a/Detour/Source/DetourNavMeshQuery.cpp +++ b/Detour/Source/DetourNavMeshQuery.cpp @@ -759,7 +759,7 @@ public: /// return #DT_SUCCESS, but @p nearestRef will be zero. So if in doubt, check /// @p nearestRef before using @p nearestPt. /// -dtStatus dtNavMeshQuery::findNearestPoly(const float* center, const float* extents, +dtStatus dtNavMeshQuery::findNearestPoly(const float* center, const float* halfExtents, const dtQueryFilter* filter, dtPolyRef* nearestRef, float* nearestPt) const { @@ -770,7 +770,7 @@ dtStatus dtNavMeshQuery::findNearestPoly(const float* center, const float* exten dtFindNearestPolyQuery query(this, center); - dtStatus status = queryPolygons(center, extents, filter, &query); + dtStatus status = queryPolygons(center, halfExtents, filter, &query); if (dtStatusFailed(status)) return status; @@ -943,7 +943,7 @@ public: /// be filled to capacity. The method of choosing which polygons from the /// full set are included in the partial result set is undefined. /// -dtStatus dtNavMeshQuery::queryPolygons(const float* center, const float* extents, +dtStatus dtNavMeshQuery::queryPolygons(const float* center, const float* halfExtents, const dtQueryFilter* filter, dtPolyRef* polys, int* polyCount, const int maxPolys) const { @@ -952,7 +952,7 @@ dtStatus dtNavMeshQuery::queryPolygons(const float* center, const float* extents dtCollectPolysQuery collector(polys, maxPolys); - dtStatus status = queryPolygons(center, extents, filter, &collector); + dtStatus status = queryPolygons(center, halfExtents, filter, &collector); if (dtStatusFailed(status)) return status; @@ -963,21 +963,21 @@ dtStatus dtNavMeshQuery::queryPolygons(const float* center, const float* extents /// @par /// /// The query will be invoked with batches of polygons. Polygons passed -/// to the query have bounding boxes that overlap with the center and extents +/// to the query have bounding boxes that overlap with the center and halfExtents /// passed to this function. The dtPolyQuery::process function is invoked multiple /// times until all overlapping polygons have been processed. /// -dtStatus dtNavMeshQuery::queryPolygons(const float* center, const float* extents, +dtStatus dtNavMeshQuery::queryPolygons(const float* center, const float* halfExtents, const dtQueryFilter* filter, dtPolyQuery* query) const { dtAssert(m_nav); - if (!center || !extents || !filter || !query) + if (!center || !halfExtents || !filter || !query) return DT_FAILURE | DT_INVALID_PARAM; float bmin[3], bmax[3]; - dtVsub(bmin, center, extents); - dtVadd(bmax, center, extents); + dtVsub(bmin, center, halfExtents); + dtVadd(bmax, center, halfExtents); // Find tiles the query touches. int minx, miny, maxx, maxy; diff --git a/DetourCrowd/Include/DetourCrowd.h b/DetourCrowd/Include/DetourCrowd.h index 045fc216..95205087 100644 --- a/DetourCrowd/Include/DetourCrowd.h +++ b/DetourCrowd/Include/DetourCrowd.h @@ -217,7 +217,7 @@ class dtCrowd dtPolyRef* m_pathResult; int m_maxPathResult; - float m_ext[3]; + float m_agentPlacementHalfExtents[3]; dtQueryFilter m_filters[DT_CROWD_MAX_QUERY_FILTER_TYPE]; @@ -325,9 +325,13 @@ public: /// @return The filter used by the crowd. inline dtQueryFilter* getEditableFilter(const int i) { return (i >= 0 && i < DT_CROWD_MAX_QUERY_FILTER_TYPE) ? &m_filters[i] : 0; } - /// Gets the search extents [(x, y, z)] used by the crowd for query operations. - /// @return The search extents used by the crowd. [(x, y, z)] - const float* getQueryExtents() const { return m_ext; } + /// Gets the search halfExtents [(x, y, z)] used by the crowd for query operations. + /// @return The search halfExtents used by the crowd. [(x, y, z)] + const float* getQueryHalfExtents() const { return m_agentPlacementHalfExtents; } + + /// Same as getQueryHalfExtents. Left to maintain backwards compatibility. + /// @return The search halfExtents used by the crowd. [(x, y, z)] + const float* getQueryExtents() const { return m_agentPlacementHalfExtents; } /// Gets the velocity sample count. /// @return The velocity sample count. diff --git a/DetourCrowd/Source/DetourCrowd.cpp b/DetourCrowd/Source/DetourCrowd.cpp index f9f43607..1e76e40c 100644 --- a/DetourCrowd/Source/DetourCrowd.cpp +++ b/DetourCrowd/Source/DetourCrowd.cpp @@ -386,7 +386,8 @@ bool dtCrowd::init(const int maxAgents, const float maxAgentRadius, dtNavMesh* n m_maxAgents = maxAgents; m_maxAgentRadius = maxAgentRadius; - dtVset(m_ext, m_maxAgentRadius*2.0f,m_maxAgentRadius*1.5f,m_maxAgentRadius*2.0f); + // Larger than agent radius because it is also used for agent recovery. + dtVset(m_agentPlacementHalfExtents, m_maxAgentRadius*2.0f, m_maxAgentRadius*1.5f, m_maxAgentRadius*2.0f); m_grid = dtAllocProximityGrid(); if (!m_grid) @@ -531,7 +532,7 @@ int dtCrowd::addAgent(const float* pos, const dtCrowdAgentParams* params) float nearest[3]; dtPolyRef ref = 0; dtVcopy(nearest, pos); - dtStatus status = m_navquery->findNearestPoly(pos, m_ext, &m_filters[ag->params.queryFilterType], &ref, nearest); + dtStatus status = m_navquery->findNearestPoly(pos, m_agentPlacementHalfExtents, &m_filters[ag->params.queryFilterType], &ref, nearest); if (dtStatusFailed(status)) { dtVcopy(nearest, pos); @@ -965,7 +966,7 @@ void dtCrowd::checkPathValidity(dtCrowdAgent** agents, const int nagents, const float nearest[3]; dtVcopy(nearest, agentPos); agentRef = 0; - m_navquery->findNearestPoly(ag->npos, m_ext, &m_filters[ag->params.queryFilterType], &agentRef, nearest); + m_navquery->findNearestPoly(ag->npos, m_agentPlacementHalfExtents, &m_filters[ag->params.queryFilterType], &agentRef, nearest); dtVcopy(agentPos, nearest); if (!agentRef) @@ -1001,7 +1002,7 @@ void dtCrowd::checkPathValidity(dtCrowdAgent** agents, const int nagents, const float nearest[3]; dtVcopy(nearest, ag->targetPos); ag->targetRef = 0; - m_navquery->findNearestPoly(ag->targetPos, m_ext, &m_filters[ag->params.queryFilterType], &ag->targetRef, nearest); + m_navquery->findNearestPoly(ag->targetPos, m_agentPlacementHalfExtents, &m_filters[ag->params.queryFilterType], &ag->targetRef, nearest); dtVcopy(ag->targetPos, nearest); replan = true; } diff --git a/DetourCrowd/Source/DetourPathCorridor.cpp b/DetourCrowd/Source/DetourPathCorridor.cpp index 54a2ab8b..e54d4637 100644 --- a/DetourCrowd/Source/DetourPathCorridor.cpp +++ b/DetourCrowd/Source/DetourPathCorridor.cpp @@ -431,7 +431,7 @@ Behavior: - The new position will be located in the adjusted corridor's first polygon. The expected use case is that the desired position will be 'near' the current corridor. What is considered 'near' -depends on local polygon density, query search extents, etc. +depends on local polygon density, query search half extents, etc. The resulting position will differ from the desired position if the desired position is not on the navigation mesh, or it can't be reached using a local search. @@ -470,7 +470,7 @@ Behavior: - The corridor is automatically adjusted (shorted or lengthened) in order to remain valid. - The new target will be located in the adjusted corridor's last polygon. -The expected use case is that the desired target will be 'near' the current corridor. What is considered 'near' depends on local polygon density, query search extents, etc. +The expected use case is that the desired target will be 'near' the current corridor. What is considered 'near' depends on local polygon density, query search half extents, etc. The resulting target will differ from the desired target if the desired target is not on the navigation mesh, or it can't be reached using a local search. */ diff --git a/DetourTileCache/Include/DetourTileCache.h b/DetourTileCache/Include/DetourTileCache.h index aba0c138..75713366 100644 --- a/DetourTileCache/Include/DetourTileCache.h +++ b/DetourTileCache/Include/DetourTileCache.h @@ -58,7 +58,7 @@ struct dtObstacleBox struct dtObstacleOrientedBox { float center[ 3 ]; - float extents[ 3 ]; + float halfExtents[ 3 ]; float rotAux[ 2 ]; //{ cos(0.5f*angle)*sin(-0.5f*angle); cos(0.5f*angle)*cos(0.5f*angle) - 0.5 } }; @@ -146,7 +146,7 @@ public: dtStatus addBoxObstacle(const float* bmin, const float* bmax, dtObstacleRef* result); // Box obstacle: can be rotated in Y. - dtStatus addBoxObstacle(const float* center, const float* extents, const float yRadians, dtObstacleRef* result); + dtStatus addBoxObstacle(const float* center, const float* halfExtents, const float yRadians, dtObstacleRef* result); dtStatus removeObstacle(const dtObstacleRef ref); diff --git a/DetourTileCache/Include/DetourTileCacheBuilder.h b/DetourTileCache/Include/DetourTileCacheBuilder.h index 7b95abc1..ff610919 100644 --- a/DetourTileCache/Include/DetourTileCacheBuilder.h +++ b/DetourTileCache/Include/DetourTileCacheBuilder.h @@ -131,7 +131,7 @@ dtStatus dtMarkBoxArea(dtTileCacheLayer& layer, const float* orig, const float c const float* bmin, const float* bmax, const unsigned char areaId); dtStatus dtMarkBoxArea(dtTileCacheLayer& layer, const float* orig, const float cs, const float ch, - const float* center, const float* extents, const float* rotAux, const unsigned char areaId); + const float* center, const float* halfExtents, const float* rotAux, const unsigned char areaId); dtStatus dtBuildTileCacheRegions(dtTileCacheAlloc* alloc, dtTileCacheLayer& layer, diff --git a/DetourTileCache/Source/DetourTileCache.cpp b/DetourTileCache/Source/DetourTileCache.cpp index 1b152dfe..707c1923 100644 --- a/DetourTileCache/Source/DetourTileCache.cpp +++ b/DetourTileCache/Source/DetourTileCache.cpp @@ -420,7 +420,7 @@ dtStatus dtTileCache::addBoxObstacle(const float* bmin, const float* bmax, dtObs return DT_SUCCESS; } -dtStatus dtTileCache::addBoxObstacle(const float* center, const float* extents, const float yRadians, dtObstacleRef* result) +dtStatus dtTileCache::addBoxObstacle(const float* center, const float* halfExtents, const float yRadians, dtObstacleRef* result) { if (m_nreqs >= MAX_REQUESTS) return DT_FAILURE | DT_BUFFER_TOO_SMALL; @@ -441,7 +441,7 @@ dtStatus dtTileCache::addBoxObstacle(const float* center, const float* extents, ob->state = DT_OBSTACLE_PROCESSING; ob->type = DT_OBSTACLE_ORIENTED_BOX; dtVcopy(ob->orientedBox.center, center); - dtVcopy(ob->orientedBox.extents, extents); + dtVcopy(ob->orientedBox.halfExtents, halfExtents); float coshalf= cosf(0.5f*yRadians); float sinhalf = sinf(-0.5f*yRadians); @@ -694,7 +694,7 @@ dtStatus dtTileCache::buildNavMeshTile(const dtCompressedTileRef ref, dtNavMesh* else if (ob->type == DT_OBSTACLE_ORIENTED_BOX) { dtMarkBoxArea(*bc.layer, tile->header->bmin, m_params.cs, m_params.ch, - ob->orientedBox.center, ob->orientedBox.extents, ob->orientedBox.rotAux, 0); + ob->orientedBox.center, ob->orientedBox.halfExtents, ob->orientedBox.rotAux, 0); } } } @@ -809,11 +809,11 @@ void dtTileCache::getObstacleBounds(const struct dtTileCacheObstacle* ob, float* { const dtObstacleOrientedBox &orientedBox = ob->orientedBox; - float maxr = 1.41f*dtMax(orientedBox.extents[0], orientedBox.extents[2]); + float maxr = 1.41f*dtMax(orientedBox.halfExtents[0], orientedBox.halfExtents[2]); bmin[0] = orientedBox.center[0] - maxr; bmax[0] = orientedBox.center[0] + maxr; - bmin[1] = orientedBox.center[1] - orientedBox.extents[1]; - bmax[1] = orientedBox.center[1] + orientedBox.extents[1]; + bmin[1] = orientedBox.center[1] - orientedBox.halfExtents[1]; + bmax[1] = orientedBox.center[1] + orientedBox.halfExtents[1]; bmin[2] = orientedBox.center[2] - maxr; bmax[2] = orientedBox.center[2] + maxr; } diff --git a/DetourTileCache/Source/DetourTileCacheBuilder.cpp b/DetourTileCache/Source/DetourTileCacheBuilder.cpp index 56ee1341..1bbbcc48 100644 --- a/DetourTileCache/Source/DetourTileCacheBuilder.cpp +++ b/DetourTileCache/Source/DetourTileCacheBuilder.cpp @@ -2042,7 +2042,7 @@ dtStatus dtMarkBoxArea(dtTileCacheLayer& layer, const float* orig, const float c } dtStatus dtMarkBoxArea(dtTileCacheLayer& layer, const float* orig, const float cs, const float ch, - const float* center, const float* extents, const float* rotAux, const unsigned char areaId) + const float* center, const float* halfExtents, const float* rotAux, const unsigned char areaId) { const int w = (int)layer.header->width; const int h = (int)layer.header->height; @@ -2052,13 +2052,13 @@ dtStatus dtMarkBoxArea(dtTileCacheLayer& layer, const float* orig, const float c float cx = (center[0] - orig[0])*ics; float cz = (center[2] - orig[2])*ics; - float maxr = 1.41f*dtMax(extents[0], extents[2]); + float maxr = 1.41f*dtMax(halfExtents[0], halfExtents[2]); int minx = (int)floorf(cx - maxr*ics); int maxx = (int)floorf(cx + maxr*ics); int minz = (int)floorf(cz - maxr*ics); int maxz = (int)floorf(cz + maxr*ics); - int miny = (int)floorf((center[1]-extents[1]-orig[1])*ich); - int maxy = (int)floorf((center[1]+extents[1]-orig[1])*ich); + int miny = (int)floorf((center[1]-halfExtents[1]-orig[1])*ich); + int maxy = (int)floorf((center[1]+halfExtents[1]-orig[1])*ich); if (maxx < 0) return DT_SUCCESS; if (minx >= w) return DT_SUCCESS; @@ -2070,8 +2070,8 @@ dtStatus dtMarkBoxArea(dtTileCacheLayer& layer, const float* orig, const float c if (minz < 0) minz = 0; if (maxz >= h) maxz = h-1; - float xhalf = extents[0]*ics + 0.5f; - float zhalf = extents[2]*ics + 0.5f; + float xhalf = halfExtents[0]*ics + 0.5f; + float zhalf = halfExtents[2]*ics + 0.5f; for (int z = minz; z <= maxz; ++z) { diff --git a/RecastDemo/Include/Sample_Debug.h b/RecastDemo/Include/Sample_Debug.h index 7340355c..129e4bc6 100644 --- a/RecastDemo/Include/Sample_Debug.h +++ b/RecastDemo/Include/Sample_Debug.h @@ -31,7 +31,7 @@ protected: rcContourSet* m_cset; rcPolyMesh* m_pmesh; - float m_ext[3]; + float m_halfExtents[3]; float m_center[3]; float m_bmin[3], m_bmax[3]; dtPolyRef m_ref; diff --git a/RecastDemo/Source/ConvexVolumeTool.cpp b/RecastDemo/Source/ConvexVolumeTool.cpp index 33e7c45f..09173414 100644 --- a/RecastDemo/Source/ConvexVolumeTool.cpp +++ b/RecastDemo/Source/ConvexVolumeTool.cpp @@ -248,7 +248,7 @@ void ConvexVolumeTool::handleRender() { duDebugDraw& dd = m_sample->getDebugDraw(); - // Find height extents of the shape. + // Find height extent of the shape. float minh = FLT_MAX, maxh = 0; for (int i = 0; i < m_npts; ++i) minh = rcMin(minh, m_pts[i*3+1]); diff --git a/RecastDemo/Source/CrowdTool.cpp b/RecastDemo/Source/CrowdTool.cpp index c7a8f1f2..77833795 100644 --- a/RecastDemo/Source/CrowdTool.cpp +++ b/RecastDemo/Source/CrowdTool.cpp @@ -712,7 +712,7 @@ void CrowdToolState::setMoveTarget(const float* p, bool adjust) dtNavMeshQuery* navquery = m_sample->getNavMeshQuery(); dtCrowd* crowd = m_sample->getCrowd(); const dtQueryFilter* filter = crowd->getFilter(0); - const float* ext = crowd->getQueryExtents(); + const float* halfExtents = crowd->getQueryExtents(); if (adjust) { @@ -740,7 +740,7 @@ void CrowdToolState::setMoveTarget(const float* p, bool adjust) } else { - navquery->findNearestPoly(p, ext, filter, &m_targetRef, m_targetPos); + navquery->findNearestPoly(p, halfExtents, filter, &m_targetRef, m_targetPos); if (m_agentDebug.idx != -1) { @@ -1032,10 +1032,10 @@ void CrowdTool::handleClick(const float* s, const float* p, bool shift) if (nav && navquery) { dtQueryFilter filter; - const float* ext = crowd->getQueryExtents(); + const float* halfExtents = crowd->getQueryExtents(); float tgt[3]; dtPolyRef ref; - navquery->findNearestPoly(p, ext, &filter, &ref, tgt); + navquery->findNearestPoly(p, halfExtents, &filter, &ref, tgt); if (ref) { unsigned short flags = 0; diff --git a/RecastDemo/Source/NavMeshPruneTool.cpp b/RecastDemo/Source/NavMeshPruneTool.cpp index 95656d5c..72606143 100644 --- a/RecastDemo/Source/NavMeshPruneTool.cpp +++ b/RecastDemo/Source/NavMeshPruneTool.cpp @@ -252,10 +252,10 @@ void NavMeshPruneTool::handleClick(const float* s, const float* p, bool shift) m_flags->init(nav); } - const float ext[3] = {2,4,2}; + const float halfExtents[3] = { 2, 4, 2 }; dtQueryFilter filter; dtPolyRef ref = 0; - query->findNearestPoly(p, ext, &filter, &ref, 0); + query->findNearestPoly(p, halfExtents, &filter, &ref, 0); floodNavmesh(nav, m_flags, ref, 1); } diff --git a/RecastDemo/Source/Sample_Debug.cpp b/RecastDemo/Source/Sample_Debug.cpp index a8b8e2f5..d842e64b 100644 --- a/RecastDemo/Source/Sample_Debug.cpp +++ b/RecastDemo/Source/Sample_Debug.cpp @@ -124,12 +124,12 @@ Sample_Debug::Sample_Debug() : m_navMesh->addTileAt(-14,-14, data, dataSize, true); } - const float ext[3] = {40,100,40}; + const float halfExtents[3] = {40,100,40}; const float center[3] = { -1667.9491f, 135.52649f, -1680.6149f }; dtQueryFilter filter; - m_ref = m_navMesh->findNearestPoly(center, ext, &filter, 0); + m_ref = m_navMesh->findNearestPoly(center, halfExtents, &filter, 0); - vcopy(m_ext, ext); + vcopy(m_halfExtents, halfExtents); vcopy(m_center, center);*/ @@ -204,8 +204,8 @@ void Sample_Debug::handleRender() duDebugDrawNavMeshPoly(&m_dd, *m_navMesh, m_ref, duRGBA(255,0,0,128)); /* float bmin[3], bmax[3]; - rcVsub(bmin, m_center, m_ext); - rcVadd(bmax, m_center, m_ext); + rcVsub(bmin, m_center, m_halfExtents); + rcVadd(bmax, m_center, m_halfExtents); duDebugDrawBoxWire(&dd, bmin[0],bmin[1],bmin[2], bmax[0],bmax[1],bmax[2], duRGBA(255,255,255,128), 1.0f); duDebugDrawCross(&dd, m_center[0], m_center[1], m_center[2], 1.0f, duRGBA(255,255,255,128), 2.0f);*/ From c70fc8a89ab75b86a9c76ce290bf6309cff639c1 Mon Sep 17 00:00:00 2001 From: Matthew Endsley Date: Wed, 23 Aug 2017 22:02:45 -0700 Subject: [PATCH 04/99] Return OOM error when navmeshtile allocations fail This was previsously return a DT_SUCCESS mask, as the prior call to dtAllocTileCache* had succeeded. --- DetourTileCache/Source/DetourTileCache.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DetourTileCache/Source/DetourTileCache.cpp b/DetourTileCache/Source/DetourTileCache.cpp index 707c1923..a82cd135 100644 --- a/DetourTileCache/Source/DetourTileCache.cpp +++ b/DetourTileCache/Source/DetourTileCache.cpp @@ -706,7 +706,7 @@ dtStatus dtTileCache::buildNavMeshTile(const dtCompressedTileRef ref, dtNavMesh* bc.lcset = dtAllocTileCacheContourSet(m_talloc); if (!bc.lcset) - return status; + return DT_FAILURE | DT_OUT_OF_MEMORY; status = dtBuildTileCacheContours(m_talloc, *bc.layer, walkableClimbVx, m_params.maxSimplificationError, *bc.lcset); if (dtStatusFailed(status)) @@ -714,7 +714,7 @@ dtStatus dtTileCache::buildNavMeshTile(const dtCompressedTileRef ref, dtNavMesh* bc.lmesh = dtAllocTileCachePolyMesh(m_talloc); if (!bc.lmesh) - return status; + return DT_FAILURE | DT_OUT_OF_MEMORY; status = dtBuildTileCachePolyMesh(m_talloc, *bc.lcset, *bc.lmesh); if (dtStatusFailed(status)) return status; From 7b7c94118162589b0423d432268bf609d4c78a42 Mon Sep 17 00:00:00 2001 From: Jonathan Adamczewski Date: Thu, 12 Jan 2017 16:13:27 -0800 Subject: [PATCH 05/99] Disable exception handling in MS std headers --- RecastDemo/premake5.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RecastDemo/premake5.lua b/RecastDemo/premake5.lua index 04334b41..2dfab884 100644 --- a/RecastDemo/premake5.lua +++ b/RecastDemo/premake5.lua @@ -35,7 +35,7 @@ solution "recastnavigation" -- windows specific configuration "windows" - defines { "WIN32", "_WINDOWS", "_CRT_SECURE_NO_WARNINGS" } + defines { "WIN32", "_WINDOWS", "_CRT_SECURE_NO_WARNINGS", "_HAS_EXCEPTIONS=0" } -- linux specific configuration { "linux", "gmake" } From 2c85309280dbc9c82029e7ab16dfb01b9235c74e Mon Sep 17 00:00:00 2001 From: Jonathan Adamczewski Date: Thu, 12 Jan 2017 16:13:51 -0800 Subject: [PATCH 06/99] Suppress warning --- RecastDemo/Contrib/fastlz/fastlz.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/RecastDemo/Contrib/fastlz/fastlz.c b/RecastDemo/Contrib/fastlz/fastlz.c index 3c9d6f6f..645d2947 100644 --- a/RecastDemo/Contrib/fastlz/fastlz.c +++ b/RecastDemo/Contrib/fastlz/fastlz.c @@ -81,6 +81,11 @@ typedef unsigned char flzuint8; typedef unsigned short flzuint16; typedef unsigned int flzuint32; +/* Disable "conversion from A to B, possible loss of data" warning when using MSVC */ +#if defined(_MSC_VER) +#pragma warning(disable: 4244) +#endif + /* prototypes */ int fastlz_compress(const void* input, int length, void* output); int fastlz_compress_level(int level, const void* input, int length, void* output); From 0004bf4a232a22631abf4f4a750790d9ff7b789a Mon Sep 17 00:00:00 2001 From: Liam Mitchell Date: Sat, 6 Jan 2018 00:20:58 +1300 Subject: [PATCH 07/99] No cast in SampleInterfaces BuildContext::doStopTimer (#299) Removed int cast because it led to loss of time data. Fixes #289 --- RecastDemo/Source/SampleInterfaces.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RecastDemo/Source/SampleInterfaces.cpp b/RecastDemo/Source/SampleInterfaces.cpp index 44b306b0..72bb34c2 100644 --- a/RecastDemo/Source/SampleInterfaces.cpp +++ b/RecastDemo/Source/SampleInterfaces.cpp @@ -68,7 +68,7 @@ void BuildContext::doStartTimer(const rcTimerLabel label) void BuildContext::doStopTimer(const rcTimerLabel label) { const TimeVal endTime = getPerfTime(); - const int deltaTime = (int)(endTime - m_startTime[label]); + const TimeVal deltaTime = endTime - m_startTime[label]; if (m_accTime[label] == -1) m_accTime[label] = deltaTime; else From bda59b7c62a1f0d40d3d1b9e8587b12383fe5538 Mon Sep 17 00:00:00 2001 From: chenshungen Date: Fri, 5 Jan 2018 19:33:33 +0800 Subject: [PATCH 08/99] Fixed load .gset file error (#293) --- RecastDemo/Source/Filelist.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/RecastDemo/Source/Filelist.cpp b/RecastDemo/Source/Filelist.cpp index 8a8b7c9e..7e051304 100644 --- a/RecastDemo/Source/Filelist.cpp +++ b/RecastDemo/Source/Filelist.cpp @@ -55,10 +55,11 @@ void scanDirectoryAppend(const string& path, const string& ext, vector& return; } + int extLen = strlen(ext.c_str()); while ((current = readdir(dp)) != 0) { int len = strlen(current->d_name); - if (len > 4 && strncmp(current->d_name + len - 4, ext.c_str(), 4) == 0) + if (len > extLen && strncmp(current->d_name + len - extLen, ext.c_str(), extLen) == 0) { filelist.push_back(current->d_name); } @@ -74,4 +75,4 @@ void scanDirectory(const string& path, const string& ext, vector& fileli { filelist.clear(); scanDirectoryAppend(path, ext, filelist); -} \ No newline at end of file +} From 4988ecbaf094df18b1bf61a27a018d7e4eef4225 Mon Sep 17 00:00:00 2001 From: Aurelien Rainone Date: Fri, 5 Jan 2018 12:38:53 +0100 Subject: [PATCH 09/99] RecastDemo: add Load & Save buttons for SoloMesh Sample, and fix warnings (#258) * Implement saveAll loadAll for Sample_SoloMesh - Solo mesh sample is loaded and saved as a tiled mesh containing an unique tile, so as to remain compatible with tiled mesh - navmesh is saved to/loaded from `solo_navmesh.bin` binary file - add and connect the corresponding 'Save/Load' GUI elements * Fix `implit-fallthrough` gcc warnings On linux with gcc 7.2.0+ and all warnings treated as errors, implicit fallthroughs in case statements are considered as errors. If the comment just below the next `case` or `default` matches various strings, one of which is `falls through` the fallthrough is then considered explicit. --- RecastDemo/Include/Sample.h | 3 + RecastDemo/Include/Sample_SoloMesh.h | 2 +- RecastDemo/Source/InputGeom.cpp | 1 + RecastDemo/Source/Sample.cpp | 125 ++++++++++++++++++++++++++ RecastDemo/Source/Sample_SoloMesh.cpp | 24 ++++- RecastDemo/Source/Sample_TileMesh.cpp | 112 +---------------------- RecastDemo/Source/TestCase.cpp | 1 + 7 files changed, 154 insertions(+), 114 deletions(-) diff --git a/RecastDemo/Include/Sample.h b/RecastDemo/Include/Sample.h index f59975c8..aa46f860 100644 --- a/RecastDemo/Include/Sample.h +++ b/RecastDemo/Include/Sample.h @@ -132,6 +132,9 @@ protected: SampleDebugDraw m_dd; + dtNavMesh* loadAll(const char* path); + void saveAll(const char* path, const dtNavMesh* mesh); + public: Sample(); virtual ~Sample(); diff --git a/RecastDemo/Include/Sample_SoloMesh.h b/RecastDemo/Include/Sample_SoloMesh.h index 70afb11a..a278529e 100644 --- a/RecastDemo/Include/Sample_SoloMesh.h +++ b/RecastDemo/Include/Sample_SoloMesh.h @@ -62,7 +62,7 @@ protected: DrawMode m_drawMode; void cleanup(); - + public: Sample_SoloMesh(); virtual ~Sample_SoloMesh(); diff --git a/RecastDemo/Source/InputGeom.cpp b/RecastDemo/Source/InputGeom.cpp index 29af6c36..a5f9a0d1 100644 --- a/RecastDemo/Source/InputGeom.cpp +++ b/RecastDemo/Source/InputGeom.cpp @@ -92,6 +92,7 @@ static char* parseRow(char* buf, char* bufEnd, char* row, int len) case '\t': case ' ': if (start) break; + // else falls through default: start = false; row[n++] = c; diff --git a/RecastDemo/Source/Sample.cpp b/RecastDemo/Source/Sample.cpp index 26f2f76c..d8c4cfdb 100644 --- a/RecastDemo/Source/Sample.cpp +++ b/RecastDemo/Source/Sample.cpp @@ -322,3 +322,128 @@ void Sample::renderOverlayToolStates(double* proj, double* model, int* view) } } +static const int NAVMESHSET_MAGIC = 'M'<<24 | 'S'<<16 | 'E'<<8 | 'T'; //'MSET'; +static const int NAVMESHSET_VERSION = 1; + +struct NavMeshSetHeader +{ + int magic; + int version; + int numTiles; + dtNavMeshParams params; +}; + +struct NavMeshTileHeader +{ + dtTileRef tileRef; + int dataSize; +}; + +dtNavMesh* Sample::loadAll(const char* path) +{ + FILE* fp = fopen(path, "rb"); + if (!fp) return 0; + + // Read header. + NavMeshSetHeader header; + size_t readLen = fread(&header, sizeof(NavMeshSetHeader), 1, fp); + if (readLen != 1) + { + fclose(fp); + return 0; + } + if (header.magic != NAVMESHSET_MAGIC) + { + fclose(fp); + return 0; + } + if (header.version != NAVMESHSET_VERSION) + { + fclose(fp); + return 0; + } + + dtNavMesh* mesh = dtAllocNavMesh(); + if (!mesh) + { + fclose(fp); + return 0; + } + dtStatus status = mesh->init(&header.params); + if (dtStatusFailed(status)) + { + fclose(fp); + return 0; + } + + // Read tiles. + for (int i = 0; i < header.numTiles; ++i) + { + NavMeshTileHeader tileHeader; + readLen = fread(&tileHeader, sizeof(tileHeader), 1, fp); + if (readLen != 1) + { + fclose(fp); + return 0; + } + + if (!tileHeader.tileRef || !tileHeader.dataSize) + break; + + unsigned char* data = (unsigned char*)dtAlloc(tileHeader.dataSize, DT_ALLOC_PERM); + if (!data) break; + memset(data, 0, tileHeader.dataSize); + readLen = fread(data, tileHeader.dataSize, 1, fp); + if (readLen != 1) + { + dtFree(data); + fclose(fp); + return 0; + } + + mesh->addTile(data, tileHeader.dataSize, DT_TILE_FREE_DATA, tileHeader.tileRef, 0); + } + + fclose(fp); + + return mesh; +} + +void Sample::saveAll(const char* path, const dtNavMesh* mesh) +{ + if (!mesh) return; + + FILE* fp = fopen(path, "wb"); + if (!fp) + return; + + // Store header. + NavMeshSetHeader header; + header.magic = NAVMESHSET_MAGIC; + header.version = NAVMESHSET_VERSION; + header.numTiles = 0; + for (int i = 0; i < mesh->getMaxTiles(); ++i) + { + const dtMeshTile* tile = mesh->getTile(i); + if (!tile || !tile->header || !tile->dataSize) continue; + header.numTiles++; + } + memcpy(&header.params, mesh->getParams(), sizeof(dtNavMeshParams)); + fwrite(&header, sizeof(NavMeshSetHeader), 1, fp); + + // Store tiles. + for (int i = 0; i < mesh->getMaxTiles(); ++i) + { + const dtMeshTile* tile = mesh->getTile(i); + if (!tile || !tile->header || !tile->dataSize) continue; + + NavMeshTileHeader tileHeader; + tileHeader.tileRef = mesh->getTileRef(tile); + tileHeader.dataSize = tile->dataSize; + fwrite(&tileHeader, sizeof(tileHeader), 1, fp); + + fwrite(tile->data, tile->dataSize, 1, fp); + } + + fclose(fp); +} diff --git a/RecastDemo/Source/Sample_SoloMesh.cpp b/RecastDemo/Source/Sample_SoloMesh.cpp index 68cf7c88..2dd1af16 100644 --- a/RecastDemo/Source/Sample_SoloMesh.cpp +++ b/RecastDemo/Source/Sample_SoloMesh.cpp @@ -79,20 +79,38 @@ void Sample_SoloMesh::cleanup() dtFreeNavMesh(m_navMesh); m_navMesh = 0; } - + void Sample_SoloMesh::handleSettings() { Sample::handleCommonSettings(); - + if (imguiCheck("Keep Itermediate Results", m_keepInterResults)) m_keepInterResults = !m_keepInterResults; imguiSeparator(); + + imguiIndent(); + imguiIndent(); + + if (imguiButton("Save")) + { + Sample::saveAll("solo_navmesh.bin", m_navMesh); + } + + if (imguiButton("Load")) + { + dtFreeNavMesh(m_navMesh); + m_navMesh = Sample::loadAll("solo_navmesh.bin"); + m_navQuery->init(m_navMesh, 2048); + } + + imguiUnindent(); + imguiUnindent(); char msg[64]; snprintf(msg, 64, "Build Time: %.1fms", m_totalBuildTimeMs); imguiLabel(msg); - + imguiSeparator(); } diff --git a/RecastDemo/Source/Sample_TileMesh.cpp b/RecastDemo/Source/Sample_TileMesh.cpp index 7641987b..3167cea5 100644 --- a/RecastDemo/Source/Sample_TileMesh.cpp +++ b/RecastDemo/Source/Sample_TileMesh.cpp @@ -242,114 +242,6 @@ struct NavMeshTileHeader int dataSize; }; -void Sample_TileMesh::saveAll(const char* path, const dtNavMesh* mesh) -{ - if (!mesh) return; - - FILE* fp = fopen(path, "wb"); - if (!fp) - return; - - // Store header. - NavMeshSetHeader header; - header.magic = NAVMESHSET_MAGIC; - header.version = NAVMESHSET_VERSION; - header.numTiles = 0; - for (int i = 0; i < mesh->getMaxTiles(); ++i) - { - const dtMeshTile* tile = mesh->getTile(i); - if (!tile || !tile->header || !tile->dataSize) continue; - header.numTiles++; - } - memcpy(&header.params, mesh->getParams(), sizeof(dtNavMeshParams)); - fwrite(&header, sizeof(NavMeshSetHeader), 1, fp); - - // Store tiles. - for (int i = 0; i < mesh->getMaxTiles(); ++i) - { - const dtMeshTile* tile = mesh->getTile(i); - if (!tile || !tile->header || !tile->dataSize) continue; - - NavMeshTileHeader tileHeader; - tileHeader.tileRef = mesh->getTileRef(tile); - tileHeader.dataSize = tile->dataSize; - fwrite(&tileHeader, sizeof(tileHeader), 1, fp); - - fwrite(tile->data, tile->dataSize, 1, fp); - } - - fclose(fp); -} - -dtNavMesh* Sample_TileMesh::loadAll(const char* path) -{ - FILE* fp = fopen(path, "rb"); - if (!fp) return 0; - - // Read header. - NavMeshSetHeader header; - size_t readLen = fread(&header, sizeof(NavMeshSetHeader), 1, fp); - if (readLen != 1) - { - fclose(fp); - return 0; - } - if (header.magic != NAVMESHSET_MAGIC) - { - fclose(fp); - return 0; - } - if (header.version != NAVMESHSET_VERSION) - { - fclose(fp); - return 0; - } - - dtNavMesh* mesh = dtAllocNavMesh(); - if (!mesh) - { - fclose(fp); - return 0; - } - dtStatus status = mesh->init(&header.params); - if (dtStatusFailed(status)) - { - fclose(fp); - return 0; - } - - // Read tiles. - for (int i = 0; i < header.numTiles; ++i) - { - NavMeshTileHeader tileHeader; - readLen = fread(&tileHeader, sizeof(tileHeader), 1, fp); - if (readLen != 1) - { - fclose(fp); - return 0; - } - - if (!tileHeader.tileRef || !tileHeader.dataSize) - break; - - unsigned char* data = (unsigned char*)dtAlloc(tileHeader.dataSize, DT_ALLOC_PERM); - if (!data) break; - memset(data, 0, tileHeader.dataSize); - readLen = fread(data, tileHeader.dataSize, 1, fp); - if (readLen != 1) - { - fclose(fp); - return 0; - } - - mesh->addTile(data, tileHeader.dataSize, DT_TILE_FREE_DATA, tileHeader.tileRef, 0); - } - - fclose(fp); - - return mesh; -} - void Sample_TileMesh::handleSettings() { Sample::handleCommonSettings(); @@ -401,13 +293,13 @@ void Sample_TileMesh::handleSettings() if (imguiButton("Save")) { - saveAll("all_tiles_navmesh.bin", m_navMesh); + Sample::saveAll("all_tiles_navmesh.bin", m_navMesh); } if (imguiButton("Load")) { dtFreeNavMesh(m_navMesh); - m_navMesh = loadAll("all_tiles_navmesh.bin"); + m_navMesh = Sample::loadAll("all_tiles_navmesh.bin"); m_navQuery->init(m_navMesh, 2048); } diff --git a/RecastDemo/Source/TestCase.cpp b/RecastDemo/Source/TestCase.cpp index aecf9a89..cf1214a7 100644 --- a/RecastDemo/Source/TestCase.cpp +++ b/RecastDemo/Source/TestCase.cpp @@ -76,6 +76,7 @@ static char* parseRow(char* buf, char* bufEnd, char* row, int len) case '\t': case ' ': if (start) break; + // else falls through default: start = false; row[n++] = c; From 5d4186046c8b6aec6a0784f07fb1fe5c771e6b98 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Fri, 5 Jan 2018 12:57:16 +0100 Subject: [PATCH 10/99] Mark a fall-through with a comment Fix #283 --- RecastDemo/Source/MeshLoaderObj.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/RecastDemo/Source/MeshLoaderObj.cpp b/RecastDemo/Source/MeshLoaderObj.cpp index 8b44127d..38e6c4cb 100644 --- a/RecastDemo/Source/MeshLoaderObj.cpp +++ b/RecastDemo/Source/MeshLoaderObj.cpp @@ -99,6 +99,7 @@ static char* parseRow(char* buf, char* bufEnd, char* row, int len) case '\t': case ' ': if (start) break; + // else falls through default: start = false; row[n++] = c; From 494c2967a71dcea44433e50b2c8fbbf860927ece Mon Sep 17 00:00:00 2001 From: but0n Date: Sat, 3 Mar 2018 16:05:08 +0800 Subject: [PATCH 11/99] doc: fix typo SDl --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7db79963..5827a7a8 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ RecastDemo uses [premake5](http://premake.github.io/) to build platform specific #### Linux -- Install SDl2 and its dependencies according to your distro's guidelines. +- Install SDL2 and its dependencies according to your distro's guidelines. - run `premake5 gmake` from the `RecastDemo` folder. - `cd Build/gmake` then `make` - Run `RecastDemo\Bin\RecastDemo` From 367065415cf24170a2b5439f69765aa556a1075f Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 24 Dec 2017 15:24:38 +0300 Subject: [PATCH 12/99] Unconditionally initialize rcCompactHeightfield borderSize To avoid uninitialized value when borderSize <= 0. --- Recast/Source/RecastRegion.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Recast/Source/RecastRegion.cpp b/Recast/Source/RecastRegion.cpp index 38a2bd6b..9f7fe582 100644 --- a/Recast/Source/RecastRegion.cpp +++ b/Recast/Source/RecastRegion.cpp @@ -1391,9 +1391,9 @@ bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf, paintRectRegion(w-bw, w, 0, h, id|RC_BORDER_REG, chf, srcReg); id++; paintRectRegion(0, w, 0, bh, id|RC_BORDER_REG, chf, srcReg); id++; paintRectRegion(0, w, h-bh, h, id|RC_BORDER_REG, chf, srcReg); id++; - - chf.borderSize = borderSize; } + + chf.borderSize = borderSize; rcIntArray prev(256); @@ -1581,9 +1581,9 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, paintRectRegion(w-bw, w, 0, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++; paintRectRegion(0, w, 0, bh, regionId|RC_BORDER_REG, chf, srcReg); regionId++; paintRectRegion(0, w, h-bh, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++; - - chf.borderSize = borderSize; } + + chf.borderSize = borderSize; int sId = -1; while (level > 0) @@ -1709,9 +1709,9 @@ bool rcBuildLayerRegions(rcContext* ctx, rcCompactHeightfield& chf, paintRectRegion(w-bw, w, 0, h, id|RC_BORDER_REG, chf, srcReg); id++; paintRectRegion(0, w, 0, bh, id|RC_BORDER_REG, chf, srcReg); id++; paintRectRegion(0, w, h-bh, h, id|RC_BORDER_REG, chf, srcReg); id++; - - chf.borderSize = borderSize; } + + chf.borderSize = borderSize; rcIntArray prev(256); From 05dd77db196961704de7df1cd103884918fac612 Mon Sep 17 00:00:00 2001 From: cexikitin Date: Tue, 9 Jan 2018 14:05:47 -0500 Subject: [PATCH 13/99] Provide better feedback when dtNavMesh::addTile fails due to a tile already being present at a given x,y --- Detour/Include/DetourStatus.h | 1 + Detour/Source/DetourNavMesh.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Detour/Include/DetourStatus.h b/Detour/Include/DetourStatus.h index af822c4a..8e1bb44b 100644 --- a/Detour/Include/DetourStatus.h +++ b/Detour/Include/DetourStatus.h @@ -35,6 +35,7 @@ static const unsigned int DT_INVALID_PARAM = 1 << 3; // An input parameter was i static const unsigned int DT_BUFFER_TOO_SMALL = 1 << 4; // Result buffer for the query was too small to store all results. static const unsigned int DT_OUT_OF_NODES = 1 << 5; // Query ran out of nodes during search. static const unsigned int DT_PARTIAL_RESULT = 1 << 6; // Query did not reach the end location, returning best guess. +static const unsigned int DT_ALREADY_OCCUPIED = 1 << 7; // A tile has already been assigned to the given x,y coordinate // Returns true of status is success. diff --git a/Detour/Source/DetourNavMesh.cpp b/Detour/Source/DetourNavMesh.cpp index b81a2567..17df26d8 100644 --- a/Detour/Source/DetourNavMesh.cpp +++ b/Detour/Source/DetourNavMesh.cpp @@ -855,7 +855,7 @@ dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags, // Make sure the location is free. if (getTileAt(header->x, header->y, header->layer)) - return DT_FAILURE; + return DT_FAILURE | DT_ALREADY_OCCUPIED; // Allocate a tile. dtMeshTile* tile = 0; From 4566d01c8fc6032c846bf0ae1219ed16c18ad383 Mon Sep 17 00:00:00 2001 From: Roman Siromakha Date: Sat, 21 Apr 2018 01:16:52 +0300 Subject: [PATCH 14/99] Support build by CMake (#310) --- .gitignore | 4 + .travis.yml | 13 +- CMakeLists.txt | 27 ++++ DebugUtils/CMakeLists.txt | 26 ++++ Detour/CMakeLists.txt | 23 +++ DetourCrowd/CMakeLists.txt | 24 +++ DetourTileCache/CMakeLists.txt | 25 ++++ Recast/CMakeLists.txt | 23 +++ RecastDemo/CMakeLists.txt | 27 ++++ RecastDemo/cmake/FindSDL2.cmake | 249 ++++++++++++++++++++++++++++++++ Tests/CMakeLists.txt | 10 ++ 11 files changed, 447 insertions(+), 4 deletions(-) create mode 100644 CMakeLists.txt create mode 100644 DebugUtils/CMakeLists.txt create mode 100644 Detour/CMakeLists.txt create mode 100644 DetourCrowd/CMakeLists.txt create mode 100644 DetourTileCache/CMakeLists.txt create mode 100644 Recast/CMakeLists.txt create mode 100644 RecastDemo/CMakeLists.txt create mode 100644 RecastDemo/cmake/FindSDL2.cmake create mode 100644 Tests/CMakeLists.txt diff --git a/.gitignore b/.gitignore index c4a9ba04..98f17e4b 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,7 @@ RecastDemo/Contrib/SDL/* ## Generated doc files Docs/html + +## IDE files +.idea/ +cmake-build-*/ diff --git a/.travis.yml b/.travis.yml index 6b504e3c..17ec14a6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,8 +10,10 @@ compiler: # Build both debug and release configurations, through use of an environment variable in the build matrix. env: - - CONFIGURATION=debug - - CONFIGURATION=release + - CONFIGURATION: debug + CMAKE_BUILD_TYPE: Debug + - CONFIGURATION: release + CMAKE_BUILD_TYPE: Release install: # Download and build SDL2 from source. @@ -23,7 +25,7 @@ install: - tar -xzf SDL2.tar.gz - cd SDL2-2.0.4 - ./configure --prefix=$PREFIX - - make -j5 + - make -j$(nproc) - make install - cd .. # Download and build premake5 from source; the Travis environment doesn't have the right version of glibc6 for the prebuilt binaries to work. @@ -38,8 +40,11 @@ install: # Have to cd into directory and back out since premake5 doesn't appear to accept a directory argument. before_script: - cd RecastDemo && ../premake5 gmake && cd .. + - mkdir build && cd build && cmake -DRECASTNAVIGATION_STATIC=TRUE -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} .. && cd .. # Run make in the directory containing generated makefiles, on the configuration specified by the environment variable. script: - - make -C RecastDemo/Build/gmake config=$CONFIGURATION + - make -C RecastDemo/Build/gmake -j$(nproc) config=$CONFIGURATION - RecastDemo/Bin/Tests + - make -C build -j$(nproc) + - cd build && ctest diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..d4fff420 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.0) + +project(RecastNavigation) + +# lib versions +SET(SOVERSION 1) +SET(VERSION 1.0.0) + +option(RECASTNAVIGATION_DEMO "Build demo" ON) +option(RECASTNAVIGATION_TESTS "Build tests" ON) +option(RECASTNAVIGATION_EXAMPLES "Build examples" ON) +option(RECASTNAVIGATION_STATIC "Build static libraries" OFF) + +add_subdirectory(DebugUtils) +add_subdirectory(Detour) +add_subdirectory(DetourCrowd) +add_subdirectory(DetourTileCache) +add_subdirectory(Recast) + +if (RECASTNAVIGATION_DEMO) + add_subdirectory(RecastDemo) +endif () + +if (RECASTNAVIGATION_TESTS) + enable_testing() + add_subdirectory(Tests) +endif () diff --git a/DebugUtils/CMakeLists.txt b/DebugUtils/CMakeLists.txt new file mode 100644 index 00000000..36ebc0da --- /dev/null +++ b/DebugUtils/CMakeLists.txt @@ -0,0 +1,26 @@ +file(GLOB SOURCES Source/*.cpp) + +include_directories(../Recast/Include) +include_directories(../Detour/Include) +include_directories(../DetourTileCache/Include) +include_directories(Include) + +if (RECASTNAVIGATION_STATIC) + add_library(DebugUtils STATIC ${SOURCES}) +else() + add_library(DebugUtils SHARED ${SOURCES}) +endif() + +set_target_properties(DebugUtils PROPERTIES + SOVERSION ${SOVERSION} + VERSION ${VERSION} + ) + +install(TARGETS DebugUtils + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + COMPONENT library + ) + +file(GLOB INCLUDES Include/*.h) +install(FILES ${INCLUDES} DESTINATION include) diff --git a/Detour/CMakeLists.txt b/Detour/CMakeLists.txt new file mode 100644 index 00000000..eb6dd184 --- /dev/null +++ b/Detour/CMakeLists.txt @@ -0,0 +1,23 @@ +file(GLOB SOURCES Source/*.cpp) + +include_directories(Include) + +if(RECASTNAVIGATION_STATIC) + add_library(Detour STATIC ${SOURCES}) +else() + add_library(Detour SHARED ${SOURCES}) +endif() + +set_target_properties(Detour PROPERTIES + SOVERSION ${SOVERSION} + VERSION ${VERSION} + ) + +install(TARGETS Detour + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + COMPONENT library + ) + +file(GLOB INCLUDES Include/*.h) +install(FILES ${INCLUDES} DESTINATION include) diff --git a/DetourCrowd/CMakeLists.txt b/DetourCrowd/CMakeLists.txt new file mode 100644 index 00000000..19314605 --- /dev/null +++ b/DetourCrowd/CMakeLists.txt @@ -0,0 +1,24 @@ +file(GLOB SOURCES Source/*.cpp) + +include_directories(../Detour/Include) +include_directories(Include) + +if (RECASTNAVIGATION_STATIC) + add_library(DetourCrowd STATIC ${SOURCES}) +else () + add_library(DetourCrowd SHARED ${SOURCES}) +endif () + +set_target_properties(DetourCrowd PROPERTIES + SOVERSION ${SOVERSION} + VERSION ${VERSION} + ) + +install(TARGETS DetourCrowd + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + COMPONENT library + ) + +file(GLOB INCLUDES Include/*.h) +install(FILES ${INCLUDES} DESTINATION include) diff --git a/DetourTileCache/CMakeLists.txt b/DetourTileCache/CMakeLists.txt new file mode 100644 index 00000000..a8d68afa --- /dev/null +++ b/DetourTileCache/CMakeLists.txt @@ -0,0 +1,25 @@ +file(GLOB SOURCES Source/*.cpp) + +include_directories(../Detour/Include) +include_directories(Include) + +if (RECASTNAVIGATION_STATIC) + add_library(DetourTileCache STATIC ${SOURCES}) +else () + add_library(DetourTileCache SHARED ${SOURCES}) +endif () + +set_target_properties(DetourTileCache PROPERTIES + SOVERSION ${SOVERSION} + VERSION ${VERSION} + ) + + +install(TARGETS DetourTileCache + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + COMPONENT library + ) + +file(GLOB INCLUDES Include/*.h) +install(FILES ${INCLUDES} DESTINATION include) diff --git a/Recast/CMakeLists.txt b/Recast/CMakeLists.txt new file mode 100644 index 00000000..94da5963 --- /dev/null +++ b/Recast/CMakeLists.txt @@ -0,0 +1,23 @@ +file(GLOB SOURCES Source/*.cpp) + +include_directories(Include) + +if (RECASTNAVIGATION_STATIC) + add_library(Recast STATIC ${SOURCES}) +else () + add_library(Recast SHARED ${SOURCES}) +endif () + +set_target_properties(Recast PROPERTIES + SOVERSION ${SOVERSION} + VERSION ${VERSION} + ) + +install(TARGETS Recast + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + COMPONENT library + ) + +file(GLOB INCLUDES Include/*.h) +install(FILES ${INCLUDES} DESTINATION include) diff --git a/RecastDemo/CMakeLists.txt b/RecastDemo/CMakeLists.txt new file mode 100644 index 00000000..d3208d28 --- /dev/null +++ b/RecastDemo/CMakeLists.txt @@ -0,0 +1,27 @@ +file(GLOB SOURCES Source/*.cpp Contrib/fastlz/fastlz.c) + +include(cmake/FindSDL2.cmake) + +find_package(OpenGL REQUIRED) +find_package(SDL2 REQUIRED) + +include_directories(SYSTEM ${OPENGL_INCLUDE_DIR}) +include_directories(SYSTEM ${SDL2_INCLUDE_DIRS}) +include_directories(SYSTEM Contrib/fastlz) +include_directories(SYSTEM Contrib) +include_directories(../DebugUtils/Include) +include_directories(../Detour/Include) +include_directories(../DetourCrowd/Include) +include_directories(../DetourTileCache/Include) +include_directories(../Recast/Include) +include_directories(Include) + +add_executable(RecastDemo ${SOURCES}) +file(COPY Bin/Meshes DESTINATION ${CMAKE_BINARY_DIR}/RecastDemo) +file(COPY Bin/TestCases DESTINATION ${CMAKE_BINARY_DIR}/RecastDemo) +file(COPY Bin/DroidSans.ttf DESTINATION ${CMAKE_BINARY_DIR}/RecastDemo) + +add_dependencies(RecastDemo DebugUtils Detour DetourCrowd DetourTileCache Recast) +target_link_libraries(RecastDemo ${OPENGL_LIBRARIES} ${SDL2_LIBRARY} DebugUtils Detour DetourCrowd DetourTileCache Recast) + +install(TARGETS RecastDemo RUNTIME DESTINATION bin) diff --git a/RecastDemo/cmake/FindSDL2.cmake b/RecastDemo/cmake/FindSDL2.cmake new file mode 100644 index 00000000..f69c68a1 --- /dev/null +++ b/RecastDemo/cmake/FindSDL2.cmake @@ -0,0 +1,249 @@ +# - Find SDL2 +# Find the SDL2 headers and libraries +# +# SDL2::SDL2 - Imported target to use for building a library +# SDL2::SDL2main - Imported interface target to use if you want SDL and SDLmain. +# SDL2_FOUND - True if SDL2 was found. +# SDL2_DYNAMIC - If we found a DLL version of SDL (meaning you might want to copy a DLL from SDL2::SDL2) +# +# Original Author: +# 2015 Ryan Pavlik +# +# Copyright Sensics, Inc. 2015. +# 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) + +# Set up architectures (for windows) and prefixes (for mingw builds) +if(WIN32) + if(MINGW) + include(MinGWSearchPathExtras OPTIONAL) + if(MINGWSEARCH_TARGET_TRIPLE) + set(SDL2_PREFIX ${MINGWSEARCH_TARGET_TRIPLE}) + endif() + endif() + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(SDL2_LIB_PATH_SUFFIX lib/x64) + if(NOT MSVC AND NOT SDL2_PREFIX) + set(SDL2_PREFIX x86_64-w64-mingw32) + endif() + else() + set(SDL2_LIB_PATH_SUFFIX lib/x86) + if(NOT MSVC AND NOT SDL2_PREFIX) + set(SDL2_PREFIX i686-w64-mingw32) + endif() + endif() +endif() + +if(SDL2_PREFIX) + set(SDL2_ORIGPREFIXPATH ${CMAKE_PREFIX_PATH}) + if(SDL2_ROOT_DIR) + list(APPEND CMAKE_PREFIX_PATH "${SDL2_ROOT_DIR}") + endif() + if(CMAKE_PREFIX_PATH) + foreach(_prefix ${CMAKE_PREFIX_PATH}) + list(APPEND CMAKE_PREFIX_PATH "${_prefix}/${SDL2_PREFIX}") + endforeach() + endif() + if(MINGWSEARCH_PREFIXES) + list(APPEND CMAKE_PREFIX_PATH ${MINGWSEARCH_PREFIXES}) + endif() +endif() + +# Invoke pkgconfig for hints +find_package(PkgConfig QUIET) +set(SDL2_INCLUDE_HINTS) +set(SDL2_LIB_HINTS) +if(PKG_CONFIG_FOUND) + pkg_search_module(SDL2PC QUIET sdl2) + if(SDL2PC_INCLUDE_DIRS) + set(SDL2_INCLUDE_HINTS ${SDL2PC_INCLUDE_DIRS}) + endif() + if(SDL2PC_LIBRARY_DIRS) + set(SDL2_LIB_HINTS ${SDL2PC_LIBRARY_DIRS}) + endif() +endif() + +include(FindPackageHandleStandardArgs) + +find_library(SDL2_LIBRARY + NAMES + SDL2 + HINTS + ${SDL2_LIB_HINTS} + PATHS + ${SDL2_ROOT_DIR} + ENV SDL2DIR + PATH_SUFFIXES lib SDL2 ${SDL2_LIB_PATH_SUFFIX}) + +set(_sdl2_framework FALSE) +# Some special-casing if we've found/been given a framework. +# Handles whether we're given the library inside the framework or the framework itself. +if(APPLE AND "${SDL2_LIBRARY}" MATCHES "(/[^/]+)*.framework(/.*)?$") + set(_sdl2_framework TRUE) + set(SDL2_FRAMEWORK "${SDL2_LIBRARY}") + # Move up in the directory tree as required to get the framework directory. + while("${SDL2_FRAMEWORK}" MATCHES "(/[^/]+)*.framework(/.*)$" AND NOT "${SDL2_FRAMEWORK}" MATCHES "(/[^/]+)*.framework$") + get_filename_component(SDL2_FRAMEWORK "${SDL2_FRAMEWORK}" DIRECTORY) + endwhile() + if("${SDL2_FRAMEWORK}" MATCHES "(/[^/]+)*.framework$") + set(SDL2_FRAMEWORK_NAME ${CMAKE_MATCH_1}) + # If we found a framework, do a search for the header ahead of time that will be more likely to get the framework header. + find_path(SDL2_INCLUDE_DIR + NAMES + SDL_haptic.h # this file was introduced with SDL2 + HINTS + "${SDL2_FRAMEWORK}/Headers/") + else() + # For some reason we couldn't get the framework directory itself. + # Shouldn't happen, but might if something is weird. + unset(SDL2_FRAMEWORK) + endif() +endif() + +find_path(SDL2_INCLUDE_DIR + NAMES + SDL_haptic.h # this file was introduced with SDL2 + HINTS + ${SDL2_INCLUDE_HINTS} + PATHS + ${SDL2_ROOT_DIR} + ENV SDL2DIR + PATH_SUFFIXES include include/sdl2 include/SDL2 SDL2) + +if(WIN32 AND SDL2_LIBRARY) + find_file(SDL2_RUNTIME_LIBRARY + NAMES + SDL2.dll + libSDL2.dll + HINTS + ${SDL2_LIB_HINTS} + PATHS + ${SDL2_ROOT_DIR} + ENV SDL2DIR + PATH_SUFFIXES bin lib ${SDL2_LIB_PATH_SUFFIX}) +endif() + + +if(WIN32 OR ANDROID OR IOS OR (APPLE AND NOT _sdl2_framework)) + set(SDL2_EXTRA_REQUIRED SDL2_SDLMAIN_LIBRARY) + find_library(SDL2_SDLMAIN_LIBRARY + NAMES + SDL2main + PATHS + ${SDL2_ROOT_DIR} + ENV SDL2DIR + PATH_SUFFIXES lib ${SDL2_LIB_PATH_SUFFIX}) +endif() + +if(MINGW AND NOT SDL2PC_FOUND) + find_library(SDL2_MINGW_LIBRARY mingw32) + find_library(SDL2_MWINDOWS_LIBRARY mwindows) +endif() + +if(SDL2_PREFIX) + # Restore things the way they used to be. + set(CMAKE_PREFIX_PATH ${SDL2_ORIGPREFIXPATH}) +endif() + +# handle the QUIETLY and REQUIRED arguments and set QUATLIB_FOUND to TRUE if +# all listed variables are TRUE +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(SDL2 + DEFAULT_MSG + SDL2_LIBRARY + SDL2_INCLUDE_DIR + ${SDL2_EXTRA_REQUIRED}) + +if(SDL2_FOUND) + if(NOT TARGET SDL2::SDL2) + # Create SDL2::SDL2 + if(WIN32 AND SDL2_RUNTIME_LIBRARY) + set(SDL2_DYNAMIC TRUE) + add_library(SDL2::SDL2 SHARED IMPORTED) + set_target_properties(SDL2::SDL2 + PROPERTIES + IMPORTED_IMPLIB "${SDL2_LIBRARY}" + IMPORTED_LOCATION "${SDL2_RUNTIME_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${SDL2_INCLUDE_DIR}" + ) + else() + add_library(SDL2::SDL2 UNKNOWN IMPORTED) + if(SDL2_FRAMEWORK AND SDL2_FRAMEWORK_NAME) + # Handle the case that SDL2 is a framework and we were able to decompose it above. + set_target_properties(SDL2::SDL2 PROPERTIES + IMPORTED_LOCATION "${SDL2_FRAMEWORK}/${SDL2_FRAMEWORK_NAME}") + elseif(_sdl2_framework AND SDL2_LIBRARY MATCHES "(/[^/]+)*.framework$") + # Handle the case that SDL2 is a framework and SDL_LIBRARY is just the framework itself. + + # This takes the basename of the framework, without the extension, + # and sets it (as a child of the framework) as the imported location for the target. + # This is the library symlink inside of the framework. + set_target_properties(SDL2::SDL2 PROPERTIES + IMPORTED_LOCATION "${SDL2_LIBRARY}/${CMAKE_MATCH_1}") + else() + # Handle non-frameworks (including non-Mac), as well as the case that we're given the library inside of the framework + set_target_properties(SDL2::SDL2 PROPERTIES + IMPORTED_LOCATION "${SDL2_LIBRARY}") + endif() + set_target_properties(SDL2::SDL2 + PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${SDL2_INCLUDE_DIR}" + ) + endif() + + if(APPLE) + # Need Cocoa here, is always a framework + find_library(SDL2_COCOA_LIBRARY Cocoa) + list(APPEND SDL2_EXTRA_REQUIRED SDL2_COCOA_LIBRARY) + if(SDL2_COCOA_LIBRARY) + set_target_properties(SDL2::SDL2 PROPERTIES + IMPORTED_LINK_INTERFACE_LIBRARIES ${SDL2_COCOA_LIBRARY}) + endif() + endif() + + + # Compute what to do with SDL2main + set(SDL2MAIN_LIBRARIES SDL2::SDL2) + add_library(SDL2::SDL2main INTERFACE IMPORTED) + if(SDL2_SDLMAIN_LIBRARY) + add_library(SDL2::SDL2main_real STATIC IMPORTED) + set_target_properties(SDL2::SDL2main_real + PROPERTIES + IMPORTED_LOCATION "${SDL2_SDLMAIN_LIBRARY}") + set(SDL2MAIN_LIBRARIES SDL2::SDL2main_real ${SDL2MAIN_LIBRARIES}) + endif() + if(MINGW) + # MinGW requires some additional libraries to appear earlier in the link line. + if(SDL2PC_LIBRARIES) + # Use pkgconfig-suggested extra libraries if available. + list(REMOVE_ITEM SDL2PC_LIBRARIES SDL2main SDL2) + set(SDL2MAIN_LIBRARIES ${SDL2PC_LIBRARIES} ${SDL2MAIN_LIBRARIES}) + else() + # fall back to extra libraries specified in pkg-config in + # an official binary distro of SDL2 for MinGW I downloaded + if(SDL2_MINGW_LIBRARY) + set(SDL2MAIN_LIBRARIES ${SDL2_MINGW_LIBRARY} ${SDL2MAIN_LIBRARIES}) + endif() + if(SDL2_MWINDOWS_LIBRARY) + set(SDL2MAIN_LIBRARIES ${SDL2_MWINDOWS_LIBRARY} ${SDL2MAIN_LIBRARIES}) + endif() + endif() + set_target_properties(SDL2::SDL2main + PROPERTIES + INTERFACE_COMPILE_DEFINITIONS "main=SDL_main") + endif() + set_target_properties(SDL2::SDL2main + PROPERTIES + INTERFACE_LINK_LIBRARIES "${SDL2MAIN_LIBRARIES}") + endif() + mark_as_advanced(SDL2_ROOT_DIR) +endif() + +mark_as_advanced(SDL2_LIBRARY + SDL2_RUNTIME_LIBRARY + SDL2_INCLUDE_DIR + SDL2_SDLMAIN_LIBRARY + SDL2_COCOA_LIBRARY + SDL2_MINGW_LIBRARY + SDL2_MWINDOWS_LIBRARY) diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt new file mode 100644 index 00000000..a98ccbf1 --- /dev/null +++ b/Tests/CMakeLists.txt @@ -0,0 +1,10 @@ +file(GLOB TESTS_SOURCES *.cpp Detour/*.cpp Recast/*.cpp) + +include_directories(../Detour/Include) +include_directories(../Recast/Include) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) + +add_executable(Tests ${TESTS_SOURCES}) +add_dependencies(Tests Recast Detour) +target_link_libraries(Tests Recast Detour) +add_test(Tests Tests) From 18562383f4c5cffa0678c709049340516ebc5e40 Mon Sep 17 00:00:00 2001 From: mbabinski-at-google <31736984+mbabinski-at-google@users.noreply.github.com> Date: Fri, 27 Apr 2018 15:08:52 -0500 Subject: [PATCH 15/99] Remove redundant copying in rcBuildRegions. (#314) Remove dstReg and dstDist from rcBuildRegions entirely -- just use temporary scratch space in expandRegions. This saves a considerable amount of time when the number of spans is large. A sample map tried has 16M spans, and only writes at most 4000 of them. --- Recast/Source/RecastRegion.cpp | 56 ++++++++++++++-------------------- 1 file changed, 23 insertions(+), 33 deletions(-) diff --git a/Recast/Source/RecastRegion.cpp b/Recast/Source/RecastRegion.cpp index 9f7fe582..ece26502 100644 --- a/Recast/Source/RecastRegion.cpp +++ b/Recast/Source/RecastRegion.cpp @@ -343,12 +343,11 @@ static bool floodRegion(int x, int y, int i, return count > 0; } -static unsigned short* expandRegions(int maxIter, unsigned short level, - rcCompactHeightfield& chf, - unsigned short* srcReg, unsigned short* srcDist, - unsigned short* dstReg, unsigned short* dstDist, - rcIntArray& stack, - bool fillStack) +static void expandRegions(int maxIter, unsigned short level, + rcCompactHeightfield& chf, + unsigned short* srcReg, unsigned short* srcDist, + rcIntArray& stack, + bool fillStack) { const int w = chf.width; const int h = chf.height; @@ -385,13 +384,12 @@ static unsigned short* expandRegions(int maxIter, unsigned short level, } } + rcIntArray dirtyEntries; int iter = 0; while (stack.size() > 0) { int failed = 0; - - memcpy(dstReg, srcReg, sizeof(unsigned short)*chf.spanCount); - memcpy(dstDist, srcDist, sizeof(unsigned short)*chf.spanCount); + dirtyEntries.resize(0); for (int j = 0; j < stack.size(); j += 3) { @@ -427,8 +425,9 @@ static unsigned short* expandRegions(int maxIter, unsigned short level, if (r) { stack[j+2] = -1; // mark as used - dstReg[i] = r; - dstDist[i] = d2; + dirtyEntries.push(i); + dirtyEntries.push(r); + dirtyEntries.push(d2); } else { @@ -436,9 +435,12 @@ static unsigned short* expandRegions(int maxIter, unsigned short level, } } - // rcSwap source and dest. - rcSwap(srcReg, dstReg); - rcSwap(srcDist, dstDist); + // Copy entries that differ between src and dst to keep them in sync. + for (int i = 0; i < dirtyEntries.size(); i+=3) { + int idx = dirtyEntries[i]; + srcReg[idx] = (unsigned short)dirtyEntries[i+1]; + srcDist[idx] = (unsigned short)dirtyEntries[i+2]; + } if (failed*3 == stack.size()) break; @@ -450,15 +452,13 @@ static unsigned short* expandRegions(int maxIter, unsigned short level, break; } } - - return srcReg; } static void sortCellsByLevel(unsigned short startLevel, rcCompactHeightfield& chf, - unsigned short* srcReg, + const unsigned short* srcReg, unsigned int nbStacks, rcIntArray* stacks, unsigned short loglevelsPerStack) // the levels per stack (2 in our case) as a bit shift { @@ -497,7 +497,7 @@ static void sortCellsByLevel(unsigned short startLevel, static void appendStacks(rcIntArray& srcStack, rcIntArray& dstStack, - unsigned short* srcReg) + const unsigned short* srcReg) { for (int j=0; j buf((unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount*4, RC_ALLOC_TEMP)); + rcScopedDelete buf((unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount*2, RC_ALLOC_TEMP)); if (!buf) { ctx->log(RC_LOG_ERROR, "rcBuildRegions: Out of memory 'tmp' (%d).", chf.spanCount*4); @@ -1555,8 +1555,6 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, unsigned short* srcReg = buf; unsigned short* srcDist = buf+chf.spanCount; - unsigned short* dstReg = buf+chf.spanCount*2; - unsigned short* dstDist = buf+chf.spanCount*3; memset(srcReg, 0, sizeof(unsigned short)*chf.spanCount); memset(srcDist, 0, sizeof(unsigned short)*chf.spanCount); @@ -1604,11 +1602,7 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, rcScopedTimer timerExpand(ctx, RC_TIMER_BUILD_REGIONS_EXPAND); // Expand current regions until no empty connected cells found. - if (expandRegions(expandIters, level, chf, srcReg, srcDist, dstReg, dstDist, lvlStacks[sId], false) != srcReg) - { - rcSwap(srcReg, dstReg); - rcSwap(srcDist, dstDist); - } + expandRegions(expandIters, level, chf, srcReg, srcDist, lvlStacks[sId], false); } { @@ -1638,11 +1632,7 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, } // Expand current regions until no empty connected cells found. - if (expandRegions(expandIters*8, 0, chf, srcReg, srcDist, dstReg, dstDist, stack, true) != srcReg) - { - rcSwap(srcReg, dstReg); - rcSwap(srcDist, dstDist); - } + expandRegions(expandIters*8, 0, chf, srcReg, srcDist, stack, true); ctx->stopTimer(RC_TIMER_BUILD_REGIONS_WATERSHED); From fa7debb0c3f7ea50a2cbad17d23cd421e981911e Mon Sep 17 00:00:00 2001 From: mbabinski-at-google <31736984+mbabinski-at-google@users.noreply.github.com> Date: Sun, 13 May 2018 09:22:56 -0500 Subject: [PATCH 16/99] Add homebrewed rcVector class and tests, and use it to implement rcIntArray. (#318) This implements most of the std::vector interface, with some minor elisions of infrequently used methods such as "insert", and some STL bookkeeping. Performance is favorable compared to std::vector, and, in some cases, somewhat better than naive code to manually manage a malloc'd dynamic array. BM_FlatArray_Push: 100 iterations in 72889721 nanos: 728897.21 nanos/it BM_FlatArray_Fill: 100 iterations in 2700746 nanos: 27007.46 nanos/it BM_FlatArray_Memset: 100 iterations in 2687810 nanos: 26878.10 nanos/it BM_rcVector_Push: 100 iterations in 51114316 nanos: 511143.16 nanos/it BM_rcVector_PushPreallocated: 100 iterations in 9532106 nanos: 95321.06 nanos/it BM_rcVector_Assign: 100 iterations in 2371534 nanos: 23715.34 nanos/it BM_rcVector_AssignIndices: 100 iterations in 2573052 nanos: 25730.52 nanos/it BM_rcVector_Resize: 100 iterations in 2455761 nanos: 24557.61 nanos/it BM_stdvector_Push: 100 iterations in 71739167 nanos: 717391.67 nanos/it BM_stdvector_PushPreallocated: 100 iterations in 15723737 nanos: 157237.37 nanos/it BM_stdvector_Assign: 100 iterations in 2564985 nanos: 25649.85 nanos/it BM_stdvector_AssignIndices: 100 iterations in 5174923 nanos: 51749.23 nanos/it BM_stdvector_Resize: 100 iterations in 2520054 nanos: 25200.54 nanos/it --- Recast/Include/RecastAlloc.h | 288 +++++++++++++++++++----- Recast/Source/RecastAlloc.cpp | 26 --- Tests/Recast/Tests_Recast.cpp | 400 ++++++++++++++++++++++++++++++++++ 3 files changed, 637 insertions(+), 77 deletions(-) diff --git a/Recast/Include/RecastAlloc.h b/Recast/Include/RecastAlloc.h index 3cdd450d..c71c0356 100644 --- a/Recast/Include/RecastAlloc.h +++ b/Recast/Include/RecastAlloc.h @@ -20,6 +20,9 @@ #define RECASTALLOC_H #include +#include + +#include /// Provides hint values to the memory allocator on how long the /// memory is expected to be used. @@ -58,64 +61,247 @@ void* rcAlloc(size_t size, rcAllocHint hint); /// @see rcAlloc void rcFree(void* ptr); +/// An implementation of operator new usable for placement new. The default one is part of STL (which we don't use). +/// rcNewTag is a dummy type used to differentiate our operator from the STL one, in case users import both Recast +/// and STL. +struct rcNewTag {}; +inline void* operator new(size_t, const rcNewTag&, void* p) { return p; } -/// A simple dynamic array of integers. +/// Signed to avoid warnnings when comparing to int loop indexes, and common error with comparing to zero. +/// MSVC2010 has a bug where ssize_t is unsigned (!!!). +typedef intptr_t rcSizeType; +#define RC_SIZE_MAX INTPTR_MAX + +/// Macros to hint to the compiler about the likeliest branch. Please add a benchmark that demonstrates a performance +/// improvement before intrudcing use cases. +#if defined(__GNUC__) || defined(__clang__) +#define rcLikely(x) __builtin_expect((x), true) +#define rcUnlikely(x) __builtin_expect((x), false) +#else +#define rcLikely(x) (x) +#define rcUnlikely(x) (x) +#endif + +/// Variable-sized storage type. Mimics the interface of std::vector with some notable differences: +/// * Uses rcAlloc()/rcFree() to handle storage. +/// * No support for a custom allocator. +/// * Uses signed size instead of size_t to avoid warnings in for loops: "for (int i = 0; i < foo.size(); i++)" +/// * Omits methods of limited utility: insert/erase, (bad performance), at (we don't use exceptions), operator=. +/// * assign() and the pre-sizing constructor follow C++11 semantics -- they don't construct a temporary if no value is provided. +/// * push_back() and resize() support adding values from the current vector. Range-based constructors and assign(begin, end) do not. +/// * No specialization for bool. +template +class rcVectorBase { + rcSizeType m_size; + rcSizeType m_cap; + T* m_data; + // Constructs a T at the give address with either the copy constructor or the default. + static void construct(T* p, const T& v) { ::new(rcNewTag(), (void*)p) T(v); } + static void construct(T* p) { ::new(rcNewTag(), (void*)p) T; } + static void construct_range(T* begin, T* end); + static void construct_range(T* begin, T* end, const T& value); + static void copy_range(T* dst, const T* begin, const T* end); + void destroy_range(rcSizeType begin, rcSizeType end); + // Creates an array of the given size, copies all of this vector's data into it, and returns it. + T* allocate_and_copy(rcSizeType size); + void resize_impl(rcSizeType size, const T* value); + public: + typedef rcSizeType size_type; + typedef T value_type; + + rcVectorBase() : m_size(0), m_cap(0), m_data(0) {}; + rcVectorBase(const rcVectorBase& other) : m_size(0), m_cap(0), m_data(0) { assign(other.begin(), other.end()); } + explicit rcVectorBase(rcSizeType count) : m_size(0), m_cap(0), m_data(0) { resize(count); } + rcVectorBase(rcSizeType count, const T& value) : m_size(0), m_cap(0), m_data(0) { resize(count, value); } + rcVectorBase(const T* begin, const T* end) : m_size(0), m_cap(0), m_data(0) { assign(begin, end); } + ~rcVectorBase() { destroy_range(0, m_size); rcFree(m_data); } + + void reserve(rcSizeType size); + + void assign(rcSizeType count, const T& value) { clear(); resize(count, value); } + void assign(const T* begin, const T* end); + + void resize(rcSizeType size) { resize_impl(size, NULL); } + void resize(rcSizeType size, const T& value) { resize_impl(size, &value); } + + void push_back(const T& value); + void pop_back() { rcAssert(m_size > 0); back().~T(); m_size--; } + void clear() { resize(0); } + rcSizeType size() const { return m_size; } + rcSizeType capacity() const { return m_cap; } + bool empty() const { return size() == 0; } + + const T& operator[](rcSizeType i) const { rcAssert(i >= 0 && i < m_size); return m_data[i]; } + T& operator[](rcSizeType i) { rcAssert(i >= 0 && i < m_size); return m_data[i]; } + + const T& front() const { rcAssert(m_size); return m_data[0]; } + T& front() { rcAssert(m_size); return m_data[0]; } + const T& back() const { rcAssert(m_size); return m_data[m_size - 1]; }; + T& back() { rcAssert(m_size); return m_data[m_size - 1]; }; + const T* data() const { return m_data; } + T* data() { return m_data; } + + T* begin() { return m_data; } + T* end() { return m_data + m_size; } + const T* begin() const { return m_data; } + const T* end() const { return m_data + m_size; } + + void swap(rcVectorBase& other); + + // Explicitly deleted. + rcVectorBase& operator=(const rcVectorBase& other); +}; + +template +void rcVectorBase::reserve(rcSizeType count) { + if (count <= m_cap) { + return; + } + T* new_data = allocate_and_copy(count); + destroy_range(0, m_size); + rcFree(m_data); + m_data = new_data; + m_cap = count; +} +template +T* rcVectorBase::allocate_and_copy(rcSizeType size) { + rcAssert(RC_SIZE_MAX / sizeof(T) >= size); + T* new_data = static_cast(rcAlloc(sizeof(T) * size, H)); + copy_range(new_data, m_data, m_data + m_size); + return new_data; +} +template +void rcVectorBase::assign(const T* begin, const T* end) { + clear(); + reserve(end - begin); + m_size = end - begin; + copy_range(m_data, begin, end); +} +template +void rcVectorBase::push_back(const T& value) { + // rcLikely increases performance by ~50% on BM_rcVector_PushPreallocated, + // and by ~2-5% on BM_rcVector_Push. + if (rcLikely(m_size < m_cap)) { + construct(m_data + m_size++, value); + return; + } + + rcAssert(RC_SIZE_MAX / 2 >= m_size); + rcSizeType new_cap = m_size ? 2*m_size : 1; + T* data = allocate_and_copy(new_cap); + // construct between allocate and destroy+free in case value is + // in this vector. + construct(data + m_size, value); + destroy_range(0, m_size); + m_size++; + m_cap = new_cap; + rcFree(m_data); + m_data = data; +} +template +void rcVectorBase::resize_impl(rcSizeType size, const T* value) { + if (size < m_size) { + destroy_range(size, m_size); + m_size = size; + } else if (size > m_size) { + T* new_data = allocate_and_copy(size); + // We defer deconstructing/freeing old data until after constructing + // new elements in case "value" is there. + if (value) { + construct_range(new_data + m_size, new_data + size, *value); + } else { + construct_range(new_data + m_size, new_data + size); + } + destroy_range(0, m_size); + rcFree(m_data); + m_data = new_data; + m_cap = size; + m_size = size; + } +} +template +void rcVectorBase::swap(rcVectorBase& other) { + // TODO: Reorganize headers so we can use rcSwap here. + rcSizeType tmp_cap = other.m_cap; + rcSizeType tmp_size = other.m_size; + T* tmp_data = other.m_data; + + other.m_cap = m_cap; + other.m_size = m_size; + other.m_data = m_data; + + m_cap = tmp_cap; + m_size = tmp_size; + m_data = tmp_data; +} +// static +template +void rcVectorBase::construct_range(T* begin, T* end) { + for (T* p = begin; p < end; p++) { + construct(p); + } +} +// static +template +void rcVectorBase::construct_range(T* begin, T* end, const T& value) { + for (T* p = begin; p < end; p++) { + construct(p, value); + } +} +// static +template +void rcVectorBase::copy_range(T* dst, const T* begin, const T* end) { + for (rcSizeType i = 0 ; i < end - begin; i++) { + construct(dst + i, begin[i]); + } +} +template +void rcVectorBase::destroy_range(rcSizeType begin, rcSizeType end) { + for (rcSizeType i = begin; i < end; i++) { + m_data[i].~T(); + } +} + +template +class rcTempVector : public rcVectorBase { + typedef rcVectorBase Base; +public: + rcTempVector() : Base() {} + explicit rcTempVector(rcSizeType size) : Base(size) {} + rcTempVector(rcSizeType size, const T& value) : Base(size, value) {} + rcTempVector(const rcTempVector& other) : Base(other) {} + rcTempVector(const T* begin, const T* end) : Base(begin, end) {} +}; +template +class rcPermVector : public rcVectorBase { + typedef rcVectorBase Base; +public: + rcPermVector() : Base() {} + explicit rcPermVector(rcSizeType size) : Base(size) {} + rcPermVector(rcSizeType size, const T& value) : Base(size, value) {} + rcPermVector(const rcPermVector& other) : Base(other) {} + rcPermVector(const T* begin, const T* end) : Base(begin, end) {} +}; + + +/// Legacy class. Prefer rcVector. class rcIntArray { - int* m_data; - int m_size, m_cap; - - void doResize(int n); - - // Explicitly disabled copy constructor and copy assignment operator. - rcIntArray(const rcIntArray&); - rcIntArray& operator=(const rcIntArray&); - + rcTempVector m_impl; public: - /// Constructs an instance with an initial array size of zero. - rcIntArray() : m_data(0), m_size(0), m_cap(0) {} - - /// Constructs an instance initialized to the specified size. - /// @param[in] n The initial size of the integer array. - rcIntArray(int n) : m_data(0), m_size(0), m_cap(0) { resize(n); } - ~rcIntArray() { rcFree(m_data); } - - /// Specifies the new size of the integer array. - /// @param[in] n The new size of the integer array. - void resize(int n) - { - if (n > m_cap) - doResize(n); - - m_size = n; - } - - /// Push the specified integer onto the end of the array and increases the size by one. - /// @param[in] item The new value. - void push(int item) { resize(m_size+1); m_data[m_size-1] = item; } - - /// Returns the value at the end of the array and reduces the size by one. - /// @return The value at the end of the array. + rcIntArray() {} + rcIntArray(int n) : m_impl(n, 0) {} + void push(int item) { m_impl.push_back(item); } + void resize(int size) { m_impl.resize(size); } int pop() { - if (m_size > 0) - m_size--; - - return m_data[m_size]; + int v = m_impl.back(); + m_impl.pop_back(); + return v; } - - /// The value at the specified array index. - /// @warning Does not provide overflow protection. - /// @param[in] i The index of the value. - const int& operator[](int i) const { return m_data[i]; } - - /// The value at the specified array index. - /// @warning Does not provide overflow protection. - /// @param[in] i The index of the value. - int& operator[](int i) { return m_data[i]; } - - /// The current size of the integer array. - int size() const { return m_size; } + int size() const { return m_impl.size(); } + int& operator[](int index) { return m_impl[index]; } + int operator[](int index) const { return m_impl[index]; } }; /// A simple helper class used to delete an array when it goes out of scope. diff --git a/Recast/Source/RecastAlloc.cpp b/Recast/Source/RecastAlloc.cpp index 453b5fa6..bdc36611 100644 --- a/Recast/Source/RecastAlloc.cpp +++ b/Recast/Source/RecastAlloc.cpp @@ -58,29 +58,3 @@ void rcFree(void* ptr) if (ptr) sRecastFreeFunc(ptr); } - -/// @class rcIntArray -/// -/// While it is possible to pre-allocate a specific array size during -/// construction or by using the #resize method, certain methods will -/// automatically resize the array as needed. -/// -/// @warning The array memory is not initialized to zero when the size is -/// manually set during construction or when using #resize. - -/// @par -/// -/// Using this method ensures the array is at least large enough to hold -/// the specified number of elements. This can improve performance by -/// avoiding auto-resizing during use. -void rcIntArray::doResize(int n) -{ - if (!m_cap) m_cap = n; - while (m_cap < n) m_cap *= 2; - int* newData = (int*)rcAlloc(m_cap*sizeof(int), RC_ALLOC_TEMP); - rcAssert(newData); - if (m_size && newData) memcpy(newData, m_data, m_size*sizeof(int)); - rcFree(m_data); - m_data = newData; -} - diff --git a/Tests/Recast/Tests_Recast.cpp b/Tests/Recast/Tests_Recast.cpp index b9176220..11def671 100644 --- a/Tests/Recast/Tests_Recast.cpp +++ b/Tests/Recast/Tests_Recast.cpp @@ -1,6 +1,14 @@ +#include +#include + #include "catch.hpp" #include "Recast.h" +#include "RecastAlloc.h" +#include "RecastAssert.h" + +// For comparing to rcVector in benchmarks. +#include TEST_CASE("rcSwap") { @@ -828,3 +836,395 @@ TEST_CASE("rcRasterizeTriangles") REQUIRE(!solid.spans[1 + 2 * width]->next); } } + +// Used to verify that rcVector constructs/destroys objects correctly. +struct Incrementor { + static int constructions; + static int destructions; + static int copies; + Incrementor() { constructions++; } + ~Incrementor() { destructions++; } + Incrementor(const Incrementor&) { copies++; } + Incrementor& operator=(const Incrementor&); // Deleted assignment. + + static void Reset() { + constructions = 0; + destructions = 0; + copies = 0; + } +}; +int Incrementor::constructions = 0; +int Incrementor::destructions = 0; +int Incrementor::copies = 0; + +const int kMaxAllocSize = 1024; +const unsigned char kClearValue = 0xff; +// Simple alloc/free that clears the memory on free.. +void* AllocAndInit(size_t size, rcAllocHint) { + rcAssert(kMaxAllocSize >= size); + return memset(malloc(kMaxAllocSize), 0, kMaxAllocSize); +} +void FreeAndClear(void* mem) { + if (mem) { + memset(mem, kClearValue, kMaxAllocSize); + } + free(mem); +} +// Verifies that memory has been initialized by AllocAndInit, and not cleared by FreeAndClear. +struct Copier { + const static int kAlive; + const static int kDead; + Copier() : value(kAlive) {} + + // checks that the source of the copy is valid. + Copier(const Copier& other) : value(kAlive) { + other.Verify(); + } + Copier& operator=(const Copier&); + + // Marks the value as dead. + ~Copier() { value = kDead; } + void Verify() const { + REQUIRE(value == kAlive); + } + volatile int value; +}; +const int Copier::kAlive = 0x1f; +const int Copier::kDead = 0xde; + +TEST_CASE("rcVector") +{ + SECTION("Vector basics.") + { + rcTempVector vec; + REQUIRE(vec.size() == 0); + vec.push_back(10); + vec.push_back(12); + REQUIRE(vec.size() == 2); + REQUIRE(vec.capacity() >= 2); + REQUIRE(vec[0] == 10); + REQUIRE(vec[1] == 12); + vec.pop_back(); + REQUIRE(vec.size() == 1); + REQUIRE(vec[0] == 10); + vec.pop_back(); + REQUIRE(vec.size() == 0); + vec.resize(100, 5); + REQUIRE(vec.size() == 100); + for (int i = 0; i < 100; i++) { + REQUIRE(vec[i] == 5); + vec[i] = i; + } + for (int i = 0; i < 100; i++) { + REQUIRE(vec[i] == i); + } + } + + SECTION("Constructors/Destructors") + { + Incrementor::Reset(); + rcTempVector vec; + REQUIRE(Incrementor::constructions == 0); + REQUIRE(Incrementor::destructions == 0); + REQUIRE(Incrementor::copies == 0); + vec.push_back(Incrementor()); + // push_back() may create and copy objects internally. + REQUIRE(Incrementor::constructions == 1); + REQUIRE(Incrementor::destructions >= 1); + // REQUIRE(Incrementor::copies >= 2); + + vec.clear(); + Incrementor::Reset(); + vec.resize(100); + // Initialized with default instance. Temporaries may be constructed, then destroyed. + REQUIRE(Incrementor::constructions == 100); + REQUIRE(Incrementor::destructions == 0); + REQUIRE(Incrementor::copies == 0); + + Incrementor::Reset(); + for (int i = 0; i < 100; i++) { + REQUIRE(Incrementor::destructions == i); + vec.pop_back(); + } + REQUIRE(Incrementor::constructions == 0); + REQUIRE(Incrementor::destructions == 100); + REQUIRE(Incrementor::copies == 0); + + vec.resize(100); + Incrementor::Reset(); + vec.clear(); + // One temp object is constructed for the default argumnet of resize(). + REQUIRE(Incrementor::constructions == 0); + REQUIRE(Incrementor::destructions == 100); + REQUIRE(Incrementor::copies == 0); + + Incrementor::Reset(); + vec.resize(100, Incrementor()); + REQUIRE(Incrementor::constructions == 1); + REQUIRE(Incrementor::destructions == 1); + REQUIRE(Incrementor::copies == 100); + } + + SECTION("Copying Contents") + { + + // veriyf event counts after doubling size -- should require a lot of copying and destorying. + rcTempVector vec; + Incrementor::Reset(); + vec.resize(100); + REQUIRE(Incrementor::constructions == 100); + REQUIRE(Incrementor::destructions == 0); + REQUIRE(Incrementor::copies == 0); + Incrementor::Reset(); + vec.resize(200); + REQUIRE(vec.size() == vec.capacity()); + REQUIRE(Incrementor::constructions == 100); // Construc new elements. + REQUIRE(Incrementor::destructions == 100); // Destroy old contents. + REQUIRE(Incrementor::copies == 100); // Copy old elements into new array. + } + + SECTION("Swap") + { + rcTempVector a(10, 0xa); + rcTempVector b; + + int* a_data = a.data(); + int* b_data = b.data(); + + a.swap(b); + REQUIRE(a.size() == 0); + REQUIRE(b.size() == 10); + REQUIRE(b[0] == 0xa); + REQUIRE(b[9] == 0xa); + REQUIRE(a.data() == b_data); + REQUIRE(b.data() == a_data); + } + + SECTION("Overlapping init") + { + rcAllocSetCustom(&AllocAndInit, &FreeAndClear); + rcTempVector vec; + // Force a realloc during push_back(). + vec.resize(64); + REQUIRE(vec.capacity() == vec.size()); + REQUIRE(vec.capacity() > 0); + REQUIRE(vec.size() == vec.capacity()); + + // Don't crash. + vec.push_back(vec[0]); + rcAllocSetCustom(NULL, NULL); + } + + SECTION("Vector Destructor") + { + { + rcTempVector vec; + vec.resize(10); + Incrementor::Reset(); + } + REQUIRE(Incrementor::destructions == 10); + } + + SECTION("Assign") + { + rcTempVector a(10, 0xa); + a.assign(5, 0xb); + REQUIRE(a.size() == 5); + REQUIRE(a[0] == 0xb); + REQUIRE(a[4] == 0xb); + a.assign(15, 0xc); + REQUIRE(a.size() == 15); + REQUIRE(a[0] == 0xc); + REQUIRE(a[14] == 0xc); + + rcTempVector b; + b.assign(a.data(), a.data() + a.size()); + REQUIRE(b.size() == a.size()); + REQUIRE(b[0] == a[0]); + } + + SECTION("Copy") + { + rcTempVector a(10, 0xa); + rcTempVector b(a); + REQUIRE(a.size() == 10); + REQUIRE(a.size() == b.size()); + REQUIRE(a[0] == b[0]); + REQUIRE(a.data() != b.data()); + rcTempVector c(a.data(), a.data() + a.size()); + REQUIRE(c.size() == a.size()); + REQUIRE(c[0] == a[0]); + + rcTempVector d(10); + Incrementor::Reset(); + rcTempVector e(d); + REQUIRE(Incrementor::constructions == 0); + REQUIRE(Incrementor::destructions == 0); + REQUIRE(Incrementor::copies == 10); + + Incrementor::Reset(); + rcTempVector f(d.data(), d.data() + d.size()); + REQUIRE(Incrementor::constructions == 0); + REQUIRE(Incrementor::destructions == 0); + REQUIRE(Incrementor::copies == 10); + } +} + +// TODO: Implement benchmarking for platforms other than posix. +#ifdef __unix__ +#include +#ifdef _POSIX_TIMERS +#include +#include + +int64_t NowNanos() { + struct timespec tp; + clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tp); + return tp.tv_nsec + 1000000000LL * tp.tv_sec; +} + +#define BM(name, iterations) \ + struct BM_ ## name { \ + static void Run() { \ + int64_t begin_time = NowNanos(); \ + for (int i = 0 ; i < iterations; i++) { \ + Body(); \ + } \ + int64_t nanos = NowNanos() - begin_time; \ + printf("BM_%-35s %ld iterations in %10ld nanos: %10.2f nanos/it\n", #name ":", (int64_t)iterations, nanos, double(nanos) / iterations); \ + } \ + static void Body(); \ + }; \ + TEST_CASE(#name) { \ + BM_ ## name::Run(); \ + } \ + void BM_ ## name::Body() + +const int64_t kNumLoops = 100; +const int64_t kNumInserts = 100000; + +// Prevent compiler from eliding a calculation. +// TODO: Implement for MSVC. +template +void DoNotOptimize(T* v) { + asm volatile ("" : "+r" (v)); +} + +BM(FlatArray_Push, kNumLoops) +{ + int cap = 64; + int* v = (int*)rcAlloc(cap * sizeof(int), RC_ALLOC_TEMP); + for (int j = 0; j < kNumInserts; j++) { + if (j == cap) { + cap *= 2; + int* tmp = (int*)rcAlloc(sizeof(int) * cap, RC_ALLOC_TEMP); + memcpy(tmp, v, j * sizeof(int)); + rcFree(v); + v = tmp; + } + v[j] = 2; + } + + DoNotOptimize(v); + rcFree(v); +} +BM(FlatArray_Fill, kNumLoops) +{ + int* v = (int*)rcAlloc(sizeof(int) * kNumInserts, RC_ALLOC_TEMP); + for (int j = 0; j < kNumInserts; j++) { + v[j] = 2; + } + + DoNotOptimize(v); + rcFree(v); +} +BM(FlatArray_Memset, kNumLoops) +{ + int* v = (int*)rcAlloc(sizeof(int) * kNumInserts, RC_ALLOC_TEMP); + memset(v, 0, kNumInserts * sizeof(int)); + + DoNotOptimize(v); + rcFree(v); +} + +BM(rcVector_Push, kNumLoops) +{ + rcTempVector v; + for (int j = 0; j < kNumInserts; j++) { + v.push_back(2); + } + DoNotOptimize(v.data()); +} +BM(rcVector_PushPreallocated, kNumLoops) +{ + rcTempVector v; + v.reserve(kNumInserts); + for (int j = 0; j < kNumInserts; j++) { + v.push_back(2); + } + DoNotOptimize(v.data()); +} +BM(rcVector_Assign, kNumLoops) +{ + rcTempVector v; + v.assign(kNumInserts, 2); + DoNotOptimize(v.data()); +} +BM(rcVector_AssignIndices, kNumLoops) +{ + rcTempVector v; + v.resize(kNumInserts); + for (int j = 0; j < kNumInserts; j++) { + v[j] = 2; + } + DoNotOptimize(v.data()); +} +BM(rcVector_Resize, kNumLoops) +{ + rcTempVector v; + v.resize(kNumInserts, 2); + DoNotOptimize(v.data()); +} + +BM(stdvector_Push, kNumLoops) +{ + std::vector v; + for (int j = 0; j < kNumInserts; j++) { + v.push_back(2); + } + DoNotOptimize(v.data()); +} +BM(stdvector_PushPreallocated, kNumLoops) +{ + std::vector v; + v.reserve(kNumInserts); + for (int j = 0; j < kNumInserts; j++) { + v.push_back(2); + } + DoNotOptimize(v.data()); +} +BM(stdvector_Assign, kNumLoops) +{ + std::vector v; + v.assign(kNumInserts, 2); + DoNotOptimize(v.data()); +} +BM(stdvector_AssignIndices, kNumLoops) +{ + std::vector v; + v.resize(kNumInserts); + for (int j = 0; j < kNumInserts; j++) { + v[j] = 2; + } + DoNotOptimize(v.data()); +} +BM(stdvector_Resize, kNumLoops) +{ + std::vector v; + v.resize(kNumInserts, 2); + DoNotOptimize(v.data()); +} + +#undef BM +#endif // _POSIX_TIMERS +#endif // __unix__ From 689cf00bd9563fa62fdc6282a81f6386eed26d57 Mon Sep 17 00:00:00 2001 From: Nikolay Zapolnov Date: Mon, 14 May 2018 01:47:09 +0200 Subject: [PATCH 17/99] Call proper memory deallocation function. --- DetourTileCache/Source/DetourTileCacheBuilder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DetourTileCache/Source/DetourTileCacheBuilder.cpp b/DetourTileCache/Source/DetourTileCacheBuilder.cpp index 1bbbcc48..21a59f1a 100644 --- a/DetourTileCache/Source/DetourTileCacheBuilder.cpp +++ b/DetourTileCache/Source/DetourTileCacheBuilder.cpp @@ -2196,7 +2196,7 @@ dtStatus dtDecompressTileCacheLayer(dtTileCacheAlloc* alloc, dtTileCacheCompress grids, gridsSize, &size); if (dtStatusFailed(status)) { - dtFree(buffer); + alloc->free(buffer); return status; } From bec073c9470f849bfc5cf364ebb3a9ef5a5d3a8e Mon Sep 17 00:00:00 2001 From: Maciej Babinski Date: Tue, 15 May 2018 07:59:19 -0500 Subject: [PATCH 18/99] Switch expandRegions to using a rcTempVector of structs rather than a blob of ints. Also updates rcVector::clear() to not require the contained type to be default-constructible. --- Recast/Include/RecastAlloc.h | 4 +++- Recast/Source/RecastRegion.cpp | 24 +++++++++++++++--------- Tests/Recast/Tests_Recast.cpp | 15 +++++++++++++++ 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/Recast/Include/RecastAlloc.h b/Recast/Include/RecastAlloc.h index c71c0356..a048e28d 100644 --- a/Recast/Include/RecastAlloc.h +++ b/Recast/Include/RecastAlloc.h @@ -123,10 +123,12 @@ class rcVectorBase { void resize(rcSizeType size) { resize_impl(size, NULL); } void resize(rcSizeType size, const T& value) { resize_impl(size, &value); } + // Not implemented as resize(0) because resize requires T to be default-constructible. + void clear() { destroy_range(0, m_size); m_size = 0; } void push_back(const T& value); void pop_back() { rcAssert(m_size > 0); back().~T(); m_size--; } - void clear() { resize(0); } + rcSizeType size() const { return m_size; } rcSizeType capacity() const { return m_cap; } bool empty() const { return size() == 0; } diff --git a/Recast/Source/RecastRegion.cpp b/Recast/Source/RecastRegion.cpp index ece26502..7d7c8df9 100644 --- a/Recast/Source/RecastRegion.cpp +++ b/Recast/Source/RecastRegion.cpp @@ -343,6 +343,14 @@ static bool floodRegion(int x, int y, int i, return count > 0; } +// Struct to keep track of entries in the region table that have been changed. +struct DirtyEntry { + DirtyEntry(int index_, unsigned short region_, unsigned short distance2_) + : index(index_), region(region_), distance2(distance2_) {} + int index; + unsigned short region; + unsigned short distance2; +}; static void expandRegions(int maxIter, unsigned short level, rcCompactHeightfield& chf, unsigned short* srcReg, unsigned short* srcDist, @@ -384,12 +392,12 @@ static void expandRegions(int maxIter, unsigned short level, } } - rcIntArray dirtyEntries; + rcTempVector dirtyEntries; int iter = 0; while (stack.size() > 0) { int failed = 0; - dirtyEntries.resize(0); + dirtyEntries.clear(); for (int j = 0; j < stack.size(); j += 3) { @@ -425,9 +433,7 @@ static void expandRegions(int maxIter, unsigned short level, if (r) { stack[j+2] = -1; // mark as used - dirtyEntries.push(i); - dirtyEntries.push(r); - dirtyEntries.push(d2); + dirtyEntries.push_back(DirtyEntry(i, r, d2)); } else { @@ -436,10 +442,10 @@ static void expandRegions(int maxIter, unsigned short level, } // Copy entries that differ between src and dst to keep them in sync. - for (int i = 0; i < dirtyEntries.size(); i+=3) { - int idx = dirtyEntries[i]; - srcReg[idx] = (unsigned short)dirtyEntries[i+1]; - srcDist[idx] = (unsigned short)dirtyEntries[i+2]; + for (int i = 0; i < dirtyEntries.size(); i++) { + int idx = dirtyEntries[i].index; + srcReg[idx] = dirtyEntries[i].region; + srcDist[idx] = dirtyEntries[i].distance2; } if (failed*3 == stack.size()) diff --git a/Tests/Recast/Tests_Recast.cpp b/Tests/Recast/Tests_Recast.cpp index 11def671..7c12bc77 100644 --- a/Tests/Recast/Tests_Recast.cpp +++ b/Tests/Recast/Tests_Recast.cpp @@ -892,6 +892,10 @@ struct Copier { const int Copier::kAlive = 0x1f; const int Copier::kDead = 0xde; +struct NotDefaultConstructible { + NotDefaultConstructible(int) {} +}; + TEST_CASE("rcVector") { SECTION("Vector basics.") @@ -1068,6 +1072,17 @@ TEST_CASE("rcVector") REQUIRE(Incrementor::destructions == 0); REQUIRE(Incrementor::copies == 10); } + + SECTION("Type Requirements") + { + // This section verifies that we don't enforce unnecessary + // requirements on the types we hold. + + // Implementing clear as resize(0) will cause this to fail + // as resize(0) requires T to be default constructible. + rcTempVector v; + v.clear(); + } } // TODO: Implement benchmarking for platforms other than posix. From 3ddb9ba074ce75bd79606a4780a7b464bd7c0ca9 Mon Sep 17 00:00:00 2001 From: mbabinski-at-google <31736984+mbabinski-at-google@users.noreply.github.com> Date: Thu, 17 May 2018 10:35:05 -0500 Subject: [PATCH 19/99] Convert lvlStacks to be vectors of structs instead of a bag of ints. (#323) --- Recast/Source/RecastRegion.cpp | 100 +++++++++++++++++---------------- 1 file changed, 52 insertions(+), 48 deletions(-) diff --git a/Recast/Source/RecastRegion.cpp b/Recast/Source/RecastRegion.cpp index 7d7c8df9..2b721a71 100644 --- a/Recast/Source/RecastRegion.cpp +++ b/Recast/Source/RecastRegion.cpp @@ -27,6 +27,16 @@ #include "RecastAssert.h" #include +namespace +{ +struct LevelStackEntry +{ + LevelStackEntry(int x_, int y_, int index_) : x(x_), y(y_), index(index_) {} + int x; + int y; + int index; +}; +} // namespace static void calculateDistanceField(rcCompactHeightfield& chf, unsigned short* src, unsigned short& maxDist) { @@ -245,17 +255,15 @@ static bool floodRegion(int x, int y, int i, unsigned short level, unsigned short r, rcCompactHeightfield& chf, unsigned short* srcReg, unsigned short* srcDist, - rcIntArray& stack) + rcTempVector& stack) { const int w = chf.width; const unsigned char area = chf.areas[i]; // Flood fill mark region. - stack.resize(0); - stack.push((int)x); - stack.push((int)y); - stack.push((int)i); + stack.clear(); + stack.push_back(LevelStackEntry(x, y, i)); srcReg[i] = r; srcDist[i] = 0; @@ -264,9 +272,11 @@ static bool floodRegion(int x, int y, int i, while (stack.size() > 0) { - int ci = stack.pop(); - int cy = stack.pop(); - int cx = stack.pop(); + LevelStackEntry& back = stack.back(); + int cx = back.x; + int cy = back.y; + int ci = back.index; + stack.pop_back(); const rcCompactSpan& cs = chf.spans[ci]; @@ -332,9 +342,7 @@ static bool floodRegion(int x, int y, int i, { srcReg[ai] = r; srcDist[ai] = 0; - stack.push(ax); - stack.push(ay); - stack.push(ai); + stack.push_back(LevelStackEntry(ax, ay, ai)); } } } @@ -344,7 +352,8 @@ static bool floodRegion(int x, int y, int i, } // Struct to keep track of entries in the region table that have been changed. -struct DirtyEntry { +struct DirtyEntry +{ DirtyEntry(int index_, unsigned short region_, unsigned short distance2_) : index(index_), region(region_), distance2(distance2_) {} int index; @@ -354,7 +363,7 @@ struct DirtyEntry { static void expandRegions(int maxIter, unsigned short level, rcCompactHeightfield& chf, unsigned short* srcReg, unsigned short* srcDist, - rcIntArray& stack, + rcTempVector& stack, bool fillStack) { const int w = chf.width; @@ -363,7 +372,7 @@ static void expandRegions(int maxIter, unsigned short level, if (fillStack) { // Find cells revealed by the raised level. - stack.resize(0); + stack.clear(); for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) @@ -373,9 +382,7 @@ static void expandRegions(int maxIter, unsigned short level, { if (chf.dist[i] >= level && srcReg[i] == 0 && chf.areas[i] != RC_NULL_AREA) { - stack.push(x); - stack.push(y); - stack.push(i); + stack.push_back(LevelStackEntry(x, y, i)); } } } @@ -384,11 +391,11 @@ static void expandRegions(int maxIter, unsigned short level, else // use cells in the input stack { // mark all cells which already have a region - for (int j=0; j 0) @@ -465,7 +472,7 @@ static void expandRegions(int maxIter, unsigned short level, static void sortCellsByLevel(unsigned short startLevel, rcCompactHeightfield& chf, const unsigned short* srcReg, - unsigned int nbStacks, rcIntArray* stacks, + unsigned int nbStacks, rcTempVector* stacks, unsigned short loglevelsPerStack) // the levels per stack (2 in our case) as a bit shift { const int w = chf.width; @@ -473,7 +480,7 @@ static void sortCellsByLevel(unsigned short startLevel, startLevel = startLevel >> loglevelsPerStack; for (unsigned int j=0; j& srcStack, + rcTempVector& dstStack, const unsigned short* srcReg) { - for (int j=0; j lvlStacks[NB_STACKS]; for (int i=0; i stack; + stack.reserve(256); unsigned short* srcReg = buf; unsigned short* srcDist = buf+chf.spanCount; @@ -1615,11 +1619,12 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, rcScopedTimer timerFloor(ctx, RC_TIMER_BUILD_REGIONS_FLOOD); // Mark new regions with IDs. - for (int j = 0; j= 0 && srcReg[i] == 0) { if (floodRegion(x, y, i, level, regionId, chf, srcReg, srcDist, stack)) @@ -1805,9 +1810,8 @@ bool rcBuildLayerRegions(rcContext* ctx, rcCompactHeightfield& chf, rcScopedTimer timerFilter(ctx, RC_TIMER_BUILD_REGIONS_FILTER); // Merge monotone regions to layers and remove small regions. - rcIntArray overlaps; chf.maxRegions = id; - if (!mergeAndFilterLayerRegions(ctx, minRegionArea, chf.maxRegions, chf, srcReg, overlaps)) + if (!mergeAndFilterLayerRegions(ctx, minRegionArea, chf.maxRegions, chf, srcReg)) return false; } From 1284a2b1915f4b5becd20573b31be47c8fd88220 Mon Sep 17 00:00:00 2001 From: mbabinski-at-google <31736984+mbabinski-at-google@users.noreply.github.com> Date: Fri, 18 May 2018 06:50:42 -0500 Subject: [PATCH 20/99] Remove placement-new in RecastRegion.cpp in favor of rcTempVector. (#326) --- Recast/Include/RecastAlloc.h | 17 ++++++++++++----- Recast/Source/RecastRegion.cpp | 28 ++++++++-------------------- 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/Recast/Include/RecastAlloc.h b/Recast/Include/RecastAlloc.h index a048e28d..a3c5fbdb 100644 --- a/Recast/Include/RecastAlloc.h +++ b/Recast/Include/RecastAlloc.h @@ -73,7 +73,7 @@ typedef intptr_t rcSizeType; #define RC_SIZE_MAX INTPTR_MAX /// Macros to hint to the compiler about the likeliest branch. Please add a benchmark that demonstrates a performance -/// improvement before intrudcing use cases. +/// improvement before introducing use cases. #if defined(__GNUC__) || defined(__clang__) #define rcLikely(x) __builtin_expect((x), true) #define rcUnlikely(x) __builtin_expect((x), false) @@ -116,7 +116,8 @@ class rcVectorBase { rcVectorBase(const T* begin, const T* end) : m_size(0), m_cap(0), m_data(0) { assign(begin, end); } ~rcVectorBase() { destroy_range(0, m_size); rcFree(m_data); } - void reserve(rcSizeType size); + // Unlike in std::vector, we return a bool to indicate whether the alloc was successful. + bool reserve(rcSizeType size); void assign(rcSizeType count, const T& value) { clear(); resize(count, value); } void assign(const T* begin, const T* end); @@ -155,21 +156,27 @@ class rcVectorBase { }; template -void rcVectorBase::reserve(rcSizeType count) { +bool rcVectorBase::reserve(rcSizeType count) { if (count <= m_cap) { - return; + return true; } T* new_data = allocate_and_copy(count); + if (!new_data) { + return false; + } destroy_range(0, m_size); rcFree(m_data); m_data = new_data; m_cap = count; + return true; } template T* rcVectorBase::allocate_and_copy(rcSizeType size) { rcAssert(RC_SIZE_MAX / sizeof(T) >= size); T* new_data = static_cast(rcAlloc(sizeof(T) * size, H)); - copy_range(new_data, m_data, m_data + m_size); + if (new_data) { + copy_range(new_data, m_data, m_data + m_size); + } return new_data; } template diff --git a/Recast/Source/RecastRegion.cpp b/Recast/Source/RecastRegion.cpp index 2b721a71..e1fc0ee7 100644 --- a/Recast/Source/RecastRegion.cpp +++ b/Recast/Source/RecastRegion.cpp @@ -25,7 +25,6 @@ #include "Recast.h" #include "RecastAlloc.h" #include "RecastAssert.h" -#include namespace { @@ -796,16 +795,15 @@ static bool mergeAndFilterRegions(rcContext* ctx, int minRegionArea, int mergeRe const int h = chf.height; const int nreg = maxRegionId+1; - rcRegion* regions = (rcRegion*)rcAlloc(sizeof(rcRegion)*nreg, RC_ALLOC_TEMP); - if (!regions) - { + rcTempVector regions; + if (!regions.reserve(nreg)) { ctx->log(RC_LOG_ERROR, "mergeAndFilterRegions: Out of memory 'regions' (%d).", nreg); return false; } // Construct regions for (int i = 0; i < nreg; ++i) - new(®ions[i]) rcRegion((unsigned short)i); + regions.push_back(rcRegion((unsigned short) i)); // Find edge of a region and find connections around the contour. for (int y = 0; y < h; ++y) @@ -1031,11 +1029,6 @@ static bool mergeAndFilterRegions(rcContext* ctx, int minRegionArea, int mergeRe if (regions[i].overlap) overlaps.push(regions[i].id); - for (int i = 0; i < nreg; ++i) - regions[i].~rcRegion(); - rcFree(regions); - - return true; } @@ -1057,16 +1050,15 @@ static bool mergeAndFilterLayerRegions(rcContext* ctx, int minRegionArea, const int h = chf.height; const int nreg = maxRegionId+1; - rcRegion* regions = (rcRegion*)rcAlloc(sizeof(rcRegion)*nreg, RC_ALLOC_TEMP); - if (!regions) - { + rcTempVector regions; + + // Construct regions + if (!regions.reserve(nreg)) { ctx->log(RC_LOG_ERROR, "mergeAndFilterLayerRegions: Out of memory 'regions' (%d).", nreg); return false; } - - // Construct regions for (int i = 0; i < nreg; ++i) - new(®ions[i]) rcRegion((unsigned short)i); + regions.push_back(rcRegion((unsigned short) i)); // Find region neighbours and overlapping regions. rcIntArray lregs(32); @@ -1244,10 +1236,6 @@ static bool mergeAndFilterLayerRegions(rcContext* ctx, int minRegionArea, srcReg[i] = regions[srcReg[i]].id; } - for (int i = 0; i < nreg; ++i) - regions[i].~rcRegion(); - rcFree(regions); - return true; } From 9235b4a0c49dac035ff3af9597738a892a6325f3 Mon Sep 17 00:00:00 2001 From: mbabinski-at-google <31736984+mbabinski-at-google@users.noreply.github.com> Date: Fri, 18 May 2018 08:40:37 -0500 Subject: [PATCH 21/99] Add rcNew/rcDelete, template functions that allocate/deallocate objects using rcAlloc/rcFree. (#324) Add rcNew/rcDelete, template functions that allocate/deallocate objects using rcAlloc/rcNew. The intention is to use these to move construction logic out of rcAllocateFooObject/rcFreeFooObject and into constructors/destructors, giving users flexbility in how objects are allocated. In particular, this allows stack allocation. --- Recast/Include/Recast.h | 2 ++ Recast/Source/Recast.cpp | 69 ++++++++++++++++++++++++++++++++-------- 2 files changed, 57 insertions(+), 14 deletions(-) diff --git a/Recast/Include/Recast.h b/Recast/Include/Recast.h index e85c0d2e..a5e22c8e 100644 --- a/Recast/Include/Recast.h +++ b/Recast/Include/Recast.h @@ -332,6 +332,8 @@ struct rcCompactSpan /// @ingroup recast struct rcCompactHeightfield { + rcCompactHeightfield(); + ~rcCompactHeightfield(); int width; ///< The width of the heightfield. (Along the x-axis in cell units.) int height; ///< The height of the heightfield. (Along the z-axis in cell units.) int spanCount; ///< The number of spans in the heightfield. diff --git a/Recast/Source/Recast.cpp b/Recast/Source/Recast.cpp index 8308d197..5d15c6b0 100644 --- a/Recast/Source/Recast.cpp +++ b/Recast/Source/Recast.cpp @@ -23,11 +23,34 @@ #include #include #include -#include #include "Recast.h" #include "RecastAlloc.h" #include "RecastAssert.h" +namespace +{ +/// Allocates and constructs an object of the given type, returning a pointer. +/// TODO: Support constructor args. +/// @param[in] hint Hint to the allocator. +template +T* rcNew(rcAllocHint hint) { + T* ptr = (T*)rcAlloc(sizeof(T), hint); + ::new(rcNewTag(), (void*)ptr) T(); + return ptr; +} + +/// Destroys and frees an object allocated with rcNew. +/// @param[in] ptr The object pointer to delete. +template +void rcDelete(T* ptr) { + if (ptr) { + ptr->~T(); + rcFree((void*)ptr); + } +} +} // namespace + + float rcSqrt(float x) { return sqrtf(x); @@ -73,7 +96,7 @@ void rcContext::log(const rcLogCategory category, const char* format, ...) rcHeightfield* rcAllocHeightfield() { - return new (rcAlloc(sizeof(rcHeightfield), RC_ALLOC_PERM)) rcHeightfield; + return rcNew(RC_ALLOC_PERM); } rcHeightfield::rcHeightfield() @@ -104,26 +127,44 @@ rcHeightfield::~rcHeightfield() void rcFreeHeightField(rcHeightfield* hf) { - if (!hf) return; - hf->~rcHeightfield(); - rcFree(hf); + rcDelete(hf); } rcCompactHeightfield* rcAllocCompactHeightfield() { - rcCompactHeightfield* chf = (rcCompactHeightfield*)rcAlloc(sizeof(rcCompactHeightfield), RC_ALLOC_PERM); - memset(chf, 0, sizeof(rcCompactHeightfield)); - return chf; + return rcNew(RC_ALLOC_PERM); } void rcFreeCompactHeightfield(rcCompactHeightfield* chf) { - if (!chf) return; - rcFree(chf->cells); - rcFree(chf->spans); - rcFree(chf->dist); - rcFree(chf->areas); - rcFree(chf); + rcDelete(chf); +} + +rcCompactHeightfield::rcCompactHeightfield() + : width(0), + height(0), + spanCount(0), + walkableHeight(0), + walkableClimb(0), + borderSize(0), + maxDistance(0), + maxRegions(0), + bmin(), + bmax(), + cs(0), + ch(0), + cells(0), + spans(0), + dist(0), + areas(0) +{ +} +rcCompactHeightfield::~rcCompactHeightfield() +{ + rcFree(cells); + rcFree(spans); + rcFree(dist); + rcFree(areas); } rcHeightfieldLayerSet* rcAllocHeightfieldLayerSet() From ba12abead9956e69afa1248e535b07249df45e48 Mon Sep 17 00:00:00 2001 From: Maciej Babinski Date: Fri, 18 May 2018 22:39:36 -0500 Subject: [PATCH 22/99] Add more constructors. --- Recast/Include/Recast.h | 6 ++ Recast/Source/Recast.cpp | 128 ++++++++++++++++++++++++--------------- 2 files changed, 85 insertions(+), 49 deletions(-) diff --git a/Recast/Include/Recast.h b/Recast/Include/Recast.h index a5e22c8e..4d557389 100644 --- a/Recast/Include/Recast.h +++ b/Recast/Include/Recast.h @@ -378,6 +378,8 @@ struct rcHeightfieldLayer /// @see rcAllocHeightfieldLayerSet, rcFreeHeightfieldLayerSet struct rcHeightfieldLayerSet { + rcHeightfieldLayerSet(); + ~rcHeightfieldLayerSet(); rcHeightfieldLayer* layers; ///< The layers in the set. [Size: #nlayers] int nlayers; ///< The number of layers in the set. }; @@ -397,6 +399,8 @@ struct rcContour /// @ingroup recast struct rcContourSet { + rcContourSet(); + ~rcContourSet(); rcContour* conts; ///< An array of the contours in the set. [Size: #nconts] int nconts; ///< The number of contours in the set. float bmin[3]; ///< The minimum bounds in world space. [(x, y, z)] @@ -413,6 +417,8 @@ struct rcContourSet /// @ingroup recast struct rcPolyMesh { + rcPolyMesh(); + ~rcPolyMesh(); unsigned short* verts; ///< The mesh vertices. [Form: (x, y, z) * #nverts] unsigned short* polys; ///< Polygon and neighbor data. [Length: #maxpolys * 2 * #nvp] unsigned short* regs; ///< The region id assigned to each polygon. [Length: #maxpolys] diff --git a/Recast/Source/Recast.cpp b/Recast/Source/Recast.cpp index 5d15c6b0..1b71710c 100644 --- a/Recast/Source/Recast.cpp +++ b/Recast/Source/Recast.cpp @@ -98,7 +98,6 @@ rcHeightfield* rcAllocHeightfield() { return rcNew(RC_ALLOC_PERM); } - rcHeightfield::rcHeightfield() : width() , height() @@ -141,22 +140,22 @@ void rcFreeCompactHeightfield(rcCompactHeightfield* chf) } rcCompactHeightfield::rcCompactHeightfield() - : width(0), - height(0), - spanCount(0), - walkableHeight(0), - walkableClimb(0), - borderSize(0), - maxDistance(0), - maxRegions(0), + : width(), + height(), + spanCount(), + walkableHeight(), + walkableClimb(), + borderSize(), + maxDistance(), + maxRegions(), bmin(), bmax(), - cs(0), - ch(0), - cells(0), - spans(0), - dist(0), - areas(0) + cs(), + ch(), + cells(), + spans(), + dist(), + areas() { } rcCompactHeightfield::~rcCompactHeightfield() @@ -169,60 +168,91 @@ rcCompactHeightfield::~rcCompactHeightfield() rcHeightfieldLayerSet* rcAllocHeightfieldLayerSet() { - rcHeightfieldLayerSet* lset = (rcHeightfieldLayerSet*)rcAlloc(sizeof(rcHeightfieldLayerSet), RC_ALLOC_PERM); - memset(lset, 0, sizeof(rcHeightfieldLayerSet)); - return lset; + return rcNew(RC_ALLOC_PERM); } - void rcFreeHeightfieldLayerSet(rcHeightfieldLayerSet* lset) { - if (!lset) return; - for (int i = 0; i < lset->nlayers; ++i) + rcDelete(lset); +} + +rcHeightfieldLayerSet::rcHeightfieldLayerSet() + : layers(), nlayers() {} +rcHeightfieldLayerSet::~rcHeightfieldLayerSet() +{ + for (int i = 0; i < nlayers; ++i) { - rcFree(lset->layers[i].heights); - rcFree(lset->layers[i].areas); - rcFree(lset->layers[i].cons); + rcFree(layers[i].heights); + rcFree(layers[i].areas); + rcFree(layers[i].cons); } - rcFree(lset->layers); - rcFree(lset); + rcFree(layers); } rcContourSet* rcAllocContourSet() { - rcContourSet* cset = (rcContourSet*)rcAlloc(sizeof(rcContourSet), RC_ALLOC_PERM); - memset(cset, 0, sizeof(rcContourSet)); - return cset; + return rcNew(RC_ALLOC_PERM); } - void rcFreeContourSet(rcContourSet* cset) { - if (!cset) return; - for (int i = 0; i < cset->nconts; ++i) - { - rcFree(cset->conts[i].verts); - rcFree(cset->conts[i].rverts); - } - rcFree(cset->conts); - rcFree(cset); + rcDelete(cset); } +rcContourSet::rcContourSet() + : conts(), + nconts(), + bmin(), + bmax(), + cs(), + ch(), + width(), + height(), + borderSize(), + maxError() {} +rcContourSet::~rcContourSet() +{ + for (int i = 0; i < nconts; ++i) + { + rcFree(conts[i].verts); + rcFree(conts[i].rverts); + } + rcFree(conts); +} + + rcPolyMesh* rcAllocPolyMesh() { - rcPolyMesh* pmesh = (rcPolyMesh*)rcAlloc(sizeof(rcPolyMesh), RC_ALLOC_PERM); - memset(pmesh, 0, sizeof(rcPolyMesh)); - return pmesh; + return rcNew(RC_ALLOC_PERM); } - void rcFreePolyMesh(rcPolyMesh* pmesh) { - if (!pmesh) return; - rcFree(pmesh->verts); - rcFree(pmesh->polys); - rcFree(pmesh->regs); - rcFree(pmesh->flags); - rcFree(pmesh->areas); - rcFree(pmesh); + rcDelete(pmesh); +} + +rcPolyMesh::rcPolyMesh() + : verts(), + polys(), + regs(), + flags(), + areas(), + nverts(), + npolys(), + maxpolys(), + nvp(), + bmin(), + bmax(), + cs(), + ch(), + borderSize(), + maxEdgeError() {} + +rcPolyMesh::~rcPolyMesh() +{ + rcFree(verts); + rcFree(polys); + rcFree(regs); + rcFree(flags); + rcFree(areas); } rcPolyMeshDetail* rcAllocPolyMeshDetail() From c4ad0b2eb417fe9bdd24b20e40fd1bea655e6df3 Mon Sep 17 00:00:00 2001 From: Peter Boyer Date: Fri, 15 Jun 2018 22:58:49 -0400 Subject: [PATCH 23/99] Fix for typo We shouldn't leave such a shocking typo on such a nice description, now should we? ;) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5827a7a8..afa03689 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ and then casting a navigation mesh over it. The process consists of three steps, building the voxel mold, partitioning the mold into simple regions, peeling off the regions as simple polygons. -1. The voxel mold is build from the input triangle mesh by rasterizing the triangles into a multi-layer heightfield. Some simple filters are then applied to the mold to prune out locations where the character would not be able to move. +1. The voxel mold is built from the input triangle mesh by rasterizing the triangles into a multi-layer heightfield. Some simple filters are then applied to the mold to prune out locations where the character would not be able to move. 2. The walkable areas described by the mold are divided into simple overlayed 2D regions. The resulting regions have only one non-overlapping contour, which simplifies the final step of the process tremendously. 3. The navigation polygons are peeled off from the regions by first tracing the boundaries and then simplifying them. The resulting polygons are finally converted to convex polygons which makes them perfect for pathfinding and spatial reasoning about the level. From c2d7463a387a31884e6fad3a8275c90d17baf20e Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Fri, 15 Jun 2018 23:01:02 +0200 Subject: [PATCH 24/99] Add Clang to Travis and treat warnings as errors The GCC version used by Travis is old and appears not to support all the warnings we would like to get, so reenable Clang. Additionally make lots of cleanups to Travis config and make the build a lot faster too. Also change both Travis and AppVeyor builds to treat warnings as errors, and include VS2017 in the matrix for AppVeyor. I have also removed VS2010 because it is ancient at this point. Updated Premake to alpha12 and fixed the premake build file appropriately. --- .editorconfig | 6 +++++- .travis.yml | 38 ++++++++++++-------------------------- RecastDemo/CMakeLists.txt | 4 ++-- RecastDemo/premake5.lua | 29 +++++++++++++---------------- appveyor.yml | 25 +++++++++++++------------ 5 files changed, 45 insertions(+), 57 deletions(-) diff --git a/.editorconfig b/.editorconfig index 8a938ecd..08f28f44 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,4 +5,8 @@ root = true [*] indent_size = 4 -indent_style = tab \ No newline at end of file +indent_style = tab + +[*.yml] +indent_size = 2 +indent_style = space diff --git a/.travis.yml b/.travis.yml index 17ec14a6..f39e8c63 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,36 +5,22 @@ language: cpp # Build with gcc and clang. compiler: - gcc -# Disable clang for now since it seems there's a problem using it with fast fp math; it's likely Travis' ancient environment that's at fault. -# - clang + - clang # Build both debug and release configurations, through use of an environment variable in the build matrix. env: - - CONFIGURATION: debug - CMAKE_BUILD_TYPE: Debug - - CONFIGURATION: release - CMAKE_BUILD_TYPE: Release + - BUILD_TYPE=debug CMAKE_BUILD_TYPE=Debug + - BUILD_TYPE=release CMAKE_BUILD_TYPE=Release + +addons: + apt: + packages: + - libsdl2-dev install: - # Download and build SDL2 from source. - - mkdir dummyprefix - - export PREFIX=$PWD/dummyprefix - - export PKG_CONFIG_PATH=$PREFIX/lib/pkgconfig - - export PATH=$PATH:$PWD/dummyprefix/bin - - wget https://www.libsdl.org/release/SDL2-2.0.4.tar.gz -O SDL2.tar.gz - - tar -xzf SDL2.tar.gz - - cd SDL2-2.0.4 - - ./configure --prefix=$PREFIX - - make -j$(nproc) - - make install - - cd .. - # Download and build premake5 from source; the Travis environment doesn't have the right version of glibc6 for the prebuilt binaries to work. - - wget https://github.com/premake/premake-core/releases/download/v5.0.0-alpha6/premake-5.0.0-alpha6-src.zip -O premake.zip - - unzip premake.zip - - cd premake-5.0.0-alpha6/build/gmake.unix - - make config=release - - cd ../../.. - - mv premake-5.0.0-alpha6/bin/release/premake5 premake5 + - wget https://github.com/premake/premake-core/releases/download/v5.0.0-alpha12/premake-5.0.0-alpha12-linux.tar.gz -O premake.tar.gz + - tar -xf premake.tar.gz + - rm premake.tar.gz # Run premake to generate makefiles. # Have to cd into directory and back out since premake5 doesn't appear to accept a directory argument. @@ -44,7 +30,7 @@ before_script: # Run make in the directory containing generated makefiles, on the configuration specified by the environment variable. script: - - make -C RecastDemo/Build/gmake -j$(nproc) config=$CONFIGURATION + - make -C RecastDemo/Build/gmake -j$(nproc) config=${BUILD_TYPE} - RecastDemo/Bin/Tests - make -C build -j$(nproc) - cd build && ctest diff --git a/RecastDemo/CMakeLists.txt b/RecastDemo/CMakeLists.txt index d3208d28..c3ee81b8 100644 --- a/RecastDemo/CMakeLists.txt +++ b/RecastDemo/CMakeLists.txt @@ -1,12 +1,12 @@ file(GLOB SOURCES Source/*.cpp Contrib/fastlz/fastlz.c) -include(cmake/FindSDL2.cmake) +LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") find_package(OpenGL REQUIRED) find_package(SDL2 REQUIRED) include_directories(SYSTEM ${OPENGL_INCLUDE_DIR}) -include_directories(SYSTEM ${SDL2_INCLUDE_DIRS}) +include_directories(SYSTEM ${SDL2_INCLUDE_DIR}) include_directories(SYSTEM Contrib/fastlz) include_directories(SYSTEM Contrib) include_directories(../DebugUtils/Include) diff --git a/RecastDemo/premake5.lua b/RecastDemo/premake5.lua index 2dfab884..5f72e8a3 100644 --- a/RecastDemo/premake5.lua +++ b/RecastDemo/premake5.lua @@ -11,16 +11,14 @@ solution "recastnavigation" "Debug", "Release" } + location (todir) - -- extra warnings, no exceptions or rtti - flags { - "ExtraWarnings", - "FloatFast", - "Symbols" - } + floatingpoint "Fast" + symbols "On" exceptionhandling "Off" rtti "Off" + flags { "FatalCompileWarnings" } -- debug configs configuration "Debug*" @@ -30,19 +28,20 @@ solution "recastnavigation" -- release configs configuration "Release*" defines { "NDEBUG" } - flags { "Optimize" } + optimize "On" targetdir ( todir .. "/lib/Release" ) + configuration "not windows" + warnings "Extra" + -- windows specific configuration "windows" defines { "WIN32", "_WINDOWS", "_CRT_SECURE_NO_WARNINGS", "_HAS_EXCEPTIONS=0" } - - -- linux specific - configuration { "linux", "gmake" } - buildoptions { - "-Wall", - "-Werror" - } + -- warnings "Extra" uses /W4 which is too aggressive for us, so use W3 instead. + -- Disable: + -- * C4351: new behavior for array initialization + -- * C4291: no matching operator delete found; we don't use exceptions, so doesn't matter + buildoptions { "/W3", "/wd4351", "/wd4291" } project "DebugUtils" language "C++" @@ -171,7 +170,6 @@ project "RecastDemo" configuration { "macosx" } kind "ConsoleApp" -- xcode4 failes to run the project if using WindowedApp includedirs { "/Library/Frameworks/SDL2.framework/Headers" } - buildoptions { "-Wunused-value -Wshadow -Wreorder -Wsign-compare -Wall" } links { "OpenGL.framework", "SDL2.framework", @@ -248,7 +246,6 @@ project "Tests" configuration { "macosx" } kind "ConsoleApp" includedirs { "/Library/Frameworks/SDL2.framework/Headers" } - buildoptions { "-Wunused-value -Wshadow -Wreorder -Wsign-compare -Wall" } links { "OpenGL.framework", "SDL2.framework", diff --git a/appveyor.yml b/appveyor.yml index 07050551..c61bf887 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,17 +1,21 @@ shallow_clone: true -os: - - Visual Studio 2015 - environment: matrix: - - TOOLSET: vs2010 - - TOOLSET: vs2013 - - TOOLSET: vs2015 + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013 + PREMAKE_ACTION: vs2013 + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 + PREMAKE_ACTION: vs2015 + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + PREMAKE_ACTION: vs2017 + +configuration: + - Debug + - Release install: # Download Premake - - ps: Start-FileDownload 'https://github.com/premake/premake-core/releases/download/v5.0.0-alpha7/premake-5.0.0-alpha7-windows.zip' 'premake.zip' + - ps: Start-FileDownload 'https://github.com/premake/premake-core/releases/download/v5.0.0-alpha12/premake-5.0.0-alpha12-windows.zip' 'premake.zip' # Extract it in-place; premake5.exe is at the top level. - 7z x premake.zip @@ -23,14 +27,11 @@ install: - cd RecastDemo\Contrib && 7z x SDL.zip && ren SDL2-2.0.4 SDL && cd ..\.. # Generate solution files using premake. - - cd RecastDemo && "../premake5.exe" %TOOLSET% && cd .. + - cd RecastDemo && "../premake5.exe" %PREMAKE_ACTION% && cd .. -configuration: - - Debug - - Release build: - project: RecastDemo/Build/$(TOOLSET)/recastnavigation.sln + project: RecastDemo/Build/$(PREMAKE_ACTION)/recastnavigation.sln after_test: - RecastDemo\Bin\Tests.exe From 5c24b5960fd46a28d88faa62768bdb3f48ed1433 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Sat, 16 Jun 2018 01:32:32 +0200 Subject: [PATCH 25/99] Fix warning due to signedness Fixes #334 --- Recast/Include/RecastAlloc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Recast/Include/RecastAlloc.h b/Recast/Include/RecastAlloc.h index a3c5fbdb..bbf3cedb 100644 --- a/Recast/Include/RecastAlloc.h +++ b/Recast/Include/RecastAlloc.h @@ -172,7 +172,7 @@ bool rcVectorBase::reserve(rcSizeType count) { } template T* rcVectorBase::allocate_and_copy(rcSizeType size) { - rcAssert(RC_SIZE_MAX / sizeof(T) >= size); + rcAssert(RC_SIZE_MAX / static_cast(sizeof(T)) >= size); T* new_data = static_cast(rcAlloc(sizeof(T) * size, H)); if (new_data) { copy_range(new_data, m_data, m_data + m_size); From 6e36a351c27b1b2d07d06ce0d1b101db1c763127 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Sat, 16 Jun 2018 01:33:36 +0200 Subject: [PATCH 26/99] Remove unused code causing warning --- RecastDemo/Source/Sample_TileMesh.cpp | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/RecastDemo/Source/Sample_TileMesh.cpp b/RecastDemo/Source/Sample_TileMesh.cpp index 3167cea5..1de55818 100644 --- a/RecastDemo/Source/Sample_TileMesh.cpp +++ b/RecastDemo/Source/Sample_TileMesh.cpp @@ -224,24 +224,6 @@ void Sample_TileMesh::cleanup() m_dmesh = 0; } - -static const int NAVMESHSET_MAGIC = 'M'<<24 | 'S'<<16 | 'E'<<8 | 'T'; //'MSET'; -static const int NAVMESHSET_VERSION = 1; - -struct NavMeshSetHeader -{ - int magic; - int version; - int numTiles; - dtNavMeshParams params; -}; - -struct NavMeshTileHeader -{ - dtTileRef tileRef; - int dataSize; -}; - void Sample_TileMesh::handleSettings() { Sample::handleCommonSettings(); From b39e967e4f0ad7948a6fad950707ea3851ca9ecf Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Sat, 16 Jun 2018 01:35:21 +0200 Subject: [PATCH 27/99] Fix test that fails on Clang due to float compares These results were failing because of trying to do exact floating point equality on computed values. --- Tests/Detour/Tests_Detour.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Tests/Detour/Tests_Detour.cpp b/Tests/Detour/Tests_Detour.cpp index dd2a6ebf..252be20e 100644 --- a/Tests/Detour/Tests_Detour.cpp +++ b/Tests/Detour/Tests_Detour.cpp @@ -16,18 +16,18 @@ TEST_CASE("dtRandomPointInConvexPoly") float out[3]; dtRandomPointInConvexPoly(pts, npts, areas, 0.0f, 1.0f, out); - REQUIRE(out[0] == 0); - REQUIRE(out[1] == 0); - REQUIRE(out[2] == 1); + REQUIRE(out[0] == Approx(0)); + REQUIRE(out[1] == Approx(0)); + REQUIRE(out[2] == Approx(1)); dtRandomPointInConvexPoly(pts, npts, areas, 0.5f, 1.0f, out); - REQUIRE(out[0] == 1.0f / 2); - REQUIRE(out[1] == 0); - REQUIRE(out[2] == 1.0f / 2); + REQUIRE(out[0] == Approx(1.0f / 2)); + REQUIRE(out[1] == Approx(0)); + REQUIRE(out[2] == Approx(1.0f / 2)); dtRandomPointInConvexPoly(pts, npts, areas, 1.0f, 1.0f, out); - REQUIRE(out[0] == 1); - REQUIRE(out[1] == 0); - REQUIRE(out[2] == 0); + REQUIRE(out[0] == Approx(1)); + REQUIRE(out[1] == Approx(0)); + REQUIRE(out[2] == Approx(0)); } } From 05b2b8da80037887d3e79af8a59a8f0f8ed02602 Mon Sep 17 00:00:00 2001 From: Roman Siromakha Date: Tue, 19 Jun 2018 00:11:36 +0300 Subject: [PATCH 28/99] CMake for Windows (#339) * Build with cmake in appveyor * Install content files and tests binary to bin * Install SDL2.dll on windows * Build RecastDemo as GUI application on Windows and Mac OS * Copy SDL2.dll to current binary dir --- .travis.yml | 2 +- CMakeLists.txt | 2 +- RecastDemo/CMakeLists.txt | 35 ++++++++++++++++++++++++++++------- Tests/CMakeLists.txt | 2 ++ appveyor.yml | 15 +++++++++++++-- 5 files changed, 45 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index f39e8c63..6044cd6b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,7 +26,7 @@ install: # Have to cd into directory and back out since premake5 doesn't appear to accept a directory argument. before_script: - cd RecastDemo && ../premake5 gmake && cd .. - - mkdir build && cd build && cmake -DRECASTNAVIGATION_STATIC=TRUE -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} .. && cd .. + - mkdir build && cd build && cmake -D CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} .. && cd .. # Run make in the directory containing generated makefiles, on the configuration specified by the environment variable. script: diff --git a/CMakeLists.txt b/CMakeLists.txt index d4fff420..d23859df 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,7 +9,7 @@ SET(VERSION 1.0.0) option(RECASTNAVIGATION_DEMO "Build demo" ON) option(RECASTNAVIGATION_TESTS "Build tests" ON) option(RECASTNAVIGATION_EXAMPLES "Build examples" ON) -option(RECASTNAVIGATION_STATIC "Build static libraries" OFF) +option(RECASTNAVIGATION_STATIC "Build static libraries" ON) add_subdirectory(DebugUtils) add_subdirectory(Detour) diff --git a/RecastDemo/CMakeLists.txt b/RecastDemo/CMakeLists.txt index c3ee81b8..ffeac6a4 100644 --- a/RecastDemo/CMakeLists.txt +++ b/RecastDemo/CMakeLists.txt @@ -1,12 +1,15 @@ file(GLOB SOURCES Source/*.cpp Contrib/fastlz/fastlz.c) -LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) + +if(NOT SDL2_ROOT_DIR) + set(SDL2_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Contrib/SDL") +endif() find_package(OpenGL REQUIRED) find_package(SDL2 REQUIRED) include_directories(SYSTEM ${OPENGL_INCLUDE_DIR}) -include_directories(SYSTEM ${SDL2_INCLUDE_DIR}) include_directories(SYSTEM Contrib/fastlz) include_directories(SYSTEM Contrib) include_directories(../DebugUtils/Include) @@ -16,12 +19,30 @@ include_directories(../DetourTileCache/Include) include_directories(../Recast/Include) include_directories(Include) -add_executable(RecastDemo ${SOURCES}) -file(COPY Bin/Meshes DESTINATION ${CMAKE_BINARY_DIR}/RecastDemo) -file(COPY Bin/TestCases DESTINATION ${CMAKE_BINARY_DIR}/RecastDemo) -file(COPY Bin/DroidSans.ttf DESTINATION ${CMAKE_BINARY_DIR}/RecastDemo) +if (WIN32) + add_executable(RecastDemo WIN32 ${SOURCES}) +elseif(APPLE) + add_executable(RecastDemo MACOSX_BUNDLE ${SOURCES}) +else() + add_executable(RecastDemo ${SOURCES}) +endif() + +file(COPY Bin/Meshes DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) +file(COPY Bin/TestCases DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) +file(COPY Bin/DroidSans.ttf DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) + +if (WIN32) + file(COPY "${SDL2_RUNTIME_LIBRARY}" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) +endif() add_dependencies(RecastDemo DebugUtils Detour DetourCrowd DetourTileCache Recast) -target_link_libraries(RecastDemo ${OPENGL_LIBRARIES} ${SDL2_LIBRARY} DebugUtils Detour DetourCrowd DetourTileCache Recast) +target_link_libraries(RecastDemo ${OPENGL_LIBRARIES} SDL2::SDL2main DebugUtils Detour DetourCrowd DetourTileCache Recast) install(TARGETS RecastDemo RUNTIME DESTINATION bin) +install(DIRECTORY Bin/Meshes DESTINATION bin) +install(DIRECTORY Bin/TestCases DESTINATION bin) +install(FILES Bin/DroidSans.ttf DESTINATION bin) + +if (WIN32) + install(FILES "${SDL2_RUNTIME_LIBRARY}" DESTINATION bin) +endif() diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index a98ccbf1..7c174696 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -8,3 +8,5 @@ add_executable(Tests ${TESTS_SOURCES}) add_dependencies(Tests Recast Detour) target_link_libraries(Tests Recast Detour) add_test(Tests Tests) + +install(TARGETS Tests RUNTIME DESTINATION bin) diff --git a/appveyor.yml b/appveyor.yml index c61bf887..2d943ab5 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,10 +4,13 @@ environment: matrix: - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013 PREMAKE_ACTION: vs2013 + CMAKE_GENERATOR: Visual Studio 12 2013 Win64 - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 PREMAKE_ACTION: vs2015 + CMAKE_GENERATOR: Visual Studio 14 2015 Win64 - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 PREMAKE_ACTION: vs2017 + CMAKE_GENERATOR: Visual Studio 15 2017 Win64 configuration: - Debug @@ -22,10 +25,10 @@ install: # Download SDL. - ps: Start-FileDownload 'https://www.libsdl.org/release/SDL2-devel-2.0.4-VC.zip' 'RecastDemo/Contrib/SDL.zip' - + # Extract it, put it in the right place, and rename it. - cd RecastDemo\Contrib && 7z x SDL.zip && ren SDL2-2.0.4 SDL && cd ..\.. - + # Generate solution files using premake. - cd RecastDemo && "../premake5.exe" %PREMAKE_ACTION% && cd .. @@ -33,5 +36,13 @@ install: build: project: RecastDemo/Build/$(PREMAKE_ACTION)/recastnavigation.sln +after_build: + - mkdir build + - cd build + - cmake -G "%CMAKE_GENERATOR%" -DCMAKE_BUILD_TYPE="%CONFIGURATION%" -D CMAKE_INSTALL_PREFIX=. .. + - cmake --build . --config "%CONFIGURATION%" --target install -- /m:%NUMBER_OF_PROCESSORS% + - ctest -V + - cd .. + after_test: - RecastDemo\Bin\Tests.exe From 13dc549bbe549822f99a2ae815e6e5ef030e6636 Mon Sep 17 00:00:00 2001 From: MoYummy Date: Thu, 23 Aug 2018 23:18:20 +0800 Subject: [PATCH 29/99] Improve triangulateHull to start from an ear with shortest perimeter --- Recast/Source/RecastMeshDetail.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Recast/Source/RecastMeshDetail.cpp b/Recast/Source/RecastMeshDetail.cpp index f953132f..68ab726a 100644 --- a/Recast/Source/RecastMeshDetail.cpp +++ b/Recast/Source/RecastMeshDetail.cpp @@ -557,15 +557,16 @@ static float polyMinExtent(const float* verts, const int nverts) inline int prev(int i, int n) { return i-1 >= 0 ? i-1 : n-1; } inline int next(int i, int n) { return i+1 < n ? i+1 : 0; } -static void triangulateHull(const int /*nverts*/, const float* verts, const int nhull, const int* hull, rcIntArray& tris) +static void triangulateHull(const int /*nverts*/, const float* verts, const int nhull, const int* hull, const int nin, rcIntArray& tris) { int start = 0, left = 1, right = nhull-1; // Start from an ear with shortest perimeter. // This tends to favor well formed triangles as starting point. - float dmin = 0; + float dmin = FLT_MAX; for (int i = 0; i < nhull; i++) { + if (hull[i] >= nin) continue; // Ears are triangles with original vertices as middle vertex while others are actually line segments on edges int pi = prev(i, nhull); int ni = next(i, nhull); const float* pv = &verts[hull[pi]*3]; @@ -770,7 +771,7 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin, // If the polygon minimum extent is small (sliver or small triangle), do not try to add internal points. if (minExtent < sampleDist*2) { - triangulateHull(nverts, verts, nhull, hull, tris); + triangulateHull(nverts, verts, nhull, hull, nin, tris); return true; } @@ -778,7 +779,7 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin, // We're using the triangulateHull instead of delaunayHull as it tends to // create a bit better triangulation for long thin triangles when there // are no internal points. - triangulateHull(nverts, verts, nhull, hull, tris); + triangulateHull(nverts, verts, nhull, hull, nin, tris); if (tris.size() == 0) { From 9f9efe943ed54a31b02270fb13b301754e918528 Mon Sep 17 00:00:00 2001 From: Zeno Ahn Date: Sun, 2 Sep 2018 21:16:29 +0900 Subject: [PATCH 30/99] Removed unnecessary comment --- RecastDemo/Source/CrowdTool.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/RecastDemo/Source/CrowdTool.cpp b/RecastDemo/Source/CrowdTool.cpp index 77833795..0f45ef44 100644 --- a/RecastDemo/Source/CrowdTool.cpp +++ b/RecastDemo/Source/CrowdTool.cpp @@ -144,8 +144,6 @@ void CrowdToolState::init(class Sample* sample) if (m_sample != sample) { m_sample = sample; -// m_oldFlags = m_sample->getNavMeshDrawFlags(); -// m_sample->setNavMeshDrawFlags(m_oldFlags & ~DU_DRAWNAVMESH_CLOSEDLIST); } dtNavMesh* nav = m_sample->getNavMesh(); From 56dbf4c482741f06d10528f86e953ac57f86a3cb Mon Sep 17 00:00:00 2001 From: Jarrett Date: Mon, 15 Oct 2018 18:02:51 -0400 Subject: [PATCH 31/99] Allow 64 bit windows builds (#301) --- Recast/Include/RecastAlloc.h | 2 +- RecastDemo/Include/Sample_TempObstacles.h | 2 +- RecastDemo/Source/Sample_TempObstacles.cpp | 2 +- RecastDemo/Source/imgui.cpp | 2 +- RecastDemo/premake5.lua | 13 +- Tests/catch.hpp | 3241 +++++++++++++------- appveyor.yml | 3 + 7 files changed, 2198 insertions(+), 1067 deletions(-) diff --git a/Recast/Include/RecastAlloc.h b/Recast/Include/RecastAlloc.h index bbf3cedb..3e78496d 100644 --- a/Recast/Include/RecastAlloc.h +++ b/Recast/Include/RecastAlloc.h @@ -308,7 +308,7 @@ public: m_impl.pop_back(); return v; } - int size() const { return m_impl.size(); } + int size() const { return static_cast(m_impl.size()); } int& operator[](int index) { return m_impl[index]; } int operator[](int index) const { return m_impl[index]; } }; diff --git a/RecastDemo/Include/Sample_TempObstacles.h b/RecastDemo/Include/Sample_TempObstacles.h index b9ce4a92..3ba41e14 100644 --- a/RecastDemo/Include/Sample_TempObstacles.h +++ b/RecastDemo/Include/Sample_TempObstacles.h @@ -40,7 +40,7 @@ protected: int m_cacheCompressedSize; int m_cacheRawSize; int m_cacheLayerCount; - int m_cacheBuildMemUsage; + unsigned int m_cacheBuildMemUsage; enum DrawMode { diff --git a/RecastDemo/Source/Sample_TempObstacles.cpp b/RecastDemo/Source/Sample_TempObstacles.cpp index 76a14312..6c1367f2 100644 --- a/RecastDemo/Source/Sample_TempObstacles.cpp +++ b/RecastDemo/Source/Sample_TempObstacles.cpp @@ -1339,7 +1339,7 @@ bool Sample_TempObstacles::handleBuild() m_ctx->stopTimer(RC_TIMER_TOTAL); m_cacheBuildTimeMs = m_ctx->getAccumulatedTime(RC_TIMER_TOTAL)/1000.0f; - m_cacheBuildMemUsage = m_talloc->high; + m_cacheBuildMemUsage = static_cast(m_talloc->high); const dtNavMesh* nav = m_navMesh; diff --git a/RecastDemo/Source/imgui.cpp b/RecastDemo/Source/imgui.cpp index 89fcc19b..be72c39c 100644 --- a/RecastDemo/Source/imgui.cpp +++ b/RecastDemo/Source/imgui.cpp @@ -33,7 +33,7 @@ static char g_textPool[TEXT_POOL_SIZE]; static unsigned g_textPoolSize = 0; static const char* allocText(const char* text) { - unsigned len = strlen(text)+1; + unsigned len = static_cast(strlen(text)+1); if (g_textPoolSize + len >= TEXT_POOL_SIZE) return 0; char* dst = &g_textPool[g_textPoolSize]; diff --git a/RecastDemo/premake5.lua b/RecastDemo/premake5.lua index 5f72e8a3..8545c6d7 100644 --- a/RecastDemo/premake5.lua +++ b/RecastDemo/premake5.lua @@ -36,6 +36,7 @@ solution "recastnavigation" -- windows specific configuration "windows" + platforms { "Win32", "Win64" } defines { "WIN32", "_WINDOWS", "_CRT_SECURE_NO_WARNINGS", "_HAS_EXCEPTIONS=0" } -- warnings "Extra" uses /W4 which is too aggressive for us, so use W3 instead. -- Disable: @@ -43,6 +44,12 @@ solution "recastnavigation" -- * C4291: no matching operator delete found; we don't use exceptions, so doesn't matter buildoptions { "/W3", "/wd4351", "/wd4291" } + filter "platforms:Win32" + architecture "x32" + + filter "platforms:Win64" + architecture "x64" + project "DebugUtils" language "C++" kind "StaticLib" @@ -153,7 +160,7 @@ project "RecastDemo" -- windows library cflags and libs configuration { "windows" } includedirs { "../RecastDemo/Contrib/SDL/include" } - libdirs { "../RecastDemo/Contrib/SDL/lib/x86" } + libdirs { "../RecastDemo/Contrib/SDL/lib/%{cfg.architecture:gsub('x86_64', 'x64')}" } debugdir "../RecastDemo/Bin/" links { "glu32", @@ -163,7 +170,7 @@ project "RecastDemo" } postbuildcommands { -- Copy the SDL2 dll to the Bin folder. - '{COPY} "%{wks.location}../../Contrib/SDL/lib/x86/SDL2.dll" "%{cfg.targetdir}"' + '{COPY} "%{wks.location}../../Contrib/SDL/lib/%{cfg.architecture:gsub("x86_64", "x64")}/SDL2.dll" "%{cfg.targetdir}"' } -- mac includes and libs @@ -233,7 +240,7 @@ project "Tests" -- windows library cflags and libs configuration { "windows" } includedirs { "../RecastDemo/Contrib/SDL/include" } - libdirs { "../RecastDemo/Contrib/SDL/lib/x86" } + libdirs { "../RecastDemo/Contrib/SDL/lib/%{cfg.architecture:gsub('x86_64', 'x64')}" } debugdir "../RecastDemo/Bin/" links { "glu32", diff --git a/Tests/catch.hpp b/Tests/catch.hpp index 1d49e738..fdb046fe 100644 --- a/Tests/catch.hpp +++ b/Tests/catch.hpp @@ -1,6 +1,6 @@ /* - * Catch v1.6.0 - * Generated: 2017-01-11 16:38:09.405017 + * Catch v1.12.2 + * Generated: 2018-05-14 15:10:01.112442 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. @@ -40,6 +40,8 @@ #elif defined __GNUC__ # pragma GCC diagnostic ignored "-Wvariadic-macros" # pragma GCC diagnostic ignored "-Wunused-variable" +# pragma GCC diagnostic ignored "-Wparentheses" + # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wpadded" #endif @@ -74,11 +76,15 @@ // CATCH_CONFIG_CPP11_LONG_LONG : is long long supported? // CATCH_CONFIG_CPP11_OVERRIDE : is override supported? // CATCH_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr) +// CATCH_CONFIG_CPP11_SHUFFLE : is std::shuffle supported? +// CATCH_CONFIG_CPP11_TYPE_TRAITS : are type_traits and enable_if supported? // CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported? // CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported? // CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? +// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? +// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? // **************** // Note to maintainers: if new toggles are added please document them // in configuration.md, too @@ -114,11 +120,46 @@ # endif # if defined(CATCH_CPP11_OR_GREATER) -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) +# define CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) +# define CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ + _Pragma( "clang diagnostic pop" ) + +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) +# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic pop" ) # endif #endif // __clang__ +//////////////////////////////////////////////////////////////////////////////// +// We know some environments not to support full POSIX signals +#if defined(__CYGWIN__) || defined(__QNX__) + +# if !defined(CATCH_CONFIG_POSIX_SIGNALS) +# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +# endif + +#endif + +#ifdef __OS400__ +# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +# define CATCH_CONFIG_COLOUR_NONE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Cygwin +#ifdef __CYGWIN__ + +// Required for some versions of Cygwin to declare gettimeofday +// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin +# define _BSD_SOURCE + +#endif // __CYGWIN__ + //////////////////////////////////////////////////////////////////////////////// // Borland #ifdef __BORLANDC__ @@ -145,10 +186,6 @@ # define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR # endif -# if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) && defined(CATCH_CPP11_OR_GREATER) -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS _Pragma( "GCC diagnostic ignored \"-Wparentheses\"" ) -# endif - // - otherwise more recent versions define __cplusplus >= 201103L // and will get picked up below @@ -158,6 +195,8 @@ // Visual C++ #ifdef _MSC_VER +#define CATCH_INTERNAL_CONFIG_WINDOWS_SEH + #if (_MSC_VER >= 1600) # define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR # define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR @@ -167,6 +206,7 @@ #define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT #define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS #define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE +#define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS #endif #endif // _MSC_VER @@ -185,10 +225,15 @@ // Use __COUNTER__ if the compiler supports it #if ( defined _MSC_VER && _MSC_VER >= 1300 ) || \ - ( defined __GNUC__ && __GNUC__ >= 4 && __GNUC_MINOR__ >= 3 ) || \ + ( defined __GNUC__ && ( __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3 )) ) || \ ( defined __clang__ && __clang_major__ >= 3 ) -#define CATCH_INTERNAL_CONFIG_COUNTER +// Use of __COUNTER__ is suppressed during code analysis in CLion/AppCode 2017.2.x and former, +// because __COUNTER__ is not properly handled by it. +// This does not affect compilation +#if ( !defined __JETBRAINS_IDE__ || __JETBRAINS_IDE__ >= 20170300L ) + #define CATCH_INTERNAL_CONFIG_COUNTER +#endif #endif @@ -235,6 +280,9 @@ # if !defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) # define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE # endif +# if !defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS) +# define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS +# endif #endif // __cplusplus >= 201103L @@ -272,9 +320,24 @@ #if defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_NO_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_NO_CPP11) # define CATCH_CONFIG_CPP11_SHUFFLE #endif +# if defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_NO_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_TYPE_TRAITS +# endif +#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) +# define CATCH_CONFIG_WINDOWS_SEH +#endif +// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. +#if !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) +# define CATCH_CONFIG_POSIX_SIGNALS +#endif #if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS #endif // noexcept support: @@ -319,7 +382,6 @@ #define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr ) #include -#include #include namespace Catch { @@ -359,14 +421,14 @@ namespace Catch { }; template - inline void deleteAll( ContainerT& container ) { + void deleteAll( ContainerT& container ) { typename ContainerT::const_iterator it = container.begin(); typename ContainerT::const_iterator itEnd = container.end(); for(; it != itEnd; ++it ) delete *it; } template - inline void deleteAllValues( AssociativeContainerT& container ) { + void deleteAllValues( AssociativeContainerT& container ) { typename AssociativeContainerT::const_iterator it = container.begin(); typename AssociativeContainerT::const_iterator itEnd = container.end(); for(; it != itEnd; ++it ) @@ -374,7 +436,9 @@ namespace Catch { } bool startsWith( std::string const& s, std::string const& prefix ); + bool startsWith( std::string const& s, char prefix ); bool endsWith( std::string const& s, std::string const& suffix ); + bool endsWith( std::string const& s, char suffix ); bool contains( std::string const& s, std::string const& infix ); void toLowerInPlace( std::string& s ); std::string toLower( std::string const& s ); @@ -394,8 +458,8 @@ namespace Catch { SourceLineInfo(); SourceLineInfo( char const* _file, std::size_t _line ); - SourceLineInfo( SourceLineInfo const& other ); # ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + SourceLineInfo(SourceLineInfo const& other) = default; SourceLineInfo( SourceLineInfo && ) = default; SourceLineInfo& operator = ( SourceLineInfo const& ) = default; SourceLineInfo& operator = ( SourceLineInfo && ) = default; @@ -404,7 +468,7 @@ namespace Catch { bool operator == ( SourceLineInfo const& other ) const; bool operator < ( SourceLineInfo const& other ) const; - std::string file; + char const* file; std::size_t line; }; @@ -438,15 +502,12 @@ namespace Catch { #define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) #define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO ); -#include - namespace Catch { class NotImplementedException : public std::exception { public: NotImplementedException( SourceLineInfo const& lineInfo ); - NotImplementedException( NotImplementedException const& ) {} virtual ~NotImplementedException() CATCH_NOEXCEPT {} @@ -572,10 +633,6 @@ namespace Catch { #pragma clang diagnostic pop #endif -#include -#include -#include - namespace Catch { class TestCase; @@ -719,59 +776,76 @@ void registerTestCaseFunction /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ static void TestName(); \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); }\ + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); } /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ static void TestName() #define INTERNAL_CATCH_TESTCASE( ... ) \ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); } + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); } /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ namespace{ \ struct TestName : ClassName{ \ void test(); \ }; \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestName::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestName::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); /* NOLINT */ \ } \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ void TestName::test() #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ - Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS #else /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TESTCASE2( TestName, Name, Desc ) \ static void TestName(); \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\ + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); } /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ static void TestName() #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), Name, Desc ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); } + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); } /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestCaseName, ClassName, TestName, Desc )\ + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ namespace{ \ struct TestCaseName : ClassName{ \ void test(); \ }; \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestCaseName::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestCaseName::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); /* NOLINT */ \ } \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ void TestCaseName::test() #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, TestName, Desc ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, Name, Desc ) \ - Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); + CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ + Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS + #endif // #included from: internal/catch_capture.hpp @@ -839,27 +913,83 @@ namespace Catch { namespace Catch { + struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison; + + struct DecomposedExpression + { + virtual ~DecomposedExpression() {} + virtual bool isBinaryExpression() const { + return false; + } + virtual void reconstructExpression( std::string& dest ) const = 0; + + // Only simple binary comparisons can be decomposed. + // If more complex check is required then wrap sub-expressions in parentheses. + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator % ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( T const& ); + + private: + DecomposedExpression& operator = (DecomposedExpression const&); + }; + struct AssertionInfo { - AssertionInfo() {} - AssertionInfo( std::string const& _macroName, + AssertionInfo(); + AssertionInfo( char const * _macroName, SourceLineInfo const& _lineInfo, - std::string const& _capturedExpression, - ResultDisposition::Flags _resultDisposition ); + char const * _capturedExpression, + ResultDisposition::Flags _resultDisposition, + char const * _secondArg = ""); - std::string macroName; + char const * macroName; SourceLineInfo lineInfo; - std::string capturedExpression; + char const * capturedExpression; ResultDisposition::Flags resultDisposition; + char const * secondArg; }; struct AssertionResultData { - AssertionResultData() : resultType( ResultWas::Unknown ) {} + AssertionResultData() : decomposedExpression( CATCH_NULL ) + , resultType( ResultWas::Unknown ) + , negated( false ) + , parenthesized( false ) {} - std::string reconstructedExpression; + void negate( bool parenthesize ) { + negated = !negated; + parenthesized = parenthesize; + if( resultType == ResultWas::Ok ) + resultType = ResultWas::ExpressionFailed; + else if( resultType == ResultWas::ExpressionFailed ) + resultType = ResultWas::Ok; + } + + std::string const& reconstructExpression() const { + if( decomposedExpression != CATCH_NULL ) { + decomposedExpression->reconstructExpression( reconstructedExpression ); + if( parenthesized ) { + reconstructedExpression.insert( 0, 1, '(' ); + reconstructedExpression.append( 1, ')' ); + } + if( negated ) { + reconstructedExpression.insert( 0, 1, '!' ); + } + decomposedExpression = CATCH_NULL; + } + return reconstructedExpression; + } + + mutable DecomposedExpression const* decomposedExpression; + mutable std::string reconstructedExpression; std::string message; ResultWas::OfType resultType; + bool negated; + bool parenthesized; }; class AssertionResult { @@ -886,6 +1016,8 @@ namespace Catch { std::string getMessage() const; SourceLineInfo getSourceInfo() const; std::string getTestMacroName() const; + void discardDecomposedExpression() const; + void expandDecomposedExpression() const; protected: AssertionInfo m_info; @@ -901,313 +1033,161 @@ namespace Catch { namespace Matchers { namespace Impl { - namespace Generic { - template class AllOf; - template class AnyOf; - template class Not; - } + template struct MatchAllOf; + template struct MatchAnyOf; + template struct MatchNotOf; - template - struct Matcher : SharedImpl - { - typedef ExpressionT ExpressionType; - - virtual ~Matcher() {} - virtual Ptr clone() const = 0; - virtual bool match( ExpressionT const& expr ) const = 0; - virtual std::string toString() const = 0; - - Generic::AllOf operator && ( Matcher const& other ) const; - Generic::AnyOf operator || ( Matcher const& other ) const; - Generic::Not operator ! () const; - }; - - template - struct MatcherImpl : Matcher { - - virtual Ptr > clone() const { - return Ptr >( new DerivedT( static_cast( *this ) ) ); - } - }; - - namespace Generic { - template - class Not : public MatcherImpl, ExpressionT> { + class MatcherUntypedBase { public: - explicit Not( Matcher const& matcher ) : m_matcher(matcher.clone()) {} - Not( Not const& other ) : m_matcher( other.m_matcher ) {} - - virtual bool match( ExpressionT const& expr ) const CATCH_OVERRIDE { - return !m_matcher->match( expr ); + std::string toString() const { + if( m_cachedToString.empty() ) + m_cachedToString = describe(); + return m_cachedToString; } - virtual std::string toString() const CATCH_OVERRIDE { - return "not " + m_matcher->toString(); - } + protected: + virtual ~MatcherUntypedBase(); + virtual std::string describe() const = 0; + mutable std::string m_cachedToString; private: - Ptr< Matcher > m_matcher; + MatcherUntypedBase& operator = ( MatcherUntypedBase const& ); }; - template - class AllOf : public MatcherImpl, ExpressionT> { - public: + template + struct MatcherMethod { + virtual bool match( ObjectT const& arg ) const = 0; + }; + template + struct MatcherMethod { + virtual bool match( PtrT* arg ) const = 0; + }; - AllOf() {} - AllOf( AllOf const& other ) : m_matchers( other.m_matchers ) {} + template + struct MatcherBase : MatcherUntypedBase, MatcherMethod { - AllOf& add( Matcher const& matcher ) { - m_matchers.push_back( matcher.clone() ); - return *this; - } - virtual bool match( ExpressionT const& expr ) const - { - for( std::size_t i = 0; i < m_matchers.size(); ++i ) - if( !m_matchers[i]->match( expr ) ) + MatchAllOf operator && ( MatcherBase const& other ) const; + MatchAnyOf operator || ( MatcherBase const& other ) const; + MatchNotOf operator ! () const; + }; + + template + struct MatchAllOf : MatcherBase { + virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE { + for( std::size_t i = 0; i < m_matchers.size(); ++i ) { + if (!m_matchers[i]->match(arg)) return false; + } return true; } - virtual std::string toString() const { - std::ostringstream oss; - oss << "( "; + virtual std::string describe() const CATCH_OVERRIDE { + std::string description; + description.reserve( 4 + m_matchers.size()*32 ); + description += "( "; for( std::size_t i = 0; i < m_matchers.size(); ++i ) { if( i != 0 ) - oss << " and "; - oss << m_matchers[i]->toString(); + description += " and "; + description += m_matchers[i]->toString(); } - oss << " )"; - return oss.str(); + description += " )"; + return description; } - AllOf operator && ( Matcher const& other ) const { - AllOf allOfExpr( *this ); - allOfExpr.add( other ); - return allOfExpr; - } - - private: - std::vector > > m_matchers; - }; - - template - class AnyOf : public MatcherImpl, ExpressionT> { - public: - - AnyOf() {} - AnyOf( AnyOf const& other ) : m_matchers( other.m_matchers ) {} - - AnyOf& add( Matcher const& matcher ) { - m_matchers.push_back( matcher.clone() ); + MatchAllOf& operator && ( MatcherBase const& other ) { + m_matchers.push_back( &other ); return *this; } - virtual bool match( ExpressionT const& expr ) const - { - for( std::size_t i = 0; i < m_matchers.size(); ++i ) - if( m_matchers[i]->match( expr ) ) + + std::vector const*> m_matchers; + }; + template + struct MatchAnyOf : MatcherBase { + + virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE { + for( std::size_t i = 0; i < m_matchers.size(); ++i ) { + if (m_matchers[i]->match(arg)) return true; + } return false; } - virtual std::string toString() const { - std::ostringstream oss; - oss << "( "; + virtual std::string describe() const CATCH_OVERRIDE { + std::string description; + description.reserve( 4 + m_matchers.size()*32 ); + description += "( "; for( std::size_t i = 0; i < m_matchers.size(); ++i ) { if( i != 0 ) - oss << " or "; - oss << m_matchers[i]->toString(); + description += " or "; + description += m_matchers[i]->toString(); } - oss << " )"; - return oss.str(); + description += " )"; + return description; } - AnyOf operator || ( Matcher const& other ) const { - AnyOf anyOfExpr( *this ); - anyOfExpr.add( other ); - return anyOfExpr; + MatchAnyOf& operator || ( MatcherBase const& other ) { + m_matchers.push_back( &other ); + return *this; } - private: - std::vector > > m_matchers; + std::vector const*> m_matchers; }; - } // namespace Generic + template + struct MatchNotOf : MatcherBase { - template - Generic::AllOf Matcher::operator && ( Matcher const& other ) const { - Generic::AllOf allOfExpr; - allOfExpr.add( *this ); - allOfExpr.add( other ); - return allOfExpr; - } - - template - Generic::AnyOf Matcher::operator || ( Matcher const& other ) const { - Generic::AnyOf anyOfExpr; - anyOfExpr.add( *this ); - anyOfExpr.add( other ); - return anyOfExpr; - } - - template - Generic::Not Matcher::operator ! () const { - return Generic::Not( *this ); - } - - namespace StdString { - - inline std::string makeString( std::string const& str ) { return str; } - inline std::string makeString( const char* str ) { return str ? std::string( str ) : std::string(); } - - struct CasedString - { - CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ) - : m_caseSensitivity( caseSensitivity ), - m_str( adjustString( str ) ) - {} - std::string adjustString( std::string const& str ) const { - return m_caseSensitivity == CaseSensitive::No - ? toLower( str ) - : str; + MatchNotOf( MatcherBase const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {} + virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE { + return !m_underlyingMatcher.match( arg ); } - std::string toStringSuffix() const - { - return m_caseSensitivity == CaseSensitive::No - ? " (case insensitive)" - : ""; + + virtual std::string describe() const CATCH_OVERRIDE { + return "not " + m_underlyingMatcher.toString(); } - CaseSensitive::Choice m_caseSensitivity; - std::string m_str; + MatcherBase const& m_underlyingMatcher; }; - struct Equals : MatcherImpl { - Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) - : m_data( str, caseSensitivity ) - {} - Equals( Equals const& other ) : m_data( other.m_data ){} + template + MatchAllOf MatcherBase::operator && ( MatcherBase const& other ) const { + return MatchAllOf() && *this && other; + } + template + MatchAnyOf MatcherBase::operator || ( MatcherBase const& other ) const { + return MatchAnyOf() || *this || other; + } + template + MatchNotOf MatcherBase::operator ! () const { + return MatchNotOf( *this ); + } - virtual ~Equals(); - - virtual bool match( std::string const& expr ) const { - return m_data.m_str == m_data.adjustString( expr );; - } - virtual std::string toString() const { - return "equals: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); - } - - CasedString m_data; - }; - - struct Contains : MatcherImpl { - Contains( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) - : m_data( substr, caseSensitivity ){} - Contains( Contains const& other ) : m_data( other.m_data ){} - - virtual ~Contains(); - - virtual bool match( std::string const& expr ) const { - return m_data.adjustString( expr ).find( m_data.m_str ) != std::string::npos; - } - virtual std::string toString() const { - return "contains: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); - } - - CasedString m_data; - }; - - struct StartsWith : MatcherImpl { - StartsWith( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) - : m_data( substr, caseSensitivity ){} - - StartsWith( StartsWith const& other ) : m_data( other.m_data ){} - - virtual ~StartsWith(); - - virtual bool match( std::string const& expr ) const { - return startsWith( m_data.adjustString( expr ), m_data.m_str ); - } - virtual std::string toString() const { - return "starts with: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); - } - - CasedString m_data; - }; - - struct EndsWith : MatcherImpl { - EndsWith( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) - : m_data( substr, caseSensitivity ){} - EndsWith( EndsWith const& other ) : m_data( other.m_data ){} - - virtual ~EndsWith(); - - virtual bool match( std::string const& expr ) const { - return endsWith( m_data.adjustString( expr ), m_data.m_str ); - } - virtual std::string toString() const { - return "ends with: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); - } - - CasedString m_data; - }; - } // namespace StdString } // namespace Impl // The following functions create the actual matcher objects. // This allows the types to be inferred - template - inline Impl::Generic::Not Not( Impl::Matcher const& m ) { - return Impl::Generic::Not( m ); + // - deprecated: prefer ||, && and ! + template + Impl::MatchNotOf Not( Impl::MatcherBase const& underlyingMatcher ) { + return Impl::MatchNotOf( underlyingMatcher ); } - - template - inline Impl::Generic::AllOf AllOf( Impl::Matcher const& m1, - Impl::Matcher const& m2 ) { - return Impl::Generic::AllOf().add( m1 ).add( m2 ); + template + Impl::MatchAllOf AllOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2 ) { + return Impl::MatchAllOf() && m1 && m2; } - template - inline Impl::Generic::AllOf AllOf( Impl::Matcher const& m1, - Impl::Matcher const& m2, - Impl::Matcher const& m3 ) { - return Impl::Generic::AllOf().add( m1 ).add( m2 ).add( m3 ); + template + Impl::MatchAllOf AllOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2, Impl::MatcherBase const& m3 ) { + return Impl::MatchAllOf() && m1 && m2 && m3; } - template - inline Impl::Generic::AnyOf AnyOf( Impl::Matcher const& m1, - Impl::Matcher const& m2 ) { - return Impl::Generic::AnyOf().add( m1 ).add( m2 ); + template + Impl::MatchAnyOf AnyOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2 ) { + return Impl::MatchAnyOf() || m1 || m2; } - template - inline Impl::Generic::AnyOf AnyOf( Impl::Matcher const& m1, - Impl::Matcher const& m2, - Impl::Matcher const& m3 ) { - return Impl::Generic::AnyOf().add( m1 ).add( m2 ).add( m3 ); - } - - inline Impl::StdString::Equals Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { - return Impl::StdString::Equals( str, caseSensitivity ); - } - inline Impl::StdString::Equals Equals( const char* str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { - return Impl::StdString::Equals( Impl::StdString::makeString( str ), caseSensitivity ); - } - inline Impl::StdString::Contains Contains( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { - return Impl::StdString::Contains( substr, caseSensitivity ); - } - inline Impl::StdString::Contains Contains( const char* substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { - return Impl::StdString::Contains( Impl::StdString::makeString( substr ), caseSensitivity ); - } - inline Impl::StdString::StartsWith StartsWith( std::string const& substr ) { - return Impl::StdString::StartsWith( substr ); - } - inline Impl::StdString::StartsWith StartsWith( const char* substr ) { - return Impl::StdString::StartsWith( Impl::StdString::makeString( substr ) ); - } - inline Impl::StdString::EndsWith EndsWith( std::string const& substr ) { - return Impl::StdString::EndsWith( substr ); - } - inline Impl::StdString::EndsWith EndsWith( const char* substr ) { - return Impl::StdString::EndsWith( Impl::StdString::makeString( substr ) ); + template + Impl::MatchAnyOf AnyOf( Impl::MatcherBase const& m1, Impl::MatcherBase const& m2, Impl::MatcherBase const& m3 ) { + return Impl::MatchAnyOf() || m1 || m2 || m3; } } // namespace Matchers using namespace Matchers; +using Matchers::Impl::MatcherBase; } // namespace Catch @@ -1217,28 +1197,27 @@ namespace Catch { template class ExpressionLhs; - struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison; - struct CopyableStream { CopyableStream() {} CopyableStream( CopyableStream const& other ) { oss << other.oss.str(); } CopyableStream& operator=( CopyableStream const& other ) { - oss.str(""); + oss.str(std::string()); oss << other.oss.str(); return *this; } std::ostringstream oss; }; - class ResultBuilder { + class ResultBuilder : public DecomposedExpression { public: ResultBuilder( char const* macroName, SourceLineInfo const& lineInfo, char const* capturedExpression, ResultDisposition::Flags resultDisposition, char const* secondArg = "" ); + ~ResultBuilder(); template ExpressionLhs operator <= ( T const& operand ); @@ -1246,46 +1225,60 @@ namespace Catch { template ResultBuilder& operator << ( T const& value ) { - m_stream.oss << value; + stream().oss << value; return *this; } - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& ); - ResultBuilder& setResultType( ResultWas::OfType result ); ResultBuilder& setResultType( bool result ); - ResultBuilder& setLhs( std::string const& lhs ); - ResultBuilder& setRhs( std::string const& rhs ); - ResultBuilder& setOp( std::string const& op ); - void endExpression(); + void endExpression( DecomposedExpression const& expr ); + + virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE; - std::string reconstructExpression() const; AssertionResult build() const; + AssertionResult build( DecomposedExpression const& expr ) const; void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal ); void captureResult( ResultWas::OfType resultType ); void captureExpression(); void captureExpectedException( std::string const& expectedMessage ); - void captureExpectedException( Matchers::Impl::Matcher const& matcher ); + void captureExpectedException( Matchers::Impl::MatcherBase const& matcher ); void handleResult( AssertionResult const& result ); void react(); bool shouldDebugBreak() const; bool allowThrows() const; + template + void captureMatch( ArgT const& arg, MatcherT const& matcher, char const* matcherString ); + + void setExceptionGuard(); + void unsetExceptionGuard(); + private: AssertionInfo m_assertionInfo; AssertionResultData m_data; - struct ExprComponents { - ExprComponents() : testFalse( false ) {} - bool testFalse; - std::string lhs, rhs, op; - } m_exprComponents; - CopyableStream m_stream; + + CopyableStream &stream() + { + if(!m_usedStream) + { + m_usedStream = true; + m_stream().oss.str(""); + } + return m_stream(); + } + + static CopyableStream &m_stream() + { + static CopyableStream s; + return s; + } bool m_shouldDebugBreak; bool m_shouldThrow; + bool m_guardException; + bool m_usedStream; }; } // namespace Catch @@ -1300,6 +1293,8 @@ namespace Catch { #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4389) // '==' : signed/unsigned mismatch +#pragma warning(disable:4018) // more "signed/unsigned mismatch" +#pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform) #endif #include @@ -1325,7 +1320,7 @@ namespace Internal { template<> struct OperatorTraits{ static const char* getName(){ return ">="; } }; template - inline T& opCast(T const& t) { return const_cast(t); } + T& opCast(T const& t) { return const_cast(t); } // nullptr_t support based on pull request #154 from Konstantin Baumann #ifdef CATCH_CONFIG_CPP11_NULLPTR @@ -1335,7 +1330,7 @@ namespace Internal { // So the compare overloads can be operator agnostic we convey the operator as a template // enum, which is used to specialise an Evaluator for doing the comparison. template - class Evaluator{}; + struct Evaluator{}; template struct Evaluator { @@ -1601,7 +1596,7 @@ std::string toString( std::nullptr_t ); #ifdef __OBJC__ std::string toString( NSString const * const& nsstring ); - std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ); + std::string toString( NSString * CATCH_ARC_STRONG & nsstring ); std::string toString( NSObject* const& nsObject ); #endif @@ -1609,6 +1604,7 @@ namespace Detail { extern const std::string unprintableString; + #if !defined(CATCH_CONFIG_CPP11_STREAM_INSERTABLE_CHECK) struct BorgType { template BorgType( T const& ); }; @@ -1627,6 +1623,20 @@ namespace Detail { static T const&t; enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) }; }; +#else + template + class IsStreamInsertable { + template + static auto test(int) + -> decltype( std::declval() << std::declval(), std::true_type() ); + + template + static auto test(...) -> std::false_type; + + public: + static const bool value = decltype(test(0))::value; + }; +#endif #if defined(CATCH_CONFIG_CPP11_IS_ENUM) template - inline std::string rawMemoryToString( const T& object ) { + std::string rawMemoryToString( const T& object ) { return rawMemoryToString( &object, sizeof(object) ); } @@ -1803,90 +1813,159 @@ std::string toString( T const& value ) { namespace Catch { -// Wraps the LHS of an expression and captures the operator and RHS (if any) - -// wrapping them all in a ResultBuilder object -template -class ExpressionLhs { - ExpressionLhs& operator = ( ExpressionLhs const& ); -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - ExpressionLhs& operator = ( ExpressionLhs && ) = delete; -# endif +template +class BinaryExpression; +template +class MatchExpression; + +// Wraps the LHS of an expression and overloads comparison operators +// for also capturing those and RHS (if any) +template +class ExpressionLhs : public DecomposedExpression { public: - ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ) {} -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - ExpressionLhs( ExpressionLhs const& ) = default; - ExpressionLhs( ExpressionLhs && ) = default; -# endif + ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ), m_truthy(false) {} + + ExpressionLhs& operator = ( const ExpressionLhs& ); template - ResultBuilder& operator == ( RhsT const& rhs ) { + BinaryExpression + operator == ( RhsT const& rhs ) { return captureExpression( rhs ); } template - ResultBuilder& operator != ( RhsT const& rhs ) { + BinaryExpression + operator != ( RhsT const& rhs ) { return captureExpression( rhs ); } template - ResultBuilder& operator < ( RhsT const& rhs ) { + BinaryExpression + operator < ( RhsT const& rhs ) { return captureExpression( rhs ); } template - ResultBuilder& operator > ( RhsT const& rhs ) { + BinaryExpression + operator > ( RhsT const& rhs ) { return captureExpression( rhs ); } template - ResultBuilder& operator <= ( RhsT const& rhs ) { + BinaryExpression + operator <= ( RhsT const& rhs ) { return captureExpression( rhs ); } template - ResultBuilder& operator >= ( RhsT const& rhs ) { + BinaryExpression + operator >= ( RhsT const& rhs ) { return captureExpression( rhs ); } - ResultBuilder& operator == ( bool rhs ) { + BinaryExpression operator == ( bool rhs ) { return captureExpression( rhs ); } - ResultBuilder& operator != ( bool rhs ) { + BinaryExpression operator != ( bool rhs ) { return captureExpression( rhs ); } void endExpression() { - bool value = m_lhs ? true : false; + m_truthy = m_lhs ? true : false; m_rb - .setLhs( Catch::toString( value ) ) - .setResultType( value ) - .endExpression(); + .setResultType( m_truthy ) + .endExpression( *this ); } - // Only simple binary expressions are allowed on the LHS. - // If more complex compositions are required then place the sub expression in parentheses - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( RhsT const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( RhsT const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( RhsT const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( RhsT const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& ); + virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE { + dest = Catch::toString( m_lhs ); + } private: template - ResultBuilder& captureExpression( RhsT const& rhs ) { - return m_rb - .setResultType( Internal::compare( m_lhs, rhs ) ) - .setLhs( Catch::toString( m_lhs ) ) - .setRhs( Catch::toString( rhs ) ) - .setOp( Internal::OperatorTraits::getName() ); + BinaryExpression captureExpression( RhsT& rhs ) const { + return BinaryExpression( m_rb, m_lhs, rhs ); + } + + template + BinaryExpression captureExpression( bool rhs ) const { + return BinaryExpression( m_rb, m_lhs, rhs ); } private: ResultBuilder& m_rb; T m_lhs; + bool m_truthy; +}; + +template +class BinaryExpression : public DecomposedExpression { +public: + BinaryExpression( ResultBuilder& rb, LhsT lhs, RhsT rhs ) + : m_rb( rb ), m_lhs( lhs ), m_rhs( rhs ) {} + + BinaryExpression& operator = ( BinaryExpression& ); + + void endExpression() const { + m_rb + .setResultType( Internal::compare( m_lhs, m_rhs ) ) + .endExpression( *this ); + } + + virtual bool isBinaryExpression() const CATCH_OVERRIDE { + return true; + } + + virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE { + std::string lhs = Catch::toString( m_lhs ); + std::string rhs = Catch::toString( m_rhs ); + char delim = lhs.size() + rhs.size() < 40 && + lhs.find('\n') == std::string::npos && + rhs.find('\n') == std::string::npos ? ' ' : '\n'; + dest.reserve( 7 + lhs.size() + rhs.size() ); + // 2 for spaces around operator + // 2 for operator + // 2 for parentheses (conditionally added later) + // 1 for negation (conditionally added later) + dest = lhs; + dest += delim; + dest += Internal::OperatorTraits::getName(); + dest += delim; + dest += rhs; + } + +private: + ResultBuilder& m_rb; + LhsT m_lhs; + RhsT m_rhs; +}; + +template +class MatchExpression : public DecomposedExpression { +public: + MatchExpression( ArgT arg, MatcherT matcher, char const* matcherString ) + : m_arg( arg ), m_matcher( matcher ), m_matcherString( matcherString ) {} + + virtual bool isBinaryExpression() const CATCH_OVERRIDE { + return true; + } + + virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE { + std::string matcherAsString = m_matcher.toString(); + dest = Catch::toString( m_arg ); + dest += ' '; + if( matcherAsString == Detail::unprintableString ) + dest += m_matcherString; + else + dest += matcherAsString; + } + +private: + ArgT m_arg; + MatcherT m_matcher; + char const* m_matcherString; }; } // end namespace Catch @@ -1895,7 +1974,7 @@ private: namespace Catch { template - inline ExpressionLhs ResultBuilder::operator <= ( T const& operand ) { + ExpressionLhs ResultBuilder::operator <= ( T const& operand ) { return ExpressionLhs( *this, operand ); } @@ -1903,6 +1982,14 @@ namespace Catch { return ExpressionLhs( *this, value ); } + template + void ResultBuilder::captureMatch( ArgT const& arg, MatcherT const& matcher, + char const* matcherString ) { + MatchExpression expr( arg, matcher, matcherString ); + setResultType( matcher.match( arg ) ); + endExpression( expr ); + } + } // namespace Catch // #included from: catch_message.h @@ -1992,7 +2079,13 @@ namespace Catch { virtual std::string getCurrentTestName() const = 0; virtual const AssertionResult* getLastResult() const = 0; + virtual void exceptionEarlyReported() = 0; + virtual void handleFatalErrorCondition( std::string const& message ) = 0; + + virtual bool lastAssertionPassed() = 0; + virtual void assertionPassed() = 0; + virtual void assertionRun() = 0; }; IResultCapture& getResultCapture(); @@ -2005,11 +2098,19 @@ namespace Catch { #define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) -#define CATCH_PLATFORM_MAC +# define CATCH_PLATFORM_MAC #elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) -#define CATCH_PLATFORM_IPHONE +# define CATCH_PLATFORM_IPHONE +#elif defined(linux) || defined(__linux) || defined(__linux__) +# define CATCH_PLATFORM_LINUX #elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) -#define CATCH_PLATFORM_WINDOWS +# define CATCH_PLATFORM_WINDOWS +# if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX) +# define CATCH_DEFINES_NOMINMAX +# endif +# if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN) +# define CATCH_DEFINES_WIN32_LEAN_AND_MEAN +# endif #endif #include @@ -2024,27 +2125,36 @@ namespace Catch{ // The following code snippet based on: // http://cocoawithlove.com/2008/03/break-into-debugger.html - #ifdef DEBUG - #if defined(__ppc64__) || defined(__ppc__) - #define CATCH_BREAK_INTO_DEBUGGER() \ - if( Catch::isDebuggerActive() ) { \ - __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \ - : : : "memory","r0","r3","r4" ); \ - } - #else - #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) {__asm__("int $3\n" : : );} - #endif + #if defined(__ppc64__) || defined(__ppc__) + #define CATCH_TRAP() \ + __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \ + : : : "memory","r0","r3","r4" ) /* NOLINT */ + #else + #define CATCH_TRAP() __asm__("int $3\n" : : /* NOLINT */ ) #endif +#elif defined(CATCH_PLATFORM_LINUX) + // If we can use inline assembler, do it because this allows us to break + // directly at the location of the failing check instead of breaking inside + // raise() called from it, i.e. one stack frame below. + #if defined(__GNUC__) && (defined(__i386) || defined(__x86_64)) + #define CATCH_TRAP() asm volatile ("int $3") /* NOLINT */ + #else // Fall back to the generic way. + #include + + #define CATCH_TRAP() raise(SIGTRAP) + #endif #elif defined(_MSC_VER) - #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { __debugbreak(); } + #define CATCH_TRAP() __debugbreak() #elif defined(__MINGW32__) extern "C" __declspec(dllimport) void __stdcall DebugBreak(); - #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { DebugBreak(); } + #define CATCH_TRAP() DebugBreak() #endif -#ifndef CATCH_BREAK_INTO_DEBUGGER -#define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue(); +#ifdef CATCH_TRAP + #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } +#else + #define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue(); #endif // #included from: catch_interfaces_runner.h @@ -2059,6 +2169,47 @@ namespace Catch { }; } +#if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION) +# define CATCH_INTERNAL_STRINGIFY(expr) #expr +#else +# define CATCH_INTERNAL_STRINGIFY(expr) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION" +#endif + +#if defined(CATCH_CONFIG_FAST_COMPILE) +/////////////////////////////////////////////////////////////////////////////// +// We can speedup compilation significantly by breaking into debugger lower in +// the callstack, because then we don't have to expand CATCH_BREAK_INTO_DEBUGGER +// macro in each assertion +#define INTERNAL_CATCH_REACT( resultBuilder ) \ + resultBuilder.react(); + +/////////////////////////////////////////////////////////////////////////////// +// Another way to speed-up compilation is to omit local try-catch for REQUIRE* +// macros. +// This can potentially cause false negative, if the test code catches +// the exception before it propagates back up to the runner. +#define INTERNAL_CATCH_TEST_NO_TRY( macroName, resultDisposition, expr ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr), resultDisposition ); \ + __catchResult.setExceptionGuard(); \ + CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + ( __catchResult <= expr ).endExpression(); \ + CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ + __catchResult.unsetExceptionGuard(); \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::isTrue( false && static_cast( !!(expr) ) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look +// The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. + +#define INTERNAL_CHECK_THAT_NO_TRY( macroName, matcher, resultDisposition, arg ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ + __catchResult.setExceptionGuard(); \ + __catchResult.captureMatch( arg, matcher, CATCH_INTERNAL_STRINGIFY(matcher) ); \ + __catchResult.unsetExceptionGuard(); \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::alwaysFalse() ) + +#else /////////////////////////////////////////////////////////////////////////////// // In the event of a failure works out if the debugger needs to be invoked // and/or an exception thrown and takes appropriate action. @@ -2067,37 +2218,40 @@ namespace Catch { #define INTERNAL_CATCH_REACT( resultBuilder ) \ if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \ resultBuilder.react(); +#endif /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ) \ +#define INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr), resultDisposition ); \ try { \ CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ ( __catchResult <= expr ).endExpression(); \ + CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ } \ catch( ... ) { \ - __catchResult.useActiveException( Catch::ResultDisposition::Normal ); \ + __catchResult.useActiveException( resultDisposition ); \ } \ INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::isTrue( false && !!(expr) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look + } while( Catch::isTrue( false && static_cast( !!(expr) ) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look + // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_IF( expr, resultDisposition, macroName ) \ - INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \ - if( Catch::getResultCapture().getLastResult()->succeeded() ) +#define INTERNAL_CATCH_IF( macroName, resultDisposition, expr ) \ + INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ); \ + if( Catch::getResultCapture().lastAssertionPassed() ) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_ELSE( expr, resultDisposition, macroName ) \ - INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \ - if( !Catch::getResultCapture().getLastResult()->succeeded() ) +#define INTERNAL_CATCH_ELSE( macroName, resultDisposition, expr ) \ + INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ); \ + if( !Catch::getResultCapture().lastAssertionPassed() ) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_NO_THROW( expr, resultDisposition, macroName ) \ +#define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, expr ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr), resultDisposition ); \ try { \ - expr; \ + static_cast(expr); \ __catchResult.captureResult( Catch::ResultWas::Ok ); \ } \ catch( ... ) { \ @@ -2107,12 +2261,12 @@ namespace Catch { } while( Catch::alwaysFalse() ) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_THROWS( expr, resultDisposition, matcher, macroName ) \ +#define INTERNAL_CATCH_THROWS( macroName, resultDisposition, matcher, expr ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition, #matcher ); \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr), resultDisposition, CATCH_INTERNAL_STRINGIFY(matcher) ); \ if( __catchResult.allowThrows() ) \ try { \ - expr; \ + static_cast(expr); \ __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ } \ catch( ... ) { \ @@ -2124,12 +2278,12 @@ namespace Catch { } while( Catch::alwaysFalse() ) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_THROWS_AS( expr, exceptionType, resultDisposition, macroName ) \ +#define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr) ", " CATCH_INTERNAL_STRINGIFY(exceptionType), resultDisposition ); \ if( __catchResult.allowThrows() ) \ try { \ - expr; \ + static_cast(expr); \ __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ } \ catch( exceptionType ) { \ @@ -2145,7 +2299,7 @@ namespace Catch { /////////////////////////////////////////////////////////////////////////////// #ifdef CATCH_CONFIG_VARIADIC_MACROS - #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, ... ) \ + #define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \ do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ __catchResult << __VA_ARGS__ + ::Catch::StreamEndStop(); \ @@ -2153,7 +2307,7 @@ namespace Catch { INTERNAL_CATCH_REACT( __catchResult ) \ } while( Catch::alwaysFalse() ) #else - #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, log ) \ + #define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, log ) \ do { \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ __catchResult << log + ::Catch::StreamEndStop(); \ @@ -2163,21 +2317,15 @@ namespace Catch { #endif /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_INFO( log, macroName ) \ +#define INTERNAL_CATCH_INFO( macroName, log ) \ Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log; /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CHECK_THAT( arg, matcher, resultDisposition, macroName ) \ +#define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ try { \ - std::string matcherAsString = (matcher).toString(); \ - __catchResult \ - .setLhs( Catch::toString( arg ) ) \ - .setRhs( matcherAsString == Catch::Detail::unprintableString ? #matcher : matcherAsString ) \ - .setOp( "matches" ) \ - .setResultType( (matcher).match( arg ) ); \ - __catchResult.captureExpression(); \ + __catchResult.captureMatch( arg, matcher, CATCH_INTERNAL_STRINGIFY(matcher) ); \ } catch( ... ) { \ __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \ } \ @@ -2260,6 +2408,8 @@ namespace Catch { }; } +#include + namespace Catch { struct SectionInfo { @@ -2288,14 +2438,19 @@ namespace Catch { // #included from: catch_timer.h #define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED -#ifdef CATCH_PLATFORM_WINDOWS -typedef unsigned long long uint64_t; +#ifdef _MSC_VER + +namespace Catch { + typedef unsigned long long UInt64; +} #else #include +namespace Catch { + typedef uint64_t UInt64; +} #endif namespace Catch { - class Timer { public: Timer() : m_ticks( 0 ) {} @@ -2305,7 +2460,7 @@ namespace Catch { double getElapsedSeconds() const; private: - uint64_t m_ticks; + UInt64 m_ticks; }; } // namespace Catch @@ -2344,7 +2499,6 @@ namespace Catch { // #included from: internal/catch_generators.hpp #define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED -#include #include #include #include @@ -2458,7 +2612,7 @@ public: private: void move( CompositeGenerator& other ) { - std::copy( other.m_composed.begin(), other.m_composed.end(), std::back_inserter( m_composed ) ); + m_composed.insert( m_composed.end(), other.m_composed.begin(), other.m_composed.end() ); m_totalSize += other.m_totalSize; other.m_composed.clear(); } @@ -2540,12 +2694,15 @@ namespace Catch { struct IExceptionTranslator; struct IReporterRegistry; struct IReporterFactory; + struct ITagAliasRegistry; struct IRegistryHub { virtual ~IRegistryHub(); virtual IReporterRegistry const& getReporterRegistry() const = 0; virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; + virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0; + virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0; }; @@ -2555,6 +2712,7 @@ namespace Catch { virtual void registerListener( Ptr const& factory ) = 0; virtual void registerTest( TestCase const& testInfo ) = 0; virtual void registerTranslator( const IExceptionTranslator* translator ) = 0; + virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0; }; IRegistryHub& getRegistryHub(); @@ -2630,6 +2788,10 @@ namespace Catch { #include #include +#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) +#include +#endif + namespace Catch { namespace Detail { @@ -2637,30 +2799,112 @@ namespace Detail { public: explicit Approx ( double value ) : m_epsilon( std::numeric_limits::epsilon()*100 ), + m_margin( 0.0 ), m_scale( 1.0 ), m_value( value ) {} - Approx( Approx const& other ) - : m_epsilon( other.m_epsilon ), - m_scale( other.m_scale ), - m_value( other.m_value ) - {} - static Approx custom() { return Approx( 0 ); } +#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) + + template ::value>::type> + Approx operator()( T value ) { + Approx approx( static_cast(value) ); + approx.epsilon( m_epsilon ); + approx.margin( m_margin ); + approx.scale( m_scale ); + return approx; + } + + template ::value>::type> + explicit Approx( T value ): Approx(static_cast(value)) + {} + + template ::value>::type> + friend bool operator == ( const T& lhs, Approx const& rhs ) { + // Thanks to Richard Harris for his help refining this formula + auto lhs_v = double(lhs); + bool relativeOK = std::fabs(lhs_v - rhs.m_value) < rhs.m_epsilon * (rhs.m_scale + (std::max)(std::fabs(lhs_v), std::fabs(rhs.m_value))); + if (relativeOK) { + return true; + } + + return std::fabs(lhs_v - rhs.m_value) <= rhs.m_margin; + } + + template ::value>::type> + friend bool operator == ( Approx const& lhs, const T& rhs ) { + return operator==( rhs, lhs ); + } + + template ::value>::type> + friend bool operator != ( T lhs, Approx const& rhs ) { + return !operator==( lhs, rhs ); + } + + template ::value>::type> + friend bool operator != ( Approx const& lhs, T rhs ) { + return !operator==( rhs, lhs ); + } + + template ::value>::type> + friend bool operator <= ( T lhs, Approx const& rhs ) { + return double(lhs) < rhs.m_value || lhs == rhs; + } + + template ::value>::type> + friend bool operator <= ( Approx const& lhs, T rhs ) { + return lhs.m_value < double(rhs) || lhs == rhs; + } + + template ::value>::type> + friend bool operator >= ( T lhs, Approx const& rhs ) { + return double(lhs) > rhs.m_value || lhs == rhs; + } + + template ::value>::type> + friend bool operator >= ( Approx const& lhs, T rhs ) { + return lhs.m_value > double(rhs) || lhs == rhs; + } + + template ::value>::type> + Approx& epsilon( T newEpsilon ) { + m_epsilon = double(newEpsilon); + return *this; + } + + template ::value>::type> + Approx& margin( T newMargin ) { + m_margin = double(newMargin); + return *this; + } + + template ::value>::type> + Approx& scale( T newScale ) { + m_scale = double(newScale); + return *this; + } + +#else + Approx operator()( double value ) { Approx approx( value ); approx.epsilon( m_epsilon ); + approx.margin( m_margin ); approx.scale( m_scale ); return approx; } friend bool operator == ( double lhs, Approx const& rhs ) { // Thanks to Richard Harris for his help refining this formula - return fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( fabs(lhs), fabs(rhs.m_value) ) ); + bool relativeOK = std::fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( std::fabs(lhs), std::fabs(rhs.m_value) ) ); + if (relativeOK) { + return true; + } + return std::fabs(lhs - rhs.m_value) <= rhs.m_margin; } friend bool operator == ( Approx const& lhs, double rhs ) { @@ -2675,24 +2919,20 @@ namespace Detail { return !operator==( rhs, lhs ); } - friend bool operator <= ( double lhs, Approx const& rhs ) - { - return lhs < rhs.m_value || lhs == rhs; + friend bool operator <= ( double lhs, Approx const& rhs ) { + return lhs < rhs.m_value || lhs == rhs; } - friend bool operator <= ( Approx const& lhs, double rhs ) - { - return lhs.m_value < rhs || lhs == rhs; + friend bool operator <= ( Approx const& lhs, double rhs ) { + return lhs.m_value < rhs || lhs == rhs; } - friend bool operator >= ( double lhs, Approx const& rhs ) - { - return lhs > rhs.m_value || lhs == rhs; + friend bool operator >= ( double lhs, Approx const& rhs ) { + return lhs > rhs.m_value || lhs == rhs; } - friend bool operator >= ( Approx const& lhs, double rhs ) - { - return lhs.m_value > rhs || lhs == rhs; + friend bool operator >= ( Approx const& lhs, double rhs ) { + return lhs.m_value > rhs || lhs == rhs; } Approx& epsilon( double newEpsilon ) { @@ -2700,10 +2940,16 @@ namespace Detail { return *this; } + Approx& margin( double newMargin ) { + m_margin = newMargin; + return *this; + } + Approx& scale( double newScale ) { m_scale = newScale; return *this; } +#endif std::string toString() const { std::ostringstream oss; @@ -2713,6 +2959,7 @@ namespace Detail { private: double m_epsilon; + double m_margin; double m_scale; double m_value; }; @@ -2725,6 +2972,153 @@ inline std::string toString( Detail::Approx const& value ) { } // end namespace Catch +// #included from: internal/catch_matchers_string.h +#define TWOBLUECUBES_CATCH_MATCHERS_STRING_H_INCLUDED + +namespace Catch { +namespace Matchers { + + namespace StdString { + + struct CasedString + { + CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ); + std::string adjustString( std::string const& str ) const; + std::string caseSensitivitySuffix() const; + + CaseSensitive::Choice m_caseSensitivity; + std::string m_str; + }; + + struct StringMatcherBase : MatcherBase { + StringMatcherBase( std::string const& operation, CasedString const& comparator ); + virtual std::string describe() const CATCH_OVERRIDE; + + CasedString m_comparator; + std::string m_operation; + }; + + struct EqualsMatcher : StringMatcherBase { + EqualsMatcher( CasedString const& comparator ); + virtual bool match( std::string const& source ) const CATCH_OVERRIDE; + }; + struct ContainsMatcher : StringMatcherBase { + ContainsMatcher( CasedString const& comparator ); + virtual bool match( std::string const& source ) const CATCH_OVERRIDE; + }; + struct StartsWithMatcher : StringMatcherBase { + StartsWithMatcher( CasedString const& comparator ); + virtual bool match( std::string const& source ) const CATCH_OVERRIDE; + }; + struct EndsWithMatcher : StringMatcherBase { + EndsWithMatcher( CasedString const& comparator ); + virtual bool match( std::string const& source ) const CATCH_OVERRIDE; + }; + + } // namespace StdString + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + + StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + +} // namespace Matchers +} // namespace Catch + +// #included from: internal/catch_matchers_vector.h +#define TWOBLUECUBES_CATCH_MATCHERS_VECTOR_H_INCLUDED + +namespace Catch { +namespace Matchers { + + namespace Vector { + + template + struct ContainsElementMatcher : MatcherBase, T> { + + ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {} + + bool match(std::vector const &v) const CATCH_OVERRIDE { + return std::find(v.begin(), v.end(), m_comparator) != v.end(); + } + + virtual std::string describe() const CATCH_OVERRIDE { + return "Contains: " + Catch::toString( m_comparator ); + } + + T const& m_comparator; + }; + + template + struct ContainsMatcher : MatcherBase, std::vector > { + + ContainsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} + + bool match(std::vector const &v) const CATCH_OVERRIDE { + // !TBD: see note in EqualsMatcher + if (m_comparator.size() > v.size()) + return false; + for (size_t i = 0; i < m_comparator.size(); ++i) + if (std::find(v.begin(), v.end(), m_comparator[i]) == v.end()) + return false; + return true; + } + virtual std::string describe() const CATCH_OVERRIDE { + return "Contains: " + Catch::toString( m_comparator ); + } + + std::vector const& m_comparator; + }; + + template + struct EqualsMatcher : MatcherBase, std::vector > { + + EqualsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} + + bool match(std::vector const &v) const CATCH_OVERRIDE { + // !TBD: This currently works if all elements can be compared using != + // - a more general approach would be via a compare template that defaults + // to using !=. but could be specialised for, e.g. std::vector etc + // - then just call that directly + if (m_comparator.size() != v.size()) + return false; + for (size_t i = 0; i < v.size(); ++i) + if (m_comparator[i] != v[i]) + return false; + return true; + } + virtual std::string describe() const CATCH_OVERRIDE { + return "Equals: " + Catch::toString( m_comparator ); + } + std::vector const& m_comparator; + }; + + } // namespace Vector + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + + template + Vector::ContainsMatcher Contains( std::vector const& comparator ) { + return Vector::ContainsMatcher( comparator ); + } + + template + Vector::ContainsElementMatcher VectorContains( T const& comparator ) { + return Vector::ContainsElementMatcher( comparator ); + } + + template + Vector::EqualsMatcher Equals( std::vector const& comparator ) { + return Vector::EqualsMatcher( comparator ); + } + +} // namespace Matchers +} // namespace Catch + // #included from: internal/catch_interfaces_tag_alias_registry.h #define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED @@ -2736,7 +3130,7 @@ inline std::string toString( Detail::Approx const& value ) { namespace Catch { struct TagAlias { - TagAlias( std::string _tag, SourceLineInfo _lineInfo ) : tag( _tag ), lineInfo( _lineInfo ) {} + TagAlias( std::string const& _tag, SourceLineInfo _lineInfo ) : tag( _tag ), lineInfo( _lineInfo ) {} std::string tag; SourceLineInfo lineInfo; @@ -2808,8 +3202,18 @@ namespace Catch { } private: - T* nullableValue; - char storage[sizeof(T)]; + T *nullableValue; + union { + char storage[sizeof(T)]; + + // These are here to force alignment for the storage + long double dummy1; + void (*dummy2)(); + long double dummy3; +#ifdef CATCH_CONFIG_CPP11_LONG_LONG + long long dummy4; +#endif + }; }; } // end namespace Catch @@ -2849,7 +3253,8 @@ namespace Catch { IsHidden = 1 << 1, ShouldFail = 1 << 2, MayFail = 1 << 3, - Throws = 1 << 4 + Throws = 1 << 4, + NonPortable = 1 << 5 }; TestCaseInfo( std::string const& _name, @@ -3007,64 +3412,67 @@ namespace Catch { namespace Impl { namespace NSStringMatchers { - template - struct StringHolder : MatcherImpl{ + struct StringHolder : MatcherBase{ StringHolder( NSString* substr ) : m_substr( [substr copy] ){} StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){} StringHolder() { arcSafeRelease( m_substr ); } + virtual bool match( NSString* arg ) const CATCH_OVERRIDE { + return false; + } + NSString* m_substr; }; - struct Equals : StringHolder { + struct Equals : StringHolder { Equals( NSString* substr ) : StringHolder( substr ){} - virtual bool match( ExpressionType const& str ) const { + virtual bool match( NSString* str ) const CATCH_OVERRIDE { return (str != nil || m_substr == nil ) && [str isEqualToString:m_substr]; } - virtual std::string toString() const { + virtual std::string describe() const CATCH_OVERRIDE { return "equals string: " + Catch::toString( m_substr ); } }; - struct Contains : StringHolder { + struct Contains : StringHolder { Contains( NSString* substr ) : StringHolder( substr ){} - virtual bool match( ExpressionType const& str ) const { + virtual bool match( NSString* str ) const { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location != NSNotFound; } - virtual std::string toString() const { + virtual std::string describe() const CATCH_OVERRIDE { return "contains string: " + Catch::toString( m_substr ); } }; - struct StartsWith : StringHolder { + struct StartsWith : StringHolder { StartsWith( NSString* substr ) : StringHolder( substr ){} - virtual bool match( ExpressionType const& str ) const { + virtual bool match( NSString* str ) const { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location == 0; } - virtual std::string toString() const { + virtual std::string describe() const CATCH_OVERRIDE { return "starts with: " + Catch::toString( m_substr ); } }; - struct EndsWith : StringHolder { + struct EndsWith : StringHolder { EndsWith( NSString* substr ) : StringHolder( substr ){} - virtual bool match( ExpressionType const& str ) const { + virtual bool match( NSString* str ) const { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location == [str length] - [m_substr length]; } - virtual std::string toString() const { + virtual std::string describe() const CATCH_OVERRIDE { return "ends with: " + Catch::toString( m_substr ); } }; @@ -3105,6 +3513,29 @@ return @ desc; \ #endif #ifdef CATCH_IMPL + +// !TBD: Move the leak detector code into a separate header +#ifdef CATCH_CONFIG_WINDOWS_CRTDBG +#include +class LeakDetector { +public: + LeakDetector() { + int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + flag |= _CRTDBG_LEAK_CHECK_DF; + flag |= _CRTDBG_ALLOC_MEM_DF; + _CrtSetDbgFlag(flag); + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); + // Change this to leaking allocation's number to break there + _CrtSetBreakAlloc(-1); + } +}; +#else +class LeakDetector {}; +#endif + +LeakDetector leakDetector; + // #included from: internal/catch_impl.hpp #define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED @@ -3144,6 +3575,8 @@ return @ desc; \ // #included from: catch_wildcard_pattern.hpp #define TWOBLUECUBES_CATCH_WILDCARD_PATTERN_HPP_INCLUDED +#include + namespace Catch { class WildcardPattern { @@ -3161,11 +3594,11 @@ namespace Catch m_wildcard( NoWildcard ), m_pattern( adjustCase( pattern ) ) { - if( startsWith( m_pattern, "*" ) ) { + if( startsWith( m_pattern, '*' ) ) { m_pattern = m_pattern.substr( 1 ); m_wildcard = WildcardAtStart; } - if( endsWith( m_pattern, "*" ) ) { + if( endsWith( m_pattern, '*' ) ) { m_pattern = m_pattern.substr( 0, m_pattern.size()-1 ); m_wildcard = static_cast( m_wildcard | WildcardAtEnd ); } @@ -3295,7 +3728,7 @@ namespace Catch { ITagAliasRegistry const* m_tagAliases; public: - TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {} + TestSpecParser( ITagAliasRegistry const& tagAliases ) :m_mode(None), m_exclusion(false), m_start(0), m_pos(0), m_tagAliases( &tagAliases ) {} TestSpecParser& parse( std::string const& arg ) { m_mode = None; @@ -3352,6 +3785,8 @@ namespace Catch { m_start = start; } void escape() { + if( m_mode == None ) + m_start = m_pos; m_mode = EscapedName; m_escapeChars.push_back( m_pos ); } @@ -3360,7 +3795,7 @@ namespace Catch { void addPattern() { std::string token = subString(); for( size_t i = 0; i < m_escapeChars.size(); ++i ) - token = token.substr( 0, m_escapeChars[i] ) + token.substr( m_escapeChars[i]+1 ); + token = token.substr( 0, m_escapeChars[i]-m_start-i ) + token.substr( m_escapeChars[i]-m_start-i+1 ); m_escapeChars.clear(); if( startsWith( token, "exclude:" ) ) { m_exclusion = true; @@ -3395,7 +3830,7 @@ namespace Catch { // #included from: catch_interfaces_config.h #define TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED -#include +#include #include #include @@ -3427,6 +3862,12 @@ namespace Catch { Yes, No }; }; + struct WaitForKeypress { enum When { + Never, + BeforeStart = 1, + BeforeExit = 2, + BeforeStartAndExit = BeforeStart | BeforeExit + }; }; class TestSpec; @@ -3447,6 +3888,8 @@ namespace Catch { virtual RunTests::InWhatOrder runOrder() const = 0; virtual unsigned int rngSeed() const = 0; virtual UseColour::YesOrNo useColour() const = 0; + virtual std::vector const& getSectionsToRun() const = 0; + }; } @@ -3475,6 +3918,7 @@ namespace Catch { std::ostream& cout(); std::ostream& cerr(); + std::ostream& clog(); struct IStream { virtual ~IStream() CATCH_NOEXCEPT; @@ -3515,8 +3959,7 @@ namespace Catch { #include #include #include -#include -#include +#include #ifndef CATCH_CONFIG_CONSOLE_WIDTH #define CATCH_CONFIG_CONSOLE_WIDTH 80 @@ -3531,25 +3974,29 @@ namespace Catch { listTags( false ), listReporters( false ), listTestNamesOnly( false ), + listExtraInfo( false ), showSuccessfulTests( false ), shouldDebugBreak( false ), noThrow( false ), showHelp( false ), showInvisibles( false ), filenamesAsTags( false ), + libIdentify( false ), abortAfter( -1 ), rngSeed( 0 ), verbosity( Verbosity::Normal ), warnings( WarnAbout::Nothing ), showDurations( ShowDurations::DefaultForReporter ), runOrder( RunTests::InDeclarationOrder ), - useColour( UseColour::Auto ) + useColour( UseColour::Auto ), + waitForKeypress( WaitForKeypress::Never ) {} bool listTests; bool listTags; bool listReporters; bool listTestNamesOnly; + bool listExtraInfo; bool showSuccessfulTests; bool shouldDebugBreak; @@ -3557,6 +4004,7 @@ namespace Catch { bool showHelp; bool showInvisibles; bool filenamesAsTags; + bool libIdentify; int abortAfter; unsigned int rngSeed; @@ -3566,6 +4014,7 @@ namespace Catch { ShowDurations::OrNot showDurations; RunTests::InWhatOrder runOrder; UseColour::YesOrNo useColour; + WaitForKeypress::When waitForKeypress; std::string outputFilename; std::string name; @@ -3573,6 +4022,7 @@ namespace Catch { std::vector reporterNames; std::vector testsOrTags; + std::vector sectionsToRun; }; class Config : public SharedImpl { @@ -3597,8 +4047,7 @@ namespace Catch { } } - virtual ~Config() { - } + virtual ~Config() {} std::string const& getFilename() const { return m_data.outputFilename ; @@ -3608,30 +4057,30 @@ namespace Catch { bool listTestNamesOnly() const { return m_data.listTestNamesOnly; } bool listTags() const { return m_data.listTags; } bool listReporters() const { return m_data.listReporters; } + bool listExtraInfo() const { return m_data.listExtraInfo; } std::string getProcessName() const { return m_data.processName; } - bool shouldDebugBreak() const { return m_data.shouldDebugBreak; } + std::vector const& getReporterNames() const { return m_data.reporterNames; } + std::vector const& getSectionsToRun() const CATCH_OVERRIDE { return m_data.sectionsToRun; } - std::vector getReporterNames() const { return m_data.reporterNames; } - - int abortAfter() const { return m_data.abortAfter; } - - TestSpec const& testSpec() const { return m_testSpec; } + virtual TestSpec const& testSpec() const CATCH_OVERRIDE { return m_testSpec; } bool showHelp() const { return m_data.showHelp; } - bool showInvisibles() const { return m_data.showInvisibles; } // IConfig interface - virtual bool allowThrows() const { return !m_data.noThrow; } - virtual std::ostream& stream() const { return m_stream->stream(); } - virtual std::string name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } - virtual bool includeSuccessfulResults() const { return m_data.showSuccessfulTests; } - virtual bool warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; } - virtual ShowDurations::OrNot showDurations() const { return m_data.showDurations; } - virtual RunTests::InWhatOrder runOrder() const { return m_data.runOrder; } - virtual unsigned int rngSeed() const { return m_data.rngSeed; } - virtual UseColour::YesOrNo useColour() const { return m_data.useColour; } + virtual bool allowThrows() const CATCH_OVERRIDE { return !m_data.noThrow; } + virtual std::ostream& stream() const CATCH_OVERRIDE { return m_stream->stream(); } + virtual std::string name() const CATCH_OVERRIDE { return m_data.name.empty() ? m_data.processName : m_data.name; } + virtual bool includeSuccessfulResults() const CATCH_OVERRIDE { return m_data.showSuccessfulTests; } + virtual bool warnAboutMissingAssertions() const CATCH_OVERRIDE { return m_data.warnings & WarnAbout::NoAssertions; } + virtual ShowDurations::OrNot showDurations() const CATCH_OVERRIDE { return m_data.showDurations; } + virtual RunTests::InWhatOrder runOrder() const CATCH_OVERRIDE { return m_data.runOrder; } + virtual unsigned int rngSeed() const CATCH_OVERRIDE { return m_data.rngSeed; } + virtual UseColour::YesOrNo useColour() const CATCH_OVERRIDE { return m_data.useColour; } + virtual bool shouldDebugBreak() const CATCH_OVERRIDE { return m_data.shouldDebugBreak; } + virtual int abortAfter() const CATCH_OVERRIDE { return m_data.abortAfter; } + virtual bool showInvisibles() const CATCH_OVERRIDE { return m_data.showInvisibles; } private: @@ -3696,6 +4145,7 @@ namespace Catch { #include #include #include +#include // Use optional outer namespace #ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE @@ -3805,7 +4255,7 @@ namespace Tbc { return oss.str(); } - inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { + friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); it != itEnd; ++it ) { if( it != _text.begin() ) @@ -4038,7 +4488,7 @@ namespace Clara { _dest = _source; } char toLowerCh(char c) { - return static_cast( ::tolower( c ) ); + return static_cast( std::tolower( c ) ); } inline void convertInto( std::string const& _source, bool& _dest ) { std::string sourceLC = _source; @@ -4192,12 +4642,13 @@ namespace Clara { } void parseIntoTokens( std::string const& arg, std::vector& tokens ) { - for( std::size_t i = 0; i <= arg.size(); ++i ) { + for( std::size_t i = 0; i < arg.size(); ++i ) { char c = arg[i]; if( c == '"' ) inQuotes = !inQuotes; mode = handleMode( i, c, arg, tokens ); } + mode = handleMode( arg.size(), '\0', arg, tokens ); } Mode handleMode( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { switch( mode ) { @@ -4230,6 +4681,7 @@ namespace Clara { default: from = i; return ShortOpt; } } + Mode handleOpt( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { if( std::string( ":=\0", 3 ).find( c ) == std::string::npos ) return mode; @@ -4561,7 +5013,7 @@ namespace Clara { } std::vector parseInto( std::vector const& args, ConfigT& config ) const { - std::string processName = args[0]; + std::string processName = args.empty() ? std::string() : args[0]; std::size_t lastSlash = processName.find_last_of( "/\\" ); if( lastSlash != std::string::npos ) processName = processName.substr( lastSlash+1 ); @@ -4693,6 +5145,7 @@ STITCH_CLARA_CLOSE_NAMESPACE #endif #include +#include namespace Catch { @@ -4703,13 +5156,14 @@ namespace Catch { config.abortAfter = x; } inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); } + inline void addSectionToRun( ConfigData& config, std::string const& sectionName ) { config.sectionsToRun.push_back( sectionName ); } inline void addReporterName( ConfigData& config, std::string const& _reporterName ) { config.reporterNames.push_back( _reporterName ); } inline void addWarning( ConfigData& config, std::string const& _warning ) { if( _warning == "NoAssertions" ) config.warnings = static_cast( config.warnings | WarnAbout::NoAssertions ); else - throw std::runtime_error( "Unrecognised warning: '" + _warning + "'" ); + throw std::runtime_error( "Unrecognised warning: '" + _warning + '\'' ); } inline void setOrder( ConfigData& config, std::string const& order ) { if( startsWith( "declared", order ) ) @@ -4719,7 +5173,7 @@ namespace Catch { else if( startsWith( "random", order ) ) config.runOrder = RunTests::InRandomOrder; else - throw std::runtime_error( "Unrecognised ordering: '" + order + "'" ); + throw std::runtime_error( "Unrecognised ordering: '" + order + '\'' ); } inline void setRngSeed( ConfigData& config, std::string const& seed ) { if( seed == "time" ) { @@ -4730,7 +5184,7 @@ namespace Catch { ss << seed; ss >> config.rngSeed; if( ss.fail() ) - throw std::runtime_error( "Argment to --rng-seed should be the word 'time' or a number" ); + throw std::runtime_error( "Argument to --rng-seed should be the word 'time' or a number" ); } } inline void setVerbosity( ConfigData& config, int level ) { @@ -4754,6 +5208,18 @@ namespace Catch { else throw std::runtime_error( "colour mode must be one of: auto, yes or no" ); } + inline void setWaitForKeypress( ConfigData& config, std::string const& keypress ) { + std::string keypressLc = toLower( keypress ); + if( keypressLc == "start" ) + config.waitForKeypress = WaitForKeypress::BeforeStart; + else if( keypressLc == "exit" ) + config.waitForKeypress = WaitForKeypress::BeforeExit; + else if( keypressLc == "both" ) + config.waitForKeypress = WaitForKeypress::BeforeStartAndExit; + else + throw std::runtime_error( "keypress argument must be one of: start, exit or both. '" + keypress + "' not recognised" ); + }; + inline void forceColour( ConfigData& config ) { config.useColour = UseColour::Yes; } @@ -4765,10 +5231,10 @@ namespace Catch { std::string line; while( std::getline( f, line ) ) { line = trim(line); - if( !line.empty() && !startsWith( line, "#" ) ) { - if( !startsWith( line, "\"" ) ) - line = "\"" + line + "\""; - addTestOrTags( config, line + "," ); + if( !line.empty() && !startsWith( line, '#' ) ) { + if( !startsWith( line, '"' ) ) + line = '"' + line + '"'; + addTestOrTags( config, line + ',' ); } } } @@ -4856,11 +5322,19 @@ namespace Catch { .describe( "adds a tag for the filename" ) .bind( &ConfigData::filenamesAsTags ); + cli["-c"]["--section"] + .describe( "specify section to run" ) + .bind( &addSectionToRun, "section name" ); + // Less common commands which don't have a short form cli["--list-test-names-only"] .describe( "list all/matching test cases names only" ) .bind( &ConfigData::listTestNamesOnly ); + cli["--list-extra-info"] + .describe( "list all/matching test cases with more info" ) + .bind( &ConfigData::listExtraInfo ); + cli["--list-reporters"] .describe( "list all reporters" ) .bind( &ConfigData::listReporters ); @@ -4881,6 +5355,14 @@ namespace Catch { .describe( "should output be colourised" ) .bind( &setUseColour, "yes|no" ); + cli["--libidentify"] + .describe( "report name and version according to libidentify standard" ) + .bind( &ConfigData::libIdentify ); + + cli["--wait-for-keypress"] + .describe( "waits for a keypress before exiting" ) + .bind( &setWaitForKeypress, "start|exit|both" ); + return cli; } @@ -4928,19 +5410,16 @@ namespace Tbc { TextAttributes() : initialIndent( std::string::npos ), indent( 0 ), - width( consoleWidth-1 ), - tabChar( '\t' ) + width( consoleWidth-1 ) {} TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } - TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; } std::size_t initialIndent; // indent of first line, or npos std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos std::size_t width; // maximum width of text, including indent. Longer text will wrap - char tabChar; // If this char is seen the indent is changed to current pos }; class Text { @@ -4948,62 +5427,76 @@ namespace Tbc { Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) : attr( _attr ) { - std::string wrappableChars = " [({.,/|\\-"; - std::size_t indent = _attr.initialIndent != std::string::npos - ? _attr.initialIndent - : _attr.indent; - std::string remainder = _str; + const std::string wrappableBeforeChars = "[({<\t"; + const std::string wrappableAfterChars = "])}>-,./|\\"; + const std::string wrappableInsteadOfChars = " \n\r"; + std::string indent = _attr.initialIndent != std::string::npos + ? std::string( _attr.initialIndent, ' ' ) + : std::string( _attr.indent, ' ' ); + + typedef std::string::const_iterator iterator; + iterator it = _str.begin(); + const iterator strEnd = _str.end(); + + while( it != strEnd ) { - while( !remainder.empty() ) { if( lines.size() >= 1000 ) { lines.push_back( "... message truncated due to excessive size" ); return; } - std::size_t tabPos = std::string::npos; - std::size_t width = (std::min)( remainder.size(), _attr.width - indent ); - std::size_t pos = remainder.find_first_of( '\n' ); - if( pos <= width ) { - width = pos; - } - pos = remainder.find_last_of( _attr.tabChar, width ); - if( pos != std::string::npos ) { - tabPos = pos; - if( remainder[width] == '\n' ) - width--; - remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 ); - } - if( width == remainder.size() ) { - spliceLine( indent, remainder, width ); - } - else if( remainder[width] == '\n' ) { - spliceLine( indent, remainder, width ); - if( width <= 1 || remainder.size() != 1 ) - remainder = remainder.substr( 1 ); - indent = _attr.indent; - } - else { - pos = remainder.find_last_of( wrappableChars, width ); - if( pos != std::string::npos && pos > 0 ) { - spliceLine( indent, remainder, pos ); - if( remainder[0] == ' ' ) - remainder = remainder.substr( 1 ); + std::string suffix; + std::size_t width = (std::min)( static_cast( strEnd-it ), _attr.width-static_cast( indent.size() ) ); + iterator itEnd = it+width; + iterator itNext = _str.end(); + + iterator itNewLine = std::find( it, itEnd, '\n' ); + if( itNewLine != itEnd ) + itEnd = itNewLine; + + if( itEnd != strEnd ) { + bool foundWrapPoint = false; + iterator findIt = itEnd; + do { + if( wrappableAfterChars.find( *findIt ) != std::string::npos && findIt != itEnd ) { + itEnd = findIt+1; + itNext = findIt+1; + foundWrapPoint = true; + } + else if( findIt > it && wrappableBeforeChars.find( *findIt ) != std::string::npos ) { + itEnd = findIt; + itNext = findIt; + foundWrapPoint = true; + } + else if( wrappableInsteadOfChars.find( *findIt ) != std::string::npos ) { + itNext = findIt+1; + itEnd = findIt; + foundWrapPoint = true; + } + if( findIt == it ) + break; + else + --findIt; + } + while( !foundWrapPoint ); + + if( !foundWrapPoint ) { + // No good wrap char, so we'll break mid word and add a hyphen + --itEnd; + itNext = itEnd; + suffix = "-"; } else { - spliceLine( indent, remainder, width-1 ); - lines.back() += "-"; + while( itEnd > it && wrappableInsteadOfChars.find( *(itEnd-1) ) != std::string::npos ) + --itEnd; } - if( lines.size() == 1 ) - indent = _attr.indent; - if( tabPos != std::string::npos ) - indent += tabPos; } - } - } + lines.push_back( indent + std::string( it, itEnd ) + suffix ); - void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) { - lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) ); - _remainder = _remainder.substr( _pos ); + if( indent.size() != _attr.indent ) + indent = std::string( _attr.indent, ' ' ); + it = itNext; + } } typedef std::vector::const_iterator const_iterator; @@ -5112,7 +5605,6 @@ namespace Catch { #include #include #include -#include namespace Catch { @@ -5379,8 +5871,9 @@ namespace Catch { } std::size_t matchedTests = 0; - TextAttributes nameAttr, tagsAttr; + TextAttributes nameAttr, descAttr, tagsAttr; nameAttr.setInitialIndent( 2 ).setIndent( 4 ); + descAttr.setIndent( 4 ); tagsAttr.setIndent( 6 ); std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); @@ -5395,14 +5888,21 @@ namespace Catch { Colour colourGuard( colour ); Catch::cout() << Text( testCaseInfo.name, nameAttr ) << std::endl; + if( config.listExtraInfo() ) { + Catch::cout() << " " << testCaseInfo.lineInfo << std::endl; + std::string description = testCaseInfo.description; + if( description.empty() ) + description = "(NO DESCRIPTION)"; + Catch::cout() << Text( description, descAttr ) << std::endl; + } if( !testCaseInfo.tags.empty() ) Catch::cout() << Text( testCaseInfo.tagsAsString, tagsAttr ) << std::endl; } if( !config.testSpec().hasFilters() ) - Catch::cout() << pluralise( matchedTests, "test case" ) << "\n" << std::endl; + Catch::cout() << pluralise( matchedTests, "test case" ) << '\n' << std::endl; else - Catch::cout() << pluralise( matchedTests, "matching test case" ) << "\n" << std::endl; + Catch::cout() << pluralise( matchedTests, "matching test case" ) << '\n' << std::endl; return matchedTests; } @@ -5417,10 +5917,13 @@ namespace Catch { ++it ) { matchedTests++; TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); - if( startsWith( testCaseInfo.name, "#" ) ) - Catch::cout() << "\"" << testCaseInfo.name << "\"" << std::endl; + if( startsWith( testCaseInfo.name, '#' ) ) + Catch::cout() << '"' << testCaseInfo.name << '"'; else - Catch::cout() << testCaseInfo.name << std::endl; + Catch::cout() << testCaseInfo.name; + if ( config.listExtraInfo() ) + Catch::cout() << "\t@" << testCaseInfo.lineInfo; + Catch::cout() << std::endl; } return matchedTests; } @@ -5481,9 +5984,9 @@ namespace Catch { .setInitialIndent( 0 ) .setIndent( oss.str().size() ) .setWidth( CATCH_CONFIG_CONSOLE_WIDTH-10 ) ); - Catch::cout() << oss.str() << wrapper << "\n"; + Catch::cout() << oss.str() << wrapper << '\n'; } - Catch::cout() << pluralise( tagCounts.size(), "tag" ) << "\n" << std::endl; + Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl; return tagCounts.size(); } @@ -5502,9 +6005,9 @@ namespace Catch { .setWidth( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) ); Catch::cout() << " " << it->first - << ":" + << ':' << std::string( maxNameLen - it->first.size() + 2, ' ' ) - << wrapper << "\n"; + << wrapper << '\n'; } Catch::cout() << std::endl; return factories.size(); @@ -5512,7 +6015,7 @@ namespace Catch { inline Option list( Config const& config ) { Option listedCount; - if( config.listTests() ) + if( config.listTests() || ( config.listExtraInfo() && !config.listTestNamesOnly() ) ) listedCount = listedCount.valueOr(0) + listTests( config ); if( config.listTestNamesOnly() ) listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config ); @@ -5531,19 +6034,32 @@ namespace Catch { // #included from: catch_test_case_tracker.hpp #define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED -#include +#include #include #include #include +#include + +CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS namespace Catch { namespace TestCaseTracking { + struct NameAndLocation { + std::string name; + SourceLineInfo location; + + NameAndLocation( std::string const& _name, SourceLineInfo const& _location ) + : name( _name ), + location( _location ) + {} + }; + struct ITracker : SharedImpl<> { virtual ~ITracker(); // static queries - virtual std::string name() const = 0; + virtual NameAndLocation const& nameAndLocation() const = 0; // dynamic queries virtual bool isComplete() const = 0; // Successfully completed or failed @@ -5559,7 +6075,7 @@ namespace TestCaseTracking { virtual void markAsNeedingAnotherRun() = 0; virtual void addChild( Ptr const& child ) = 0; - virtual ITracker* findChild( std::string const& name ) = 0; + virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) = 0; virtual void openChild() = 0; // Debug/ checking @@ -5567,7 +6083,7 @@ namespace TestCaseTracking { virtual bool isIndexTracker() const = 0; }; - class TrackerContext { + class TrackerContext { enum RunState { NotStarted, @@ -5629,30 +6145,32 @@ namespace TestCaseTracking { Failed }; class TrackerHasName { - std::string m_name; + NameAndLocation m_nameAndLocation; public: - TrackerHasName( std::string const& name ) : m_name( name ) {} + TrackerHasName( NameAndLocation const& nameAndLocation ) : m_nameAndLocation( nameAndLocation ) {} bool operator ()( Ptr const& tracker ) { - return tracker->name() == m_name; + return + tracker->nameAndLocation().name == m_nameAndLocation.name && + tracker->nameAndLocation().location == m_nameAndLocation.location; } }; typedef std::vector > Children; - std::string m_name; + NameAndLocation m_nameAndLocation; TrackerContext& m_ctx; ITracker* m_parent; Children m_children; CycleState m_runState; public: - TrackerBase( std::string const& name, TrackerContext& ctx, ITracker* parent ) - : m_name( name ), + TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : m_nameAndLocation( nameAndLocation ), m_ctx( ctx ), m_parent( parent ), m_runState( NotStarted ) {} virtual ~TrackerBase(); - virtual std::string name() const CATCH_OVERRIDE { - return m_name; + virtual NameAndLocation const& nameAndLocation() const CATCH_OVERRIDE { + return m_nameAndLocation; } virtual bool isComplete() const CATCH_OVERRIDE { return m_runState == CompletedSuccessfully || m_runState == Failed; @@ -5671,8 +6189,8 @@ namespace TestCaseTracking { m_children.push_back( child ); } - virtual ITracker* findChild( std::string const& name ) CATCH_OVERRIDE { - Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( name ) ); + virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) CATCH_OVERRIDE { + Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( nameAndLocation ) ); return( it != m_children.end() ) ? it->get() : CATCH_NULL; @@ -5750,41 +6268,65 @@ namespace TestCaseTracking { }; class SectionTracker : public TrackerBase { + std::vector m_filters; public: - SectionTracker( std::string const& name, TrackerContext& ctx, ITracker* parent ) - : TrackerBase( name, ctx, parent ) - {} + SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : TrackerBase( nameAndLocation, ctx, parent ) + { + if( parent ) { + while( !parent->isSectionTracker() ) + parent = &parent->parent(); + + SectionTracker& parentSection = static_cast( *parent ); + addNextFilters( parentSection.m_filters ); + } + } virtual ~SectionTracker(); virtual bool isSectionTracker() const CATCH_OVERRIDE { return true; } - static SectionTracker& acquire( TrackerContext& ctx, std::string const& name ) { + static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { SectionTracker* section = CATCH_NULL; ITracker& currentTracker = ctx.currentTracker(); - if( ITracker* childTracker = currentTracker.findChild( name ) ) { + if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) { assert( childTracker ); assert( childTracker->isSectionTracker() ); section = static_cast( childTracker ); } else { - section = new SectionTracker( name, ctx, ¤tTracker ); + section = new SectionTracker( nameAndLocation, ctx, ¤tTracker ); currentTracker.addChild( section ); } - if( !ctx.completedCycle() && !section->isComplete() ) { - - section->open(); - } + if( !ctx.completedCycle() ) + section->tryOpen(); return *section; } + + void tryOpen() { + if( !isComplete() && (m_filters.empty() || m_filters[0].empty() || m_filters[0] == m_nameAndLocation.name ) ) + open(); + } + + void addInitialFilters( std::vector const& filters ) { + if( !filters.empty() ) { + m_filters.push_back(""); // Root - should never be consulted + m_filters.push_back(""); // Test Case - not a section filter + m_filters.insert( m_filters.end(), filters.begin(), filters.end() ); + } + } + void addNextFilters( std::vector const& filters ) { + if( filters.size() > 1 ) + m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() ); + } }; class IndexTracker : public TrackerBase { int m_size; int m_index; public: - IndexTracker( std::string const& name, TrackerContext& ctx, ITracker* parent, int size ) - : TrackerBase( name, ctx, parent ), + IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ) + : TrackerBase( nameAndLocation, ctx, parent ), m_size( size ), m_index( -1 ) {} @@ -5792,17 +6334,17 @@ namespace TestCaseTracking { virtual bool isIndexTracker() const CATCH_OVERRIDE { return true; } - static IndexTracker& acquire( TrackerContext& ctx, std::string const& name, int size ) { + static IndexTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) { IndexTracker* tracker = CATCH_NULL; ITracker& currentTracker = ctx.currentTracker(); - if( ITracker* childTracker = currentTracker.findChild( name ) ) { + if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) { assert( childTracker ); assert( childTracker->isIndexTracker() ); tracker = static_cast( childTracker ); } else { - tracker = new IndexTracker( name, ctx, ¤tTracker, size ); + tracker = new IndexTracker( nameAndLocation, ctx, ¤tTracker, size ); currentTracker.addChild( tracker ); } @@ -5830,7 +6372,7 @@ namespace TestCaseTracking { }; inline ITracker& TrackerContext::startRun() { - m_rootTracker = new SectionTracker( "{root}", *this, CATCH_NULL ); + m_rootTracker = new SectionTracker( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, CATCH_NULL ); m_currentTracker = CATCH_NULL; m_runState = Executing; return *m_rootTracker; @@ -5845,40 +6387,144 @@ using TestCaseTracking::IndexTracker; } // namespace Catch +CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS + // #included from: catch_fatal_condition.hpp #define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED namespace Catch { - // Report the error condition then exit the process - inline void fatal( std::string const& message, int exitCode ) { + // Report the error condition + inline void reportFatal( std::string const& message ) { IContext& context = Catch::getCurrentContext(); IResultCapture* resultCapture = context.getResultCapture(); resultCapture->handleFatalErrorCondition( message ); - - if( Catch::alwaysTrue() ) // avoids "no return" warnings - exit( exitCode ); } } // namespace Catch #if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// +// #included from: catch_windows_h_proxy.h + +#define TWOBLUECUBES_CATCH_WINDOWS_H_PROXY_H_INCLUDED + +#ifdef CATCH_DEFINES_NOMINMAX +# define NOMINMAX +#endif +#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif + +#ifdef __AFXDLL +#include +#else +#include +#endif + +#ifdef CATCH_DEFINES_NOMINMAX +# undef NOMINMAX +#endif +#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN +# undef WIN32_LEAN_AND_MEAN +#endif + + +# if !defined ( CATCH_CONFIG_WINDOWS_SEH ) + +namespace Catch { + struct FatalConditionHandler { + void reset() {} + }; +} + +# else // CATCH_CONFIG_WINDOWS_SEH is defined namespace Catch { + struct SignalDefs { DWORD id; const char* name; }; + extern SignalDefs signalDefs[]; + // There is no 1-1 mapping between signals and windows exceptions. + // Windows can easily distinguish between SO and SigSegV, + // but SigInt, SigTerm, etc are handled differently. + SignalDefs signalDefs[] = { + { EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal" }, + { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" }, + { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" }, + { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" }, + }; + struct FatalConditionHandler { - void reset() {} - }; + + static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { + for (int i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { + if (ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) { + reportFatal(signalDefs[i].name); + } + } + // If its not an exception we care about, pass it along. + // This stops us from eating debugger breaks etc. + return EXCEPTION_CONTINUE_SEARCH; + } + + FatalConditionHandler() { + isSet = true; + // 32k seems enough for Catch to handle stack overflow, + // but the value was found experimentally, so there is no strong guarantee + guaranteeSize = 32 * 1024; + exceptionHandlerHandle = CATCH_NULL; + // Register as first handler in current chain + exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException); + // Pass in guarantee size to be filled + SetThreadStackGuarantee(&guaranteeSize); + } + + static void reset() { + if (isSet) { + // Unregister handler and restore the old guarantee + RemoveVectoredExceptionHandler(exceptionHandlerHandle); + SetThreadStackGuarantee(&guaranteeSize); + exceptionHandlerHandle = CATCH_NULL; + isSet = false; + } + } + + ~FatalConditionHandler() { + reset(); + } + private: + static bool isSet; + static ULONG guaranteeSize; + static PVOID exceptionHandlerHandle; + }; + + bool FatalConditionHandler::isSet = false; + ULONG FatalConditionHandler::guaranteeSize = 0; + PVOID FatalConditionHandler::exceptionHandlerHandle = CATCH_NULL; } // namespace Catch +# endif // CATCH_CONFIG_WINDOWS_SEH + #else // Not Windows - assumed to be POSIX compatible ////////////////////////// +# if !defined(CATCH_CONFIG_POSIX_SIGNALS) + +namespace Catch { + struct FatalConditionHandler { + void reset() {} + }; +} + +# else // CATCH_CONFIG_POSIX_SIGNALS is defined + #include namespace Catch { - struct SignalDefs { int id; const char* name; }; + struct SignalDefs { + int id; + const char* name; + }; extern SignalDefs signalDefs[]; SignalDefs signalDefs[] = { { SIGINT, "SIGINT - Terminal interrupt signal" }, @@ -5887,39 +6533,73 @@ namespace Catch { { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, { SIGTERM, "SIGTERM - Termination request signal" }, { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } - }; + }; struct FatalConditionHandler { + static bool isSet; + static struct sigaction oldSigActions [sizeof(signalDefs)/sizeof(SignalDefs)]; + static stack_t oldSigStack; + static char altStackMem[SIGSTKSZ]; + static void handleSignal( int sig ) { - for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) - if( sig == signalDefs[i].id ) - fatal( signalDefs[i].name, -sig ); - fatal( "", -sig ); + std::string name = ""; + for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { + SignalDefs &def = signalDefs[i]; + if (sig == def.id) { + name = def.name; + break; + } + } + reset(); + reportFatal(name); + raise( sig ); } - FatalConditionHandler() : m_isSet( true ) { - for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) - signal( signalDefs[i].id, handleSignal ); - } - ~FatalConditionHandler() { - reset(); - } - void reset() { - if( m_isSet ) { - for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) - signal( signalDefs[i].id, SIG_DFL ); - m_isSet = false; + FatalConditionHandler() { + isSet = true; + stack_t sigStack; + sigStack.ss_sp = altStackMem; + sigStack.ss_size = SIGSTKSZ; + sigStack.ss_flags = 0; + sigaltstack(&sigStack, &oldSigStack); + struct sigaction sa = { 0 }; + + sa.sa_handler = handleSignal; + sa.sa_flags = SA_ONSTACK; + for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) { + sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); } } - bool m_isSet; + ~FatalConditionHandler() { + reset(); + } + static void reset() { + if( isSet ) { + // Set signals back to previous values -- hopefully nobody overwrote them in the meantime + for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) { + sigaction(signalDefs[i].id, &oldSigActions[i], CATCH_NULL); + } + // Return the old stack + sigaltstack(&oldSigStack, CATCH_NULL); + isSet = false; + } + } }; + bool FatalConditionHandler::isSet = false; + struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {}; + stack_t FatalConditionHandler::oldSigStack = {}; + char FatalConditionHandler::altStackMem[SIGSTKSZ] = {}; + } // namespace Catch +# endif // CATCH_CONFIG_POSIX_SIGNALS + #endif // not Windows +#include #include #include @@ -5948,6 +6628,29 @@ namespace Catch { std::string& m_targetString; }; + // StdErr has two constituent streams in C++, std::cerr and std::clog + // This means that we need to redirect 2 streams into 1 to keep proper + // order of writes and cannot use StreamRedirect on its own + class StdErrRedirect { + public: + StdErrRedirect(std::string& targetString) + :m_cerrBuf( cerr().rdbuf() ), m_clogBuf(clog().rdbuf()), + m_targetString(targetString){ + cerr().rdbuf(m_oss.rdbuf()); + clog().rdbuf(m_oss.rdbuf()); + } + ~StdErrRedirect() { + m_targetString += m_oss.str(); + cerr().rdbuf(m_cerrBuf); + clog().rdbuf(m_clogBuf); + } + private: + std::streambuf* m_cerrBuf; + std::streambuf* m_clogBuf; + std::ostringstream m_oss; + std::string& m_targetString; + }; + /////////////////////////////////////////////////////////////////////////// class RunContext : public IResultCapture, public IRunner { @@ -5962,7 +6665,8 @@ namespace Catch { m_context( getCurrentMutableContext() ), m_activeTestCase( CATCH_NULL ), m_config( _config ), - m_reporter( reporter ) + m_reporter( reporter ), + m_shouldReportUnexpected ( true ) { m_context.setRunner( this ); m_context.setConfig( m_config ); @@ -5994,10 +6698,12 @@ namespace Catch { m_activeTestCase = &testCase; do { - m_trackerContext.startRun(); + ITracker& rootTracker = m_trackerContext.startRun(); + assert( rootTracker.isSectionTracker() ); + static_cast( rootTracker ).addInitialFilters( m_config->getSectionsToRun() ); do { m_trackerContext.startCycle(); - m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, testInfo.name ); + m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( testInfo.name, testInfo.lineInfo ) ); runCurrentTest( redirectedCout, redirectedCerr ); } while( !m_testCaseTracker->isSuccessfullyCompleted() && !aborting() ); @@ -6035,26 +6741,44 @@ namespace Catch { m_totals.assertions.passed++; } else if( !result.isOk() ) { - m_totals.assertions.failed++; + if( m_activeTestCase->getTestCaseInfo().okToFail() ) + m_totals.assertions.failedButOk++; + else + m_totals.assertions.failed++; } - if( m_reporter->assertionEnded( AssertionStats( result, m_messages, m_totals ) ) ) - m_messages.clear(); + // We have no use for the return value (whether messages should be cleared), because messages were made scoped + // and should be let to clear themselves out. + static_cast(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals))); // Reset working state m_lastAssertionInfo = AssertionInfo( "", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition ); m_lastResult = result; } + virtual bool lastAssertionPassed() + { + return m_totals.assertions.passed == (m_prevPassed + 1); + } + + virtual void assertionPassed() + { + m_totals.assertions.passed++; + m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"; + m_lastAssertionInfo.macroName = ""; + } + + virtual void assertionRun() + { + m_prevPassed = m_totals.assertions.passed; + } + virtual bool sectionStarted ( SectionInfo const& sectionInfo, Counts& assertions ) { - std::ostringstream oss; - oss << sectionInfo.name << "@" << sectionInfo.lineInfo; - - ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, oss.str() ); + ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( sectionInfo.name, sectionInfo.lineInfo ) ); if( !sectionTracker.isOpen() ) return false; m_activeSections.push_back( §ionTracker ); @@ -6113,18 +6837,26 @@ namespace Catch { virtual std::string getCurrentTestName() const { return m_activeTestCase ? m_activeTestCase->getTestCaseInfo().name - : ""; + : std::string(); } virtual const AssertionResult* getLastResult() const { return &m_lastResult; } + virtual void exceptionEarlyReported() { + m_shouldReportUnexpected = false; + } + virtual void handleFatalErrorCondition( std::string const& message ) { - ResultBuilder resultBuilder = makeUnexpectedResultBuilder(); - resultBuilder.setResultType( ResultWas::FatalErrorCondition ); - resultBuilder << message; - resultBuilder.captureExpression(); + // Don't rebuild the result -- the stringification itself can cause more fatal errors + // Instead, fake a result data. + AssertionResultData tempResult; + tempResult.resultType = ResultWas::FatalErrorCondition; + tempResult.message = message; + AssertionResult result(m_lastAssertionInfo, tempResult); + + getResultCapture().assertionEnded(result); handleUnfinishedSections(); @@ -6141,13 +6873,14 @@ namespace Catch { Totals deltaTotals; deltaTotals.testCases.failed = 1; + deltaTotals.assertions.failed = 1; m_reporter->testCaseEnded( TestCaseStats( testInfo, deltaTotals, - "", - "", + std::string(), + std::string(), false ) ); m_totals.testCases.failed++; - testGroupEnded( "", m_totals, 1, 1 ); + testGroupEnded( std::string(), m_totals, 1, 1 ); m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, false ) ); } @@ -6165,6 +6898,7 @@ namespace Catch { m_reporter->sectionStarting( testCaseSection ); Counts prevAssertions = m_totals.assertions; double duration = 0; + m_shouldReportUnexpected = true; try { m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal ); @@ -6174,7 +6908,7 @@ namespace Catch { timer.start(); if( m_reporter->getPreferences().shouldRedirectStdOut ) { StreamRedirect coutRedir( Catch::cout(), redirectedCout ); - StreamRedirect cerrRedir( Catch::cerr(), redirectedCerr ); + StdErrRedirect errRedir( redirectedCerr ); invokeActiveTestCase(); } else { @@ -6186,7 +6920,11 @@ namespace Catch { // This just means the test was aborted due to failure } catch(...) { - makeUnexpectedResultBuilder().useActiveException(); + // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions + // are reported without translation at the point of origin. + if (m_shouldReportUnexpected) { + makeUnexpectedResultBuilder().useActiveException(); + } } m_testCaseTracker->close(); handleUnfinishedSections(); @@ -6195,12 +6933,6 @@ namespace Catch { Counts assertions = m_totals.assertions - prevAssertions; bool missingAssertions = testForMissingAssertions( assertions ); - if( testCaseInfo.okToFail() ) { - std::swap( assertions.failedButOk, assertions.failed ); - m_totals.assertions.failed -= assertions.failedButOk; - m_totals.assertions.failedButOk += assertions.failedButOk; - } - SectionStats testCaseSectionStats( testCaseSection, assertions, duration, missingAssertions ); m_reporter->sectionEnded( testCaseSectionStats ); } @@ -6214,9 +6946,9 @@ namespace Catch { private: ResultBuilder makeUnexpectedResultBuilder() const { - return ResultBuilder( m_lastAssertionInfo.macroName.c_str(), + return ResultBuilder( m_lastAssertionInfo.macroName, m_lastAssertionInfo.lineInfo, - m_lastAssertionInfo.capturedExpression.c_str(), + m_lastAssertionInfo.capturedExpression, m_lastAssertionInfo.resultDisposition ); } @@ -6246,6 +6978,8 @@ namespace Catch { std::vector m_unfinishedSections; std::vector m_activeSections; TrackerContext m_trackerContext; + size_t m_prevPassed; + bool m_shouldReportUnexpected; }; IResultCapture& getResultCapture() { @@ -6267,7 +7001,7 @@ namespace Catch { Version( unsigned int _majorVersion, unsigned int _minorVersion, unsigned int _patchNumber, - std::string const& _branchName, + char const * const _branchName, unsigned int _buildNumber ); unsigned int const majorVersion; @@ -6275,7 +7009,7 @@ namespace Catch { unsigned int const patchNumber; // buildNumber is only used if branchName is not null - std::string const branchName; + char const * const branchName; unsigned int const buildNumber; friend std::ostream& operator << ( std::ostream& os, Version const& version ); @@ -6284,7 +7018,7 @@ namespace Catch { void operator=( Version const& ); }; - extern Version libraryVersion; + inline Version libraryVersion(); } #include @@ -6303,10 +7037,14 @@ namespace Catch { return reporter; } +#if !defined(CATCH_CONFIG_DEFAULT_REPORTER) +#define CATCH_CONFIG_DEFAULT_REPORTER "console" +#endif + Ptr makeReporter( Ptr const& config ) { std::vector reporters = config->getReporterNames(); if( reporters.empty() ) - reporters.push_back( "console" ); + reporters.push_back( CATCH_CONFIG_DEFAULT_REPORTER ); Ptr reporter; for( std::vector::const_iterator it = reporters.begin(), itEnd = reporters.end(); @@ -6366,11 +7104,11 @@ namespace Catch { if( lastSlash != std::string::npos ) filename = filename.substr( lastSlash+1 ); - std::string::size_type lastDot = filename.find_last_of( "." ); + std::string::size_type lastDot = filename.find_last_of( '.' ); if( lastDot != std::string::npos ) filename = filename.substr( 0, lastDot ); - tags.insert( "#" + filename ); + tags.insert( '#' + filename ); setTags( test, tags ); } } @@ -6396,11 +7134,18 @@ namespace Catch { } void showHelp( std::string const& processName ) { - Catch::cout() << "\nCatch v" << libraryVersion << "\n"; + Catch::cout() << "\nCatch v" << libraryVersion() << "\n"; m_cli.usage( Catch::cout(), processName ); Catch::cout() << "For more detail usage please see the project docs\n" << std::endl; } + void libIdentify() { + Catch::cout() + << std::left << std::setw(16) << "description: " << "A Catch test executable\n" + << std::left << std::setw(16) << "category: " << "testframework\n" + << std::left << std::setw(16) << "framework: " << "Catch Test\n" + << std::left << std::setw(16) << "version: " << libraryVersion() << std::endl; + } int applyCommandLine( int argc, char const* const* const argv, OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) { try { @@ -6408,6 +7153,8 @@ namespace Catch { m_unusedTokens = m_cli.parseInto( Clara::argsToVector( argc, argv ), m_configData ); if( m_configData.showHelp ) showHelp( m_configData.processName ); + if( m_configData.libIdentify ) + libIdentify(); m_config.reset(); } catch( std::exception& ex ) { @@ -6437,8 +7184,63 @@ namespace Catch { return returnCode; } + #if defined(WIN32) && defined(UNICODE) + int run( int argc, wchar_t const* const* const argv ) { + + char **utf8Argv = new char *[ argc ]; + + for ( int i = 0; i < argc; ++i ) { + int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, NULL, 0, NULL, NULL ); + + utf8Argv[ i ] = new char[ bufSize ]; + + WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, NULL, NULL ); + } + + int returnCode = applyCommandLine( argc, utf8Argv ); + if( returnCode == 0 ) + returnCode = run(); + + for ( int i = 0; i < argc; ++i ) + delete [] utf8Argv[ i ]; + + delete [] utf8Argv; + + return returnCode; + } + #endif + int run() { - if( m_configData.showHelp ) + if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeStart ) != 0 ) { + Catch::cout() << "...waiting for enter/ return before starting" << std::endl; + static_cast(std::getchar()); + } + int exitCode = runInternal(); + if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) { + Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << std::endl; + static_cast(std::getchar()); + } + return exitCode; + } + + Clara::CommandLine const& cli() const { + return m_cli; + } + std::vector const& unusedTokens() const { + return m_unusedTokens; + } + ConfigData& configData() { + return m_configData; + } + Config& config() { + if( !m_config ) + m_config = new Config( m_configData ); + return *m_config; + } + private: + + int runInternal() { + if( m_configData.showHelp || m_configData.libIdentify ) return 0; try @@ -6462,21 +7264,6 @@ namespace Catch { } } - Clara::CommandLine const& cli() const { - return m_cli; - } - std::vector const& unusedTokens() const { - return m_unusedTokens; - } - ConfigData& configData() { - return m_configData; - } - Config& config() { - if( !m_config ) - m_config = new Config( m_configData ); - return *m_config; - } - private: Clara::CommandLine m_cli; std::vector m_unusedTokens; ConfigData m_configData; @@ -6496,20 +7283,19 @@ namespace Catch { #include #include #include -#include #include namespace Catch { struct RandomNumberGenerator { - typedef std::ptrdiff_t result_type; + typedef unsigned int result_type; result_type operator()( result_type n ) const { return std::rand() % n; } #ifdef CATCH_CONFIG_CPP11_SHUFFLE - static constexpr result_type min() { return 0; } - static constexpr result_type max() { return 1000000; } - result_type operator()() const { return std::rand() % max(); } + static constexpr result_type (min)() { return 0; } + static constexpr result_type (max)() { return 1000000; } + result_type operator()() const { return std::rand() % (max)(); } #endif template static void shuffle( V& vector ) { @@ -6557,7 +7343,7 @@ namespace Catch { ss << Colour( Colour::Red ) << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n" - << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" + << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << '\n' << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl; throw std::runtime_error(ss.str()); @@ -6589,7 +7375,7 @@ namespace Catch { virtual void registerTest( TestCase const& testCase ) { std::string name = testCase.getTestCaseInfo().name; - if( name == "" ) { + if( name.empty() ) { std::ostringstream oss; oss << "Anonymous test case " << ++m_unnamedCount; return registerTest( testCase.withName( oss.str() ) ); @@ -6638,7 +7424,7 @@ namespace Catch { inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) { std::string className = classOrQualifiedMethodName; - if( startsWith( className, "&" ) ) + if( startsWith( className, '&' ) ) { std::size_t lastColons = className.rfind( "::" ); std::size_t penultimateColons = className.rfind( "::", lastColons-1 ); @@ -6785,6 +7571,26 @@ namespace Catch { }; } +// #included from: catch_tag_alias_registry.h +#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_H_INCLUDED + +#include + +namespace Catch { + + class TagAliasRegistry : public ITagAliasRegistry { + public: + virtual ~TagAliasRegistry(); + virtual Option find( std::string const& alias ) const; + virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const; + void add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ); + + private: + std::map m_registry; + }; + +} // end namespace Catch + namespace Catch { namespace { @@ -6806,6 +7612,9 @@ namespace Catch { virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() CATCH_OVERRIDE { return m_exceptionTranslatorRegistry; } + virtual ITagAliasRegistry const& getTagAliasRegistry() const CATCH_OVERRIDE { + return m_tagAliasRegistry; + } public: // IMutableRegistryHub virtual void registerReporter( std::string const& name, Ptr const& factory ) CATCH_OVERRIDE { @@ -6820,11 +7629,15 @@ namespace Catch { virtual void registerTranslator( const IExceptionTranslator* translator ) CATCH_OVERRIDE { m_exceptionTranslatorRegistry.registerTranslator( translator ); } + virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) CATCH_OVERRIDE { + m_tagAliasRegistry.add( alias, tag, lineInfo ); + } private: TestRegistry m_testCaseRegistry; ReporterRegistry m_reporterRegistry; ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; + TagAliasRegistry m_tagAliasRegistry; }; // Single, global, instance @@ -6856,7 +7669,7 @@ namespace Catch { // #included from: catch_notimplemented_exception.hpp #define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED -#include +#include namespace Catch { @@ -6928,7 +7741,7 @@ namespace Catch { m_ofs.open( filename.c_str() ); if( m_ofs.fail() ) { std::ostringstream oss; - oss << "Unable to open file: '" << filename << "'"; + oss << "Unable to open file: '" << filename << '\''; throw std::domain_error( oss.str() ); } } @@ -6970,6 +7783,9 @@ namespace Catch { std::ostream& cerr() { return std::cerr; } + std::ostream& clog() { + return std::clog; + } #endif } @@ -6981,6 +7797,11 @@ namespace Catch { Context( Context const& ); void operator=( Context const& ); + public: + virtual ~Context() { + deleteAllValues( m_generatorsByTestName ); + } + public: // IContext virtual IResultCapture* getResultCapture() { return m_resultCapture; @@ -7064,6 +7885,23 @@ namespace Catch { // #included from: catch_console_colour_impl.hpp #define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED +// #included from: catch_errno_guard.hpp +#define TWOBLUECUBES_CATCH_ERRNO_GUARD_HPP_INCLUDED + +#include + +namespace Catch { + + class ErrnoGuard { + public: + ErrnoGuard():m_oldErrno(errno){} + ~ErrnoGuard() { errno = m_oldErrno; } + private: + int m_oldErrno; + }; + +} + namespace Catch { namespace { @@ -7094,16 +7932,6 @@ namespace Catch { #if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) ///////////////////////////////////////// -#ifndef NOMINMAX -#define NOMINMAX -#endif - -#ifdef __AFXDLL -#include -#else -#include -#endif - namespace Catch { namespace { @@ -7209,6 +8037,7 @@ namespace { }; IColourImpl* platformColourInstance() { + ErrnoGuard guard; Ptr config = getCurrentContext().getConfig(); UseColour::YesOrNo colourMode = config ? config->useColour() @@ -7327,14 +8156,18 @@ namespace Catch { namespace Catch { - AssertionInfo::AssertionInfo( std::string const& _macroName, + AssertionInfo::AssertionInfo():macroName(""), capturedExpression(""), resultDisposition(ResultDisposition::Normal), secondArg(""){} + + AssertionInfo::AssertionInfo( char const * _macroName, SourceLineInfo const& _lineInfo, - std::string const& _capturedExpression, - ResultDisposition::Flags _resultDisposition ) + char const * _capturedExpression, + ResultDisposition::Flags _resultDisposition, + char const * _secondArg) : macroName( _macroName ), lineInfo( _lineInfo ), capturedExpression( _capturedExpression ), - resultDisposition( _resultDisposition ) + resultDisposition( _resultDisposition ), + secondArg( _secondArg ) {} AssertionResult::AssertionResult() {} @@ -7361,24 +8194,30 @@ namespace Catch { } bool AssertionResult::hasExpression() const { - return !m_info.capturedExpression.empty(); + return m_info.capturedExpression[0] != 0; } bool AssertionResult::hasMessage() const { return !m_resultData.message.empty(); } + std::string capturedExpressionWithSecondArgument( char const * capturedExpression, char const * secondArg ) { + return (secondArg[0] == 0 || secondArg[0] == '"' && secondArg[1] == '"') + ? capturedExpression + : std::string(capturedExpression) + ", " + secondArg; + } + std::string AssertionResult::getExpression() const { if( isFalseTest( m_info.resultDisposition ) ) - return "!" + m_info.capturedExpression; + return "!(" + capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg) + ")"; else - return m_info.capturedExpression; + return capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg); } std::string AssertionResult::getExpressionInMacro() const { - if( m_info.macroName.empty() ) - return m_info.capturedExpression; + if( m_info.macroName[0] == 0 ) + return capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg); else - return m_info.macroName + "( " + m_info.capturedExpression + " )"; + return std::string(m_info.macroName) + "( " + capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg) + " )"; } bool AssertionResult::hasExpandedExpression() const { @@ -7386,7 +8225,7 @@ namespace Catch { } std::string AssertionResult::getExpandedExpression() const { - return m_resultData.reconstructedExpression; + return m_resultData.reconstructExpression(); } std::string AssertionResult::getMessage() const { @@ -7400,15 +8239,25 @@ namespace Catch { return m_info.macroName; } + void AssertionResult::discardDecomposedExpression() const { + m_resultData.decomposedExpression = CATCH_NULL; + } + + void AssertionResult::expandDecomposedExpression() const { + m_resultData.reconstructExpression(); + } + } // end namespace Catch // #included from: catch_test_case_info.hpp #define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED +#include + namespace Catch { inline TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { - if( startsWith( tag, "." ) || + if( startsWith( tag, '.' ) || tag == "hide" || tag == "!hide" ) return TestCaseInfo::IsHidden; @@ -7418,25 +8267,23 @@ namespace Catch { return TestCaseInfo::ShouldFail; else if( tag == "!mayfail" ) return TestCaseInfo::MayFail; + else if( tag == "!nonportable" ) + return TestCaseInfo::NonPortable; else return TestCaseInfo::None; } inline bool isReservedTag( std::string const& tag ) { - return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !isalnum( tag[0] ); + return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( tag[0] ); } inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { if( isReservedTag( tag ) ) { - { - Colour colourGuard( Colour::Red ); - Catch::cerr() - << "Tag name [" << tag << "] not allowed.\n" - << "Tag names starting with non alpha-numeric characters are reserved\n"; - } - { - Colour colourGuard( Colour::FileName ); - Catch::cerr() << _lineInfo << std::endl; - } - exit(1); + std::ostringstream ss; + ss << Colour(Colour::Red) + << "Tag name [" << tag << "] not allowed.\n" + << "Tag names starting with non alpha-numeric characters are reserved\n" + << Colour(Colour::FileName) + << _lineInfo << '\n'; + throw std::runtime_error(ss.str()); } } @@ -7492,7 +8339,7 @@ namespace Catch { std::ostringstream oss; for( std::set::const_iterator it = tags.begin(), itEnd = tags.end(); it != itEnd; ++it ) { - oss << "[" << *it << "]"; + oss << '[' << *it << ']'; std::string lcaseTag = toLower( *it ); testCaseInfo.properties = static_cast( testCaseInfo.properties | parseSpecialTag( lcaseTag ) ); testCaseInfo.lcaseTags.insert( lcaseTag ); @@ -7598,7 +8445,7 @@ namespace Catch { ( unsigned int _majorVersion, unsigned int _minorVersion, unsigned int _patchNumber, - std::string const& _branchName, + char const * const _branchName, unsigned int _buildNumber ) : majorVersion( _majorVersion ), minorVersion( _minorVersion ), @@ -7608,18 +8455,21 @@ namespace Catch { {} std::ostream& operator << ( std::ostream& os, Version const& version ) { - os << version.majorVersion << "." - << version.minorVersion << "." + os << version.majorVersion << '.' + << version.minorVersion << '.' << version.patchNumber; - - if( !version.branchName.empty() ) { - os << "-" << version.branchName - << "." << version.buildNumber; + // branchName is never null -> 0th char is \0 if it is empty + if (version.branchName[0]) { + os << '-' << version.branchName + << '.' << version.buildNumber; } return os; } - Version libraryVersion( 1, 6, 0, "", 0 ); + inline Version libraryVersion() { + static Version version( 1, 12, 2, "", 0 ); + return version; + } } @@ -7652,9 +8502,18 @@ namespace Catch { : m_info( other.m_info ) {} +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4996) // std::uncaught_exception is deprecated in C++17 +#endif ScopedMessage::~ScopedMessage() { - getResultCapture().popScopedMessage( m_info ); + if ( !std::uncaught_exception() ){ + getResultCapture().popScopedMessage(m_info); + } } +#if defined(_MSC_VER) +#pragma warning(pop) +#endif } // end namespace Catch @@ -7790,30 +8649,32 @@ namespace Catch #endif #ifdef CATCH_PLATFORM_WINDOWS -#include + #else + #include + #endif namespace Catch { namespace { #ifdef CATCH_PLATFORM_WINDOWS - uint64_t getCurrentTicks() { - static uint64_t hz=0, hzo=0; + UInt64 getCurrentTicks() { + static UInt64 hz=0, hzo=0; if (!hz) { QueryPerformanceFrequency( reinterpret_cast( &hz ) ); QueryPerformanceCounter( reinterpret_cast( &hzo ) ); } - uint64_t t; + UInt64 t; QueryPerformanceCounter( reinterpret_cast( &t ) ); return ((t-hzo)*1000000)/hz; } #else - uint64_t getCurrentTicks() { + UInt64 getCurrentTicks() { timeval t; gettimeofday(&t,CATCH_NULL); - return static_cast( t.tv_sec ) * 1000000ull + static_cast( t.tv_usec ); + return static_cast( t.tv_sec ) * 1000000ull + static_cast( t.tv_usec ); } #endif } @@ -7839,19 +8700,28 @@ namespace Catch { // #included from: catch_common.hpp #define TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED +#include +#include + namespace Catch { bool startsWith( std::string const& s, std::string const& prefix ) { - return s.size() >= prefix.size() && s.substr( 0, prefix.size() ) == prefix; + return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin()); + } + bool startsWith( std::string const& s, char prefix ) { + return !s.empty() && s[0] == prefix; } bool endsWith( std::string const& s, std::string const& suffix ) { - return s.size() >= suffix.size() && s.substr( s.size()-suffix.size(), suffix.size() ) == suffix; + return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin()); + } + bool endsWith( std::string const& s, char suffix ) { + return !s.empty() && s[s.size()-1] == suffix; } bool contains( std::string const& s, std::string const& infix ) { return s.find( infix ) != std::string::npos; } char toLowerCh(char c) { - return static_cast( ::tolower( c ) ); + return static_cast( std::tolower( c ) ); } void toLowerInPlace( std::string& s ) { std::transform( s.begin(), s.end(), s.begin(), toLowerCh ); @@ -7866,7 +8736,7 @@ namespace Catch { std::string::size_type start = str.find_first_not_of( whitespaceChars ); std::string::size_type end = str.find_last_not_of( whitespaceChars ); - return start != std::string::npos ? str.substr( start, 1+end-start ) : ""; + return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string(); } bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) { @@ -7889,29 +8759,25 @@ namespace Catch { {} std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) { - os << pluraliser.m_count << " " << pluraliser.m_label; + os << pluraliser.m_count << ' ' << pluraliser.m_label; if( pluraliser.m_count != 1 ) - os << "s"; + os << 's'; return os; } - SourceLineInfo::SourceLineInfo() : line( 0 ){} + SourceLineInfo::SourceLineInfo() : file(""), line( 0 ){} SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line ) : file( _file ), line( _line ) {} - SourceLineInfo::SourceLineInfo( SourceLineInfo const& other ) - : file( other.file ), - line( other.line ) - {} bool SourceLineInfo::empty() const { - return file.empty(); + return file[0] == '\0'; } bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const { - return line == other.line && file == other.file; + return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0); } bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const { - return line < other.line || ( line == other.line && file < other.file ); + return line < other.line || ( line == other.line && (std::strcmp(file, other.file) < 0)); } void seedRng( IConfig const& config ) { @@ -7924,16 +8790,16 @@ namespace Catch { std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { #ifndef __GNUG__ - os << info.file << "(" << info.line << ")"; + os << info.file << '(' << info.line << ')'; #else - os << info.file << ":" << info.line; + os << info.file << ':' << info.line; #endif return os; } void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ) { std::ostringstream oss; - oss << locationInfo << ": Internal Catch error: '" << message << "'"; + oss << locationInfo << ": Internal Catch error: '" << message << '\''; if( alwaysTrue() ) throw std::logic_error( oss.str() ); } @@ -7960,6 +8826,10 @@ namespace Catch { m_timer.start(); } +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4996) // std::uncaught_exception is deprecated in C++17 +#endif Section::~Section() { if( m_sectionIncluded ) { SectionEndInfo endInfo( m_info, m_assertions, m_timer.getElapsedSeconds() ); @@ -7969,6 +8839,9 @@ namespace Catch { getResultCapture().sectionEnded( endInfo ); } } +#if defined(_MSC_VER) +#pragma warning(pop) +#endif // This indicates whether the section should be executed or not Section::operator bool() const { @@ -7980,8 +8853,6 @@ namespace Catch { // #included from: catch_debugger.hpp #define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED -#include - #ifdef CATCH_PLATFORM_MAC #include @@ -8030,6 +8901,36 @@ namespace Catch { } } // namespace Catch +#elif defined(CATCH_PLATFORM_LINUX) + #include + #include + + namespace Catch{ + // The standard POSIX way of detecting a debugger is to attempt to + // ptrace() the process, but this needs to be done from a child and not + // this process itself to still allow attaching to this process later + // if wanted, so is rather heavy. Under Linux we have the PID of the + // "debugger" (which doesn't need to be gdb, of course, it could also + // be strace, for example) in /proc/$PID/status, so just get it from + // there instead. + bool isDebuggerActive(){ + // Libstdc++ has a bug, where std::ifstream sets errno to 0 + // This way our users can properly assert over errno values + ErrnoGuard guard; + std::ifstream in("/proc/self/status"); + for( std::string line; std::getline(in, line); ) { + static const int PREFIX_LEN = 11; + if( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) { + // We're traced if the PID is not 0 and no other PID starts + // with 0 digit, so it's enough to check for just a single + // character. + return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; + } + } + + return false; + } + } // namespace Catch #elif defined(_MSC_VER) extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); namespace Catch { @@ -8051,7 +8952,7 @@ namespace Catch { #endif // Platform #ifdef CATCH_PLATFORM_WINDOWS - extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA( const char* ); + namespace Catch { void writeToDebugConsole( std::string const& text ) { ::OutputDebugStringA( text.c_str() ); @@ -8127,7 +9028,7 @@ std::string toString( std::string const& value ) { } } } - return "\"" + s + "\""; + return '"' + s + '"'; } std::string toString( std::wstring const& value ) { @@ -8148,19 +9049,19 @@ std::string toString( char* const value ) { std::string toString( const wchar_t* const value ) { - return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" ); + return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" ); } std::string toString( wchar_t* const value ) { - return Catch::toString( static_cast( value ) ); + return Catch::toString( static_cast( value ) ); } std::string toString( int value ) { std::ostringstream oss; oss << value; if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ")"; + oss << " (0x" << std::hex << value << ')'; return oss.str(); } @@ -8168,7 +9069,7 @@ std::string toString( unsigned long value ) { std::ostringstream oss; oss << value; if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ")"; + oss << " (0x" << std::hex << value << ')'; return oss.str(); } @@ -8196,7 +9097,7 @@ std::string toString( const double value ) { return fpToString( value, 10 ); } std::string toString( const float value ) { - return fpToString( value, 5 ) + "f"; + return fpToString( value, 5 ) + 'f'; } std::string toString( bool value ) { @@ -8204,9 +9105,19 @@ std::string toString( bool value ) { } std::string toString( char value ) { - return value < ' ' - ? toString( static_cast( value ) ) - : Detail::makeString( value ); + if ( value == '\r' ) + return "'\\r'"; + if ( value == '\f' ) + return "'\\f'"; + if ( value == '\n' ) + return "'\\n'"; + if ( value == '\t' ) + return "'\\t'"; + if ( '\0' <= value && value < ' ' ) + return toString( static_cast( value ) ); + char chstr[] = "' '"; + chstr[1] = value; + return chstr; } std::string toString( signed char value ) { @@ -8222,14 +9133,14 @@ std::string toString( long long value ) { std::ostringstream oss; oss << value; if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ")"; + oss << " (0x" << std::hex << value << ')'; return oss.str(); } std::string toString( unsigned long long value ) { std::ostringstream oss; oss << value; if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ")"; + oss << " (0x" << std::hex << value << ')'; return oss.str(); } #endif @@ -8246,7 +9157,7 @@ std::string toString( std::nullptr_t ) { return "nil"; return "@" + toString([nsstring UTF8String]); } - std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ) { + std::string toString( NSString * CATCH_ARC_STRONG & nsstring ) { if( !nsstring ) return "nil"; return "@" + toString([nsstring UTF8String]); @@ -8261,23 +9172,32 @@ std::string toString( std::nullptr_t ) { // #included from: catch_result_builder.hpp #define TWOBLUECUBES_CATCH_RESULT_BUILDER_HPP_INCLUDED +#include + namespace Catch { - std::string capturedExpressionWithSecondArgument( std::string const& capturedExpression, std::string const& secondArg ) { - return secondArg.empty() || secondArg == "\"\"" - ? capturedExpression - : capturedExpression + ", " + secondArg; - } ResultBuilder::ResultBuilder( char const* macroName, SourceLineInfo const& lineInfo, char const* capturedExpression, ResultDisposition::Flags resultDisposition, char const* secondArg ) - : m_assertionInfo( macroName, lineInfo, capturedExpressionWithSecondArgument( capturedExpression, secondArg ), resultDisposition ), + : m_assertionInfo( macroName, lineInfo, capturedExpression, resultDisposition, secondArg ), m_shouldDebugBreak( false ), - m_shouldThrow( false ) + m_shouldThrow( false ), + m_guardException( false ), + m_usedStream( false ) {} + ResultBuilder::~ResultBuilder() { +#if defined(CATCH_CONFIG_FAST_COMPILE) + if ( m_guardException ) { + stream().oss << "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"; + captureResult( ResultWas::ThrewException ); + getCurrentContext().getResultCapture()->exceptionEarlyReported(); + } +#endif + } + ResultBuilder& ResultBuilder::setResultType( ResultWas::OfType result ) { m_data.resultType = result; return *this; @@ -8286,27 +9206,27 @@ namespace Catch { m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed; return *this; } - ResultBuilder& ResultBuilder::setLhs( std::string const& lhs ) { - m_exprComponents.lhs = lhs; - return *this; - } - ResultBuilder& ResultBuilder::setRhs( std::string const& rhs ) { - m_exprComponents.rhs = rhs; - return *this; - } - ResultBuilder& ResultBuilder::setOp( std::string const& op ) { - m_exprComponents.op = op; - return *this; - } - void ResultBuilder::endExpression() { - m_exprComponents.testFalse = isFalseTest( m_assertionInfo.resultDisposition ); - captureExpression(); + void ResultBuilder::endExpression( DecomposedExpression const& expr ) { + // Flip bool results if FalseTest flag is set + if( isFalseTest( m_assertionInfo.resultDisposition ) ) { + m_data.negate( expr.isBinaryExpression() ); + } + + getResultCapture().assertionRun(); + + if(getCurrentContext().getConfig()->includeSuccessfulResults() || m_data.resultType != ResultWas::Ok) + { + AssertionResult result = build( expr ); + handleResult( result ); + } + else + getResultCapture().assertionPassed(); } void ResultBuilder::useActiveException( ResultDisposition::Flags resultDisposition ) { m_assertionInfo.resultDisposition = resultDisposition; - m_stream.oss << Catch::translateActiveException(); + stream().oss << Catch::translateActiveException(); captureResult( ResultWas::ThrewException ); } @@ -8314,19 +9234,20 @@ namespace Catch { setResultType( resultType ); captureExpression(); } + void ResultBuilder::captureExpectedException( std::string const& expectedMessage ) { if( expectedMessage.empty() ) - captureExpectedException( Matchers::Impl::Generic::AllOf() ); + captureExpectedException( Matchers::Impl::MatchAllOf() ); else captureExpectedException( Matchers::Equals( expectedMessage ) ); } - void ResultBuilder::captureExpectedException( Matchers::Impl::Matcher const& matcher ) { + void ResultBuilder::captureExpectedException( Matchers::Impl::MatcherBase const& matcher ) { - assert( m_exprComponents.testFalse == false ); + assert( !isFalseTest( m_assertionInfo.resultDisposition ) ); AssertionResultData data = m_data; data.resultType = ResultWas::Ok; - data.reconstructedExpression = m_assertionInfo.capturedExpression; + data.reconstructedExpression = capturedExpressionWithSecondArgument(m_assertionInfo.capturedExpression, m_assertionInfo.secondArg); std::string actualMessage = Catch::translateActiveException(); if( !matcher.match( actualMessage ) ) { @@ -8341,6 +9262,7 @@ namespace Catch { AssertionResult result = build(); handleResult( result ); } + void ResultBuilder::handleResult( AssertionResult const& result ) { getResultCapture().assertionEnded( result ); @@ -8352,7 +9274,17 @@ namespace Catch { m_shouldThrow = true; } } + void ResultBuilder::react() { +#if defined(CATCH_CONFIG_FAST_COMPILE) + if (m_shouldDebugBreak) { + /////////////////////////////////////////////////////////////////// + // To inspect the state during test, you need to go one level up the callstack + // To go back to the test and change execution, jump over the throw statement + /////////////////////////////////////////////////////////////////// + CATCH_BREAK_INTO_DEBUGGER(); + } +#endif if( m_shouldThrow ) throw Catch::TestFailureException(); } @@ -8362,43 +9294,35 @@ namespace Catch { AssertionResult ResultBuilder::build() const { - assert( m_data.resultType != ResultWas::Unknown ); + return build( *this ); + } + // CAVEAT: The returned AssertionResult stores a pointer to the argument expr, + // a temporary DecomposedExpression, which in turn holds references to + // operands, possibly temporary as well. + // It should immediately be passed to handleResult; if the expression + // needs to be reported, its string expansion must be composed before + // the temporaries are destroyed. + AssertionResult ResultBuilder::build( DecomposedExpression const& expr ) const + { + assert( m_data.resultType != ResultWas::Unknown ); AssertionResultData data = m_data; - // Flip bool results if testFalse is set - if( m_exprComponents.testFalse ) { - if( data.resultType == ResultWas::Ok ) - data.resultType = ResultWas::ExpressionFailed; - else if( data.resultType == ResultWas::ExpressionFailed ) - data.resultType = ResultWas::Ok; - } - - data.message = m_stream.oss.str(); - data.reconstructedExpression = reconstructExpression(); - if( m_exprComponents.testFalse ) { - if( m_exprComponents.op == "" ) - data.reconstructedExpression = "!" + data.reconstructedExpression; - else - data.reconstructedExpression = "!(" + data.reconstructedExpression + ")"; - } + if(m_usedStream) + data.message = m_stream().oss.str(); + data.decomposedExpression = &expr; // for lazy reconstruction return AssertionResult( m_assertionInfo, data ); } - std::string ResultBuilder::reconstructExpression() const { - if( m_exprComponents.op == "" ) - return m_exprComponents.lhs.empty() ? m_assertionInfo.capturedExpression : m_exprComponents.lhs; - else if( m_exprComponents.op == "matches" ) - return m_exprComponents.lhs + " " + m_exprComponents.rhs; - else if( m_exprComponents.op != "!" ) { - if( m_exprComponents.lhs.size() + m_exprComponents.rhs.size() < 40 && - m_exprComponents.lhs.find("\n") == std::string::npos && - m_exprComponents.rhs.find("\n") == std::string::npos ) - return m_exprComponents.lhs + " " + m_exprComponents.op + " " + m_exprComponents.rhs; - else - return m_exprComponents.lhs + "\n" + m_exprComponents.op + "\n" + m_exprComponents.rhs; - } - else - return "{can't expand - use " + m_assertionInfo.macroName + "_FALSE( " + m_assertionInfo.capturedExpression.substr(1) + " ) instead of " + m_assertionInfo.macroName + "( " + m_assertionInfo.capturedExpression + " ) for better diagnostics}"; + + void ResultBuilder::reconstructExpression( std::string& dest ) const { + dest = capturedExpressionWithSecondArgument(m_assertionInfo.capturedExpression, m_assertionInfo.secondArg); + } + + void ResultBuilder::setExceptionGuard() { + m_guardException = true; + } + void ResultBuilder::unsetExceptionGuard() { + m_guardException = false; } } // end namespace Catch @@ -8406,30 +9330,6 @@ namespace Catch { // #included from: catch_tag_alias_registry.hpp #define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED -// #included from: catch_tag_alias_registry.h -#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_H_INCLUDED - -#include - -namespace Catch { - - class TagAliasRegistry : public ITagAliasRegistry { - public: - virtual ~TagAliasRegistry(); - virtual Option find( std::string const& alias ) const; - virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const; - void add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); - static TagAliasRegistry& get(); - - private: - std::map m_registry; - }; - -} // end namespace Catch - -#include -#include - namespace Catch { TagAliasRegistry::~TagAliasRegistry() {} @@ -8457,44 +9357,120 @@ namespace Catch { return expandedTestSpec; } - void TagAliasRegistry::add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) { + void TagAliasRegistry::add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) { - if( !startsWith( alias, "[@" ) || !endsWith( alias, "]" ) ) { + if( !startsWith( alias, "[@" ) || !endsWith( alias, ']' ) ) { std::ostringstream oss; - oss << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" << lineInfo; + oss << Colour( Colour::Red ) + << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" + << Colour( Colour::FileName ) + << lineInfo << '\n'; throw std::domain_error( oss.str().c_str() ); } if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) { std::ostringstream oss; - oss << "error: tag alias, \"" << alias << "\" already registered.\n" - << "\tFirst seen at " << find(alias)->lineInfo << "\n" - << "\tRedefined at " << lineInfo; + oss << Colour( Colour::Red ) + << "error: tag alias, \"" << alias << "\" already registered.\n" + << "\tFirst seen at " + << Colour( Colour::Red ) << find(alias)->lineInfo << '\n' + << Colour( Colour::Red ) << "\tRedefined at " + << Colour( Colour::FileName) << lineInfo << '\n'; throw std::domain_error( oss.str().c_str() ); } } - TagAliasRegistry& TagAliasRegistry::get() { - static TagAliasRegistry instance; - return instance; + ITagAliasRegistry::~ITagAliasRegistry() {} + ITagAliasRegistry const& ITagAliasRegistry::get() { + return getRegistryHub().getTagAliasRegistry(); } - ITagAliasRegistry::~ITagAliasRegistry() {} - ITagAliasRegistry const& ITagAliasRegistry::get() { return TagAliasRegistry::get(); } - RegistrarForTagAliases::RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) { - try { - TagAliasRegistry::get().add( alias, tag, lineInfo ); - } - catch( std::exception& ex ) { - Colour colourGuard( Colour::Red ); - Catch::cerr() << ex.what() << std::endl; - exit(1); - } + getMutableRegistryHub().registerTagAlias( alias, tag, lineInfo ); } } // end namespace Catch +// #included from: catch_matchers_string.hpp + +namespace Catch { +namespace Matchers { + + namespace StdString { + + CasedString::CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ) + : m_caseSensitivity( caseSensitivity ), + m_str( adjustString( str ) ) + {} + std::string CasedString::adjustString( std::string const& str ) const { + return m_caseSensitivity == CaseSensitive::No + ? toLower( str ) + : str; + } + std::string CasedString::caseSensitivitySuffix() const { + return m_caseSensitivity == CaseSensitive::No + ? " (case insensitive)" + : std::string(); + } + + StringMatcherBase::StringMatcherBase( std::string const& operation, CasedString const& comparator ) + : m_comparator( comparator ), + m_operation( operation ) { + } + + std::string StringMatcherBase::describe() const { + std::string description; + description.reserve(5 + m_operation.size() + m_comparator.m_str.size() + + m_comparator.caseSensitivitySuffix().size()); + description += m_operation; + description += ": \""; + description += m_comparator.m_str; + description += "\""; + description += m_comparator.caseSensitivitySuffix(); + return description; + } + + EqualsMatcher::EqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals", comparator ) {} + + bool EqualsMatcher::match( std::string const& source ) const { + return m_comparator.adjustString( source ) == m_comparator.m_str; + } + + ContainsMatcher::ContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains", comparator ) {} + + bool ContainsMatcher::match( std::string const& source ) const { + return contains( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with", comparator ) {} + + bool StartsWithMatcher::match( std::string const& source ) const { + return startsWith( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with", comparator ) {} + + bool EndsWithMatcher::match( std::string const& source ) const { + return endsWith( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + } // namespace StdString + + StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::EqualsMatcher( StdString::CasedString( str, caseSensitivity) ); + } + StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::ContainsMatcher( StdString::CasedString( str, caseSensitivity) ); + } + StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::EndsWithMatcher( StdString::CasedString( str, caseSensitivity) ); + } + StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::StartsWithMatcher( StdString::CasedString( str, caseSensitivity) ); + } + +} // namespace Matchers +} // namespace Catch // #included from: ../reporters/catch_reporter_multi.hpp #define TWOBLUECUBES_CATCH_REPORTER_MULTI_HPP_INCLUDED @@ -8638,9 +9614,34 @@ Ptr addReporter( Ptr const& existingRepo #define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED #include +#include +#include +#include namespace Catch { + namespace { + // Because formatting using c++ streams is stateful, drop down to C is required + // Alternatively we could use stringstream, but its performance is... not good. + std::string getFormattedDuration( double duration ) { + // Max exponent + 1 is required to represent the whole part + // + 1 for decimal point + // + 3 for the 3 decimal places + // + 1 for null terminator + const size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1; + char buffer[maxDoubleSize]; + + // Save previous errno, to prevent sprintf from overwriting it + ErrnoGuard guard; +#ifdef _MSC_VER + sprintf_s(buffer, "%.3f", duration); +#else + sprintf(buffer, "%.3f", duration); +#endif + return std::string(buffer); + } + } + struct StreamingReporterBase : SharedImpl { StreamingReporterBase( ReporterConfig const& _config ) @@ -8735,12 +9736,13 @@ namespace Catch { struct BySectionInfo { BySectionInfo( SectionInfo const& other ) : m_other( other ) {} - BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} + BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} bool operator() ( Ptr const& node ) const { - return node->stats.sectionInfo.lineInfo == m_other.lineInfo; + return ((node->stats.sectionInfo.name == m_other.name) && + (node->stats.sectionInfo.lineInfo == m_other.lineInfo)); } private: - void operator=( BySectionInfo const& ); + void operator=( BySectionInfo const& ); SectionInfo const& m_other; }; @@ -8796,6 +9798,12 @@ namespace Catch { assert( !m_sectionStack.empty() ); SectionNode& sectionNode = *m_sectionStack.back(); sectionNode.assertions.push_back( assertionStats ); + // AssertionResult holds a pointer to a temporary DecomposedExpression, + // which getExpandedExpression() calls to build the expression string. + // Our section stack copy of the assertionResult will likely outlive the + // temporary, so it must be expanded or discarded now to avoid calling + // a destroyed object later. + prepareExpandedExpression( sectionNode.assertions.back().assertionResult ); return true; } virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { @@ -8830,6 +9838,13 @@ namespace Catch { virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {} + virtual void prepareExpandedExpression( AssertionResult& result ) const { + if( result.isOk() ) + result.discardDecomposedExpression(); + else + result.expandDecomposedExpression(); + } + Ptr m_config; std::ostream& stream; std::vector m_assertions; @@ -8850,7 +9865,7 @@ namespace Catch { char const* getLineOfChars() { static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; if( !*line ) { - memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); + std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0; } return line; @@ -8935,7 +9950,7 @@ namespace Catch { return new T( config ); } virtual std::string getDescription() const { - return ""; + return std::string(); } }; @@ -8953,9 +9968,13 @@ namespace Catch { #define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \ namespace{ Catch::ReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } +// Deprecated - use the form without INTERNAL_ #define INTERNAL_CATCH_REGISTER_LISTENER( listenerType ) \ namespace{ Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; } +#define CATCH_REGISTER_LISTENER( listenerType ) \ + namespace{ Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; } + // #included from: ../internal/catch_xmlwriter.hpp #define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED @@ -9004,8 +10023,11 @@ namespace Catch { default: // Escape control chars - based on contribution by @espenalb in PR #465 and // by @mrpi PR #588 - if ( ( c >= 0 && c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) - os << "&#x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << static_cast( c ) << ';'; + if ( ( c >= 0 && c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) { + // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 + os << "\\x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2) + << static_cast( c ); + } else os << c; } @@ -9059,20 +10081,17 @@ namespace Catch { XmlWriter() : m_tagIsOpen( false ), m_needsNewline( false ), - m_os( &Catch::cout() ) + m_os( Catch::cout() ) { - // We encode control characters, which requires - // XML 1.1 - // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 - *m_os << "\n"; + writeDeclaration(); } XmlWriter( std::ostream& os ) : m_tagIsOpen( false ), m_needsNewline( false ), - m_os( &os ) + m_os( os ) { - *m_os << "\n"; + writeDeclaration(); } ~XmlWriter() { @@ -9083,7 +10102,7 @@ namespace Catch { XmlWriter& startElement( std::string const& name ) { ensureTagClosed(); newlineIfNecessary(); - stream() << m_indent << "<" << name; + m_os << m_indent << '<' << name; m_tags.push_back( name ); m_indent += " "; m_tagIsOpen = true; @@ -9100,24 +10119,25 @@ namespace Catch { newlineIfNecessary(); m_indent = m_indent.substr( 0, m_indent.size()-2 ); if( m_tagIsOpen ) { - stream() << "/>\n"; + m_os << "/>"; m_tagIsOpen = false; } else { - stream() << m_indent << "\n"; + m_os << m_indent << ""; } + m_os << std::endl; m_tags.pop_back(); return *this; } XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) { if( !name.empty() && !attribute.empty() ) - stream() << " " << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << "\""; + m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"'; return *this; } XmlWriter& writeAttribute( std::string const& name, bool attribute ) { - stream() << " " << name << "=\"" << ( attribute ? "true" : "false" ) << "\""; + m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"'; return *this; } @@ -9133,8 +10153,8 @@ namespace Catch { bool tagWasOpen = m_tagIsOpen; ensureTagClosed(); if( tagWasOpen && indent ) - stream() << m_indent; - stream() << XmlEncode( text ); + m_os << m_indent; + m_os << XmlEncode( text ); m_needsNewline = true; } return *this; @@ -9142,39 +10162,39 @@ namespace Catch { XmlWriter& writeComment( std::string const& text ) { ensureTagClosed(); - stream() << m_indent << ""; + m_os << m_indent << ""; m_needsNewline = true; return *this; } + void writeStylesheetRef( std::string const& url ) { + m_os << "\n"; + } + XmlWriter& writeBlankLine() { ensureTagClosed(); - stream() << "\n"; + m_os << '\n'; return *this; } - void setStream( std::ostream& os ) { - m_os = &os; + void ensureTagClosed() { + if( m_tagIsOpen ) { + m_os << ">" << std::endl; + m_tagIsOpen = false; + } } private: XmlWriter( XmlWriter const& ); void operator=( XmlWriter const& ); - std::ostream& stream() { - return *m_os; - } - - void ensureTagClosed() { - if( m_tagIsOpen ) { - stream() << ">\n"; - m_tagIsOpen = false; - } + void writeDeclaration() { + m_os << "\n"; } void newlineIfNecessary() { if( m_needsNewline ) { - stream() << "\n"; + m_os << std::endl; m_needsNewline = false; } } @@ -9183,24 +10203,10 @@ namespace Catch { bool m_needsNewline; std::vector m_tags; std::string m_indent; - std::ostream* m_os; + std::ostream& m_os; }; } -// #included from: catch_reenable_warnings.h - -#define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED - -#ifdef __clang__ -# ifdef __ICC // icpc defines the __clang__ macro -# pragma warning(pop) -# else -# pragma clang diagnostic pop -# endif -#elif defined __GNUC__ -# pragma GCC diagnostic pop -#endif - namespace Catch { class XmlReporter : public StreamingReporterBase { @@ -9219,6 +10225,16 @@ namespace Catch { return "Reports test results as an XML document"; } + virtual std::string getStylesheetRef() const { + return std::string(); + } + + void writeSourceInfo( SourceLineInfo const& sourceInfo ) { + m_xml + .writeAttribute( "filename", sourceInfo.file ) + .writeAttribute( "line", sourceInfo.line ); + } + public: // StreamingReporterBase virtual void noMatchingTestCases( std::string const& s ) CATCH_OVERRIDE { @@ -9227,6 +10243,9 @@ namespace Catch { virtual void testRunStarting( TestRunInfo const& testInfo ) CATCH_OVERRIDE { StreamingReporterBase::testRunStarting( testInfo ); + std::string stylesheetRef = getStylesheetRef(); + if( !stylesheetRef.empty() ) + m_xml.writeStylesheetRef( stylesheetRef ); m_xml.startElement( "Catch" ); if( !m_config->name().empty() ) m_xml.writeAttribute( "name", m_config->name() ); @@ -9240,10 +10259,16 @@ namespace Catch { virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { StreamingReporterBase::testCaseStarting(testInfo); - m_xml.startElement( "TestCase" ).writeAttribute( "name", testInfo.name ); + m_xml.startElement( "TestCase" ) + .writeAttribute( "name", trim( testInfo.name ) ) + .writeAttribute( "description", testInfo.description ) + .writeAttribute( "tags", testInfo.tagsAsString ); + + writeSourceInfo( testInfo.lineInfo ); if ( m_config->showDurations() == ShowDurations::Always ) m_testCaseTimer.start(); + m_xml.ensureTagClosed(); } virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { @@ -9252,77 +10277,84 @@ namespace Catch { m_xml.startElement( "Section" ) .writeAttribute( "name", trim( sectionInfo.name ) ) .writeAttribute( "description", sectionInfo.description ); + writeSourceInfo( sectionInfo.lineInfo ); + m_xml.ensureTagClosed(); } } virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { } virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { - const AssertionResult& assertionResult = assertionStats.assertionResult; - // Print any info messages in tags. - if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) { + AssertionResult const& result = assertionStats.assertionResult; + + bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); + + if( includeResults || result.getResultType() == ResultWas::Warning ) { + // Print any info messages in tags. for( std::vector::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end(); - it != itEnd; - ++it ) { - if( it->type == ResultWas::Info ) { + it != itEnd; + ++it ) { + if( it->type == ResultWas::Info && includeResults ) { m_xml.scopedElement( "Info" ) - .writeText( it->message ); + .writeText( it->message ); } else if ( it->type == ResultWas::Warning ) { m_xml.scopedElement( "Warning" ) - .writeText( it->message ); + .writeText( it->message ); } } } // Drop out if result was successful but we're not printing them. - if( !m_config->includeSuccessfulResults() && isOk(assertionResult.getResultType()) ) + if( !includeResults && result.getResultType() != ResultWas::Warning ) return true; // Print the expression if there is one. - if( assertionResult.hasExpression() ) { + if( result.hasExpression() ) { m_xml.startElement( "Expression" ) - .writeAttribute( "success", assertionResult.succeeded() ) - .writeAttribute( "type", assertionResult.getTestMacroName() ) - .writeAttribute( "filename", assertionResult.getSourceInfo().file ) - .writeAttribute( "line", assertionResult.getSourceInfo().line ); + .writeAttribute( "success", result.succeeded() ) + .writeAttribute( "type", result.getTestMacroName() ); + + writeSourceInfo( result.getSourceInfo() ); m_xml.scopedElement( "Original" ) - .writeText( assertionResult.getExpression() ); + .writeText( result.getExpression() ); m_xml.scopedElement( "Expanded" ) - .writeText( assertionResult.getExpandedExpression() ); + .writeText( result.getExpandedExpression() ); } // And... Print a result applicable to each result type. - switch( assertionResult.getResultType() ) { + switch( result.getResultType() ) { case ResultWas::ThrewException: - m_xml.scopedElement( "Exception" ) - .writeAttribute( "filename", assertionResult.getSourceInfo().file ) - .writeAttribute( "line", assertionResult.getSourceInfo().line ) - .writeText( assertionResult.getMessage() ); + m_xml.startElement( "Exception" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); break; case ResultWas::FatalErrorCondition: - m_xml.scopedElement( "FatalErrorCondition" ) - .writeAttribute( "filename", assertionResult.getSourceInfo().file ) - .writeAttribute( "line", assertionResult.getSourceInfo().line ) - .writeText( assertionResult.getMessage() ); + m_xml.startElement( "FatalErrorCondition" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); break; case ResultWas::Info: m_xml.scopedElement( "Info" ) - .writeText( assertionResult.getMessage() ); + .writeText( result.getMessage() ); break; case ResultWas::Warning: // Warning will already have been written break; case ResultWas::ExplicitFailure: - m_xml.scopedElement( "Failure" ) - .writeText( assertionResult.getMessage() ); + m_xml.startElement( "Failure" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); break; default: break; } - if( assertionResult.hasExpression() ) + if( result.hasExpression() ) m_xml.endElement(); return true; @@ -9351,6 +10383,11 @@ namespace Catch { if ( m_config->showDurations() == ShowDurations::Always ) e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() ); + if( !testCaseStats.stdOut.empty() ) + m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), false ); + if( !testCaseStats.stdErr.empty() ) + m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), false ); + m_xml.endElement(); } @@ -9390,11 +10427,42 @@ namespace Catch { namespace Catch { + namespace { + std::string getCurrentTimestamp() { + // Beware, this is not reentrant because of backward compatibility issues + // Also, UTC only, again because of backward compatibility (%z is C++11) + time_t rawtime; + std::time(&rawtime); + const size_t timeStampSize = sizeof("2017-01-16T17:06:45Z"); + +#ifdef _MSC_VER + std::tm timeInfo = {}; + gmtime_s(&timeInfo, &rawtime); +#else + std::tm* timeInfo; + timeInfo = std::gmtime(&rawtime); +#endif + + char timeStamp[timeStampSize]; + const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; + +#ifdef _MSC_VER + std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); +#else + std::strftime(timeStamp, timeStampSize, fmt, timeInfo); +#endif + return std::string(timeStamp); + } + + } + class JunitReporter : public CumulativeReporterBase { public: JunitReporter( ReporterConfig const& _config ) : CumulativeReporterBase( _config ), - xml( _config.stream() ) + xml( _config.stream() ), + unexpectedExceptions( 0 ), + m_okToFail( false ) { m_reporterPrefs.shouldRedirectStdOut = true; } @@ -9420,8 +10488,11 @@ namespace Catch { CumulativeReporterBase::testGroupStarting( groupInfo ); } + virtual void testCaseStarting( TestCaseInfo const& testCaseInfo ) CATCH_OVERRIDE { + m_okToFail = testCaseInfo.okToFail(); + } virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { - if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException ) + if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail ) unexpectedExceptions++; return CumulativeReporterBase::assertionEnded( assertionStats ); } @@ -9454,7 +10525,7 @@ namespace Catch { xml.writeAttribute( "time", "" ); else xml.writeAttribute( "time", suiteTime ); - xml.writeAttribute( "timestamp", "tbd" ); // !TBD + xml.writeAttribute( "timestamp", getCurrentTimestamp() ); // Write test cases for( TestGroupNode::ChildNodes::const_iterator @@ -9489,7 +10560,7 @@ namespace Catch { SectionNode const& sectionNode ) { std::string name = trim( sectionNode.stats.sectionInfo.name ); if( !rootName.empty() ) - name = rootName + "/" + name; + name = rootName + '/' + name; if( !sectionNode.assertions.empty() || !sectionNode.stdOut.empty() || @@ -9567,14 +10638,14 @@ namespace Catch { std::ostringstream oss; if( !result.getMessage().empty() ) - oss << result.getMessage() << "\n"; + oss << result.getMessage() << '\n'; for( std::vector::const_iterator it = stats.infoMessages.begin(), itEnd = stats.infoMessages.end(); it != itEnd; ++it ) if( it->type == ResultWas::Info ) - oss << it->message << "\n"; + oss << it->message << '\n'; oss << "at " << result.getSourceInfo(); xml.writeText( oss.str(), false ); @@ -9586,6 +10657,7 @@ namespace Catch { std::ostringstream stdOutForSuite; std::ostringstream stdErrForSuite; unsigned int unexpectedExceptions; + bool m_okToFail; }; INTERNAL_CATCH_REGISTER_REPORTER( "junit", JunitReporter ) @@ -9595,6 +10667,10 @@ namespace Catch { // #included from: ../reporters/catch_reporter_console.hpp #define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED +#include +#include +#include + namespace Catch { struct ConsoleReporter : StreamingReporterBase { @@ -9609,7 +10685,7 @@ namespace Catch { } virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE { - stream << "No test cases matched '" << spec << "'" << std::endl; + stream << "No test cases matched '" << spec << '\'' << std::endl; } virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { @@ -9618,18 +10694,15 @@ namespace Catch { virtual bool assertionEnded( AssertionStats const& _assertionStats ) CATCH_OVERRIDE { AssertionResult const& result = _assertionStats.assertionResult; - bool printInfoMessages = true; + bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); - // Drop out if result was successful and we're not printing those - if( !m_config->includeSuccessfulResults() && result.isOk() ) { - if( result.getResultType() != ResultWas::Warning ) - return false; - printInfoMessages = false; - } + // Drop out if result was successful but we're not printing them. + if( !includeResults && result.getResultType() != ResultWas::Warning ) + return false; lazyPrint(); - AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); + AssertionPrinter printer( stream, _assertionStats, includeResults ); printer.print(); stream << std::endl; return true; @@ -9649,14 +10722,11 @@ namespace Catch { stream << "\nNo assertions in test case"; stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; } - if( m_headerPrinted ) { - if( m_config->showDurations() == ShowDurations::Always ) - stream << "Completed in " << _sectionStats.durationInSeconds << "s" << std::endl; - m_headerPrinted = false; + if( m_config->showDurations() == ShowDurations::Always ) { + stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; } - else { - if( m_config->showDurations() == ShowDurations::Always ) - stream << _sectionStats.sectionInfo.name << " completed in " << _sectionStats.durationInSeconds << "s" << std::endl; + if( m_headerPrinted ) { + m_headerPrinted = false; } StreamingReporterBase::sectionEnded( _sectionStats ); } @@ -9670,7 +10740,7 @@ namespace Catch { printSummaryDivider(); stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; printTotals( _testGroupStats.totals ); - stream << "\n" << std::endl; + stream << '\n' << std::endl; } StreamingReporterBase::testGroupEnded( _testGroupStats ); } @@ -9722,7 +10792,11 @@ namespace Catch { case ResultWas::ThrewException: colour = Colour::Error; passOrFail = "FAILED"; - messageLabel = "due to unexpected exception with message"; + messageLabel = "due to unexpected exception with "; + if (_stats.infoMessages.size() == 1) + messageLabel += "message"; + if (_stats.infoMessages.size() > 1) + messageLabel += "messages"; break; case ResultWas::FatalErrorCondition: colour = Colour::Error; @@ -9762,13 +10836,13 @@ namespace Catch { printSourceInfo(); if( stats.totals.assertions.total() > 0 ) { if( result.isOk() ) - stream << "\n"; + stream << '\n'; printResultType(); printOriginalExpression(); printReconstructedExpression(); } else { - stream << "\n"; + stream << '\n'; } printMessage(); } @@ -9785,25 +10859,25 @@ namespace Catch { Colour colourGuard( Colour::OriginalExpression ); stream << " "; stream << result.getExpressionInMacro(); - stream << "\n"; + stream << '\n'; } } void printReconstructedExpression() const { if( result.hasExpandedExpression() ) { stream << "with expansion:\n"; Colour colourGuard( Colour::ReconstructedExpression ); - stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << "\n"; + stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << '\n'; } } void printMessage() const { if( !messageLabel.empty() ) - stream << messageLabel << ":" << "\n"; + stream << messageLabel << ':' << '\n'; for( std::vector::const_iterator it = messages.begin(), itEnd = messages.end(); it != itEnd; ++it ) { // If this assertion is a warning ignore any INFO messages if( printInfoMessages || it->type != ResultWas::Info ) - stream << Text( it->message, TextAttributes().setIndent(2) ) << "\n"; + stream << Text( it->message, TextAttributes().setIndent(2) ) << '\n'; } } void printSourceInfo() const { @@ -9835,10 +10909,10 @@ namespace Catch { } } void lazyPrintRunInfo() { - stream << "\n" << getLineOfChars<'~'>() << "\n"; + stream << '\n' << getLineOfChars<'~'>() << '\n'; Colour colour( Colour::SecondaryText ); stream << currentTestRunInfo->name - << " is a Catch v" << libraryVersion << " host application.\n" + << " is a Catch v" << libraryVersion() << " host application.\n" << "Run with -? for options\n\n"; if( m_config->rngSeed() != 0 ) @@ -9866,22 +10940,22 @@ namespace Catch { printHeaderString( it->name, 2 ); } - SourceLineInfo lineInfo = m_sectionStack.front().lineInfo; + SourceLineInfo lineInfo = m_sectionStack.back().lineInfo; if( !lineInfo.empty() ){ - stream << getLineOfChars<'-'>() << "\n"; + stream << getLineOfChars<'-'>() << '\n'; Colour colourGuard( Colour::FileName ); - stream << lineInfo << "\n"; + stream << lineInfo << '\n'; } - stream << getLineOfChars<'.'>() << "\n" << std::endl; + stream << getLineOfChars<'.'>() << '\n' << std::endl; } void printClosedHeader( std::string const& _name ) { printOpenHeader( _name ); - stream << getLineOfChars<'.'>() << "\n"; + stream << getLineOfChars<'.'>() << '\n'; } void printOpenHeader( std::string const& _name ) { - stream << getLineOfChars<'-'>() << "\n"; + stream << getLineOfChars<'-'>() << '\n'; { Colour colourGuard( Colour::Headers ); printHeaderString( _name ); @@ -9898,7 +10972,7 @@ namespace Catch { i = 0; stream << Text( _string, TextAttributes() .setIndent( indent+i) - .setInitialIndent( indent ) ) << "\n"; + .setInitialIndent( indent ) ) << '\n'; } struct SummaryColumn { @@ -9913,9 +10987,9 @@ namespace Catch { std::string row = oss.str(); for( std::vector::iterator it = rows.begin(); it != rows.end(); ++it ) { while( it->size() < row.size() ) - *it = " " + *it; + *it = ' ' + *it; while( it->size() > row.size() ) - row = " " + row; + row = ' ' + row; } rows.push_back( row ); return *this; @@ -9935,8 +11009,8 @@ namespace Catch { stream << Colour( Colour::ResultSuccess ) << "All tests passed"; stream << " (" << pluralise( totals.assertions.passed, "assertion" ) << " in " - << pluralise( totals.testCases.passed, "test case" ) << ")" - << "\n"; + << pluralise( totals.testCases.passed, "test case" ) << ')' + << '\n'; } else { @@ -9971,10 +11045,10 @@ namespace Catch { else if( value != "0" ) { stream << Colour( Colour::LightGrey ) << " | "; stream << Colour( it->colour ) - << value << " " << it->label; + << value << ' ' << it->label; } } - stream << "\n"; + stream << '\n'; } static std::size_t makeRatio( std::size_t number, std::size_t total ) { @@ -10010,10 +11084,10 @@ namespace Catch { else { stream << Colour( Colour::Warning ) << std::string( CATCH_CONFIG_CONSOLE_WIDTH-1, '=' ); } - stream << "\n"; + stream << '\n'; } void printSummaryDivider() { - stream << getLineOfChars<'-'>() << "\n"; + stream << getLineOfChars<'-'>() << '\n'; } private: @@ -10048,11 +11122,10 @@ namespace Catch { } virtual void noMatchingTestCases( std::string const& spec ) { - stream << "No test cases matched '" << spec << "'" << std::endl; + stream << "No test cases matched '" << spec << '\'' << std::endl; } - virtual void assertionStarting( AssertionInfo const& ) { - } + virtual void assertionStarting( AssertionInfo const& ) {} virtual bool assertionEnded( AssertionStats const& _assertionStats ) { AssertionResult const& result = _assertionStats.assertionResult; @@ -10073,9 +11146,15 @@ namespace Catch { return true; } + virtual void sectionEnded(SectionStats const& _sectionStats) CATCH_OVERRIDE { + if (m_config->showDurations() == ShowDurations::Always) { + stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; + } + } + virtual void testRunEnded( TestRunStats const& _testRunStats ) { printTotals( _testRunStats.totals ); - stream << "\n" << std::endl; + stream << '\n' << std::endl; StreamingReporterBase::testRunEnded( _testRunStats ); } @@ -10175,26 +11254,26 @@ namespace Catch { void printSourceInfo() const { Colour colourGuard( Colour::FileName ); - stream << result.getSourceInfo() << ":"; + stream << result.getSourceInfo() << ':'; } - void printResultType( Colour::Code colour, std::string passOrFail ) const { + void printResultType( Colour::Code colour, std::string const& passOrFail ) const { if( !passOrFail.empty() ) { { Colour colourGuard( colour ); - stream << " " << passOrFail; + stream << ' ' << passOrFail; } - stream << ":"; + stream << ':'; } } - void printIssue( std::string issue ) const { - stream << " " << issue; + void printIssue( std::string const& issue ) const { + stream << ' ' << issue; } void printExpressionWas() { if( result.hasExpression() ) { - stream << ";"; + stream << ';'; { Colour colour( dimColour() ); stream << " expression was:"; @@ -10205,7 +11284,7 @@ namespace Catch { void printOriginalExpression() const { if( result.hasExpression() ) { - stream << " " << result.getExpression(); + stream << ' ' << result.getExpression(); } } @@ -10221,7 +11300,7 @@ namespace Catch { void printMessage() { if ( itMessage != messages.end() ) { - stream << " '" << itMessage->message << "'"; + stream << " '" << itMessage->message << '\''; ++itMessage; } } @@ -10236,13 +11315,13 @@ namespace Catch { { Colour colourGuard( colour ); - stream << " with " << pluralise( N, "message" ) << ":"; + stream << " with " << pluralise( N, "message" ) << ':'; } for(; itMessage != itEnd; ) { // If this assertion is a warning ignore any INFO messages if( printInfoMessages || itMessage->type != ResultWas::Info ) { - stream << " '" << itMessage->message << "'"; + stream << " '" << itMessage->message << '\''; if ( ++itMessage != itEnd ) { Colour colourGuard( dimColour() ); stream << " and"; @@ -10268,7 +11347,7 @@ namespace Catch { // - green: Passed [both/all] N tests cases with M assertions. std::string bothOrAll( std::size_t count ) const { - return count == 1 ? "" : count == 2 ? "both " : "all " ; + return count == 1 ? std::string() : count == 2 ? "both " : "all " ; } void printTotals( const Totals& totals ) const { @@ -10279,12 +11358,12 @@ namespace Catch { Colour colour( Colour::ResultError ); const std::string qualify_assertions_failed = totals.assertions.failed == totals.assertions.total() ? - bothOrAll( totals.assertions.failed ) : ""; + bothOrAll( totals.assertions.failed ) : std::string(); stream << "Failed " << bothOrAll( totals.testCases.failed ) << pluralise( totals.testCases.failed, "test case" ) << ", " "failed " << qualify_assertions_failed << - pluralise( totals.assertions.failed, "assertion" ) << "."; + pluralise( totals.assertions.failed, "assertion" ) << '.'; } else if( totals.assertions.total() == 0 ) { stream << @@ -10296,14 +11375,14 @@ namespace Catch { Colour colour( Colour::ResultError ); stream << "Failed " << pluralise( totals.testCases.failed, "test case" ) << ", " - "failed " << pluralise( totals.assertions.failed, "assertion" ) << "."; + "failed " << pluralise( totals.assertions.failed, "assertion" ) << '.'; } else { Colour colour( Colour::ResultSuccess ); stream << "Passed " << bothOrAll( totals.testCases.passed ) << pluralise( totals.testCases.passed, "test case" ) << - " with " << pluralise( totals.assertions.passed, "assertion" ) << "."; + " with " << pluralise( totals.assertions.passed, "assertion" ) << '.'; } } }; @@ -10359,11 +11438,7 @@ namespace Catch { TestSpec::NamePattern::~NamePattern() {} TestSpec::TagPattern::~TagPattern() {} TestSpec::ExcludedPattern::~ExcludedPattern() {} - - Matchers::Impl::StdString::Equals::~Equals() {} - Matchers::Impl::StdString::Contains::~Contains() {} - Matchers::Impl::StdString::StartsWith::~StartsWith() {} - Matchers::Impl::StdString::EndsWith::~EndsWith() {} + Matchers::Impl::MatcherUntypedBase::~MatcherUntypedBase() {} void Config::dummy() {} @@ -10387,9 +11462,16 @@ namespace Catch { #ifndef __OBJC__ +#if defined(WIN32) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN) +// Standard C/C++ Win32 Unicode wmain entry point +extern "C" int wmain (int argc, wchar_t * argv[], wchar_t * []) { +#else // Standard C/C++ main entry point int main (int argc, char * argv[]) { - return Catch::Session().run( argc, argv ); +#endif + + int result = Catch::Session().run( argc, argv ); + return ( result < 0xff ? result : 0xff ); } #else // __OBJC__ @@ -10407,7 +11489,7 @@ int main (int argc, char * const argv[]) { [pool drain]; #endif - return result; + return ( result < 0xff ? result : 0xff ); } #endif // __OBJC__ @@ -10423,33 +11505,43 @@ int main (int argc, char * const argv[]) { // If this config identifier is defined then all CATCH macros are prefixed with CATCH_ #ifdef CATCH_CONFIG_PREFIX_ALL -#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE" ) -#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "CATCH_REQUIRE_FALSE" ) +#if defined(CATCH_CONFIG_FAST_COMPILE) +#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, expr ) +#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr ) +#else +#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, expr ) +#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr ) +#endif -#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "", "CATCH_REQUIRE_THROWS" ) -#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS_AS" ) -#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, matcher, "CATCH_REQUIRE_THROWS_WITH" ) -#define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_NOTHROW" ) +#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", expr ) +#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) +#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) +#define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, expr ) -#define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK" ) -#define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CATCH_CHECK_FALSE" ) -#define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_IF" ) -#define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_ELSE" ) -#define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CATCH_CHECK_NOFAIL" ) +#define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, expr ) +#define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, expr ) -#define CATCH_CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "", "CATCH_CHECK_THROWS" ) -#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS_AS" ) -#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, matcher, "CATCH_CHECK_THROWS_WITH" ) -#define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_NOTHROW" ) +#define CATCH_CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", expr ) +#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, expr ) -#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THAT" ) -#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THAT" ) +#define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) -#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" ) -#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "CATCH_WARN", msg ) -#define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" ) -#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" ) -#define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" ) +#if defined(CATCH_CONFIG_FAST_COMPILE) +#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT_NO_TRY( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#else +#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#endif + +#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg ) +#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) +#define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg ) +#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CATCH_CAPTURE", #msg " := " << Catch::toString(msg) ) +#define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CATCH_CAPTURE", #msg " := " << Catch::toString(msg) ) #ifdef CATCH_CONFIG_VARIADIC_MACROS #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) @@ -10457,16 +11549,18 @@ int main (int argc, char * const argv[]) { #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) #define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) - #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", __VA_ARGS__ ) - #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", __VA_ARGS__ ) + #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) + #define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) + #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #else #define CATCH_TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) #define CATCH_TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) #define CATCH_METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) #define CATCH_REGISTER_TEST_CASE( function, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( function, name, description ) #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) - #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", msg ) - #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", msg ) + #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, msg ) + #define CATCH_FAIL_CHECK( msg ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, msg ) + #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, msg ) #endif #define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) @@ -10492,50 +11586,63 @@ int main (int argc, char * const argv[]) { // If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required #else -#define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" ) -#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "REQUIRE_FALSE" ) +#if defined(CATCH_CONFIG_FAST_COMPILE) +#define REQUIRE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "REQUIRE", Catch::ResultDisposition::Normal, expr ) +#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr ) -#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "", "REQUIRE_THROWS" ) -#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "REQUIRE_THROWS_AS" ) -#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, matcher, "REQUIRE_THROWS_WITH" ) -#define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "REQUIRE_NOTHROW" ) +#else +#define REQUIRE( expr ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, expr ) +#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr ) +#endif -#define CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK" ) -#define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CHECK_FALSE" ) -#define CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_IF" ) -#define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_ELSE" ) -#define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CHECK_NOFAIL" ) +#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", expr ) +#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) +#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) +#define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, expr ) -#define CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "", "CHECK_THROWS" ) -#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS_AS" ) -#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, matcher, "CHECK_THROWS_WITH" ) -#define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_NOTHROW" ) +#define CHECK( expr ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, expr ) +#define CHECKED_IF( expr ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, expr ) -#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THAT" ) -#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "REQUIRE_THAT" ) +#define CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", expr ) +#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, expr ) -#define INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" ) -#define WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "WARN", msg ) -#define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" ) -#define CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" ) -#define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" ) +#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) + +#if defined(CATCH_CONFIG_FAST_COMPILE) +#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT_NO_TRY( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#else +#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#endif + +#define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) +#define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) +#define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) +#define CAPTURE( msg ) INTERNAL_CATCH_INFO( "CAPTURE", #msg " := " << Catch::toString(msg) ) +#define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CAPTURE", #msg " := " << Catch::toString(msg) ) #ifdef CATCH_CONFIG_VARIADIC_MACROS - #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) - #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) - #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) - #define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) - #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) - #define FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", __VA_ARGS__ ) - #define SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", __VA_ARGS__ ) +#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) +#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) +#define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) +#define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) +#define FAIL( ... ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define SUCCEED( ... ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #else - #define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) +#define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) #define TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) #define METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) #define REGISTER_TEST_CASE( method, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( method, name, description ) #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) - #define FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", msg ) - #define SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", msg ) + #define FAIL( msg ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, msg ) + #define FAIL_CHECK( msg ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, msg ) + #define SUCCEED( msg ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, msg ) #endif #define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) @@ -10564,5 +11671,19 @@ int main (int argc, char * const argv[]) { using Catch::Detail::Approx; +// #included from: internal/catch_reenable_warnings.h + +#define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED + +#ifdef __clang__ +# ifdef __ICC // icpc defines the __clang__ macro +# pragma warning(pop) +# else +# pragma clang diagnostic pop +# endif +#elif defined __GNUC__ +# pragma GCC diagnostic pop +#endif + #endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED diff --git a/appveyor.yml b/appveyor.yml index 2d943ab5..263d1e52 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -16,6 +16,9 @@ configuration: - Debug - Release +platform: + - Win64 + install: # Download Premake - ps: Start-FileDownload 'https://github.com/premake/premake-core/releases/download/v5.0.0-alpha12/premake-5.0.0-alpha12-windows.zip' 'premake.zip' From 658b32784fbdf8f9c5bf984d52c79a0d3d699220 Mon Sep 17 00:00:00 2001 From: elsid Date: Wed, 31 Oct 2018 22:38:03 +0300 Subject: [PATCH 32/99] Add dependencies between libraries to allow linker to resolve symbols when build dynamic libraries --- DebugUtils/CMakeLists.txt | 5 +++++ DetourCrowd/CMakeLists.txt | 4 ++++ DetourTileCache/CMakeLists.txt | 4 ++++ 3 files changed, 13 insertions(+) diff --git a/DebugUtils/CMakeLists.txt b/DebugUtils/CMakeLists.txt index 36ebc0da..52c1d2bb 100644 --- a/DebugUtils/CMakeLists.txt +++ b/DebugUtils/CMakeLists.txt @@ -11,6 +11,11 @@ else() add_library(DebugUtils SHARED ${SOURCES}) endif() +target_link_libraries(DebugUtils + Recast + Detour +) + set_target_properties(DebugUtils PROPERTIES SOVERSION ${SOVERSION} VERSION ${VERSION} diff --git a/DetourCrowd/CMakeLists.txt b/DetourCrowd/CMakeLists.txt index 19314605..7eab20e7 100644 --- a/DetourCrowd/CMakeLists.txt +++ b/DetourCrowd/CMakeLists.txt @@ -9,6 +9,10 @@ else () add_library(DetourCrowd SHARED ${SOURCES}) endif () +target_link_libraries(DetourCrowd + Detour +) + set_target_properties(DetourCrowd PROPERTIES SOVERSION ${SOVERSION} VERSION ${VERSION} diff --git a/DetourTileCache/CMakeLists.txt b/DetourTileCache/CMakeLists.txt index a8d68afa..1760cf7a 100644 --- a/DetourTileCache/CMakeLists.txt +++ b/DetourTileCache/CMakeLists.txt @@ -9,6 +9,10 @@ else () add_library(DetourTileCache SHARED ${SOURCES}) endif () +target_link_libraries(DetourTileCache + Detour +) + set_target_properties(DetourTileCache PROPERTIES SOVERSION ${SOVERSION} VERSION ${VERSION} From 2b31aa9d7932a52d7ec66dadad674d272611c1b7 Mon Sep 17 00:00:00 2001 From: elsid Date: Thu, 1 Nov 2018 00:01:10 +0300 Subject: [PATCH 33/99] Bind include directories to library build target --- DebugUtils/CMakeLists.txt | 12 +++++++----- Detour/CMakeLists.txt | 8 ++++++-- DetourCrowd/CMakeLists.txt | 9 ++++++--- DetourTileCache/CMakeLists.txt | 9 ++++++--- Recast/CMakeLists.txt | 8 ++++++-- 5 files changed, 31 insertions(+), 15 deletions(-) diff --git a/DebugUtils/CMakeLists.txt b/DebugUtils/CMakeLists.txt index 52c1d2bb..b094e4a3 100644 --- a/DebugUtils/CMakeLists.txt +++ b/DebugUtils/CMakeLists.txt @@ -1,19 +1,21 @@ file(GLOB SOURCES Source/*.cpp) -include_directories(../Recast/Include) -include_directories(../Detour/Include) -include_directories(../DetourTileCache/Include) -include_directories(Include) - if (RECASTNAVIGATION_STATIC) add_library(DebugUtils STATIC ${SOURCES}) else() add_library(DebugUtils SHARED ${SOURCES}) endif() +set(DebugUtils_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Include") + +target_include_directories(DebugUtils PUBLIC + "$" +) + target_link_libraries(DebugUtils Recast Detour + DetourTileCache ) set_target_properties(DebugUtils PROPERTIES diff --git a/Detour/CMakeLists.txt b/Detour/CMakeLists.txt index eb6dd184..fedf4681 100644 --- a/Detour/CMakeLists.txt +++ b/Detour/CMakeLists.txt @@ -1,13 +1,17 @@ file(GLOB SOURCES Source/*.cpp) -include_directories(Include) - if(RECASTNAVIGATION_STATIC) add_library(Detour STATIC ${SOURCES}) else() add_library(Detour SHARED ${SOURCES}) endif() +set(Detour_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Include") + +target_include_directories(Detour PUBLIC + "$" +) + set_target_properties(Detour PROPERTIES SOVERSION ${SOVERSION} VERSION ${VERSION} diff --git a/DetourCrowd/CMakeLists.txt b/DetourCrowd/CMakeLists.txt index 7eab20e7..a321d34d 100644 --- a/DetourCrowd/CMakeLists.txt +++ b/DetourCrowd/CMakeLists.txt @@ -1,14 +1,17 @@ file(GLOB SOURCES Source/*.cpp) -include_directories(../Detour/Include) -include_directories(Include) - if (RECASTNAVIGATION_STATIC) add_library(DetourCrowd STATIC ${SOURCES}) else () add_library(DetourCrowd SHARED ${SOURCES}) endif () +set(DetourCrowd_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Include") + +target_include_directories(DetourCrowd PUBLIC + "$" +) + target_link_libraries(DetourCrowd Detour ) diff --git a/DetourTileCache/CMakeLists.txt b/DetourTileCache/CMakeLists.txt index 1760cf7a..12a99a7c 100644 --- a/DetourTileCache/CMakeLists.txt +++ b/DetourTileCache/CMakeLists.txt @@ -1,14 +1,17 @@ file(GLOB SOURCES Source/*.cpp) -include_directories(../Detour/Include) -include_directories(Include) - if (RECASTNAVIGATION_STATIC) add_library(DetourTileCache STATIC ${SOURCES}) else () add_library(DetourTileCache SHARED ${SOURCES}) endif () +set(DetourTileCache_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Include") + +target_include_directories(DetourTileCache PUBLIC + "$" +) + target_link_libraries(DetourTileCache Detour ) diff --git a/Recast/CMakeLists.txt b/Recast/CMakeLists.txt index 94da5963..6400576b 100644 --- a/Recast/CMakeLists.txt +++ b/Recast/CMakeLists.txt @@ -1,13 +1,17 @@ file(GLOB SOURCES Source/*.cpp) -include_directories(Include) - if (RECASTNAVIGATION_STATIC) add_library(Recast STATIC ${SOURCES}) else () add_library(Recast SHARED ${SOURCES}) endif () +set(Recast_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Include") + +target_include_directories(Recast PUBLIC + "$" +) + set_target_properties(Recast PROPERTIES SOVERSION ${SOVERSION} VERSION ${VERSION} From 7bfd9a1d4caccf61e0485b2b05b29966348a8b39 Mon Sep 17 00:00:00 2001 From: elsid Date: Thu, 1 Nov 2018 00:16:19 +0300 Subject: [PATCH 34/99] Add aliases with namespace for library build targets --- DebugUtils/CMakeLists.txt | 2 ++ Detour/CMakeLists.txt | 2 ++ DetourCrowd/CMakeLists.txt | 2 ++ DetourTileCache/CMakeLists.txt | 2 ++ Recast/CMakeLists.txt | 2 ++ 5 files changed, 10 insertions(+) diff --git a/DebugUtils/CMakeLists.txt b/DebugUtils/CMakeLists.txt index b094e4a3..8b6a3fcf 100644 --- a/DebugUtils/CMakeLists.txt +++ b/DebugUtils/CMakeLists.txt @@ -6,6 +6,8 @@ else() add_library(DebugUtils SHARED ${SOURCES}) endif() +add_library(RecastNavigation::DebugUtils ALIAS DebugUtils) + set(DebugUtils_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Include") target_include_directories(DebugUtils PUBLIC diff --git a/Detour/CMakeLists.txt b/Detour/CMakeLists.txt index fedf4681..de88111d 100644 --- a/Detour/CMakeLists.txt +++ b/Detour/CMakeLists.txt @@ -6,6 +6,8 @@ else() add_library(Detour SHARED ${SOURCES}) endif() +add_library(RecastNavigation::Detour ALIAS Detour) + set(Detour_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Include") target_include_directories(Detour PUBLIC diff --git a/DetourCrowd/CMakeLists.txt b/DetourCrowd/CMakeLists.txt index a321d34d..73cdf7ce 100644 --- a/DetourCrowd/CMakeLists.txt +++ b/DetourCrowd/CMakeLists.txt @@ -6,6 +6,8 @@ else () add_library(DetourCrowd SHARED ${SOURCES}) endif () +add_library(RecastNavigation::DetourCrowd ALIAS DetourCrowd) + set(DetourCrowd_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Include") target_include_directories(DetourCrowd PUBLIC diff --git a/DetourTileCache/CMakeLists.txt b/DetourTileCache/CMakeLists.txt index 12a99a7c..121b8edc 100644 --- a/DetourTileCache/CMakeLists.txt +++ b/DetourTileCache/CMakeLists.txt @@ -6,6 +6,8 @@ else () add_library(DetourTileCache SHARED ${SOURCES}) endif () +add_library(RecastNavigation::DetourTileCache ALIAS DetourTileCache) + set(DetourTileCache_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Include") target_include_directories(DetourTileCache PUBLIC diff --git a/Recast/CMakeLists.txt b/Recast/CMakeLists.txt index 6400576b..5e843762 100644 --- a/Recast/CMakeLists.txt +++ b/Recast/CMakeLists.txt @@ -6,6 +6,8 @@ else () add_library(Recast SHARED ${SOURCES}) endif () +add_library(RecastNavigation::Recast ALIAS Recast) + set(Recast_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Include") target_include_directories(Recast PUBLIC From fe4d4392a6bda7e4473d3c3cd7dbe1324cba3e8a Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Mon, 5 Nov 2018 13:09:43 +0100 Subject: [PATCH 35/99] Use SDL2.dll path relative to script file Previously this was relative to the output file. This works around a premake5 bug and as a bonus makes it easier if we in the future decide to support out-of-source builds. --- RecastDemo/premake5.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RecastDemo/premake5.lua b/RecastDemo/premake5.lua index 8545c6d7..5a7e9aa7 100644 --- a/RecastDemo/premake5.lua +++ b/RecastDemo/premake5.lua @@ -170,7 +170,7 @@ project "RecastDemo" } postbuildcommands { -- Copy the SDL2 dll to the Bin folder. - '{COPY} "%{wks.location}../../Contrib/SDL/lib/%{cfg.architecture:gsub("x86_64", "x64")}/SDL2.dll" "%{cfg.targetdir}"' + '{COPY} "%{path.getabsolute("Contrib/SDL/lib/" .. cfg.architecture:gsub("x86_64", "x64") .. "/SDL2.dll")}" "%{cfg.targetdir}"' } -- mac includes and libs From 3087e805b02d5eb8fff7851234fa2b3f71290eba Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Mon, 5 Nov 2018 14:01:29 +0100 Subject: [PATCH 36/99] Add a matching placement delete for placement new Even though it is a no-op this is good style and avoids a warning with MSVC. Fix #359 --- Recast/Include/RecastAlloc.h | 1 + RecastDemo/premake5.lua | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Recast/Include/RecastAlloc.h b/Recast/Include/RecastAlloc.h index 3e78496d..e436af9a 100644 --- a/Recast/Include/RecastAlloc.h +++ b/Recast/Include/RecastAlloc.h @@ -66,6 +66,7 @@ void rcFree(void* ptr); /// and STL. struct rcNewTag {}; inline void* operator new(size_t, const rcNewTag&, void* p) { return p; } +inline void operator delete(void*, const rcNewTag&, void*) {} /// Signed to avoid warnnings when comparing to int loop indexes, and common error with comparing to zero. /// MSVC2010 has a bug where ssize_t is unsigned (!!!). diff --git a/RecastDemo/premake5.lua b/RecastDemo/premake5.lua index 5a7e9aa7..d2a6343f 100644 --- a/RecastDemo/premake5.lua +++ b/RecastDemo/premake5.lua @@ -41,8 +41,7 @@ solution "recastnavigation" -- warnings "Extra" uses /W4 which is too aggressive for us, so use W3 instead. -- Disable: -- * C4351: new behavior for array initialization - -- * C4291: no matching operator delete found; we don't use exceptions, so doesn't matter - buildoptions { "/W3", "/wd4351", "/wd4291" } + buildoptions { "/W3", "/wd4351" } filter "platforms:Win32" architecture "x32" From 7ccb72b3839c7e441b0b83cfb28d54231fd6495e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=91=E9=A3=8E?= Date: Fri, 9 Nov 2018 21:52:00 +0800 Subject: [PATCH 37/99] Optimize dtClosestHeightPointTriangle (#364) --- Detour/Source/DetourCommon.cpp | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/Detour/Source/DetourCommon.cpp b/Detour/Source/DetourCommon.cpp index 41d0d7bd..3886f14b 100644 --- a/Detour/Source/DetourCommon.cpp +++ b/Detour/Source/DetourCommon.cpp @@ -204,32 +204,31 @@ void dtCalcPolyCenter(float* tc, const unsigned short* idx, int nidx, const floa bool dtClosestHeightPointTriangle(const float* p, const float* a, const float* b, const float* c, float& h) { float v0[3], v1[3], v2[3]; + dtVsub(v0, c,a); dtVsub(v1, b,a); dtVsub(v2, p,a); - - const float dot00 = dtVdot2D(v0, v0); - const float dot01 = dtVdot2D(v0, v1); - const float dot02 = dtVdot2D(v0, v2); - const float dot11 = dtVdot2D(v1, v1); - const float dot12 = dtVdot2D(v1, v2); - - // Compute barycentric coordinates - const float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01); - const float u = (dot11 * dot02 - dot01 * dot12) * invDenom; - const float v = (dot00 * dot12 - dot01 * dot02) * invDenom; + + // Compute scaled barycentric coordinates + float denom = v0[0] * v1[2] - v0[2] * v1[0]; + float u = v1[2] * v2[0] - v1[0] * v2[2]; + float v = v0[0] * v2[2] - v0[2] * v2[0]; + + if (denom < 0) { + denom = -denom; + u = -u; + v = -v; + } // The (sloppy) epsilon is needed to allow to get height of points which // are interpolated along the edges of the triangles. - static const float EPS = 1e-4f; - + float epsilon = - 1e-4f * denom; + // If point lies inside the triangle, return interpolated ycoord. - if (u >= -EPS && v >= -EPS && (u+v) <= 1+EPS) - { - h = a[1] + v0[1]*u + v1[1]*v; + if (u >= epsilon && v >= epsilon && (u+v) <= denom - epsilon) { + h = a[1] + (v0[1]*u + v1[1]*v) / denom; return true; } - return false; } From 14b2631527c4792e95b2c78ebfa8ac4cd3413363 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Wed, 21 Nov 2018 12:34:13 +0100 Subject: [PATCH 38/99] Use Travis-CI Matrix to add more type of builds (#366) --- .travis.yml | 90 +++++++++++++++++++++++++++------------ RecastDemo/CMakeLists.txt | 6 ++- 2 files changed, 67 insertions(+), 29 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6044cd6b..0e63abad 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,36 +1,72 @@ -sudo: false - language: cpp +branches: + only: + - master + - coverity_scan + - /recast-.*$/ -# Build with gcc and clang. -compiler: - - gcc - - clang - -# Build both debug and release configurations, through use of an environment variable in the build matrix. -env: - - BUILD_TYPE=debug CMAKE_BUILD_TYPE=Debug - - BUILD_TYPE=release CMAKE_BUILD_TYPE=Release +sudo: false addons: apt: - packages: - - libsdl2-dev + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-xenial-7 + packages: [ cmake, clang-7, clang-tools-7, gcc-8, g++-8, libsdl2-dev ] -install: - - wget https://github.com/premake/premake-core/releases/download/v5.0.0-alpha12/premake-5.0.0-alpha12-linux.tar.gz -O premake.tar.gz - - tar -xf premake.tar.gz - - rm premake.tar.gz +matrix: + include: + - name: Recastnavigation (all) on MacOS xcode9.4 + os: osx + osx_image: xcode9.4 + before_install: + - brew update + - brew install sdl2 + if: branch != coverity_scan + - name: Recastnavigation on Ubuntu Xenial GCC-5 + os: linux + dist: xenial + sudo: required + if: branch != coverity_scan + - name: Recastnavigation on Ubuntu Xenial GCC-8 + os: linux + dist: xenial + sudo: required + env: + - MATRIX_EVAL="CC=gcc-8 && CXX=g++-8" + if: branch != coverity_scan + - name: Recastnavigation on Ubuntu Xenial GCC-5 using Premake5 + os: linux + dist: xenial + sudo: required + if: branch != coverity_scan + before_install: + - wget https://github.com/premake/premake-core/releases/download/v5.0.0-alpha12/premake-5.0.0-alpha12-linux.tar.gz -O premake.tar.gz + - tar -xf premake.tar.gz + env: + - PREMAKE=1 + - name: Recastnavigation on Ubuntu Xenial Clang-7 with Static Analysis + os: linux + dist: xenial + sudo: required + env: + - MATRIX_EVAL="CC=clang-7 && CXX=clang++-7" + - ANALYZE="scan-build-7 --force-analyze-debug-code --use-cc clang-7 --use-c++ clang++-7" + if: branch != coverity_scan + compiler: clang + - name: Recastnavigation Coverity Scan + os: linux + dist: xenial + sudo: required + if: branch = coverity_scan -# Run premake to generate makefiles. -# Have to cd into directory and back out since premake5 doesn't appear to accept a directory argument. before_script: - - cd RecastDemo && ../premake5 gmake && cd .. - - mkdir build && cd build && cmake -D CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} .. && cd .. + - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then eval "${MATRIX_EVAL}"; fi + - if [ "${PREMAKE}" = "1" ]; then cd RecastDemo && ../premake5 gmake && cd ..; fi + - if [ "${PREMAKE}" != "1" ]; then mkdir -p build && cd build && ${ANALYZE} cmake ../ && cd ..; fi -# Run make in the directory containing generated makefiles, on the configuration specified by the environment variable. -script: - - make -C RecastDemo/Build/gmake -j$(nproc) config=${BUILD_TYPE} - - RecastDemo/Bin/Tests - - make -C build -j$(nproc) - - cd build && ctest +script: # 2 CPUs on Travis-CI + 1 extra for IO bound process + - if [ "${PREMAKE}" = "1" ]; then make -C RecastDemo/Build/gmake -j3; fi + - if [ "${PREMAKE}" != "1" ]; then make -C build -j3; fi + - if [ "${PREMAKE}" = "1" ]; then RecastDemo/Bin/Tests; fi + - if [ "${PREMAKE}" != "1" ]; then cd build && ctest; fi diff --git a/RecastDemo/CMakeLists.txt b/RecastDemo/CMakeLists.txt index ffeac6a4..a6a1a733 100644 --- a/RecastDemo/CMakeLists.txt +++ b/RecastDemo/CMakeLists.txt @@ -38,11 +38,13 @@ endif() add_dependencies(RecastDemo DebugUtils Detour DetourCrowd DetourTileCache Recast) target_link_libraries(RecastDemo ${OPENGL_LIBRARIES} SDL2::SDL2main DebugUtils Detour DetourCrowd DetourTileCache Recast) -install(TARGETS RecastDemo RUNTIME DESTINATION bin) +install(TARGETS RecastDemo + RUNTIME DESTINATION bin + BUNDLE DESTINATION bin) install(DIRECTORY Bin/Meshes DESTINATION bin) install(DIRECTORY Bin/TestCases DESTINATION bin) install(FILES Bin/DroidSans.ttf DESTINATION bin) if (WIN32) install(FILES "${SDL2_RUNTIME_LIBRARY}" DESTINATION bin) -endif() +endif() \ No newline at end of file From 3a619d773deb7e3a15ee215217c825995fe71312 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Sun, 24 Feb 2019 21:10:42 +0100 Subject: [PATCH 39/99] Add consistent input validation to dtNavMeshQuery (#374) Validate input values, including that points are finite. --- Detour/Include/DetourCommon.h | 22 ++++ Detour/Include/DetourMath.h | 4 + Detour/Source/DetourNavMeshQuery.cpp | 180 ++++++++++++++++++++------- 3 files changed, 162 insertions(+), 44 deletions(-) diff --git a/Detour/Include/DetourCommon.h b/Detour/Include/DetourCommon.h index 739858cd..113e8c33 100644 --- a/Detour/Include/DetourCommon.h +++ b/Detour/Include/DetourCommon.h @@ -283,6 +283,28 @@ inline bool dtVequal(const float* p0, const float* p1) return d < thr; } +/// Checks that the specified vector's components are all finite. +/// @param[in] v A point. [(x, y, z)] +/// @return True if all of the point's components are finite, i.e. not NaN +/// or any of the infinities. +inline bool dtVisfinite(const float* v) +{ + bool result = + dtMathIsfinite(v[0]) && + dtMathIsfinite(v[1]) && + dtMathIsfinite(v[2]); + + return result; +} + +/// Checks that the specified vector's 2D components are finite. +/// @param[in] v A point. [(x, y, z)] +inline bool dtVisfinite2D(const float* v) +{ + bool result = dtMathIsfinite(v[0]) && dtMathIsfinite(v[2]); + return result; +} + /// Derives the dot product of two vectors on the xz-plane. (@p u . @p v) /// @param[in] u A vector [(x, y, z)] /// @param[in] v A vector [(x, y, z)] diff --git a/Detour/Include/DetourMath.h b/Detour/Include/DetourMath.h index 95e14f88..54af8af0 100644 --- a/Detour/Include/DetourMath.h +++ b/Detour/Include/DetourMath.h @@ -8,6 +8,9 @@ Members in this module are wrappers around the standard math library #define DETOURMATH_H #include +// This include is required because libstdc++ has problems with isfinite +// if cmath is included before math.h. +#include inline float dtMathFabsf(float x) { return fabsf(x); } inline float dtMathSqrtf(float x) { return sqrtf(x); } @@ -16,5 +19,6 @@ inline float dtMathCeilf(float x) { return ceilf(x); } inline float dtMathCosf(float x) { return cosf(x); } inline float dtMathSinf(float x) { return sinf(x); } inline float dtMathAtan2f(float y, float x) { return atan2f(y, x); } +inline bool dtMathIsfinite(float x) { return std::isfinite(x); } #endif diff --git a/Detour/Source/DetourNavMeshQuery.cpp b/Detour/Source/DetourNavMeshQuery.cpp index 90999f2f..c5ef385f 100644 --- a/Detour/Source/DetourNavMeshQuery.cpp +++ b/Detour/Source/DetourNavMeshQuery.cpp @@ -222,7 +222,10 @@ dtStatus dtNavMeshQuery::findRandomPoint(const dtQueryFilter* filter, float (*fr dtPolyRef* randomRef, float* randomPt) const { dtAssert(m_nav); - + + if (!filter || !frand || !randomRef || !randomPt) + return DT_FAILURE | DT_INVALID_PARAM; + // Randomly pick one tile. Assume that all tiles cover roughly the same area. const dtMeshTile* tile = 0; float tsum = 0.0f; @@ -319,8 +322,13 @@ dtStatus dtNavMeshQuery::findRandomPointAroundCircle(dtPolyRef startRef, const f dtAssert(m_openList); // Validate input - if (!startRef || !m_nav->isValidPolyRef(startRef)) + if (!m_nav->isValidPolyRef(startRef) || + !centerPos || !dtVisfinite(centerPos) || + maxRadius < 0 || !dtMathIsfinite(maxRadius) || + !filter || !frand || !randomRef || !randomPt) + { return DT_FAILURE | DT_INVALID_PARAM; + } const dtMeshTile* startTile = 0; const dtPoly* startPoly = 0; @@ -512,6 +520,9 @@ dtStatus dtNavMeshQuery::closestPointOnPoly(dtPolyRef ref, const float* pos, flo return DT_FAILURE | DT_INVALID_PARAM; if (!tile) return DT_FAILURE | DT_INVALID_PARAM; + + if (!pos || !dtVisfinite(pos) || !closest) + return DT_FAILURE | DT_INVALID_PARAM; // Off-mesh connections don't have detail polygons. if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) @@ -607,6 +618,9 @@ dtStatus dtNavMeshQuery::closestPointOnPolyBoundary(dtPolyRef ref, const float* const dtPoly* poly = 0; if (dtStatusFailed(m_nav->getTileAndPolyByRef(ref, &tile, &poly))) return DT_FAILURE | DT_INVALID_PARAM; + + if (!pos || !dtVisfinite(pos) || !closest) + return DT_FAILURE | DT_INVALID_PARAM; // Collect vertices. float verts[DT_VERTS_PER_POLYGON*3]; @@ -659,6 +673,9 @@ dtStatus dtNavMeshQuery::getPolyHeight(dtPolyRef ref, const float* pos, float* h const dtPoly* poly = 0; if (dtStatusFailed(m_nav->getTileAndPolyByRef(ref, &tile, &poly))) return DT_FAILURE | DT_INVALID_PARAM; + + if (!pos || !dtVisfinite2D(pos)) + return DT_FAILURE | DT_INVALID_PARAM; if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) { @@ -767,6 +784,8 @@ dtStatus dtNavMeshQuery::findNearestPoly(const float* center, const float* halfE if (!nearestRef) return DT_FAILURE | DT_INVALID_PARAM; + + // queryPolygons below will check rest of params dtFindNearestPolyQuery query(this, center); @@ -972,8 +991,12 @@ dtStatus dtNavMeshQuery::queryPolygons(const float* center, const float* halfExt { dtAssert(m_nav); - if (!center || !halfExtents || !filter || !query) + if (!center || !dtVisfinite(center) || + !halfExtents || !dtVisfinite(halfExtents) || + !filter || !query) + { return DT_FAILURE | DT_INVALID_PARAM; + } float bmin[3], bmax[3]; dtVsub(bmin, center, halfExtents); @@ -1021,14 +1044,20 @@ dtStatus dtNavMeshQuery::findPath(dtPolyRef startRef, dtPolyRef endRef, dtAssert(m_nav); dtAssert(m_nodePool); dtAssert(m_openList); - - if (pathCount) - *pathCount = 0; + + if (!pathCount) + return DT_FAILURE | DT_INVALID_PARAM; + + *pathCount = 0; // Validate input if (!m_nav->isValidPolyRef(startRef) || !m_nav->isValidPolyRef(endRef) || - !startPos || !endPos || !filter || maxPath <= 0 || !path || !pathCount) + !startPos || !dtVisfinite(startPos) || + !endPos || !dtVisfinite(endPos) || + !filter || !path || maxPath <= 0) + { return DT_FAILURE | DT_INVALID_PARAM; + } if (startRef == endRef) { @@ -1263,18 +1292,21 @@ dtStatus dtNavMeshQuery::initSlicedFindPath(dtPolyRef startRef, dtPolyRef endRef m_query.status = DT_FAILURE; m_query.startRef = startRef; m_query.endRef = endRef; - dtVcopy(m_query.startPos, startPos); - dtVcopy(m_query.endPos, endPos); + if (startPos) + dtVcopy(m_query.startPos, startPos); + if (endPos) + dtVcopy(m_query.endPos, endPos); m_query.filter = filter; m_query.options = options; m_query.raycastLimitSqr = FLT_MAX; - if (!startRef || !endRef) - return DT_FAILURE | DT_INVALID_PARAM; - // Validate input - if (!m_nav->isValidPolyRef(startRef) || !m_nav->isValidPolyRef(endRef)) + if (!m_nav->isValidPolyRef(startRef) || !m_nav->isValidPolyRef(endRef) || + !startPos || !dtVisfinite(startPos) || + !endPos || !dtVisfinite(endPos) || !filter) + { return DT_FAILURE | DT_INVALID_PARAM; + } // trade quality with performance? if (options & DT_FINDPATH_ANY_ANGLE) @@ -1530,7 +1562,13 @@ dtStatus dtNavMeshQuery::updateSlicedFindPath(const int maxIter, int* doneIters) dtStatus dtNavMeshQuery::finalizeSlicedFindPath(dtPolyRef* path, int* pathCount, const int maxPath) { + if (!pathCount) + return DT_FAILURE | DT_INVALID_PARAM; + *pathCount = 0; + + if (!path || maxPath <= 0) + return DT_FAILURE | DT_INVALID_PARAM; if (dtStatusFailed(m_query.status)) { @@ -1615,12 +1653,13 @@ dtStatus dtNavMeshQuery::finalizeSlicedFindPath(dtPolyRef* path, int* pathCount, dtStatus dtNavMeshQuery::finalizeSlicedFindPathPartial(const dtPolyRef* existing, const int existingSize, dtPolyRef* path, int* pathCount, const int maxPath) { + if (!pathCount) + return DT_FAILURE | DT_INVALID_PARAM; + *pathCount = 0; - - if (existingSize == 0) - { - return DT_FAILURE; - } + + if (!existing || existingSize <= 0 || !path || !pathCount || maxPath <= 0) + return DT_FAILURE | DT_INVALID_PARAM; if (dtStatusFailed(m_query.status)) { @@ -1823,14 +1862,19 @@ dtStatus dtNavMeshQuery::findStraightPath(const float* startPos, const float* en int* straightPathCount, const int maxStraightPath, const int options) const { dtAssert(m_nav); - + + if (!straightPathCount) + return DT_FAILURE | DT_INVALID_PARAM; + *straightPathCount = 0; - - if (!maxStraightPath) - return DT_FAILURE | DT_INVALID_PARAM; - - if (!path[0]) + + if (!startPos || !dtVisfinite(startPos) || + !endPos || !dtVisfinite(endPos) || + !path || pathSize <= 0 || !path[0] || + maxStraightPath <= 0) + { return DT_FAILURE | DT_INVALID_PARAM; + } dtStatus stat = 0; @@ -2070,13 +2114,19 @@ dtStatus dtNavMeshQuery::moveAlongSurface(dtPolyRef startRef, const float* start dtAssert(m_nav); dtAssert(m_tinyNodePool); + if (!visitedCount) + return DT_FAILURE | DT_INVALID_PARAM; + *visitedCount = 0; - - // Validate input - if (!startRef) - return DT_FAILURE | DT_INVALID_PARAM; - if (!m_nav->isValidPolyRef(startRef)) + + if (!m_nav->isValidPolyRef(startRef) || + !startPos || !dtVisfinite(startPos) || + !endPos || !dtVisfinite(endPos) || + !filter || !resultPos || !visited || + maxVisitedSize <= 0) + { return DT_FAILURE | DT_INVALID_PARAM; + } dtStatus status = DT_SUCCESS; @@ -2484,16 +2534,23 @@ dtStatus dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, cons dtRaycastHit* hit, dtPolyRef prevRef) const { dtAssert(m_nav); - + + if (!hit) + return DT_FAILURE | DT_INVALID_PARAM; + hit->t = 0; hit->pathCount = 0; hit->pathCost = 0; // Validate input - if (!startRef || !m_nav->isValidPolyRef(startRef)) - return DT_FAILURE | DT_INVALID_PARAM; - if (prevRef && !m_nav->isValidPolyRef(prevRef)) + if (!m_nav->isValidPolyRef(startRef) || + !startPos || !dtVisfinite(startPos) || + !endPos || !dtVisfinite(endPos) || + !filter || + (prevRef && !m_nav->isValidPolyRef(prevRef))) + { return DT_FAILURE | DT_INVALID_PARAM; + } float dir[3], curPos[3], lastPos[3]; float verts[DT_VERTS_PER_POLYGON*3+3]; @@ -2735,11 +2792,18 @@ dtStatus dtNavMeshQuery::findPolysAroundCircle(dtPolyRef startRef, const float* dtAssert(m_nodePool); dtAssert(m_openList); - *resultCount = 0; - - // Validate input - if (!startRef || !m_nav->isValidPolyRef(startRef)) + if (!resultCount) return DT_FAILURE | DT_INVALID_PARAM; + + *resultCount = 0; + + if (!m_nav->isValidPolyRef(startRef) || + !centerPos || !dtVisfinite(centerPos) || + radius < 0 || !dtMathIsfinite(radius) || + !filter || maxResult < 0) + { + return DT_FAILURE | DT_INVALID_PARAM; + } m_nodePool->clear(); m_openList->clear(); @@ -2901,8 +2965,18 @@ dtStatus dtNavMeshQuery::findPolysAroundShape(dtPolyRef startRef, const float* v dtAssert(m_nav); dtAssert(m_nodePool); dtAssert(m_openList); - + + if (!resultCount) + return DT_FAILURE | DT_INVALID_PARAM; + *resultCount = 0; + + if (!m_nav->isValidPolyRef(startRef) || + !verts || nverts < 3 || + !filter || maxResult < 0) + { + return DT_FAILURE | DT_INVALID_PARAM; + } // Validate input if (!startRef || !m_nav->isValidPolyRef(startRef)) @@ -3088,13 +3162,20 @@ dtStatus dtNavMeshQuery::findLocalNeighbourhood(dtPolyRef startRef, const float* { dtAssert(m_nav); dtAssert(m_tinyNodePool); - + + if (!resultCount) + return DT_FAILURE | DT_INVALID_PARAM; + *resultCount = 0; - // Validate input - if (!startRef || !m_nav->isValidPolyRef(startRef)) + if (!m_nav->isValidPolyRef(startRef) || + !centerPos || !dtVisfinite(centerPos) || + radius < 0 || !dtMathIsfinite(radius) || + !filter || maxResult < 0) + { return DT_FAILURE | DT_INVALID_PARAM; - + } + static const int MAX_STACK = 48; dtNode* stack[MAX_STACK]; int nstack = 0; @@ -3301,13 +3382,19 @@ dtStatus dtNavMeshQuery::getPolyWallSegments(dtPolyRef ref, const dtQueryFilter* const int maxSegments) const { dtAssert(m_nav); + + if (!segmentCount) + return DT_FAILURE | DT_INVALID_PARAM; *segmentCount = 0; - + const dtMeshTile* tile = 0; const dtPoly* poly = 0; if (dtStatusFailed(m_nav->getTileAndPolyByRef(ref, &tile, &poly))) return DT_FAILURE | DT_INVALID_PARAM; + + if (!filter || !segmentVerts || maxSegments < 0) + return DT_FAILURE | DT_INVALID_PARAM; int n = 0; static const int MAX_INTERVAL = 16; @@ -3455,8 +3542,13 @@ dtStatus dtNavMeshQuery::findDistanceToWall(dtPolyRef startRef, const float* cen dtAssert(m_openList); // Validate input - if (!startRef || !m_nav->isValidPolyRef(startRef)) + if (!m_nav->isValidPolyRef(startRef) || + !centerPos || !dtVisfinite(centerPos) || + maxRadius < 0 || !dtMathIsfinite(maxRadius) || + !filter || !hitDist || !hitPos || !hitNormal) + { return DT_FAILURE | DT_INVALID_PARAM; + } m_nodePool->clear(); m_openList->clear(); From a0e6605642a7c2e63b114bf85dcc39ed922a514d Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Sun, 3 Mar 2019 11:18:51 +0100 Subject: [PATCH 40/99] Deduplicate closestPointOnPoly --- Detour/Include/DetourNavMesh.h | 2 + Detour/Include/DetourNavMeshQuery.h | 2 - Detour/Source/DetourNavMeshQuery.cpp | 84 ++-------------------------- 3 files changed, 7 insertions(+), 81 deletions(-) diff --git a/Detour/Include/DetourNavMesh.h b/Detour/Include/DetourNavMesh.h index 02ee5e78..09b778f5 100644 --- a/Detour/Include/DetourNavMesh.h +++ b/Detour/Include/DetourNavMesh.h @@ -655,6 +655,8 @@ private: unsigned int m_tileBits; ///< Number of tile bits in the tile ID. unsigned int m_polyBits; ///< Number of poly bits in the tile ID. #endif + + friend class dtNavMeshQuery; }; /// Allocates a navigation mesh object using the Detour allocator. diff --git a/Detour/Include/DetourNavMeshQuery.h b/Detour/Include/DetourNavMeshQuery.h index 1c23e485..0b40371b 100644 --- a/Detour/Include/DetourNavMeshQuery.h +++ b/Detour/Include/DetourNavMeshQuery.h @@ -119,8 +119,6 @@ public: }; - - /// Provides information about raycast hit /// filled by dtNavMeshQuery::raycast /// @ingroup detour diff --git a/Detour/Source/DetourNavMeshQuery.cpp b/Detour/Source/DetourNavMeshQuery.cpp index c5ef385f..520f915a 100644 --- a/Detour/Source/DetourNavMeshQuery.cpp +++ b/Detour/Source/DetourNavMeshQuery.cpp @@ -514,88 +514,14 @@ dtStatus dtNavMeshQuery::findRandomPointAroundCircle(dtPolyRef startRef, const f dtStatus dtNavMeshQuery::closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest, bool* posOverPoly) const { dtAssert(m_nav); - const dtMeshTile* tile = 0; - const dtPoly* poly = 0; - if (dtStatusFailed(m_nav->getTileAndPolyByRef(ref, &tile, &poly))) + if (!m_nav->isValidPolyRef(ref) || + !pos || !dtVisfinite(pos) || + !closest) + { return DT_FAILURE | DT_INVALID_PARAM; - if (!tile) - return DT_FAILURE | DT_INVALID_PARAM; - - if (!pos || !dtVisfinite(pos) || !closest) - return DT_FAILURE | DT_INVALID_PARAM; - - // Off-mesh connections don't have detail polygons. - if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) - { - const float* v0 = &tile->verts[poly->verts[0]*3]; - const float* v1 = &tile->verts[poly->verts[1]*3]; - const float d0 = dtVdist(pos, v0); - const float d1 = dtVdist(pos, v1); - const float u = d0 / (d0+d1); - dtVlerp(closest, v0, v1, u); - if (posOverPoly) - *posOverPoly = false; - return DT_SUCCESS; } - const unsigned int ip = (unsigned int)(poly - tile->polys); - const dtPolyDetail* pd = &tile->detailMeshes[ip]; - - // Clamp point to be inside the polygon. - float verts[DT_VERTS_PER_POLYGON*3]; - float edged[DT_VERTS_PER_POLYGON]; - float edget[DT_VERTS_PER_POLYGON]; - const int nv = poly->vertCount; - for (int i = 0; i < nv; ++i) - dtVcopy(&verts[i*3], &tile->verts[poly->verts[i]*3]); - - dtVcopy(closest, pos); - if (!dtDistancePtPolyEdgesSqr(pos, verts, nv, edged, edget)) - { - // Point is outside the polygon, dtClamp to nearest edge. - float dmin = edged[0]; - int imin = 0; - for (int i = 1; i < nv; ++i) - { - if (edged[i] < dmin) - { - dmin = edged[i]; - imin = i; - } - } - const float* va = &verts[imin*3]; - const float* vb = &verts[((imin+1)%nv)*3]; - dtVlerp(closest, va, vb, edget[imin]); - - if (posOverPoly) - *posOverPoly = false; - } - else - { - if (posOverPoly) - *posOverPoly = true; - } - - // Find height at the location. - for (int j = 0; j < pd->triCount; ++j) - { - const unsigned char* t = &tile->detailTris[(pd->triBase+j)*4]; - const float* v[3]; - for (int k = 0; k < 3; ++k) - { - if (t[k] < poly->vertCount) - v[k] = &tile->verts[poly->verts[t[k]]*3]; - else - v[k] = &tile->detailVerts[(pd->vertBase+(t[k]-poly->vertCount))*3]; - } - float h; - if (dtClosestHeightPointTriangle(closest, v[0], v[1], v[2], h)) - { - closest[1] = h; - break; - } - } - + m_nav->closestPointOnPoly(ref, pos, closest, posOverPoly); return DT_SUCCESS; } From 21f48fae3786300de8f21944213cc1a6c1bd3866 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Sat, 9 Mar 2019 21:19:59 +0100 Subject: [PATCH 41/99] Add a mesh for testing crowds at a large distance of origin --- RecastDemo/Bin/Meshes/undulating.obj | 8002 ++++++++++++++++++++++++++ 1 file changed, 8002 insertions(+) create mode 100644 RecastDemo/Bin/Meshes/undulating.obj diff --git a/RecastDemo/Bin/Meshes/undulating.obj b/RecastDemo/Bin/Meshes/undulating.obj new file mode 100644 index 00000000..22bb8b08 --- /dev/null +++ b/RecastDemo/Bin/Meshes/undulating.obj @@ -0,0 +1,8002 @@ +v 5000 0 5000 +v 5002 1.9177022 5000 +v 5004 3.36588383 5000 +v 5006 3.98998 5000 +v 5008 3.63718963 5000 +v 5010 2.39388847 5000 +v 5012 0.56448 5000 +v 5014 -1.40313292 5000 +v 5016 -3.02721 5000 +v 5018 -3.91012049 5000 +v 5020 -3.83569717 5000 +v 5022 -2.8221612 5000 +v 5024 -1.117662 5000 +v 5026 0.860479951 5000 +v 5028 2.62794638 5000 +v 5030 3.75199986 5000 +v 5032 3.957433 5000 +v 5034 3.19394851 5000 +v 5036 1.648474 5000 +v 5038 -0.3006045 5000 +v 5040 -2.17608452 5000 +v 5042 -3.518783 5000 +v 5044 -3.999961 5000 +v 5046 -3.50180864 5000 +v 5048 -2.14629173 5000 +v 5050 -0.265287578 5000 +v 5052 1.68066812 5000 +v 5054 3.21513772 5000 +v 5056 3.96242952 5000 +v 5058 3.73958015 5000 +v 5060 2.60115147 5000 +v 5062 0.8258699 5000 +v 5064 -1.15161324 5000 +v 5066 -2.84714127 5000 +v 5068 -3.84558988 5000 +v 5070 -3.902504 5000 +v 5072 -3.003949 5000 +v 5074 -1.36992252 5000 +v 5076 0.5995088 5000 +v 5078 2.42215943 5000 +v 5080 3.651781 5000 +v 5082 3.98731923 5000 +v 5084 3.34662247 5000 +v 5086 1.886556 5000 +v 5088 -0.0354052372 5000 +v 5090 -1.948698 5000 +v 5092 -3.38488173 5000 +v 5094 -3.99232817 5000 +v 5096 -3.6223135 5000 +v 5098 -2.36543 5000 +v 5000 0 5002 +v 5002 1.682942 5002 +v 5004 2.953841 5002 +v 5006 3.50153685 5002 +v 5008 3.191934 5002 +v 5010 2.10083485 5002 +v 5012 0.4953778 5002 +v 5014 -1.231365 5002 +v 5016 -2.6566267 5002 +v 5018 -3.43145347 5002 +v 5020 -3.36614084 5002 +v 5022 -2.47667933 5002 +v 5024 -0.9808406 5002 +v 5026 0.7551422 5002 +v 5028 2.30623984 5002 +v 5030 3.29268956 5002 +v 5032 3.472974 5002 +v 5034 2.80295348 5002 +v 5036 1.446672 5002 +v 5038 -0.26380527 5002 +v 5040 -1.90969384 5002 +v 5042 -3.08802271 5002 +v 5044 -3.51029587 5002 +v 5046 -3.073126 5002 +v 5048 -1.88354814 5002 +v 5050 -0.232811749 5002 +v 5052 1.474925 5002 +v 5054 2.8215487 5002 +v 5056 3.477359 5002 +v 5058 3.28179026 5002 +v 5060 2.282725 5002 +v 5062 0.724769056 5002 +v 5064 -1.01063573 5002 +v 5066 -2.49860144 5002 +v 5068 -3.37482262 5002 +v 5070 -3.4247694 5002 +v 5072 -2.636213 5002 +v 5074 -1.20222008 5002 +v 5076 0.526118457 5002 +v 5078 2.125645 5002 +v 5080 3.20473933 5002 +v 5082 3.49920177 5002 +v 5084 2.93693757 5002 +v 5086 1.65560865 5002 +v 5088 -0.0310710184 5002 +v 5090 -1.71014345 5002 +v 5092 -2.970513 5002 +v 5094 -3.5035975 5002 +v 5096 -3.178879 5002 +v 5098 -2.07586026 5002 +v 5000 0 5004 +v 5002 1.03613889 5004 +v 5004 1.81859469 5004 +v 5006 2.15579534 5004 +v 5008 1.96518183 5004 +v 5010 1.29342341 5004 +v 5012 0.304989845 5004 +v 5014 -0.7581159 5004 +v 5016 -1.63560843 5004 +v 5018 -2.112647 5004 +v 5020 -2.07243586 5004 +v 5022 -1.52482009 5004 +v 5024 -0.6038753 5004 +v 5026 0.464919269 5004 +v 5028 1.4198854 5004 +v 5030 2.027214 5004 +v 5032 2.13821 5004 +v 5034 1.72569764 5004 +v 5036 0.890674233 5004 +v 5038 -0.1624173 5004 +v 5040 -1.17574346 5004 +v 5042 -1.90120649 5004 +v 5044 -2.161188 5004 +v 5046 -1.89203513 5004 +v 5048 -1.15964627 5004 +v 5050 -0.143335477 5004 +v 5052 0.908068836 5004 +v 5054 1.73714626 5004 +v 5056 2.14090967 5004 +v 5058 2.02050376 5004 +v 5060 1.405408 5004 +v 5062 0.446219385 5004 +v 5064 -0.622219265 5004 +v 5066 -1.538317 5004 +v 5068 -2.077781 5004 +v 5070 -2.10853171 5004 +v 5072 -1.62304044 5004 +v 5074 -0.740172267 5004 +v 5076 0.323916 5004 +v 5078 1.3086983 5004 +v 5080 1.97306561 5004 +v 5082 2.15435767 5004 +v 5084 1.80818772 5004 +v 5086 1.01931047 5004 +v 5088 -0.01912953 5004 +v 5090 -1.052886 5004 +v 5092 -1.82885933 5004 +v 5094 -2.157064 5004 +v 5096 -1.95714426 5004 +v 5098 -1.27804732 5004 +v 5000 0 5006 +v 5002 0.135652885 5006 +v 5004 0.2380932 5006 +v 5006 0.28224 5006 +v 5008 0.2572846 5006 +v 5010 0.16933696 5006 +v 5012 0.0399297327 5006 +v 5014 -0.09925369 5006 +v 5016 -0.214136347 5006 +v 5018 -0.276590973 5006 +v 5020 -0.271326482 5006 +v 5022 -0.19963178 5006 +v 5024 -0.07906027 5006 +v 5026 0.06086794 5006 +v 5028 0.185893565 5006 +v 5030 0.265405953 5006 +v 5032 0.2799377 5006 +v 5034 0.225930974 5006 +v 5036 0.116608433 5006 +v 5038 -0.02126392 5006 +v 5040 -0.153930128 5006 +v 5042 -0.248908862 5006 +v 5044 -0.28294602 5006 +v 5046 -0.247708127 5006 +v 5048 -0.151822656 5006 +v 5050 -0.0187657 5006 +v 5052 0.118885756 5006 +v 5054 0.227429837 5006 +v 5056 0.28029117 5006 +v 5058 0.2645274 5006 +v 5060 0.183998168 5006 +v 5062 0.0584197231 5006 +v 5064 -0.08146189 5006 +v 5066 -0.20139879 5006 +v 5068 -0.272026241 5006 +v 5070 -0.2760522 5006 +v 5072 -0.212490931 5006 +v 5074 -0.09690448 5006 +v 5076 0.042407576 5006 +v 5078 0.17133677 5006 +v 5080 0.258316755 5006 +v 5082 0.2820518 5006 +v 5084 0.2367307 5006 +v 5086 0.133449689 5006 +v 5088 -0.00250446727 5006 +v 5090 -0.137845442 5006 +v 5092 -0.239437044 5006 +v 5094 -0.282406121 5006 +v 5096 -0.256232321 5006 +v 5098 -0.1673239 5006 +v 5000 0 5008 +v 5002 -0.7980457 5008 +v 5004 -1.40070188 5008 +v 5006 -1.66041756 5008 +v 5008 -1.513605 5008 +v 5010 -0.996209145 5008 +v 5012 -0.234906569 5008 +v 5014 0.583909333 5008 +v 5016 1.25976384 5008 +v 5018 1.62718427 5008 +v 5020 1.59621322 5008 +v 5022 1.17443347 5008 +v 5024 0.4651115 5008 +v 5026 -0.35808602 5008 +v 5028 -1.0936116 5008 +v 5030 -1.56138289 5008 +v 5032 -1.64687324 5008 +v 5034 -1.32915163 5008 +v 5036 -0.686007261 5008 +v 5038 0.1250956 5008 +v 5040 0.9055707 5008 +v 5042 1.46433043 5008 +v 5044 1.66457117 5008 +v 5046 1.45726657 5008 +v 5048 0.8931725 5008 +v 5050 0.110398591 5008 +v 5052 -0.6994047 5008 +v 5054 -1.33796942 5008 +v 5056 -1.64895248 5008 +v 5058 -1.55621445 5008 +v 5060 -1.082461 5008 +v 5062 -0.343683153 5008 +v 5064 0.4792402 5008 +v 5066 1.18482888 5008 +v 5068 1.60033011 5008 +v 5070 1.62401474 5008 +v 5072 1.25008392 5008 +v 5074 0.5700889 5008 +v 5076 -0.2494837 5008 +v 5078 -1.007974 5008 +v 5080 -1.51967716 5008 +v 5082 -1.65931034 5008 +v 5084 -1.39268637 5008 +v 5086 -0.785084367 5008 +v 5088 0.0147337774 5008 +v 5090 0.810944557 5008 +v 5092 1.40860784 5008 +v 5094 1.66139472 5008 +v 5096 1.50741434 5008 +v 5098 0.9843663 5008 +v 5000 0 5010 +v 5002 -1.53635478 5010 +v 5004 -2.69655633 5010 +v 5006 -3.19654679 5010 +v 5008 -2.913911 5010 +v 5010 -1.91784835 5010 +v 5012 -0.45222953 5010 +v 5014 1.12411094 5010 +v 5016 2.42522979 5010 +v 5018 3.132568 5010 +v 5020 3.07294416 5010 +v 5022 2.26095629 5010 +v 5024 0.8954077 5010 +v 5026 -0.689368 5010 +v 5028 -2.10536242 5010 +v 5030 -3.00589061 5010 +v 5032 -3.17047215 5010 +v 5034 -2.55881143 5010 +v 5036 -1.32066441 5010 +v 5038 0.240827367 5010 +v 5040 1.74335611 5010 +v 5042 2.81905055 5010 +v 5044 3.204543 5010 +v 5046 2.80545163 5010 +v 5048 1.71948791 5010 +v 5050 0.212533444 5010 +v 5052 -1.34645653 5010 +v 5054 -2.575787 5010 +v 5056 -3.174475 5010 +v 5058 -2.99594069 5010 +v 5060 -2.083896 5010 +v 5062 -0.6616404 5010 +v 5064 0.922607541 5010 +v 5066 2.280969 5010 +v 5068 3.08086967 5010 +v 5070 3.126466 5010 +v 5072 2.40659451 5010 +v 5074 1.09750462 5010 +v 5076 -0.480292648 5010 +v 5078 -1.94049752 5010 +v 5080 -2.925601 5010 +v 5082 -3.19441533 5010 +v 5084 -2.68112516 5010 +v 5086 -1.51140225 5010 +v 5088 0.0283646788 5010 +v 5090 1.56118691 5010 +v 5092 2.71177626 5010 +v 5094 3.19842815 5010 +v 5096 2.90199327 5010 +v 5098 1.89504921 5010 +v 5000 0 5012 +v 5002 -1.89851081 5012 +v 5004 -3.33219981 5012 +v 5006 -3.95005035 5012 +v 5008 -3.6007905 5012 +v 5010 -2.3699317 5012 +v 5012 -0.558831 5012 +v 5014 1.389091 5012 +v 5016 2.996915 5012 +v 5018 3.87099 5012 +v 5020 3.79731154 5012 +v 5022 2.79391837 5012 +v 5024 1.1064769 5012 +v 5026 -0.8518687 5012 +v 5028 -2.60164714 5012 +v 5030 -3.71445179 5012 +v 5032 -3.917829 5012 +v 5034 -3.16198516 5012 +v 5036 -1.63197684 5012 +v 5038 0.2975962 5012 +v 5040 2.15430737 5012 +v 5042 3.483569 5012 +v 5044 3.95993137 5012 +v 5046 3.46676421 5012 +v 5048 2.1248126 5012 +v 5050 0.2626327 5012 +v 5052 -1.66384888 5012 +v 5054 -3.18296218 5012 +v 5056 -3.92277551 5012 +v 5058 -3.70215631 5012 +v 5060 -2.57512045 5012 +v 5062 -0.817605 5012 +v 5064 1.14008844 5012 +v 5066 2.81864858 5012 +v 5068 3.807105 5012 +v 5070 3.86344957 5012 +v 5072 2.973887 5012 +v 5074 1.356213 5012 +v 5076 -0.593509257 5012 +v 5078 -2.39791965 5012 +v 5080 -3.61523581 5012 +v 5082 -3.947416 5012 +v 5084 -3.313131 5012 +v 5086 -1.86767638 5012 +v 5088 0.03505092 5012 +v 5090 1.92919648 5012 +v 5092 3.35100746 5012 +v 5094 3.952375 5012 +v 5096 3.58606315 5012 +v 5098 2.341758 5012 +v 5000 0 5014 +v 5002 -1.795845 5014 +v 5004 -3.15200448 5014 +v 5006 -3.73644352 5014 +v 5008 -3.40607047 5014 +v 5010 -2.241773 5014 +v 5012 -0.528611064 5014 +v 5014 1.31397319 5014 +v 5016 2.834851 5014 +v 5018 3.66165853 5014 +v 5020 3.59196424 5014 +v 5022 2.6428318 5014 +v 5024 1.046642 5014 +v 5026 -0.8058022 5014 +v 5028 -2.460958 5014 +v 5030 -3.51358533 5014 +v 5032 -3.70596457 5014 +v 5034 -2.99099445 5014 +v 5036 -1.54372442 5014 +v 5038 0.281503081 5014 +v 5040 2.037809 5014 +v 5042 3.295188 5014 +v 5044 3.74579 5014 +v 5046 3.279292 5014 +v 5048 2.00990915 5014 +v 5050 0.248430327 5014 +v 5052 -1.57387292 5014 +v 5054 -3.010837 5014 +v 5056 -3.71064353 5014 +v 5058 -3.50195479 5014 +v 5060 -2.43586564 5014 +v 5062 -0.7733914 5014 +v 5064 1.0784359 5014 +v 5066 2.66622448 5014 +v 5068 3.60122824 5014 +v 5070 3.654526 5014 +v 5072 2.81306815 5014 +v 5074 1.28287315 5014 +v 5076 -0.561414063 5014 +v 5078 -2.26824737 5014 +v 5080 -3.41973472 5014 +v 5082 -3.73395181 5014 +v 5084 -3.133967 5014 +v 5086 -1.766678 5014 +v 5088 0.03315547 5014 +v 5090 1.8248713 5014 +v 5092 3.169795 5014 +v 5094 3.73864245 5014 +v 5096 3.39213967 5014 +v 5098 2.215123 5014 +v 5000 0 5016 +v 5002 -1.25349379 5016 +v 5004 -2.2000885 5016 +v 5006 -2.60802484 5016 +v 5008 -2.37742567 5016 +v 5010 -1.56475 5016 +v 5012 -0.368968755 5016 +v 5014 0.9171489 5016 +v 5016 1.97871649 5016 +v 5018 2.55582523 5016 +v 5020 2.507179 5016 +v 5022 1.84468758 5016 +v 5024 0.7305526 5016 +v 5026 -0.5624472 5016 +v 5028 -1.7177403 5016 +v 5030 -2.45247078 5016 +v 5032 -2.58675075 5016 +v 5034 -2.087704 5016 +v 5036 -1.07751453 5016 +v 5038 0.1964882 5016 +v 5040 1.42238379 5016 +v 5042 2.30003 5016 +v 5044 2.614549 5016 +v 5046 2.288935 5016 +v 5048 1.40290987 5016 +v 5050 0.173403531 5016 +v 5052 -1.098558 5016 +v 5054 -2.10155416 5016 +v 5056 -2.59001684 5016 +v 5058 -2.44435263 5016 +v 5060 -1.70022607 5016 +v 5062 -0.5398246 5016 +v 5064 0.7527446 5016 +v 5066 1.86101568 5016 +v 5068 2.51364517 5016 +v 5070 2.55084682 5016 +v 5072 1.96351206 5016 +v 5074 0.8954411 5016 +v 5076 -0.3918651 5016 +v 5078 -1.58322906 5016 +v 5080 -2.38696337 5016 +v 5082 -2.60628581 5016 +v 5084 -2.18749833 5016 +v 5086 -1.23313534 5016 +v 5088 0.0231424067 5016 +v 5090 1.273754 5016 +v 5092 2.21250629 5016 +v 5094 2.60955977 5016 +v 5096 2.367702 5016 +v 5098 1.5461483 5016 +v 5000 0 5018 +v 5002 -0.4042436 5018 +v 5004 -0.7095142 5018 +v 5006 -0.841071069 5018 +v 5008 -0.7667043 5018 +v 5010 -0.5046216 5018 +v 5012 -0.118990019 5018 +v 5014 0.295774519 5018 +v 5016 0.638123155 5018 +v 5018 0.824237 5018 +v 5020 0.808548868 5018 +v 5022 0.5948997 5018 +v 5024 0.235598445 5018 +v 5026 -0.181385562 5018 +v 5028 -0.5539601 5018 +v 5030 -0.790905833 5018 +v 5032 -0.8342103 5018 +v 5034 -0.673270941 5018 +v 5036 -0.3474914 5018 +v 5038 0.06336617 5018 +v 5040 0.458709478 5018 +v 5042 0.7417447 5018 +v 5044 0.843175 5018 +v 5046 0.7381666 5018 +v 5048 0.4524293 5018 +v 5050 0.05592151 5018 +v 5052 -0.3542778 5018 +v 5054 -0.677737534 5018 +v 5056 -0.83526355 5018 +v 5058 -0.7882878 5018 +v 5060 -0.5483118 5018 +v 5062 -0.174089909 5018 +v 5064 0.242755234 5018 +v 5066 0.6001654 5018 +v 5068 0.8106342 5018 +v 5070 0.8226315 5018 +v 5072 0.633219838 5018 +v 5074 0.288773924 5018 +v 5076 -0.126373947 5018 +v 5078 -0.5105811 5018 +v 5080 -0.769780159 5018 +v 5082 -0.8405102 5018 +v 5084 -0.705454 5018 +v 5086 -0.3976781 5018 +v 5088 0.00746327545 5018 +v 5090 0.41077736 5018 +v 5092 0.713518858 5018 +v 5094 0.841566 5018 +v 5096 0.763568461 5018 +v 5098 0.498622745 5018 +v 5000 0 5020 +v 5002 0.543979645 5020 +v 5004 0.954774 5020 +v 5006 1.13180649 5020 +v 5008 1.03173316 5020 +v 5010 0.6790557 5020 +v 5012 0.160121635 5020 +v 5014 -0.398015767 5020 +v 5016 -0.858705044 5020 +v 5018 -1.10915339 5020 +v 5020 -1.08804226 5020 +v 5022 -0.800540447 5020 +v 5024 -0.317038447 5020 +v 5026 0.24408564 5020 +v 5028 0.745449066 5020 +v 5030 1.06430054 5020 +v 5032 1.12257409 5020 +v 5034 0.906002462 5020 +v 5036 0.467609763 5020 +v 5038 -0.08527013 5020 +v 5040 -0.6172729 5020 +v 5042 -0.998145759 5020 +v 5044 -1.13463771 5020 +v 5046 -0.9933307 5020 +v 5048 -0.6088218 5020 +v 5050 -0.0752520561 5020 +v 5052 0.476742029 5020 +v 5054 0.912013054 5020 +v 5056 1.12399149 5020 +v 5058 1.06077754 5020 +v 5060 0.737848341 5020 +v 5062 0.234268084 5020 +v 5064 -0.326669157 5020 +v 5066 -0.807626367 5020 +v 5068 -1.09084845 5020 +v 5070 -1.10699284 5020 +v 5072 -0.85210675 5020 +v 5074 -0.388595223 5020 +v 5076 0.170058 5020 +v 5078 0.6870751 5020 +v 5080 1.03587222 5020 +v 5082 1.13105178 5020 +v 5084 0.9493103 5020 +v 5086 0.5351446 5020 +v 5088 -0.0100431275 5020 +v 5090 -0.552772 5020 +v 5092 -0.960163 5020 +v 5094 -1.13247263 5020 +v 5096 -1.02751338 5020 +v 5098 -0.670983136 5020 +v 5000 0 5022 +v 5002 1.35901761 5022 +v 5004 2.38530016 5022 +v 5006 2.82757831 5022 +v 5008 2.57756639 5022 +v 5010 1.69647646 5022 +v 5012 0.400029927 5022 +v 5014 -0.9943579 5022 +v 5016 -2.14529228 5022 +v 5018 -2.77098417 5022 +v 5020 -2.71824265 5022 +v 5022 -1.99998033 5022 +v 5024 -0.7920532 5022 +v 5026 0.609796166 5022 +v 5028 1.86234617 5022 +v 5030 2.65892887 5022 +v 5032 2.80451322 5022 +v 5034 2.26345468 5022 +v 5036 1.16822374 5022 +v 5038 -0.213029325 5022 +v 5040 -1.54212534 5022 +v 5042 -2.4936552 5022 +v 5044 -2.83465147 5022 +v 5046 -2.481626 5022 +v 5048 -1.52101207 5022 +v 5050 -0.18800129 5022 +v 5052 1.19103873 5022 +v 5054 2.278471 5022 +v 5056 2.808054 5022 +v 5058 2.65012741 5022 +v 5060 1.84335744 5022 +v 5062 0.585269034 5022 +v 5064 -0.8161135 5022 +v 5066 -2.017683 5022 +v 5068 -2.72525334 5022 +v 5070 -2.76558661 5022 +v 5072 -2.12880778 5022 +v 5074 -0.9708227 5022 +v 5076 0.424853772 5022 +v 5078 1.71651125 5022 +v 5080 2.58790684 5022 +v 5082 2.82569265 5022 +v 5084 2.37165022 5022 +v 5086 1.3369453 5022 +v 5088 -0.0250906218 5022 +v 5090 -1.38098347 5022 +v 5092 -2.39876342 5022 +v 5094 -2.82924223 5022 +v 5096 -2.56702423 5022 +v 5098 -1.67630887 5022 +v 5000 0 5024 +v 5002 1.84132063 5024 +v 5004 3.23182154 5024 +v 5006 3.83106017 5024 +v 5008 3.49232125 5024 +v 5010 2.29854059 5024 +v 5012 0.5419969 5024 +v 5014 -1.34724653 5024 +v 5016 -2.906637 5024 +v 5018 -3.75438142 5024 +v 5020 -3.68292236 5024 +v 5022 -2.70975518 5024 +v 5024 -1.07314575 5024 +v 5026 0.8262073 5024 +v 5028 2.523276 5024 +v 5030 3.60255861 5024 +v 5032 3.79980946 5024 +v 5034 3.06673431 5024 +v 5036 1.58281565 5024 +v 5038 -0.2886315 5024 +v 5040 -2.08941174 5024 +v 5042 -3.37863088 5024 +v 5044 -3.84064364 5024 +v 5046 -3.36233258 5024 +v 5048 -2.06080556 5024 +v 5050 -0.254721254 5024 +v 5052 1.61372757 5024 +v 5054 3.08707976 5024 +v 5056 3.804607 5024 +v 5058 3.59063363 5024 +v 5060 2.49754834 5024 +v 5062 0.7929757 5024 +v 5064 -1.10574484 5024 +v 5066 -2.73374033 5024 +v 5068 -3.692421 5024 +v 5070 -3.74706817 5024 +v 5072 -2.88430238 5024 +v 5074 -1.31535888 5024 +v 5076 0.575630546 5024 +v 5078 2.3256855 5024 +v 5080 3.50633168 5024 +v 5082 3.82850528 5024 +v 5084 3.21332741 5024 +v 5086 1.811415 5024 +v 5088 -0.0339950547 5024 +v 5090 -1.871082 5024 +v 5092 -3.2500627 5024 +v 5094 -3.833315 5024 +v 5096 -3.47803783 5024 +v 5098 -2.27121568 5024 +v 5000 0 5026 +v 5002 1.87280428 5026 +v 5004 3.28708053 5026 +v 5006 3.8965652 5026 +v 5008 3.55203438 5026 +v 5010 2.337842 5026 +v 5012 0.5512642 5026 +v 5014 -1.37028229 5026 +v 5016 -2.956336 5026 +v 5018 -3.81857538 5026 +v 5020 -3.74589443 5026 +v 5022 -2.75608778 5026 +v 5024 -1.09149492 5026 +v 5026 0.8403341 5026 +v 5028 2.56642 5026 +v 5030 3.66415668 5026 +v 5032 3.86478019 5026 +v 5034 3.11917067 5026 +v 5036 1.60987937 5026 +v 5038 -0.293566644 5026 +v 5040 -2.12513733 5026 +v 5042 -3.43640018 5026 +v 5044 -3.90631247 5026 +v 5046 -3.41982317 5026 +v 5048 -2.096042 5026 +v 5050 -0.259076566 5026 +v 5052 1.64131975 5026 +v 5054 3.13986373 5026 +v 5056 3.86965966 5026 +v 5058 3.65202785 5026 +v 5060 2.54025245 5026 +v 5062 0.80653435 5026 +v 5064 -1.12465131 5026 +v 5066 -2.780483 5026 +v 5068 -3.75555563 5026 +v 5070 -3.8111372 5026 +v 5072 -2.9336195 5026 +v 5074 -1.33784938 5026 +v 5076 0.585472941 5026 +v 5078 2.365451 5026 +v 5080 3.56628442 5026 +v 5082 3.89396667 5026 +v 5084 3.26827025 5026 +v 5086 1.84238732 5026 +v 5088 -0.03457632 5026 +v 5090 -1.9030745 5026 +v 5092 -3.30563378 5026 +v 5094 -3.89885831 5026 +v 5096 -3.53750658 5026 +v 5098 -2.31004977 5026 +v 5000 0 5028 +v 5002 1.44576 5028 +v 5004 2.53754735 5028 +v 5006 3.008055 5028 +v 5008 2.74208546 5028 +v 5010 1.804758 5028 +v 5012 0.425562739 5028 +v 5014 -1.05782509 5028 +v 5016 -2.28222036 5028 +v 5018 -2.94784856 5028 +v 5020 -2.8917408 5028 +v 5022 -2.12763381 5028 +v 5024 -0.842607856 5028 +v 5026 0.648717761 5028 +v 5028 1.98121476 5028 +v 5030 2.82864118 5028 +v 5032 2.98351765 5028 +v 5034 2.407925 5028 +v 5036 1.2427882 5028 +v 5038 -0.226626411 5028 +v 5040 -1.640555 5028 +v 5042 -2.65281844 5028 +v 5044 -3.01557946 5028 +v 5046 -2.64002132 5028 +v 5048 -1.61809421 5028 +v 5050 -0.2000009 5028 +v 5052 1.26705945 5028 +v 5054 2.42389965 5028 +v 5056 2.98728466 5028 +v 5058 2.819278 5028 +v 5060 1.96101391 5028 +v 5062 0.6226252 5028 +v 5064 -0.8682038 5028 +v 5066 -2.14646626 5028 +v 5068 -2.89919877 5028 +v 5070 -2.94210649 5028 +v 5072 -2.264684 5028 +v 5074 -1.03278768 5028 +v 5076 0.451971054 5028 +v 5078 1.8260715 5028 +v 5080 2.753086 5028 +v 5082 3.006049 5028 +v 5084 2.52302623 5028 +v 5086 1.42227888 5028 +v 5088 -0.0266920887 5028 +v 5090 -1.46912789 5028 +v 5092 -2.55186987 5028 +v 5094 -3.00982523 5028 +v 5096 -2.73087025 5028 +v 5098 -1.78330314 5028 +v 5000 0 5030 +v 5002 0.6647433 5030 +v 5004 1.16673422 5030 +v 5006 1.383068 5030 +v 5008 1.26077831 5030 +v 5010 0.829806268 5030 +v 5012 0.1956687 5030 +v 5014 -0.486375421 5030 +v 5016 -1.04933786 5030 +v 5018 -1.35538578 5030 +v 5020 -1.329588 5030 +v 5022 -0.978260756 5030 +v 5024 -0.3874211 5030 +v 5026 0.298272729 5030 +v 5028 0.910939038 5030 +v 5030 1.30057561 5030 +v 5032 1.371786 5030 +v 5034 1.1071353 5030 +v 5036 0.5714193 5030 +v 5038 -0.104200132 5030 +v 5040 -0.754307747 5030 +v 5042 -1.21973443 5030 +v 5044 -1.38652766 5030 +v 5046 -1.2138505 5030 +v 5048 -0.7439805 5030 +v 5050 -0.091958046 5030 +v 5052 0.5825789 5030 +v 5054 1.11448026 5030 +v 5056 1.373518 5030 +v 5058 1.29627049 5030 +v 5060 0.901650965 5030 +v 5062 0.286275685 5030 +v 5064 -0.3991898 5030 +v 5066 -0.9869197 5030 +v 5068 -1.33301723 5030 +v 5070 -1.35274565 5030 +v 5072 -1.04127479 5030 +v 5074 -0.474863529 5030 +v 5076 0.207810923 5030 +v 5078 0.839606 5030 +v 5080 1.26583624 5030 +v 5082 1.38214564 5030 +v 5084 1.16005754 5030 +v 5086 0.653946936 5030 +v 5088 -0.0122727053 5030 +v 5090 -0.6754876 5030 +v 5092 -1.17331958 5030 +v 5094 -1.38388193 5030 +v 5096 -1.25562179 5030 +v 5098 -0.8199416 5030 +v 5000 0 5032 +v 5002 -0.279025733 5032 +v 5004 -0.4897362 5032 +v 5006 -0.5805422 5032 +v 5008 -0.5292112 5032 +v 5010 -0.348310858 5032 +v 5012 -0.08213186 5032 +v 5014 0.204155892 5032 +v 5016 0.440459162 5032 +v 5018 0.568922639 5032 +v 5020 0.5580941 5032 +v 5022 0.410624564 5032 +v 5024 0.162619859 5032 +v 5026 -0.125199869 5032 +v 5028 -0.3823663 5032 +v 5030 -0.5459161 5032 +v 5032 -0.5758066 5032 +v 5034 -0.464719623 5032 +v 5036 -0.239853024 5032 +v 5038 0.0437379628 5032 +v 5040 0.31662038 5032 +v 5042 0.511983037 5032 +v 5044 0.5819945 5032 +v 5046 0.509513259 5032 +v 5048 0.3122855 5032 +v 5050 0.0385993533 5032 +v 5052 -0.244537264 5032 +v 5054 -0.467802644 5032 +v 5056 -0.5765336 5032 +v 5058 -0.544109046 5032 +v 5060 -0.378467619 5032 +v 5062 -0.120164104 5032 +v 5064 0.167559758 5032 +v 5066 0.414259166 5032 +v 5068 0.5595335 5032 +v 5070 0.567814469 5032 +v 5072 0.437074661 5032 +v 5074 0.199323773 5032 +v 5076 -0.08722855 5032 +v 5078 -0.3524243 5032 +v 5080 -0.5313343 5032 +v 5082 -0.5801551 5032 +v 5084 -0.486933678 5032 +v 5086 -0.274493963 5032 +v 5088 0.005151463 5032 +v 5090 0.28353563 5032 +v 5092 0.4925004 5032 +v 5094 0.580883861 5032 +v 5096 0.52704674 5032 +v 5098 0.344170153 5032 +v 5000 0 5034 +v 5002 -1.15447962 5034 +v 5004 -2.026302 5034 +v 5006 -2.40201545 5034 +v 5008 -2.18963146 5034 +v 5010 -1.44114935 5034 +v 5012 -0.3398237 5034 +v 5014 0.8447027 5034 +v 5016 1.82241654 5034 +v 5018 2.353939 5034 +v 5020 2.30913544 5034 +v 5022 1.69897473 5034 +v 5024 0.67284584 5034 +v 5026 -0.5180192 5034 +v 5028 -1.58205509 5034 +v 5030 -2.25874853 5034 +v 5032 -2.38242173 5034 +v 5034 -1.922795 5034 +v 5036 -0.992401 5034 +v 5038 0.18096748 5034 +v 5040 1.31002879 5034 +v 5042 2.11834931 5034 +v 5044 2.408024 5034 +v 5046 2.10813046 5034 +v 5048 1.29209316 5034 +v 5050 0.15970628 5034 +v 5052 -1.01178229 5034 +v 5054 -1.93555129 5034 +v 5056 -2.38542986 5034 +v 5058 -2.25127172 5034 +v 5060 -1.56592417 5034 +v 5062 -0.497183532 5034 +v 5064 0.693284869 5034 +v 5066 1.714013 5034 +v 5068 2.315091 5034 +v 5070 2.34935379 5034 +v 5072 1.808413 5034 +v 5074 0.8247097 5034 +v 5076 -0.360911459 5034 +v 5078 -1.45816886 5034 +v 5080 -2.19841576 5034 +v 5082 -2.40041375 5034 +v 5084 -2.01470661 5034 +v 5086 -1.13572919 5034 +v 5088 0.0213143751 5034 +v 5090 1.17313945 5034 +v 5092 2.037739 5034 +v 5094 2.403429 5034 +v 5096 2.180676 5034 +v 5098 1.42401707 5034 +v 5000 0 5036 +v 5002 -1.74727643 5036 +v 5004 -3.06675863 5036 +v 5006 -3.63539147 5036 +v 5008 -3.3139534 5036 +v 5010 -2.18114424 5036 +v 5012 -0.51431483 5036 +v 5014 1.2784369 5036 +v 5016 2.75818253 5036 +v 5018 3.562629 5036 +v 5020 3.49481964 5036 +v 5022 2.57135653 5036 +v 5024 1.01833558 5036 +v 5026 -0.784009337 5036 +v 5028 -2.39440155 5036 +v 5030 -3.4185605 5036 +v 5032 -3.605737 5036 +v 5034 -2.910103 5036 +v 5036 -1.50197446 5036 +v 5038 0.27388984 5036 +v 5040 1.98269641 5036 +v 5042 3.20606971 5036 +v 5044 3.64448547 5036 +v 5046 3.19060373 5036 +v 5048 1.95555127 5036 +v 5050 0.241711542 5036 +v 5052 -1.53130758 5036 +v 5054 -2.92940927 5036 +v 5056 -3.61028934 5036 +v 5058 -3.40724468 5036 +v 5060 -2.36998773 5036 +v 5062 -0.7524751 5036 +v 5064 1.04926968 5036 +v 5066 2.59411645 5036 +v 5068 3.50383329 5036 +v 5070 3.55568933 5036 +v 5072 2.73698878 5036 +v 5074 1.24817789 5036 +v 5076 -0.5462306 5036 +v 5078 -2.20690274 5036 +v 5080 -3.327248 5036 +v 5082 -3.63296723 5036 +v 5084 -3.04920888 5036 +v 5086 -1.7188983 5036 +v 5088 0.0322587825 5036 +v 5090 1.7755177 5036 +v 5092 3.084068 5036 +v 5094 3.637531 5036 +v 5096 3.3003993 5036 +v 5098 2.155215 5036 +v 5000 0 5038 +v 5002 -1.91227925 5038 +v 5004 -3.35636568 5038 +v 5006 -3.978697 5038 +v 5008 -3.62690425 5038 +v 5010 -2.387119 5038 +v 5012 -0.562883735 5038 +v 5014 1.39916515 5038 +v 5016 3.01864958 5038 +v 5018 3.89906335 5038 +v 5020 3.82485056 5038 +v 5022 2.81418061 5038 +v 5024 1.11450136 5038 +v 5026 -0.858046651 5038 +v 5028 -2.620515 5038 +v 5030 -3.74138975 5038 +v 5032 -3.946242 5038 +v 5034 -3.1849165 5038 +v 5036 -1.64381242 5038 +v 5038 0.299754441 5038 +v 5040 2.169931 5038 +v 5042 3.50883269 5038 +v 5044 3.98864961 5038 +v 5046 3.49190617 5038 +v 5048 2.14022231 5038 +v 5050 0.2645374 5038 +v 5052 -1.67591548 5038 +v 5054 -3.20604587 5038 +v 5056 -3.95122457 5038 +v 5058 -3.72900534 5038 +v 5060 -2.59379578 5038 +v 5062 -0.8235345 5038 +v 5064 1.14835668 5038 +v 5066 2.83909 5038 +v 5068 3.83471513 5038 +v 5070 3.89146829 5038 +v 5072 2.99545431 5038 +v 5074 1.36604857 5038 +v 5076 -0.597813547 5038 +v 5078 -2.41531 5038 +v 5080 -3.64145446 5038 +v 5082 -3.9760437 5038 +v 5084 -3.337159 5038 +v 5086 -1.88122118 5038 +v 5088 0.0353051163 5038 +v 5090 1.94318748 5038 +v 5092 3.37531 5038 +v 5094 3.98103857 5038 +v 5096 3.61207032 5038 +v 5098 2.358741 5038 +v 5000 0 5040 +v 5002 -1.60908926 5040 +v 5004 -2.82421732 5040 +v 5006 -3.34787846 5040 +v 5008 -3.05186224 5040 +v 5010 -2.00864363 5040 +v 5012 -0.4736391 5040 +v 5014 1.17732882 5040 +v 5016 2.54004574 5040 +v 5018 3.28087068 5040 +v 5020 3.21842432 5040 +v 5022 2.367995 5040 +v 5024 0.9377983 5040 +v 5026 -0.722004235 5040 +v 5028 -2.205035 5040 +v 5030 -3.14819622 5040 +v 5032 -3.32056928 5040 +v 5034 -2.67995119 5040 +v 5036 -1.38318753 5040 +v 5038 0.252228677 5040 +v 5040 1.82589054 5040 +v 5042 2.9525106 5040 +v 5044 3.35625315 5040 +v 5046 2.938268 5040 +v 5048 1.80089223 5040 +v 5050 0.222595245 5040 +v 5052 -1.41020072 5040 +v 5054 -2.69773054 5040 +v 5056 -3.32476163 5040 +v 5058 -3.13777518 5040 +v 5060 -2.182552 5040 +v 5062 -0.6929639 5040 +v 5064 0.9662859 5040 +v 5066 2.388955 5040 +v 5068 3.22672486 5040 +v 5070 3.27447987 5040 +v 5072 2.520528 5040 +v 5074 1.14946294 5040 +v 5076 -0.5030308 5040 +v 5078 -2.032365 5040 +v 5080 -3.06410551 5040 +v 5082 -3.345646 5040 +v 5084 -2.80805564 5040 +v 5086 -1.58295548 5040 +v 5088 0.0297075268 5040 +v 5090 1.635097 5040 +v 5092 2.84015775 5040 +v 5094 3.34984875 5040 +v 5096 3.03938 5040 +v 5098 1.984765 5040 +v 5000 0 5042 +v 5002 -0.91193825 5042 +v 5004 -1.60060215 5042 +v 5006 -1.89738286 5042 +v 5008 -1.72961807 5042 +v 5010 -1.13838243 5042 +v 5012 -0.2684311 5042 +v 5014 0.6672415 5042 +v 5016 1.43955016 5042 +v 5018 1.85940671 5042 +v 5020 1.82401574 5042 +v 5022 1.34204185 5042 +v 5024 0.531489551 5042 +v 5026 -0.40919 5042 +v 5028 -1.24968553 5042 +v 5030 -1.7842145 5042 +v 5032 -1.88190556 5042 +v 5034 -1.51884055 5042 +v 5036 -0.7839103 5042 +v 5038 0.142948538 5042 +v 5040 1.03480864 5042 +v 5042 1.67331135 5042 +v 5044 1.90212917 5042 +v 5046 1.66523933 5042 +v 5048 1.020641 5042 +v 5050 0.12615405 5042 +v 5052 -0.7992198 5042 +v 5054 -1.52891672 5042 +v 5056 -1.88428164 5042 +v 5058 -1.77830851 5042 +v 5060 -1.2369436 5042 +v 5062 -0.392731667 5042 +v 5064 0.547634661 5042 +v 5066 1.35392082 5042 +v 5068 1.82872009 5042 +v 5070 1.85578477 5042 +v 5072 1.42848873 5042 +v 5074 0.6514488 5042 +v 5076 -0.2850886 5042 +v 5078 -1.15182626 5042 +v 5080 -1.73655677 5042 +v 5082 -1.89611757 5042 +v 5084 -1.59144258 5042 +v 5086 -0.8971271 5042 +v 5088 0.0168364979 5042 +v 5090 0.9266779 5042 +v 5092 1.60963631 5042 +v 5094 1.89849949 5042 +v 5096 1.72254384 5042 +v 5098 1.12484944 5042 +v 5000 0 5044 +v 5002 0.008487171 5044 +v 5004 0.0148963854 5044 +v 5006 0.017658446 5044 +v 5008 0.0160971023 5044 +v 5010 0.0105946269 5044 +v 5012 0.002498218 5044 +v 5014 -0.0062098424 5044 +v 5016 -0.0133975167 5044 +v 5018 -0.0173050128 5044 +v 5020 -0.0169756375 5044 +v 5022 -0.0124900332 5044 +v 5024 -0.0049464344 5044 +v 5026 0.00380822434 5044 +v 5028 0.0116304969 5044 +v 5030 0.0166052189 5044 +v 5032 0.0175144039 5044 +v 5034 0.0141354511 5044 +v 5036 0.007295648 5044 +v 5038 -0.00133038464 5044 +v 5040 -0.009630693 5044 +v 5042 -0.0155730713 5044 +v 5044 -0.0177026186 5044 +v 5046 -0.0154979471 5044 +v 5048 -0.009498839 5044 +v 5050 -0.00117408275 5044 +v 5052 0.00743812928 5044 +v 5054 0.0142292287 5044 +v 5056 0.0175365154 5044 +v 5058 0.0165502522 5044 +v 5060 0.0115119107 5044 +v 5062 0.00365505088 5044 +v 5064 -0.005096692 5044 +v 5066 -0.0126005877 5044 +v 5068 -0.017019419 5044 +v 5070 -0.0172713045 5044 +v 5072 -0.0132945711 5044 +v 5074 -0.00606286339 5044 +v 5076 0.00265324488 5044 +v 5078 0.0107197464 5044 +v 5080 0.01616168 5044 +v 5082 0.01764667 5044 +v 5084 0.0148111405 5044 +v 5086 0.008349327 5044 +v 5088 -0.000156692884 5044 +v 5090 -0.008624349 5044 +v 5092 -0.0149804642 5044 +v 5094 -0.01766884 5044 +v 5096 -0.0160312653 5044 +v 5098 -0.0104686795 5044 +v 5000 0 5046 +v 5002 0.926834643 5046 +v 5004 1.62674773 5046 +v 5006 1.92837632 5046 +v 5008 1.75787115 5046 +v 5010 1.15697777 5046 +v 5012 0.272815883 5046 +v 5014 -0.6781408 5046 +v 5016 -1.463065 5046 +v 5018 -1.88977993 5046 +v 5020 -1.85381079 5046 +v 5022 -1.363964 5046 +v 5024 -0.5401713 5046 +v 5026 0.415874064 5046 +v 5028 1.270099 5046 +v 5030 1.81335938 5046 +v 5032 1.91264617 5046 +v 5034 1.54365051 5046 +v 5036 0.7967153 5046 +v 5038 -0.14528358 5046 +v 5040 -1.051712 5046 +v 5042 -1.70064461 5046 +v 5044 -1.93320012 5046 +v 5046 -1.69244087 5046 +v 5048 -1.037313 5046 +v 5050 -0.128214747 5046 +v 5052 0.812274933 5046 +v 5054 1.55389142 5046 +v 5056 1.91506112 5046 +v 5058 1.807357 5046 +v 5060 1.25714886 5046 +v 5062 0.399146855 5046 +v 5064 -0.5565802 5046 +v 5066 -1.376037 5046 +v 5068 -1.85859191 5046 +v 5070 -1.88609874 5046 +v 5072 -1.45182288 5046 +v 5074 -0.662090063 5046 +v 5076 0.28974548 5046 +v 5078 1.17064118 5046 +v 5080 1.76492321 5046 +v 5082 1.92709041 5046 +v 5084 1.61743855 5046 +v 5086 0.911781549 5046 +v 5088 -0.01711152 5046 +v 5090 -0.9418151 5046 +v 5092 -1.63592947 5046 +v 5094 -1.92951119 5046 +v 5096 -1.7506814 5046 +v 5098 -1.14322364 5046 +v 5000 0 5048 +v 5002 1.61826062 5048 +v 5004 2.84031439 5048 +v 5006 3.36696029 5048 +v 5008 3.06925678 5048 +v 5010 2.02009225 5048 +v 5012 0.476338685 5048 +v 5014 -1.18403924 5048 +v 5016 -2.55452323 5048 +v 5018 -3.29957056 5048 +v 5020 -3.23676825 5048 +v 5022 -2.381492 5048 +v 5024 -0.9431434 5048 +v 5026 0.7261194 5048 +v 5028 2.217603 5048 +v 5030 3.16613984 5048 +v 5032 3.33949542 5048 +v 5034 2.695226 5048 +v 5036 1.39107132 5048 +v 5038 -0.253666282 5048 +v 5040 -1.83629751 5048 +v 5042 -2.96933913 5048 +v 5044 -3.375383 5048 +v 5046 -2.955015 5048 +v 5048 -1.81115675 5048 +v 5050 -0.223863974 5048 +v 5052 1.4182384 5048 +v 5054 2.71310663 5048 +v 5056 3.34371185 5048 +v 5058 3.15565944 5048 +v 5060 2.19499183 5048 +v 5062 0.6969136 5048 +v 5064 -0.971793354 5048 +v 5066 -2.40257144 5048 +v 5068 -3.24511623 5048 +v 5070 -3.29314327 5048 +v 5072 -2.53489423 5048 +v 5074 -1.15601456 5048 +v 5076 0.5058979 5048 +v 5078 2.043949 5048 +v 5080 3.08157 5048 +v 5082 3.364715 5048 +v 5084 2.82406068 5048 +v 5086 1.59197772 5048 +v 5088 -0.0298768487 5048 +v 5090 -1.64441657 5048 +v 5092 -2.856346 5048 +v 5094 -3.36894178 5048 +v 5096 -3.05670357 5048 +v 5098 -1.99607754 5048 +v 5000 0 5050 +v 5002 1.91347992 5050 +v 5004 3.358473 5050 +v 5006 3.98119521 5050 +v 5008 3.62918139 5050 +v 5010 2.38861775 5050 +v 5012 0.5632372 5050 +v 5014 -1.40004361 5050 +v 5016 -3.02054477 5050 +v 5018 -3.90151143 5050 +v 5020 -3.827252 5050 +v 5022 -2.81594753 5050 +v 5024 -1.11520112 5050 +v 5026 0.8585854 5050 +v 5028 2.62216043 5050 +v 5030 3.743739 5050 +v 5032 3.94871974 5050 +v 5034 3.18691635 5050 +v 5036 1.64484453 5050 +v 5038 -0.299942642 5050 +v 5040 -2.17129326 5050 +v 5042 -3.51103568 5050 +v 5044 -3.991154 5050 +v 5046 -3.49409866 5050 +v 5048 -2.14156628 5050 +v 5050 -0.264703482 5050 +v 5052 1.67696774 5050 +v 5054 3.20805883 5050 +v 5056 3.95370531 5050 +v 5058 3.73134661 5050 +v 5060 2.59542441 5050 +v 5062 0.824051559 5050 +v 5064 -1.14907765 5050 +v 5066 -2.84087253 5050 +v 5068 -3.837123 5050 +v 5070 -3.8939116 5050 +v 5072 -2.997335 5050 +v 5074 -1.36690629 5050 +v 5076 0.5981889 5050 +v 5078 2.41682649 5050 +v 5080 3.643741 5050 +v 5082 3.97854018 5050 +v 5084 3.33925414 5050 +v 5086 1.8824023 5050 +v 5088 -0.0353272855 5050 +v 5090 -1.94440758 5050 +v 5092 -3.377429 5050 +v 5094 -3.98353815 5050 +v 5096 -3.61433816 5050 +v 5098 -2.360222 5050 +v 5000 0 5052 +v 5002 1.74021268 5052 +v 5004 3.05436063 5052 +v 5006 3.62069464 5052 +v 5008 3.30055618 5052 +v 5010 2.17232633 5052 +v 5012 0.5122356 5052 +v 5014 -1.27326846 5052 +v 5016 -2.747032 5052 +v 5018 -3.54822636 5052 +v 5020 -3.48069119 5052 +v 5022 -2.56096125 5052 +v 5024 -1.01421881 5052 +v 5026 0.7808398 5052 +v 5028 2.38472152 5052 +v 5030 3.40474033 5052 +v 5032 3.59115982 5052 +v 5034 2.89833832 5052 +v 5036 1.49590242 5052 +v 5038 -0.2727826 5052 +v 5040 -1.9746809 5052 +v 5042 -3.19310856 5052 +v 5044 -3.62975168 5052 +v 5046 -3.177705 5052 +v 5048 -1.94764555 5052 +v 5050 -0.240734369 5052 +v 5052 1.52511692 5052 +v 5054 2.91756654 5052 +v 5056 3.595694 5052 +v 5058 3.39347 5052 +v 5060 2.36040664 5052 +v 5062 0.749433041 5052 +v 5064 -1.04502773 5052 +v 5066 -2.58362913 5052 +v 5068 -3.48966813 5052 +v 5070 -3.54131484 5052 +v 5072 -2.72592378 5052 +v 5074 -1.24313176 5052 +v 5076 0.5440224 5052 +v 5078 2.19798088 5052 +v 5080 3.313797 5052 +v 5082 3.61828017 5052 +v 5084 3.036882 5052 +v 5086 1.71194923 5052 +v 5088 -0.0321283676 5052 +v 5090 -1.76833975 5052 +v 5092 -3.0716002 5052 +v 5094 -3.62282538 5052 +v 5096 -3.28705668 5052 +v 5098 -2.146502 5052 +v 5000 0 5054 +v 5002 1.14088058 5054 +v 5004 2.00243378 5054 +v 5006 2.37372136 5054 +v 5008 2.163839 5054 +v 5010 1.42417359 5054 +v 5012 0.3358208 5054 +v 5014 -0.834752738 5054 +v 5016 -1.80094969 5054 +v 5018 -2.32621145 5054 +v 5020 -2.28193545 5054 +v 5022 -1.67896187 5054 +v 5024 -0.664920151 5054 +v 5026 0.5119173 5054 +v 5028 1.56341958 5054 +v 5030 2.23214221 5054 +v 5032 2.35435843 5054 +v 5034 1.90014589 5054 +v 5036 0.980711162 5054 +v 5038 -0.178835809 5054 +v 5040 -1.29459763 5054 +v 5042 -2.09339666 5054 +v 5044 -2.37965918 5054 +v 5046 -2.08329821 5054 +v 5048 -1.27687323 5054 +v 5050 -0.157825053 5054 +v 5052 0.999864161 5054 +v 5054 1.91275179 5054 +v 5056 2.357331 5054 +v 5058 2.22475338 5054 +v 5060 1.54747868 5054 +v 5062 0.491327047 5054 +v 5064 -0.6851185 5054 +v 5066 -1.6938231 5054 +v 5068 -2.28782082 5054 +v 5070 -2.32168 5054 +v 5072 -1.78711116 5054 +v 5074 -0.81499517 5054 +v 5076 0.356660157 5054 +v 5078 1.44099259 5054 +v 5080 2.17252 5054 +v 5082 2.3721385 5054 +v 5084 1.99097478 5054 +v 5086 1.12235117 5054 +v 5088 -0.0210633054 5054 +v 5090 -1.15932071 5054 +v 5092 -2.013736 5054 +v 5094 -2.37511849 5054 +v 5096 -2.154989 5054 +v 5098 -1.40724313 5054 +v 5000 0 5056 +v 5002 0.262221247 5056 +v 5004 0.4602416 5056 +v 5006 0.5455787 5056 +v 5008 0.497339159 5056 +v 5010 0.327333629 5056 +v 5012 0.07718542 5056 +v 5014 -0.191860482 5056 +v 5016 -0.413932264 5056 +v 5018 -0.534658968 5056 +v 5020 -0.524482548 5056 +v 5022 -0.385894448 5056 +v 5024 -0.152825981 5056 +v 5026 0.117659628 5056 +v 5028 0.359338075 5056 +v 5030 0.513038 5056 +v 5032 0.541128337 5056 +v 5034 0.4367316 5056 +v 5036 0.225407735 5056 +v 5038 -0.04110382 5056 +v 5040 -0.297551721 5056 +v 5042 -0.4811486 5056 +v 5044 -0.5469435 5056 +v 5046 -0.478827566 5056 +v 5048 -0.293477952 5056 +v 5050 -0.0362746827 5056 +v 5052 0.22980988 5056 +v 5054 0.439628959 5056 +v 5056 0.5418116 5056 +v 5058 0.5113398 5056 +v 5060 0.3556742 5056 +v 5062 0.112927154 5056 +v 5064 -0.157468379 5056 +v 5066 -0.389310151 5056 +v 5068 -0.5258352 5056 +v 5070 -0.5336175 5056 +v 5072 -0.4107516 5056 +v 5074 -0.187319383 5056 +v 5076 0.08197516 5056 +v 5078 0.331199318 5056 +v 5080 0.499334365 5056 +v 5082 0.5452149 5056 +v 5084 0.457607836 5056 +v 5086 0.2579624 5056 +v 5088 -0.00484121358 5056 +v 5090 -0.266459525 5056 +v 5092 -0.4628393 5056 +v 5094 -0.5458998 5056 +v 5096 -0.495305061 5056 +v 5098 -0.3234423 5056 +v 5000 0 5058 +v 5002 -0.680639 5058 +v 5004 -1.19463384 5058 +v 5006 -1.41614068 5058 +v 5008 -1.29092681 5058 +v 5010 -0.8496491 5058 +v 5012 -0.200347647 5058 +v 5014 0.498005927 5058 +v 5016 1.07443023 5058 +v 5018 1.38779664 5058 +v 5020 1.361382 5058 +v 5022 1.00165343 5058 +v 5024 0.396685332 5058 +v 5026 -0.3054052 5058 +v 5028 -0.9327219 5058 +v 5030 -1.33167577 5058 +v 5032 -1.40458894 5058 +v 5034 -1.13360977 5058 +v 5036 -0.5850834 5058 +v 5038 0.10669183 5058 +v 5040 0.7723452 5058 +v 5042 1.24890149 5058 +v 5044 1.41968322 5058 +v 5046 1.24287689 5058 +v 5048 0.761771 5058 +v 5050 0.0941569954 5058 +v 5052 -0.5965099 5058 +v 5054 -1.14113033 5058 +v 5056 -1.40636241 5058 +v 5058 -1.32726777 5058 +v 5060 -0.923211753 5058 +v 5062 -0.293121278 5058 +v 5064 0.408735484 5058 +v 5066 1.0105195 5058 +v 5068 1.3648932 5058 +v 5070 1.38509333 5058 +v 5072 1.06617439 5058 +v 5074 0.48621875 5058 +v 5076 -0.212780222 5058 +v 5078 -0.859683156 5058 +v 5080 -1.29610574 5058 +v 5082 -1.4151963 5058 +v 5084 -1.18779755 5058 +v 5086 -0.6695845 5058 +v 5088 0.0125661781 5058 +v 5090 0.6916402 5058 +v 5092 1.20137668 5058 +v 5094 1.41697407 5058 +v 5096 1.28564692 5058 +v 5098 0.8395485 5058 +v 5000 0 5060 +v 5002 -1.45685518 5060 +v 5004 -2.55702114 5060 +v 5006 -3.03113961 5060 +v 5008 -2.763129 5060 +v 5010 -1.81860816 5060 +v 5012 -0.428828627 5060 +v 5014 1.06594312 5060 +v 5016 2.29973483 5060 +v 5018 2.97047114 5060 +v 5020 2.9139328 5060 +v 5022 2.14396167 5060 +v 5024 0.849074244 5060 +v 5026 -0.6536962 5060 +v 5028 -1.99641907 5060 +v 5030 -2.850349 5060 +v 5032 -3.006414 5060 +v 5034 -2.426404 5060 +v 5036 -1.25232577 5060 +v 5038 0.2283656 5060 +v 5040 1.65314507 5060 +v 5042 2.673177 5060 +v 5044 3.0387218 5060 +v 5046 2.66028166 5060 +v 5048 1.63051188 5060 +v 5050 0.201535761 5060 +v 5052 -1.27678323 5060 +v 5054 -2.44250131 5060 +v 5056 -3.0102098 5060 +v 5058 -2.84091377 5060 +v 5060 -1.97606325 5060 +v 5062 -0.6274034 5060 +v 5064 0.874866664 5060 +v 5066 2.16293883 5060 +v 5068 2.921448 5060 +v 5070 2.964685 5060 +v 5072 2.28206372 5060 +v 5074 1.04071355 5060 +v 5076 -0.4554396 5060 +v 5078 -1.84008527 5060 +v 5080 -2.77421379 5060 +v 5082 -3.029118 5060 +v 5084 -2.54238868 5060 +v 5086 -1.4331938 5060 +v 5088 0.0268969312 5060 +v 5090 1.48040235 5060 +v 5092 2.57145381 5060 +v 5094 3.03292346 5060 +v 5096 2.75182772 5060 +v 5098 1.79698861 5060 +v 5000 0 5062 +v 5002 -1.87638235 5062 +v 5004 -3.29336071 5062 +v 5006 -3.90400982 5062 +v 5008 -3.55882072 5062 +v 5010 -2.34230852 5062 +v 5012 -0.552317441 5062 +v 5014 1.37290025 5062 +v 5016 2.96198416 5062 +v 5018 3.825871 5062 +v 5020 3.753051 5062 +v 5022 2.76135349 5062 +v 5024 1.09358025 5062 +v 5026 -0.841939569 5062 +v 5028 -2.57132316 5062 +v 5030 -3.67115712 5062 +v 5032 -3.872164 5062 +v 5034 -3.12513 5062 +v 5036 -1.61295509 5062 +v 5038 0.2941275 5062 +v 5040 2.12919736 5062 +v 5042 3.44296551 5062 +v 5044 3.91377568 5062 +v 5046 3.42635679 5062 +v 5048 2.10004663 5062 +v 5050 0.259571552 5062 +v 5052 -1.64445555 5062 +v 5054 -3.14586258 5062 +v 5056 -3.87705278 5062 +v 5058 -3.65900517 5062 +v 5060 -2.5451057 5062 +v 5062 -0.808075249 5062 +v 5064 1.1268 5062 +v 5066 2.78579521 5062 +v 5068 3.7627306 5062 +v 5070 3.8184185 5062 +v 5072 2.93922424 5062 +v 5074 1.34040546 5062 +v 5076 -0.5865915 5062 +v 5078 -2.36997032 5062 +v 5080 -3.573098 5062 +v 5082 -3.90140629 5062 +v 5084 -3.27451444 5062 +v 5086 -1.84590721 5062 +v 5088 0.034642376 5062 +v 5090 1.90671039 5062 +v 5092 3.31194925 5062 +v 5094 3.90630722 5062 +v 5096 3.54426527 5062 +v 5098 2.31446338 5062 +v 5000 0 5064 +v 5002 -1.83650565 5064 +v 5004 -3.22337055 5064 +v 5006 -3.821042 5064 +v 5008 -3.483189 5064 +v 5010 -2.29253 5064 +v 5012 -0.5405796 5064 +v 5014 1.34372354 5064 +v 5016 2.89903641 5064 +v 5018 3.744564 5064 +v 5020 3.67329168 5064 +v 5022 2.70266938 5064 +v 5024 1.07033956 5064 +v 5026 -0.8240468 5064 +v 5028 -2.51667786 5064 +v 5030 -3.59313822 5064 +v 5032 -3.78987312 5064 +v 5034 -3.058715 5064 +v 5036 -1.5786767 5064 +v 5038 0.287876755 5064 +v 5040 2.083948 5064 +v 5042 3.369796 5064 +v 5044 3.8306005 5064 +v 5046 3.35354018 5064 +v 5048 2.05541658 5064 +v 5050 0.254055172 5064 +v 5052 -1.6095078 5064 +v 5054 -3.07900715 5064 +v 5056 -3.79465818 5064 +v 5058 -3.58124447 5064 +v 5060 -2.49101734 5064 +v 5062 -0.790902138 5064 +v 5064 1.1028533 5064 +v 5066 2.72659183 5064 +v 5068 3.68276572 5064 +v 5070 3.73726988 5064 +v 5072 2.87676024 5064 +v 5074 1.31191933 5064 +v 5076 -0.5741253 5064 +v 5078 -2.319604 5064 +v 5080 -3.49716282 5064 +v 5082 -3.818494 5064 +v 5084 -3.20492482 5064 +v 5086 -1.8066783 5064 +v 5088 0.03390616 5064 +v 5090 1.86618912 5064 +v 5092 3.241564 5064 +v 5094 3.82329082 5064 +v 5096 3.46894288 5064 +v 5098 2.26527667 5064 +v 5000 0 5066 +v 5002 -1.34698832 5066 +v 5004 -2.36418676 5066 +v 5006 -2.80255 5066 +v 5008 -2.55475116 5066 +v 5010 -1.68146014 5066 +v 5012 -0.396489084 5066 +v 5014 0.9855564 5066 +v 5016 2.12630343 5066 +v 5018 2.746457 5066 +v 5020 2.6941824 5066 +v 5022 1.98227775 5066 +v 5024 0.785042465 5066 +v 5026 -0.604398549 5066 +v 5028 -1.84586179 5066 +v 5030 -2.63539362 5066 +v 5032 -2.77968931 5066 +v 5034 -2.24342 5066 +v 5036 -1.15788329 5066 +v 5038 0.2111437 5066 +v 5040 1.52847528 5066 +v 5042 2.471583 5066 +v 5044 2.80956078 5066 +v 5046 2.45966 5066 +v 5048 1.50754893 5066 +v 5050 0.186337218 5066 +v 5052 -1.18049634 5066 +v 5054 -2.25830317 5066 +v 5056 -2.78319883 5066 +v 5058 -2.62667012 5066 +v 5060 -1.82704115 5066 +v 5062 -0.5800886 5066 +v 5064 0.808889747 5066 +v 5066 1.99982357 5066 +v 5068 2.70113087 5066 +v 5070 2.74110723 5066 +v 5072 2.10996485 5066 +v 5074 0.96222955 5066 +v 5076 -0.421093225 5066 +v 5078 -1.70131767 5066 +v 5080 -2.5650003 5066 +v 5082 -2.80068135 5066 +v 5084 -2.3506577 5066 +v 5086 -1.32511139 5066 +v 5088 0.0248685349 5066 +v 5090 1.36875975 5066 +v 5092 2.377531 5066 +v 5094 2.80419946 5066 +v 5096 2.54430223 5066 +v 5098 1.66147113 5066 +v 5000 0 5068 +v 5002 -0.527681351 5068 +v 5004 -0.9261679 5068 +v 5006 -1.09789622 5068 +v 5008 -1.00082123 5068 +v 5010 -0.658710361 5068 +v 5012 -0.1553242 5068 +v 5014 0.386090755 5068 +v 5016 0.832977235 5068 +v 5018 1.07592189 5068 +v 5020 1.05544329 5068 +v 5022 0.776555359 5068 +v 5024 0.3075396 5068 +v 5026 -0.236772552 5068 +v 5028 -0.72311455 5068 +v 5030 -1.03241289 5068 +v 5032 -1.0889405 5068 +v 5034 -0.878857553 5068 +v 5036 -0.453599632 5068 +v 5038 0.08271534 5068 +v 5040 0.5987787 5068 +v 5042 0.968240142 5068 +v 5044 1.10064268 5068 +v 5046 0.9635694 5068 +v 5048 0.5905808 5068 +v 5050 0.07299742 5068 +v 5052 -0.462458283 5068 +v 5054 -0.8846881 5068 +v 5056 -1.09031534 5068 +v 5058 -1.02899539 5068 +v 5060 -0.7157416 5068 +v 5062 -0.227249131 5068 +v 5064 0.316881746 5068 +v 5066 0.7834289 5068 +v 5068 1.05816543 5068 +v 5070 1.07382607 5068 +v 5072 0.82657665 5068 +v 5074 0.376952469 5068 +v 5076 -0.164962858 5068 +v 5078 -0.6664895 5068 +v 5080 -1.00483632 5068 +v 5082 -1.09716415 5068 +v 5084 -0.92086786 5068 +v 5086 -0.5191111 5068 +v 5088 0.009742224 5068 +v 5090 0.5362103 5068 +v 5092 0.9313954 5068 +v 5094 1.09854245 5068 +v 5096 0.996727943 5068 +v 5098 0.6508797 5068 +v 5000 0 5070 +v 5002 0.4208205 5070 +v 5004 0.738609433 5070 +v 5006 0.875561059 5070 +v 5008 0.798144758 5070 +v 5010 0.5253148 5070 +v 5012 0.123869471 5070 +v 5014 -0.307903439 5070 +v 5016 -0.664290845 5070 +v 5018 -0.8580367 5070 +v 5020 -0.841705263 5070 +v 5022 -0.619294941 5070 +v 5024 -0.2452597 5070 +v 5026 0.1888237 5070 +v 5028 0.5766765 5070 +v 5030 0.823338747 5070 +v 5032 0.868419 5070 +v 5034 0.700879931 5070 +v 5036 0.361741066 5070 +v 5038 -0.06596464 5070 +v 5040 -0.477519929 5070 +v 5042 -0.772161663 5070 +v 5044 -0.8777513 5070 +v 5046 -0.7684368 5070 +v 5048 -0.4709822 5070 +v 5050 -0.058214698 5070 +v 5052 0.368805766 5070 +v 5054 0.705529749 5070 +v 5056 0.8695154 5070 +v 5058 0.8206133 5070 +v 5060 0.5707966 5070 +v 5062 0.181228876 5070 +v 5064 -0.252709985 5070 +v 5066 -0.6247766 5070 +v 5068 -0.8438761 5070 +v 5070 -0.8563653 5070 +v 5072 -0.6591865 5070 +v 5074 -0.300615758 5070 +v 5076 0.1315562 5070 +v 5078 0.5315186 5070 +v 5080 0.8013467 5070 +v 5082 0.874977231 5070 +v 5084 0.734382749 5070 +v 5086 0.4139858 5070 +v 5088 -0.00776932435 5070 +v 5090 -0.427622229 5070 +v 5092 -0.742778361 5070 +v 5094 -0.876076341 5070 +v 5096 -0.794880331 5070 +v 5098 -0.5190699 5070 +v 5000 0 5072 +v 5002 1.26629078 5072 +v 5004 2.22254944 5072 +v 5006 2.63465047 5072 +v 5008 2.40169716 5072 +v 5010 1.5807246 5072 +v 5012 0.3727356 5072 +v 5014 -0.9265121 5072 +v 5016 -1.99891734 5072 +v 5018 -2.58191776 5072 +v 5020 -2.532775 5072 +v 5022 -1.86352015 5072 +v 5024 -0.7380109 5072 +v 5026 0.568189263 5072 +v 5028 1.73527694 5072 +v 5030 2.477508 5072 +v 5032 2.61315918 5072 +v 5034 2.10901761 5072 +v 5036 1.08851492 5072 +v 5038 -0.198494166 5072 +v 5040 -1.43690491 5072 +v 5042 -2.32351136 5072 +v 5044 -2.641241 5072 +v 5046 -2.31230283 5072 +v 5048 -1.41723228 5072 +v 5050 -0.175173819 5072 +v 5052 1.10977328 5072 +v 5054 2.1230092 5072 +v 5056 2.61645842 5072 +v 5058 2.46930718 5072 +v 5060 1.71758378 5072 +v 5062 0.5453357 5072 +v 5064 -0.760429442 5072 +v 5066 -1.8800149 5072 +v 5068 -2.53930736 5072 +v 5070 -2.57688856 5072 +v 5072 -1.9835577 5072 +v 5074 -0.904582739 5072 +v 5076 0.395865679 5072 +v 5078 1.59939229 5072 +v 5080 2.41133213 5072 +v 5082 2.63289356 5072 +v 5084 2.20983076 5072 +v 5086 1.24572444 5072 +v 5088 -0.02337867 5072 +v 5090 -1.28675783 5072 +v 5092 -2.235094 5072 +v 5094 -2.636201 5072 +v 5096 -2.391874 5072 +v 5098 -1.561933 5072 +v 5000 0 5074 +v 5002 1.801729 5074 +v 5004 3.16233158 5074 +v 5006 3.7486856 5074 +v 5008 3.41723013 5074 +v 5010 2.24911785 5074 +v 5012 0.530343 5074 +v 5014 -1.31827831 5074 +v 5016 -2.844139 5074 +v 5018 -3.67365551 5074 +v 5020 -3.603733 5074 +v 5022 -2.65149069 5074 +v 5024 -1.05007124 5074 +v 5026 0.808442354 5074 +v 5028 2.469021 5074 +v 5030 3.52509713 5074 +v 5032 3.71810675 5074 +v 5034 3.00079417 5074 +v 5036 1.54878235 5074 +v 5038 -0.2824254 5074 +v 5040 -2.04448557 5074 +v 5042 -3.30598426 5074 +v 5044 -3.75806284 5074 +v 5046 -3.29003644 5074 +v 5048 -2.01649451 5074 +v 5050 -0.249244288 5074 +v 5052 1.57902956 5074 +v 5054 3.020702 5074 +v 5056 3.72280121 5074 +v 5058 3.51342869 5074 +v 5060 2.44384646 5074 +v 5062 0.775925338 5074 +v 5064 -1.08196926 5074 +v 5066 -2.67496014 5074 +v 5068 -3.61302733 5074 +v 5070 -3.66649961 5074 +v 5072 -2.8222847 5074 +v 5074 -1.28707635 5074 +v 5076 0.563253462 5074 +v 5078 2.275679 5074 +v 5080 3.4309392 5074 +v 5082 3.74618554 5074 +v 5084 3.14423513 5074 +v 5086 1.7724663 5074 +v 5088 -0.0332641 5074 +v 5090 -1.83085036 5074 +v 5092 -3.18018055 5074 +v 5094 -3.75089169 5074 +v 5096 -3.40325379 5074 +v 5098 -2.2223804 5074 +v 5000 0 5076 +v 5002 1.896041 5076 +v 5004 3.327865 5076 +v 5006 3.94491172 5076 +v 5008 3.59610629 5076 +v 5010 2.36684871 5076 +v 5012 0.558104 5076 +v 5014 -1.387284 5076 +v 5016 -2.99301648 5076 +v 5018 -3.86595416 5076 +v 5020 -3.79237151 5076 +v 5022 -2.790284 5076 +v 5024 -1.10503757 5076 +v 5026 0.8507605 5076 +v 5028 2.59826279 5076 +v 5030 3.70961952 5076 +v 5032 3.91273236 5076 +v 5034 3.15787172 5076 +v 5036 1.62985384 5076 +v 5038 -0.297209054 5076 +v 5040 -2.15150476 5076 +v 5042 -3.479037 5076 +v 5044 -3.95477986 5076 +v 5046 -3.46225429 5076 +v 5048 -2.12204862 5076 +v 5050 -0.262291044 5076 +v 5052 1.66168427 5076 +v 5054 3.17882156 5076 +v 5056 3.9176724 5076 +v 5058 3.69734025 5076 +v 5060 2.57177043 5076 +v 5062 0.816541433 5076 +v 5064 -1.13860536 5076 +v 5066 -2.8149817 5076 +v 5068 -3.8021524 5076 +v 5070 -3.85842371 5076 +v 5072 -2.97001815 5076 +v 5074 -1.35444868 5076 +v 5076 0.592737138 5076 +v 5078 2.39480019 5076 +v 5080 3.61053276 5076 +v 5082 3.942281 5076 +v 5084 3.3088212 5076 +v 5086 1.86524665 5076 +v 5088 -0.03500532 5076 +v 5090 -1.92668676 5076 +v 5092 -3.34664822 5076 +v 5094 -3.9472332 5076 +v 5096 -3.581398 5076 +v 5098 -2.33871174 5076 +v 5000 0 5078 +v 5002 1.52613616 5078 +v 5004 2.67862082 5078 +v 5006 3.17528582 5078 +v 5008 2.89453 5078 +v 5010 1.90509236 5078 +v 5012 0.449221641 5078 +v 5014 -1.11663425 5078 +v 5016 -2.409099 5078 +v 5018 -3.11173248 5078 +v 5020 -3.05250525 5078 +v 5022 -2.24591827 5078 +v 5024 -0.889452159 5078 +v 5026 0.684782863 5078 +v 5028 2.09135914 5078 +v 5030 2.98589778 5078 +v 5032 3.1493845 5078 +v 5034 2.54179215 5078 +v 5036 1.31188035 5078 +v 5038 -0.239225566 5078 +v 5040 -1.73176074 5078 +v 5042 -2.80030036 5078 +v 5044 -3.18322873 5078 +v 5046 -2.7867918 5078 +v 5048 -1.70805109 5078 +v 5050 -0.211119831 5078 +v 5052 1.33750093 5078 +v 5054 2.55865479 5078 +v 5056 3.15336084 5078 +v 5058 2.976014 5078 +v 5060 2.07003522 5078 +v 5062 0.6572397 5078 +v 5064 -0.916471064 5078 +v 5066 -2.26579762 5078 +v 5068 -3.060378 5078 +v 5070 -3.10567117 5078 +v 5072 -2.39058757 5078 +v 5074 -1.09020483 5078 +v 5076 0.4770981 5078 +v 5078 1.92759085 5078 +v 5080 2.90614223 5078 +v 5082 3.17316842 5078 +v 5084 2.66329241 5078 +v 5086 1.50134957 5078 +v 5088 -0.028176019 5078 +v 5090 -1.55080307 5078 +v 5092 -2.69373965 5078 +v 5094 -3.17715454 5078 +v 5096 -2.88269138 5078 +v 5098 -1.88244474 5078 +v 5000 0 5080 +v 5002 0.7825799 5080 +v 5004 1.37355685 5080 +v 5006 1.62823927 5080 +v 5008 1.48427188 5080 +v 5010 0.976902962 5080 +v 5012 0.230354175 5080 +v 5014 -0.5725934 5080 +v 5016 -1.23535013 5080 +v 5018 -1.59565008 5080 +v 5020 -1.56527925 5080 +v 5022 -1.15167344 5080 +v 5024 -0.4560978 5080 +v 5026 0.35114643 5080 +v 5028 1.07241774 5080 +v 5030 1.53112388 5080 +v 5032 1.61495745 5080 +v 5034 1.30339313 5080 +v 5036 0.6727127 5080 +v 5038 -0.122671306 5080 +v 5040 -0.888021052 5080 +v 5042 -1.43595231 5080 +v 5044 -1.6323123 5080 +v 5046 -1.42902529 5080 +v 5048 -0.8758632 5080 +v 5050 -0.108259104 5080 +v 5052 0.6858505 5080 +v 5054 1.31204009 5080 +v 5056 1.61699641 5080 +v 5058 1.52605557 5080 +v 5060 1.06148326 5080 +v 5062 0.3370227 5080 +v 5064 -0.4699527 5080 +v 5066 -1.16186726 5080 +v 5068 -1.56931627 5080 +v 5070 -1.59254193 5080 +v 5072 -1.22585773 5080 +v 5074 -0.5590408 5080 +v 5076 0.2446488 5080 +v 5078 0.988439858 5080 +v 5080 1.49022639 5080 +v 5082 1.62715352 5080 +v 5084 1.36569667 5080 +v 5086 0.7698697 5080 +v 5088 -0.0144482423 5080 +v 5090 -0.7952287 5080 +v 5092 -1.38130951 5080 +v 5094 -1.62919748 5080 +v 5096 -1.47820115 5080 +v 5098 -0.9652896 5080 +v 5000 0 5082 +v 5002 -0.152579218 5082 +v 5004 -0.267801732 5082 +v 5006 -0.31745702 5082 +v 5008 -0.289387763 5082 +v 5010 -0.1904663 5082 +v 5012 -0.04491204 5082 +v 5014 0.111638255 5082 +v 5016 0.240855619 5082 +v 5018 0.311103135 5082 +v 5020 0.305181742 5082 +v 5022 0.2245412 5082 +v 5024 0.08892517 5082 +v 5026 -0.0684628561 5082 +v 5028 -0.209088787 5082 +v 5030 -0.298522472 5082 +v 5032 -0.314867467 5082 +v 5034 -0.254121929 5082 +v 5036 -0.131158471 5082 +v 5038 0.0239171647 5082 +v 5040 0.173137039 5082 +v 5042 0.279966921 5082 +v 5044 0.318251163 5082 +v 5046 0.278616369 5082 +v 5048 0.170766622 5082 +v 5050 0.0211072247 5082 +v 5052 -0.133719951 5082 +v 5054 -0.255807817 5082 +v 5056 -0.31526503 5082 +v 5058 -0.297534317 5082 +v 5060 -0.206956878 5082 +v 5062 -0.06570916 5082 +v 5064 0.09162646 5082 +v 5066 0.2265287 5082 +v 5068 0.305968851 5082 +v 5070 0.310497135 5082 +v 5072 0.23900488 5082 +v 5074 0.108995922 5082 +v 5076 -0.04769906 5082 +v 5078 -0.192715645 5082 +v 5080 -0.2905487 5082 +v 5082 -0.317245334 5082 +v 5084 -0.2662692 5082 +v 5086 -0.150101125 5082 +v 5088 0.002816967 5082 +v 5090 0.15504536 5082 +v 5092 0.269313246 5082 +v 5094 0.317643851 5082 +v 5096 0.288204163 5082 +v 5098 0.188202053 5082 +v 5000 0 5084 +v 5002 -1.05038154 5084 +v 5004 -1.843593 5084 +v 5006 -2.18542886 5084 +v 5008 -1.99219513 5084 +v 5010 -1.31120276 5084 +v 5012 -0.309182227 5084 +v 5014 0.7685369 5084 +v 5016 1.65809143 5084 +v 5018 2.14168739 5084 +v 5020 2.10092354 5084 +v 5022 1.5457803 5084 +v 5024 0.6121761 5084 +v 5026 -0.471310049 5084 +v 5028 -1.439403 5084 +v 5030 -2.05508018 5084 +v 5032 -2.16760182 5084 +v 5034 -1.749419 5084 +v 5036 -0.902917445 5084 +v 5038 0.164649874 5084 +v 5040 1.19190514 5084 +v 5042 1.92734039 5084 +v 5044 2.19089556 5084 +v 5046 1.918043 5084 +v 5048 1.17558682 5084 +v 5050 0.145305768 5084 +v 5052 -0.9205511 5084 +v 5054 -1.761025 5084 +v 5056 -2.17033863 5084 +v 5058 -2.04827738 5084 +v 5060 -1.42472672 5084 +v 5062 -0.45235312 5084 +v 5064 0.630772233 5084 +v 5066 1.55946255 5084 +v 5068 2.106342 5084 +v 5070 2.13751554 5084 +v 5072 1.64535069 5084 +v 5074 0.750346661 5084 +v 5076 -0.3283685 5084 +v 5078 -1.32668757 5084 +v 5080 -2.0001874 5084 +v 5082 -2.18397141 5084 +v 5084 -1.833043 5084 +v 5086 -1.033322 5084 +v 5088 0.0193924848 5084 +v 5090 1.067359 5084 +v 5092 1.85399878 5084 +v 5094 2.186715 5084 +v 5096 1.984047 5084 +v 5098 1.29561532 5084 +v 5000 0 5086 +v 5002 -1.69101393 5086 +v 5004 -2.96800852 5086 +v 5006 -3.51833129 5086 +v 5008 -3.20724368 5086 +v 5010 -2.11091113 5086 +v 5012 -0.4977538 5086 +v 5014 1.237271 5086 +v 5016 2.66936874 5086 +v 5018 3.44791174 5086 +v 5020 3.382286 5086 +v 5022 2.48855829 5086 +v 5024 0.985545039 5086 +v 5026 -0.7587641 5086 +v 5028 -2.31730127 5086 +v 5030 -3.30848241 5086 +v 5032 -3.48963165 5086 +v 5034 -2.81639743 5086 +v 5036 -1.45361066 5086 +v 5038 0.265070558 5086 +v 5040 1.91885328 5086 +v 5042 3.10283375 5086 +v 5044 3.52713227 5086 +v 5046 3.08786583 5086 +v 5048 1.8925823 5086 +v 5050 0.233928382 5086 +v 5052 -1.48199916 5086 +v 5054 -2.83508182 5086 +v 5056 -3.49403739 5086 +v 5058 -3.297531 5086 +v 5060 -2.29367375 5086 +v 5062 -0.728245258 5086 +v 5064 1.015483 5086 +v 5066 2.51058555 5086 +v 5068 3.39100933 5086 +v 5070 3.44119573 5086 +v 5072 2.64885736 5086 +v 5074 1.20798635 5086 +v 5076 -0.528641939 5086 +v 5078 -2.13584018 5086 +v 5080 -3.22011018 5086 +v 5082 -3.515985 5086 +v 5084 -2.951024 5086 +v 5086 -1.66354942 5086 +v 5088 0.0312200449 5086 +v 5090 1.71834576 5086 +v 5092 2.98476076 5086 +v 5094 3.520402 5086 +v 5096 3.19412613 5086 +v 5098 2.08581662 5086 +v 5000 0 5088 +v 5002 -1.9176271 5088 +v 5004 -3.365752 5088 +v 5006 -3.98982382 5088 +v 5008 -3.63704729 5088 +v 5010 -2.39379478 5088 +v 5012 -0.5644579 5088 +v 5014 1.403078 5088 +v 5016 3.0270915 5088 +v 5018 3.90996742 5088 +v 5020 3.835547 5088 +v 5022 2.82205057 5088 +v 5024 1.1176182 5088 +v 5026 -0.8604463 5088 +v 5028 -2.62784338 5088 +v 5030 -3.751853 5088 +v 5032 -3.957278 5088 +v 5034 -3.19382334 5088 +v 5036 -1.64840937 5088 +v 5038 0.300592721 5088 +v 5040 2.1759994 5088 +v 5042 3.51864529 5088 +v 5044 3.99980426 5088 +v 5046 3.50167155 5088 +v 5048 2.14620757 5088 +v 5050 0.265277177 5088 +v 5052 -1.68060231 5088 +v 5054 -3.21501184 5088 +v 5056 -3.96227431 5088 +v 5058 -3.73943377 5088 +v 5060 -2.60104966 5088 +v 5062 -0.825837553 5088 +v 5064 1.15156817 5088 +v 5066 2.84702969 5088 +v 5068 3.8454392 5088 +v 5070 3.90235114 5088 +v 5072 3.00383139 5088 +v 5074 1.36986887 5088 +v 5076 -0.599485338 5088 +v 5078 -2.42206454 5088 +v 5080 -3.651638 5088 +v 5082 -3.987163 5088 +v 5084 -3.34649134 5088 +v 5086 -1.88648212 5088 +v 5088 0.03540385 5088 +v 5090 1.94862175 5088 +v 5092 3.38474917 5088 +v 5094 3.99217176 5088 +v 5096 3.62217164 5088 +v 5098 2.36533737 5088 +v 5000 0 5090 +v 5002 -1.67473829 5090 +v 5004 -2.93944216 5090 +v 5006 -3.48446822 5090 +v 5008 -3.17637467 5090 +v 5010 -2.090594 5090 +v 5012 -0.492963016 5090 +v 5014 1.22536254 5090 +v 5016 2.64367652 5090 +v 5018 3.4147265 5090 +v 5020 3.34973216 5090 +v 5022 2.46460652 5090 +v 5024 0.9760594 5090 +v 5026 -0.751461148 5090 +v 5028 -2.294998 5090 +v 5030 -3.276639 5090 +v 5032 -3.45604467 5090 +v 5034 -2.78929019 5090 +v 5036 -1.43962 5090 +v 5038 0.2625193 5090 +v 5040 1.90038478 5090 +v 5042 3.07296968 5090 +v 5044 3.49318457 5090 +v 5046 3.05814576 5090 +v 5048 1.87436664 5090 +v 5050 0.231676877 5090 +v 5052 -1.46773529 5090 +v 5054 -2.80779481 5090 +v 5056 -3.46040821 5090 +v 5058 -3.26579285 5090 +v 5060 -2.27159762 5090 +v 5062 -0.72123605 5090 +v 5064 1.00570917 5090 +v 5066 2.48642182 5090 +v 5068 3.3583715 5090 +v 5070 3.40807486 5090 +v 5072 2.62336254 5090 +v 5074 1.19635975 5090 +v 5076 -0.523553848 5090 +v 5078 -2.11528325 5090 +v 5080 -3.18911743 5090 +v 5082 -3.48214459 5090 +v 5084 -2.922621 5090 +v 5086 -1.64753819 5090 +v 5088 0.03091956 5090 +v 5090 1.70180714 5090 +v 5092 2.956033 5090 +v 5094 3.48651886 5090 +v 5096 3.16338325 5090 +v 5098 2.065741 5090 +v 5000 0 5092 +v 5002 -1.02181506 5092 +v 5004 -1.79345417 5092 +v 5006 -2.12599325 5092 +v 5008 -1.93801475 5092 +v 5010 -1.27554286 5092 +v 5012 -0.3007736 5092 +v 5014 0.7476356 5092 +v 5016 1.61299753 5092 +v 5018 2.0834415 5092 +v 5020 2.04378629 5092 +v 5022 1.50374079 5092 +v 5024 0.595527232 5092 +v 5026 -0.45849216 5092 +v 5028 -1.40025663 5092 +v 5030 -1.9991895 5092 +v 5032 -2.10865116 5092 +v 5034 -1.70184124 5092 +v 5036 -0.8783614 5092 +v 5038 0.160172 5092 +v 5040 1.15948975 5092 +v 5042 1.874924 5092 +v 5044 2.13131142 5092 +v 5046 1.8658793 5092 +v 5048 1.14361513 5092 +v 5050 0.14135398 5092 +v 5052 -0.8955155 5092 +v 5054 -1.71313155 5092 +v 5056 -2.11131334 5092 +v 5058 -1.99257183 5092 +v 5060 -1.38597941 5092 +v 5062 -0.440050781 5092 +v 5064 0.6136176 5092 +v 5066 1.517051 5092 +v 5068 2.04905725 5092 +v 5070 2.07938313 5092 +v 5072 1.60060322 5092 +v 5074 0.72994 5092 +v 5076 -0.3194381 5092 +v 5078 -1.29060662 5092 +v 5080 -1.94578958 5092 +v 5082 -2.12457538 5092 +v 5084 -1.783191 5092 +v 5086 -1.00521934 5092 +v 5088 0.01886508 5092 +v 5090 1.03833067 5092 +v 5092 1.80357683 5092 +v 5094 2.12724423 5092 +v 5096 1.93008828 5092 +v 5098 1.26037931 5092 +v 5000 0 5094 +v 5002 -0.11871592 5094 +v 5004 -0.208366036 5094 +v 5006 -0.247000888 5094 +v 5008 -0.2251613 5094 +v 5010 -0.148194373 5094 +v 5012 -0.0349443 5094 +v 5014 0.08686136 5094 +v 5016 0.187400326 5094 +v 5018 0.24205716 5094 +v 5020 0.237449959 5094 +v 5022 0.174706727 5094 +v 5024 0.06918919 5094 +v 5026 -0.053268265 5094 +v 5028 -0.1626838 5094 +v 5030 -0.232268661 5094 +v 5032 -0.244986057 5094 +v 5034 -0.197722331 5094 +v 5036 -0.102049269 5094 +v 5038 0.01860901 5094 +v 5040 0.134711146 5094 +v 5042 0.2178313 5094 +v 5044 0.247618765 5094 +v 5046 0.2167805 5094 +v 5048 0.132866815 5094 +v 5050 0.0164227057 5094 +v 5052 -0.104042254 5094 +v 5054 -0.19903405 5094 +v 5056 -0.245295361 5094 +v 5058 -0.2314998 5094 +v 5060 -0.161025047 5094 +v 5062 -0.05112572 5094 +v 5064 0.0712909549 5094 +v 5066 0.176253125 5094 +v 5068 0.238062382 5094 +v 5070 0.241585657 5094 +v 5072 0.185960338 5094 +v 5074 0.08480546 5094 +v 5076 -0.03711277 5094 +v 5078 -0.1499445 5094 +v 5080 -0.226064578 5094 +v 5082 -0.246836171 5094 +v 5084 -0.207173645 5094 +v 5086 -0.116787806 5094 +v 5088 0.00219177175 5094 +v 5090 0.120634727 5094 +v 5092 0.2095421 5094 +v 5094 0.247146249 5094 +v 5096 0.224240378 5094 +v 5098 0.146432653 5094 +v 5000 0 5096 +v 5002 0.813449 5096 +v 5004 1.42773724 5096 +v 5006 1.69246578 5096 +v 5008 1.5428195 5096 +v 5010 1.01543725 5096 +v 5012 0.239440575 5096 +v 5014 -0.595179558 5096 +v 5016 -1.284079 5096 +v 5018 -1.658591 5096 +v 5020 -1.62702227 5096 +v 5022 -1.19710159 5096 +v 5024 -0.474088758 5096 +v 5026 0.364997536 5096 +v 5028 1.11471975 5096 +v 5030 1.59151959 5096 +v 5032 1.67866 5096 +v 5034 1.354806 5096 +v 5036 0.6992481 5096 +v 5038 -0.127510116 5096 +v 5040 -0.9230494 5096 +v 5042 -1.492594 5096 +v 5044 -1.6966995 5096 +v 5046 -1.48539376 5096 +v 5048 -0.9104119 5096 +v 5050 -0.112529427 5096 +v 5052 0.712904155 5096 +v 5054 1.363794 5096 +v 5056 1.68077946 5096 +v 5058 1.58625138 5096 +v 5060 1.10335386 5096 +v 5062 0.3503167 5096 +v 5064 -0.488490164 5096 +v 5066 -1.20769763 5096 +v 5068 -1.63121855 5096 +v 5070 -1.65536034 5096 +v 5072 -1.27421212 5096 +v 5074 -0.5810924 5096 +v 5076 0.254299074 5096 +v 5078 1.02742922 5096 +v 5080 1.549009 5096 +v 5082 1.69133711 5096 +v 5084 1.419567 5096 +v 5086 0.8002375 5096 +v 5088 -0.0150181586 5096 +v 5090 -0.8265968 5096 +v 5092 -1.43579578 5096 +v 5094 -1.69346189 5096 +v 5096 -1.53650939 5096 +v 5098 -1.00336587 5096 +v 5000 0 5098 +v 5002 1.54645324 5098 +v 5004 2.71428061 5098 +v 5006 3.21755767 5098 +v 5008 2.93306422 5098 +v 5010 1.93045437 5098 +v 5012 0.455202 5098 +v 5014 -1.13149965 5098 +v 5016 -2.441171 5098 +v 5018 -3.15315819 5098 +v 5020 -3.09314251 5098 +v 5022 -2.27581763 5098 +v 5024 -0.9012932 5098 +v 5026 0.6938992 5098 +v 5028 2.119201 5098 +v 5030 3.02564836 5098 +v 5032 3.19131136 5098 +v 5034 2.57563043 5098 +v 5036 1.329345 5098 +v 5038 -0.242410317 5098 +v 5040 -1.75481522 5098 +v 5042 -2.83758 5098 +v 5044 -3.22560644 5098 +v 5046 -2.82389164 5098 +v 5048 -1.73079 5098 +v 5050 -0.213930413 5098 +v 5052 1.35530674 5098 +v 5054 2.59271765 5098 +v 5056 3.19534063 5098 +v 5058 3.01563287 5098 +v 5060 2.09759331 5098 +v 5062 0.665989339 5098 +v 5064 -0.928671837 5098 +v 5066 -2.29596162 5098 +v 5068 -3.10112023 5098 +v 5070 -3.14701629 5098 +v 5072 -2.42241287 5098 +v 5074 -1.10471845 5098 +v 5076 0.4834496 5098 +v 5078 1.95325232 5098 +v 5080 2.944831 5098 +v 5082 3.215412 5098 +v 5084 2.698748 5098 +v 5086 1.52133667 5098 +v 5088 -0.0285511184 5098 +v 5090 -1.57144856 5098 +v 5092 -2.72960067 5098 +v 5094 -3.21945119 5098 +v 5096 -2.921068 5098 +v 5098 -1.90750527 5098 +v 5041.501 3.00283384 5086.939 +v 5038.10645 0.200592726 5087.356 +v 5039.2207 2.0759995 5088.569 +v 5041.501 6.002834 5086.939 +v 5038.10645 3.20059276 5087.356 +v 5039.2207 5.07599926 5088.569 +v 5086.71533 -0.216787815 5094.024 +v 5086.91553 -0.216787815 5093.42139 +v 5085.695 -1.74753821 5090.151 +v 5086.71533 2.78321218 5094.024 +v 5086.91553 2.78321218 5093.42139 +v 5085.695 1.25246179 5090.151 +v 5051.193 -1.198558 5015.15039 +v 5056.2583 -3.81064343 5014.17139 +v 5051.712 -1.7638489 5011.50146 +v 5051.193 1.801442 5015.15039 +v 5056.2583 -0.810643435 5014.17139 +v 5051.712 1.2361511 5011.50146 +v 5049.01074 -0.323863983 5048.376 +v 5046.071 -3.59409857 5049.386 +v 5050.30469 -0.340734363 5051.956 +v 5049.01074 2.676136 5048.376 +v 5046.071 -0.594098568 5049.386 +v 5050.30469 2.65926552 5051.956 +v 5084.278 1.89097476 5054.02637 +v 5082.528 3.51828027 5052.56152 +v 5083.732 1.89097476 5054.808 +v 5084.278 4.890975 5054.02637 +v 5082.528 6.51828 5052.56152 +v 5083.732 4.890975 5054.808 +v 5030.64062 1.43112385 5079.487 +v 5029.85645 3.42509723 5073.223 +v 5029.19238 2.88589787 5078.751 +v 5030.64062 4.43112373 5079.487 +v 5029.85645 6.42509747 5073.223 +v 5029.19238 5.88589764 5078.751 +v 5030.248 -3.10589051 5010.90527 +v 5031.07861 -4.017829 5012.503 +v 5031.709 -4.017829 5011.73047 +v 5030.248 -0.105890512 5010.90527 +v 5031.07861 -1.017829 5012.503 +v 5031.709 -1.017829 5011.73047 +v 5001.95459 -1.77473831 5089.92773 +v 5000.69238 -0.1 5089.886 +v 5001.50049 -1.12181509 5091.37744 +v 5001.95459 1.22526169 5089.92773 +v 5000.69238 2.9 5089.886 +v 5001.50049 1.87818491 5091.37744 +v 5040.72949 2.02919745 5061.974 +v 5045.284 3.25354028 5064.201 +v 5044.61475 3.81377578 5062.441 +v 5040.72949 5.02919769 5061.974 +v 5045.284 6.25354 5064.201 +v 5044.61475 6.813776 5062.441 +v 5002.348 1.582942 5002.953 +v 5001.407 0.936138868 5003.50537 +v 5000.959 -0.1 5004.85 +v 5002.348 4.582942 5002.953 +v 5001.407 3.93613887 5003.50537 +v 5000.959 2.9 5004.85 +v 5012.365 -0.218990028 5017.604 +v 5019.32959 2.407179 5015.48926 +v 5015.416 2.73485112 5014.367 +v 5012.365 2.78101 5017.604 +v 5019.32959 5.407179 5015.48926 +v 5015.416 5.734851 5014.367 +v 5005.55371 -0.680542231 5032.6626 +v 5005.91455 -3.73539138 5036.80664 +v 5007.769 -3.72690415 5037.0376 +v 5005.55371 2.31945777 5032.6626 +v 5005.91455 -0.7353914 5036.80664 +v 5007.769 -0.726904154 5037.0376 +v 5036.351 -0.4474914 5018.515 +v 5035.85156 -0.4474914 5018.811 +v 5039.55 0.358709484 5018.87549 +v 5036.351 2.55250859 5018.515 +v 5035.85156 2.55250859 5018.811 +v 5039.55 3.35870957 5018.87549 +v 5054.95947 -3.30604577 5038.862 +v 5056.4917 -3.42476153 5039.885 +v 5056.22266 -4.05122471 5037.2334 +v 5054.95947 -0.306045771 5038.862 +v 5056.4917 -0.424761534 5039.885 +v 5056.22266 -1.05122471 5037.2334 +v 5044.463 2.938722 5059.55029 +v 5044.81934 2.938722 5059.581 +v 5046.154 2.56028175 5059.35449 +v 5044.463 5.93872166 5059.55029 +v 5044.81934 5.93872166 5059.581 +v 5046.154 5.56028175 5059.35449 +v 5062.009 -0.393121272 5057.53271 +v 5061.40869 0.0129271522 5055.79834 +v 5060.50732 -1.02321172 5057.67773 +v 5062.009 2.60687876 5057.53271 +v 5061.40869 3.012927 5055.79834 +v 5060.50732 1.97678828 5057.67773 +v 5043.37 1.31968319 5057.062 +v 5045.03125 1.14287686 5057.47266 +v 5042.75635 -0.5811486 5055.11572 +v 5043.37 4.319683 5057.062 +v 5045.03125 4.14287663 5057.47266 +v 5042.75635 2.41885138 5055.11572 +v 5079.895 -1.10483634 5067.397 +v 5076.17871 -0.521093249 5066.159 +v 5077.734 -0.7664895 5067.25439 +v 5079.895 1.89516366 5067.397 +v 5076.17871 2.47890663 5066.159 +v 5077.734 2.23351049 5067.25439 +v 5027.43262 -1.03272188 5057.022 +v 5027.70166 -2.096419 5060.51074 +v 5028.95361 -1.03272188 5057.58057 +v 5027.43262 1.96727812 5057.022 +v 5027.70166 0.9035809 5060.51074 +v 5028.95361 1.96727812 5057.58057 +v 5075.59229 -0.137112767 5094.07 +v 5077.655 1.85325229 5098.797 +v 5077.478 0.9274292 5095.37549 +v 5075.59229 2.86288714 5094.07 +v 5077.655 4.85325241 5098.797 +v 5077.478 3.9274292 5095.37549 +v 5086.14746 0.669869661 5079.745 +v 5087.09033 -0.114448242 5079.22754 +v 5082.1 1.52715349 5079.721 +v 5086.14746 3.66986966 5079.745 +v 5087.09033 2.88555169 5079.22754 +v 5082.1 4.52715349 5079.721 +v 5064.568 -0.1814619 5006.51855 +v 5064.481 -0.1814619 5005.633 +v 5062.94 -0.443683147 5007.66846 +v 5064.568 2.81853819 5006.51855 +v 5064.481 2.81853819 5005.633 +v 5062.94 2.55631685 5007.66846 +v 5070.00146 3.554526 5014.98828 +v 5074.778 0.18877393 5018.29736 +v 5073.228 1.18287313 5013.448 +v 5070.00146 6.55452633 5014.98828 +v 5074.778 3.18877387 5018.29736 +v 5073.228 4.18287325 5013.448 +v 5033.448 1.2033931 5079.58838 +v 5032.487 3.04938459 5077.64551 +v 5032.123 3.04938459 5077.667 +v 5033.448 4.203393 5079.58838 +v 5032.487 6.04938459 5077.64551 +v 5032.123 6.04938459 5077.667 +v 5034.62549 1.80014586 5053.30225 +v 5034.233 2.79833841 5051.644 +v 5033.71045 1.80014586 5054.40674 +v 5034.62549 4.800146 5053.30225 +v 5034.233 5.79833841 5051.644 +v 5033.71045 4.800146 5054.40674 +v 5082.72656 -4.047416 5012.93457 +v 5082.686 -4.047416 5012.163 +v 5082.49072 -3.83395171 5013.39258 +v 5082.72656 -1.04741621 5012.93457 +v 5082.686 -1.04741621 5012.163 +v 5082.49072 -0.8339517 5013.39258 +v 5094.47656 -2.02951121 5045.41846 +v 5088.06738 -0.117111519 5046.66748 +v 5092.07275 -3.477429 5050.01025 +v 5094.47656 0.9704888 5045.41846 +v 5088.06738 2.88288856 5046.66748 +v 5092.07275 -0.4774289 5050.01025 +v 5061.31934 0.5659893 5097.684 +v 5062.54053 0.2503167 5095.99 +v 5059.555 1.00335383 5095.18164 +v 5061.31934 3.56598926 5097.684 +v 5062.54053 3.25031662 5095.99 +v 5059.555 4.003354 5095.18164 +v 5066.71045 -2.24646616 5027.023 +v 5066.585 -1.08691967 5029.829 +v 5069.96826 -3.04210639 5027.868 +v 5066.71045 0.75353384 5027.023 +v 5066.585 1.91308033 5029.829 +v 5069.96826 -0.04210639 5027.868 +v 5027.434 -0.3090888 5081.00049 +v 5024.72754 -0.5560978 5079.247 +v 5024.972 -0.0110748336 5081.43066 +v 5027.434 2.69091129 5081.00049 +v 5024.72754 2.44390225 5079.247 +v 5024.972 2.98892522 5081.43066 +v 5039.673 -2.00969386 5002.206 +v 5040.70166 -2.27608442 5000.578 +v 5041.24463 -3.618783 4999.552 +v 5039.673 0.990306139 5002.206 +v 5040.70166 0.7239156 5000.578 +v 5041.24463 -0.618783 4999.552 +v 5045.89648 3.326357 5061.94775 +v 5049.449 0.159571558 5061.818 +v 5051.299 -0.6965099 5058.60156 +v 5045.89648 6.326357 5061.94775 +v 5049.449 3.15957165 5061.818 +v 5051.299 2.30349016 5058.60156 +v 5077.183 1.94394886 5048.27832 +v 5076.885 0.4058979 5048.098 +v 5075.47852 0.4058979 5048.346 +v 5077.183 4.94394875 5048.27832 +v 5076.885 3.40589786 5048.098 +v 5075.47852 3.40589786 5048.346 +v 5088.019 -0.126692086 5027.35156 +v 5087 1.74238729 5026.012 +v 5087.373 -0.126692086 5027.49951 +v 5088.019 2.873308 5027.35156 +v 5087 4.74238729 5026.012 +v 5087.373 2.873308 5027.49951 +v 5050.05957 0.159571558 5062.399 +v 5042.823 2.573177 5059.9126 +v 5049.487 0.159571558 5062.8877 +v 5050.05957 3.15957165 5062.399 +v 5042.823 5.57317734 5059.9126 +v 5049.487 3.15957165 5062.8877 +v 5023.12939 -1.04314339 5047.76953 +v 5019.976 -1.95381081 5045.138 +v 5017.418 -4.00151157 5049.247 +v 5023.12939 1.95685661 5047.76953 +v 5019.976 1.04618919 5045.138 +v 5017.418 -1.00151157 5049.247 +v 5079.38867 0.399334371 5055.6377 +v 5078.63525 0.231199324 5055.705 +v 5081.033 0.4452149 5056.07129 +v 5079.38867 3.39933443 5055.6377 +v 5078.63525 3.23119926 5055.705 +v 5081.033 3.445215 5056.07129 +v 5043.314 -4.05478 5075.02539 +v 5041.356 -3.579037 5075.05029 +v 5043.212 -4.05478 5075.87 +v 5043.314 -1.05478 5075.02539 +v 5041.356 -0.579036951 5075.05029 +v 5043.212 -1.05478 5075.87 +v 5043.87549 -4.00631237 5026.29541 +v 5041.95752 -2.75281835 5028.235 +v 5044.0957 -1.48652768 5030.41162 +v 5043.87549 -1.00631237 5026.29541 +v 5041.95752 0.247181654 5028.235 +v 5044.0957 1.51347232 5030.41162 +v 5034.946 -2.91639733 5086.22168 +v 5033.58252 -3.29382324 5087.355 +v 5034.54736 -3.29382324 5087.987 +v 5034.946 0.08360267 5086.22168 +v 5033.58252 -0.293823242 5087.355 +v 5034.54736 -0.293823242 5087.987 +v 5081.63574 3.51828027 5052.64844 +v 5081.33252 2.2721386 5053.18848 +v 5084.23633 2.936882 5051.9624 +v 5081.63574 6.51828 5052.64844 +v 5081.33252 5.2721386 5053.18848 +v 5084.23633 5.936882 5051.9624 +v 5073.869 -1.1327877 5028.433 +v 5074.00537 -0.574863553 5030.79248 +v 5073.924 -1.1327877 5027.992 +v 5073.869 1.8672123 5028.433 +v 5074.00537 2.42513657 5030.79248 +v 5073.924 1.8672123 5027.992 +v 5052.847 0.268805772 5069.295 +v 5053.066 2.0230093 5072.524 +v 5054.304 -0.9846881 5068.85938 +v 5052.847 3.26880574 5069.295 +v 5053.066 5.0230093 5072.524 +v 5054.304 2.015312 5068.85938 +v 5085.903 1.14572442 5071.377 +v 5086.53955 1.14572442 5071.08936 +v 5085.64648 0.3139858 5069.964 +v 5085.903 4.1457243 5071.377 +v 5086.53955 4.1457243 5071.08936 +v 5085.64648 3.31398582 5069.964 +v 5004.832 -2.79655623 5010.06348 +v 5002.728 -1.6363548 5010.255 +v 5006.65967 -3.83644342 5013.115 +v 5004.832 0.203443766 5010.06348 +v 5002.728 1.3636452 5010.255 +v 5006.65967 -0.8364434 5013.115 +v 5003.639 -1.5007019 5008.185 +v 5005.62939 -4.05005026 5011.354 +v 5009.30371 -1.09620917 5008.21631 +v 5003.639 1.4992981 5008.185 +v 5005.62939 -1.05005026 5011.354 +v 5009.30371 1.90379083 5008.21631 +v 5023.68066 -0.3452597 5070.689 +v 5024.76465 -0.3452597 5069.889 +v 5021.40967 -0.719294965 5069.14648 +v 5023.68066 2.65474033 5070.689 +v 5024.76465 2.65474033 5069.889 +v 5021.40967 2.280705 5069.14648 +v 5015.511 0.7329772 5067.56348 +v 5015.25 0.7329772 5068.911 +v 5020.43164 0.955443263 5067.149 +v 5015.511 3.73297715 5067.56348 +v 5015.25 3.73297715 5068.911 +v 5020.43164 3.95544338 5067.149 +v 5001.436 1.796041 5076.08545 +v 5001.12744 1.70172894 5073.84961 +v 4999.106 -0.1 5075.011 +v 5001.436 4.796041 5076.08545 +v 5001.12744 4.701729 5073.84961 +v 4999.106 2.9 5075.011 +v 5066.82 -2.59860134 5001.79 +v 5065.211 -2.94714117 5000.97949 +v 5066.36328 -2.59860134 5002.433 +v 5066.82 0.401398659 5001.79 +v 5065.211 0.05285883 5000.97949 +v 5066.36328 0.401398659 5002.433 +f 2 1 51 +f 52 2 51 +f 3 2 52 +f 53 3 52 +f 4 3 53 +f 54 4 53 +f 5 4 54 +f 55 5 54 +f 6 5 55 +f 56 6 55 +f 7 6 56 +f 57 7 56 +f 8 7 57 +f 58 8 57 +f 9 8 58 +f 59 9 58 +f 10 9 59 +f 60 10 59 +f 11 10 60 +f 61 11 60 +f 12 11 61 +f 62 12 61 +f 13 12 62 +f 63 13 62 +f 14 13 63 +f 64 14 63 +f 15 14 64 +f 65 15 64 +f 16 15 65 +f 66 16 65 +f 17 16 66 +f 67 17 66 +f 18 17 67 +f 68 18 67 +f 19 18 68 +f 69 19 68 +f 20 19 69 +f 70 20 69 +f 21 20 70 +f 71 21 70 +f 22 21 71 +f 72 22 71 +f 23 22 72 +f 73 23 72 +f 24 23 73 +f 74 24 73 +f 25 24 74 +f 75 25 74 +f 26 25 75 +f 76 26 75 +f 27 26 76 +f 77 27 76 +f 28 27 77 +f 78 28 77 +f 29 28 78 +f 79 29 78 +f 30 29 79 +f 80 30 79 +f 31 30 80 +f 81 31 80 +f 32 31 81 +f 82 32 81 +f 33 32 82 +f 83 33 82 +f 34 33 83 +f 84 34 83 +f 35 34 84 +f 85 35 84 +f 36 35 85 +f 86 36 85 +f 37 36 86 +f 87 37 86 +f 38 37 87 +f 88 38 87 +f 39 38 88 +f 89 39 88 +f 40 39 89 +f 90 40 89 +f 41 40 90 +f 91 41 90 +f 42 41 91 +f 92 42 91 +f 43 42 92 +f 93 43 92 +f 44 43 93 +f 94 44 93 +f 45 44 94 +f 95 45 94 +f 46 45 95 +f 96 46 95 +f 47 46 96 +f 97 47 96 +f 48 47 97 +f 98 48 97 +f 49 48 98 +f 99 49 98 +f 50 49 99 +f 100 50 99 +f 52 51 101 +f 102 52 101 +f 53 52 102 +f 103 53 102 +f 54 53 103 +f 104 54 103 +f 55 54 104 +f 105 55 104 +f 56 55 105 +f 106 56 105 +f 57 56 106 +f 107 57 106 +f 58 57 107 +f 108 58 107 +f 59 58 108 +f 109 59 108 +f 60 59 109 +f 110 60 109 +f 61 60 110 +f 111 61 110 +f 62 61 111 +f 112 62 111 +f 63 62 112 +f 113 63 112 +f 64 63 113 +f 114 64 113 +f 65 64 114 +f 115 65 114 +f 66 65 115 +f 116 66 115 +f 67 66 116 +f 117 67 116 +f 68 67 117 +f 118 68 117 +f 69 68 118 +f 119 69 118 +f 70 69 119 +f 120 70 119 +f 71 70 120 +f 121 71 120 +f 72 71 121 +f 122 72 121 +f 73 72 122 +f 123 73 122 +f 74 73 123 +f 124 74 123 +f 75 74 124 +f 125 75 124 +f 76 75 125 +f 126 76 125 +f 77 76 126 +f 127 77 126 +f 78 77 127 +f 128 78 127 +f 79 78 128 +f 129 79 128 +f 80 79 129 +f 130 80 129 +f 81 80 130 +f 131 81 130 +f 82 81 131 +f 132 82 131 +f 83 82 132 +f 133 83 132 +f 84 83 133 +f 134 84 133 +f 85 84 134 +f 135 85 134 +f 86 85 135 +f 136 86 135 +f 87 86 136 +f 137 87 136 +f 88 87 137 +f 138 88 137 +f 89 88 138 +f 139 89 138 +f 90 89 139 +f 140 90 139 +f 91 90 140 +f 141 91 140 +f 92 91 141 +f 142 92 141 +f 93 92 142 +f 143 93 142 +f 94 93 143 +f 144 94 143 +f 95 94 144 +f 145 95 144 +f 96 95 145 +f 146 96 145 +f 97 96 146 +f 147 97 146 +f 98 97 147 +f 148 98 147 +f 99 98 148 +f 149 99 148 +f 100 99 149 +f 150 100 149 +f 102 101 151 +f 152 102 151 +f 103 102 152 +f 153 103 152 +f 104 103 153 +f 154 104 153 +f 105 104 154 +f 155 105 154 +f 106 105 155 +f 156 106 155 +f 107 106 156 +f 157 107 156 +f 108 107 157 +f 158 108 157 +f 109 108 158 +f 159 109 158 +f 110 109 159 +f 160 110 159 +f 111 110 160 +f 161 111 160 +f 112 111 161 +f 162 112 161 +f 113 112 162 +f 163 113 162 +f 114 113 163 +f 164 114 163 +f 115 114 164 +f 165 115 164 +f 116 115 165 +f 166 116 165 +f 117 116 166 +f 167 117 166 +f 118 117 167 +f 168 118 167 +f 119 118 168 +f 169 119 168 +f 120 119 169 +f 170 120 169 +f 121 120 170 +f 171 121 170 +f 122 121 171 +f 172 122 171 +f 123 122 172 +f 173 123 172 +f 124 123 173 +f 174 124 173 +f 125 124 174 +f 175 125 174 +f 126 125 175 +f 176 126 175 +f 127 126 176 +f 177 127 176 +f 128 127 177 +f 178 128 177 +f 129 128 178 +f 179 129 178 +f 130 129 179 +f 180 130 179 +f 131 130 180 +f 181 131 180 +f 132 131 181 +f 182 132 181 +f 133 132 182 +f 183 133 182 +f 134 133 183 +f 184 134 183 +f 135 134 184 +f 185 135 184 +f 136 135 185 +f 186 136 185 +f 137 136 186 +f 187 137 186 +f 138 137 187 +f 188 138 187 +f 139 138 188 +f 189 139 188 +f 140 139 189 +f 190 140 189 +f 141 140 190 +f 191 141 190 +f 142 141 191 +f 192 142 191 +f 143 142 192 +f 193 143 192 +f 144 143 193 +f 194 144 193 +f 145 144 194 +f 195 145 194 +f 146 145 195 +f 196 146 195 +f 147 146 196 +f 197 147 196 +f 148 147 197 +f 198 148 197 +f 149 148 198 +f 199 149 198 +f 150 149 199 +f 200 150 199 +f 152 151 201 +f 202 152 201 +f 153 152 202 +f 203 153 202 +f 154 153 203 +f 204 154 203 +f 155 154 204 +f 205 155 204 +f 156 155 205 +f 206 156 205 +f 157 156 206 +f 207 157 206 +f 158 157 207 +f 208 158 207 +f 159 158 208 +f 209 159 208 +f 160 159 209 +f 210 160 209 +f 161 160 210 +f 211 161 210 +f 162 161 211 +f 212 162 211 +f 163 162 212 +f 213 163 212 +f 164 163 213 +f 214 164 213 +f 165 164 214 +f 215 165 214 +f 166 165 215 +f 216 166 215 +f 167 166 216 +f 217 167 216 +f 168 167 217 +f 218 168 217 +f 169 168 218 +f 219 169 218 +f 170 169 219 +f 220 170 219 +f 171 170 220 +f 221 171 220 +f 172 171 221 +f 222 172 221 +f 173 172 222 +f 223 173 222 +f 174 173 223 +f 224 174 223 +f 175 174 224 +f 225 175 224 +f 176 175 225 +f 226 176 225 +f 177 176 226 +f 227 177 226 +f 178 177 227 +f 228 178 227 +f 179 178 228 +f 229 179 228 +f 180 179 229 +f 230 180 229 +f 181 180 230 +f 231 181 230 +f 182 181 231 +f 232 182 231 +f 183 182 232 +f 233 183 232 +f 184 183 233 +f 234 184 233 +f 185 184 234 +f 235 185 234 +f 186 185 235 +f 236 186 235 +f 187 186 236 +f 237 187 236 +f 188 187 237 +f 238 188 237 +f 189 188 238 +f 239 189 238 +f 190 189 239 +f 240 190 239 +f 191 190 240 +f 241 191 240 +f 192 191 241 +f 242 192 241 +f 193 192 242 +f 243 193 242 +f 194 193 243 +f 244 194 243 +f 195 194 244 +f 245 195 244 +f 196 195 245 +f 246 196 245 +f 197 196 246 +f 247 197 246 +f 198 197 247 +f 248 198 247 +f 199 198 248 +f 249 199 248 +f 200 199 249 +f 250 200 249 +f 202 201 251 +f 252 202 251 +f 203 202 252 +f 253 203 252 +f 204 203 253 +f 254 204 253 +f 205 204 254 +f 255 205 254 +f 206 205 255 +f 256 206 255 +f 207 206 256 +f 257 207 256 +f 208 207 257 +f 258 208 257 +f 209 208 258 +f 259 209 258 +f 210 209 259 +f 260 210 259 +f 211 210 260 +f 261 211 260 +f 212 211 261 +f 262 212 261 +f 213 212 262 +f 263 213 262 +f 214 213 263 +f 264 214 263 +f 215 214 264 +f 265 215 264 +f 216 215 265 +f 266 216 265 +f 217 216 266 +f 267 217 266 +f 218 217 267 +f 268 218 267 +f 219 218 268 +f 269 219 268 +f 220 219 269 +f 270 220 269 +f 221 220 270 +f 271 221 270 +f 222 221 271 +f 272 222 271 +f 223 222 272 +f 273 223 272 +f 224 223 273 +f 274 224 273 +f 225 224 274 +f 275 225 274 +f 226 225 275 +f 276 226 275 +f 227 226 276 +f 277 227 276 +f 228 227 277 +f 278 228 277 +f 229 228 278 +f 279 229 278 +f 230 229 279 +f 280 230 279 +f 231 230 280 +f 281 231 280 +f 232 231 281 +f 282 232 281 +f 233 232 282 +f 283 233 282 +f 234 233 283 +f 284 234 283 +f 235 234 284 +f 285 235 284 +f 236 235 285 +f 286 236 285 +f 237 236 286 +f 287 237 286 +f 238 237 287 +f 288 238 287 +f 239 238 288 +f 289 239 288 +f 240 239 289 +f 290 240 289 +f 241 240 290 +f 291 241 290 +f 242 241 291 +f 292 242 291 +f 243 242 292 +f 293 243 292 +f 244 243 293 +f 294 244 293 +f 245 244 294 +f 295 245 294 +f 246 245 295 +f 296 246 295 +f 247 246 296 +f 297 247 296 +f 248 247 297 +f 298 248 297 +f 249 248 298 +f 299 249 298 +f 250 249 299 +f 300 250 299 +f 252 251 301 +f 302 252 301 +f 253 252 302 +f 303 253 302 +f 254 253 303 +f 304 254 303 +f 255 254 304 +f 305 255 304 +f 256 255 305 +f 306 256 305 +f 257 256 306 +f 307 257 306 +f 258 257 307 +f 308 258 307 +f 259 258 308 +f 309 259 308 +f 260 259 309 +f 310 260 309 +f 261 260 310 +f 311 261 310 +f 262 261 311 +f 312 262 311 +f 263 262 312 +f 313 263 312 +f 264 263 313 +f 314 264 313 +f 265 264 314 +f 315 265 314 +f 266 265 315 +f 316 266 315 +f 267 266 316 +f 317 267 316 +f 268 267 317 +f 318 268 317 +f 269 268 318 +f 319 269 318 +f 270 269 319 +f 320 270 319 +f 271 270 320 +f 321 271 320 +f 272 271 321 +f 322 272 321 +f 273 272 322 +f 323 273 322 +f 274 273 323 +f 324 274 323 +f 275 274 324 +f 325 275 324 +f 276 275 325 +f 326 276 325 +f 277 276 326 +f 327 277 326 +f 278 277 327 +f 328 278 327 +f 279 278 328 +f 329 279 328 +f 280 279 329 +f 330 280 329 +f 281 280 330 +f 331 281 330 +f 282 281 331 +f 332 282 331 +f 283 282 332 +f 333 283 332 +f 284 283 333 +f 334 284 333 +f 285 284 334 +f 335 285 334 +f 286 285 335 +f 336 286 335 +f 287 286 336 +f 337 287 336 +f 288 287 337 +f 338 288 337 +f 289 288 338 +f 339 289 338 +f 290 289 339 +f 340 290 339 +f 291 290 340 +f 341 291 340 +f 292 291 341 +f 342 292 341 +f 293 292 342 +f 343 293 342 +f 294 293 343 +f 344 294 343 +f 295 294 344 +f 345 295 344 +f 296 295 345 +f 346 296 345 +f 297 296 346 +f 347 297 346 +f 298 297 347 +f 348 298 347 +f 299 298 348 +f 349 299 348 +f 300 299 349 +f 350 300 349 +f 302 301 351 +f 352 302 351 +f 303 302 352 +f 353 303 352 +f 304 303 353 +f 354 304 353 +f 305 304 354 +f 355 305 354 +f 306 305 355 +f 356 306 355 +f 307 306 356 +f 357 307 356 +f 308 307 357 +f 358 308 357 +f 309 308 358 +f 359 309 358 +f 310 309 359 +f 360 310 359 +f 311 310 360 +f 361 311 360 +f 312 311 361 +f 362 312 361 +f 313 312 362 +f 363 313 362 +f 314 313 363 +f 364 314 363 +f 315 314 364 +f 365 315 364 +f 316 315 365 +f 366 316 365 +f 317 316 366 +f 367 317 366 +f 318 317 367 +f 368 318 367 +f 319 318 368 +f 369 319 368 +f 320 319 369 +f 370 320 369 +f 321 320 370 +f 371 321 370 +f 322 321 371 +f 372 322 371 +f 323 322 372 +f 373 323 372 +f 324 323 373 +f 374 324 373 +f 325 324 374 +f 375 325 374 +f 326 325 375 +f 376 326 375 +f 327 326 376 +f 377 327 376 +f 328 327 377 +f 378 328 377 +f 329 328 378 +f 379 329 378 +f 330 329 379 +f 380 330 379 +f 331 330 380 +f 381 331 380 +f 332 331 381 +f 382 332 381 +f 333 332 382 +f 383 333 382 +f 334 333 383 +f 384 334 383 +f 335 334 384 +f 385 335 384 +f 336 335 385 +f 386 336 385 +f 337 336 386 +f 387 337 386 +f 338 337 387 +f 388 338 387 +f 339 338 388 +f 389 339 388 +f 340 339 389 +f 390 340 389 +f 341 340 390 +f 391 341 390 +f 342 341 391 +f 392 342 391 +f 343 342 392 +f 393 343 392 +f 344 343 393 +f 394 344 393 +f 345 344 394 +f 395 345 394 +f 346 345 395 +f 396 346 395 +f 347 346 396 +f 397 347 396 +f 348 347 397 +f 398 348 397 +f 349 348 398 +f 399 349 398 +f 350 349 399 +f 400 350 399 +f 352 351 401 +f 402 352 401 +f 353 352 402 +f 403 353 402 +f 354 353 403 +f 404 354 403 +f 355 354 404 +f 405 355 404 +f 356 355 405 +f 406 356 405 +f 357 356 406 +f 407 357 406 +f 358 357 407 +f 408 358 407 +f 359 358 408 +f 409 359 408 +f 360 359 409 +f 410 360 409 +f 361 360 410 +f 411 361 410 +f 362 361 411 +f 412 362 411 +f 363 362 412 +f 413 363 412 +f 364 363 413 +f 414 364 413 +f 365 364 414 +f 415 365 414 +f 366 365 415 +f 416 366 415 +f 367 366 416 +f 417 367 416 +f 368 367 417 +f 418 368 417 +f 369 368 418 +f 419 369 418 +f 370 369 419 +f 420 370 419 +f 371 370 420 +f 421 371 420 +f 372 371 421 +f 422 372 421 +f 373 372 422 +f 423 373 422 +f 374 373 423 +f 424 374 423 +f 375 374 424 +f 425 375 424 +f 376 375 425 +f 426 376 425 +f 377 376 426 +f 427 377 426 +f 378 377 427 +f 428 378 427 +f 379 378 428 +f 429 379 428 +f 380 379 429 +f 430 380 429 +f 381 380 430 +f 431 381 430 +f 382 381 431 +f 432 382 431 +f 383 382 432 +f 433 383 432 +f 384 383 433 +f 434 384 433 +f 385 384 434 +f 435 385 434 +f 386 385 435 +f 436 386 435 +f 387 386 436 +f 437 387 436 +f 388 387 437 +f 438 388 437 +f 389 388 438 +f 439 389 438 +f 390 389 439 +f 440 390 439 +f 391 390 440 +f 441 391 440 +f 392 391 441 +f 442 392 441 +f 393 392 442 +f 443 393 442 +f 394 393 443 +f 444 394 443 +f 395 394 444 +f 445 395 444 +f 396 395 445 +f 446 396 445 +f 397 396 446 +f 447 397 446 +f 398 397 447 +f 448 398 447 +f 399 398 448 +f 449 399 448 +f 400 399 449 +f 450 400 449 +f 402 401 451 +f 452 402 451 +f 403 402 452 +f 453 403 452 +f 404 403 453 +f 454 404 453 +f 405 404 454 +f 455 405 454 +f 406 405 455 +f 456 406 455 +f 407 406 456 +f 457 407 456 +f 408 407 457 +f 458 408 457 +f 409 408 458 +f 459 409 458 +f 410 409 459 +f 460 410 459 +f 411 410 460 +f 461 411 460 +f 412 411 461 +f 462 412 461 +f 413 412 462 +f 463 413 462 +f 414 413 463 +f 464 414 463 +f 415 414 464 +f 465 415 464 +f 416 415 465 +f 466 416 465 +f 417 416 466 +f 467 417 466 +f 418 417 467 +f 468 418 467 +f 419 418 468 +f 469 419 468 +f 420 419 469 +f 470 420 469 +f 421 420 470 +f 471 421 470 +f 422 421 471 +f 472 422 471 +f 423 422 472 +f 473 423 472 +f 424 423 473 +f 474 424 473 +f 425 424 474 +f 475 425 474 +f 426 425 475 +f 476 426 475 +f 427 426 476 +f 477 427 476 +f 428 427 477 +f 478 428 477 +f 429 428 478 +f 479 429 478 +f 430 429 479 +f 480 430 479 +f 431 430 480 +f 481 431 480 +f 432 431 481 +f 482 432 481 +f 433 432 482 +f 483 433 482 +f 434 433 483 +f 484 434 483 +f 435 434 484 +f 485 435 484 +f 436 435 485 +f 486 436 485 +f 437 436 486 +f 487 437 486 +f 438 437 487 +f 488 438 487 +f 439 438 488 +f 489 439 488 +f 440 439 489 +f 490 440 489 +f 441 440 490 +f 491 441 490 +f 442 441 491 +f 492 442 491 +f 443 442 492 +f 493 443 492 +f 444 443 493 +f 494 444 493 +f 445 444 494 +f 495 445 494 +f 446 445 495 +f 496 446 495 +f 447 446 496 +f 497 447 496 +f 448 447 497 +f 498 448 497 +f 449 448 498 +f 499 449 498 +f 450 449 499 +f 500 450 499 +f 452 451 501 +f 502 452 501 +f 453 452 502 +f 503 453 502 +f 454 453 503 +f 504 454 503 +f 455 454 504 +f 505 455 504 +f 456 455 505 +f 506 456 505 +f 457 456 506 +f 507 457 506 +f 458 457 507 +f 508 458 507 +f 459 458 508 +f 509 459 508 +f 460 459 509 +f 510 460 509 +f 461 460 510 +f 511 461 510 +f 462 461 511 +f 512 462 511 +f 463 462 512 +f 513 463 512 +f 464 463 513 +f 514 464 513 +f 465 464 514 +f 515 465 514 +f 466 465 515 +f 516 466 515 +f 467 466 516 +f 517 467 516 +f 468 467 517 +f 518 468 517 +f 469 468 518 +f 519 469 518 +f 470 469 519 +f 520 470 519 +f 471 470 520 +f 521 471 520 +f 472 471 521 +f 522 472 521 +f 473 472 522 +f 523 473 522 +f 474 473 523 +f 524 474 523 +f 475 474 524 +f 525 475 524 +f 476 475 525 +f 526 476 525 +f 477 476 526 +f 527 477 526 +f 478 477 527 +f 528 478 527 +f 479 478 528 +f 529 479 528 +f 480 479 529 +f 530 480 529 +f 481 480 530 +f 531 481 530 +f 482 481 531 +f 532 482 531 +f 483 482 532 +f 533 483 532 +f 484 483 533 +f 534 484 533 +f 485 484 534 +f 535 485 534 +f 486 485 535 +f 536 486 535 +f 487 486 536 +f 537 487 536 +f 488 487 537 +f 538 488 537 +f 489 488 538 +f 539 489 538 +f 490 489 539 +f 540 490 539 +f 491 490 540 +f 541 491 540 +f 492 491 541 +f 542 492 541 +f 493 492 542 +f 543 493 542 +f 494 493 543 +f 544 494 543 +f 495 494 544 +f 545 495 544 +f 496 495 545 +f 546 496 545 +f 497 496 546 +f 547 497 546 +f 498 497 547 +f 548 498 547 +f 499 498 548 +f 549 499 548 +f 500 499 549 +f 550 500 549 +f 502 501 551 +f 552 502 551 +f 503 502 552 +f 553 503 552 +f 504 503 553 +f 554 504 553 +f 505 504 554 +f 555 505 554 +f 506 505 555 +f 556 506 555 +f 507 506 556 +f 557 507 556 +f 508 507 557 +f 558 508 557 +f 509 508 558 +f 559 509 558 +f 510 509 559 +f 560 510 559 +f 511 510 560 +f 561 511 560 +f 512 511 561 +f 562 512 561 +f 513 512 562 +f 563 513 562 +f 514 513 563 +f 564 514 563 +f 515 514 564 +f 565 515 564 +f 516 515 565 +f 566 516 565 +f 517 516 566 +f 567 517 566 +f 518 517 567 +f 568 518 567 +f 519 518 568 +f 569 519 568 +f 520 519 569 +f 570 520 569 +f 521 520 570 +f 571 521 570 +f 522 521 571 +f 572 522 571 +f 523 522 572 +f 573 523 572 +f 524 523 573 +f 574 524 573 +f 525 524 574 +f 575 525 574 +f 526 525 575 +f 576 526 575 +f 527 526 576 +f 577 527 576 +f 528 527 577 +f 578 528 577 +f 529 528 578 +f 579 529 578 +f 530 529 579 +f 580 530 579 +f 531 530 580 +f 581 531 580 +f 532 531 581 +f 582 532 581 +f 533 532 582 +f 583 533 582 +f 534 533 583 +f 584 534 583 +f 535 534 584 +f 585 535 584 +f 536 535 585 +f 586 536 585 +f 537 536 586 +f 587 537 586 +f 538 537 587 +f 588 538 587 +f 539 538 588 +f 589 539 588 +f 540 539 589 +f 590 540 589 +f 541 540 590 +f 591 541 590 +f 542 541 591 +f 592 542 591 +f 543 542 592 +f 593 543 592 +f 544 543 593 +f 594 544 593 +f 545 544 594 +f 595 545 594 +f 546 545 595 +f 596 546 595 +f 547 546 596 +f 597 547 596 +f 548 547 597 +f 598 548 597 +f 549 548 598 +f 599 549 598 +f 550 549 599 +f 600 550 599 +f 552 551 601 +f 602 552 601 +f 553 552 602 +f 603 553 602 +f 554 553 603 +f 604 554 603 +f 555 554 604 +f 605 555 604 +f 556 555 605 +f 606 556 605 +f 557 556 606 +f 607 557 606 +f 558 557 607 +f 608 558 607 +f 559 558 608 +f 609 559 608 +f 560 559 609 +f 610 560 609 +f 561 560 610 +f 611 561 610 +f 562 561 611 +f 612 562 611 +f 563 562 612 +f 613 563 612 +f 564 563 613 +f 614 564 613 +f 565 564 614 +f 615 565 614 +f 566 565 615 +f 616 566 615 +f 567 566 616 +f 617 567 616 +f 568 567 617 +f 618 568 617 +f 569 568 618 +f 619 569 618 +f 570 569 619 +f 620 570 619 +f 571 570 620 +f 621 571 620 +f 572 571 621 +f 622 572 621 +f 573 572 622 +f 623 573 622 +f 574 573 623 +f 624 574 623 +f 575 574 624 +f 625 575 624 +f 576 575 625 +f 626 576 625 +f 577 576 626 +f 627 577 626 +f 578 577 627 +f 628 578 627 +f 579 578 628 +f 629 579 628 +f 580 579 629 +f 630 580 629 +f 581 580 630 +f 631 581 630 +f 582 581 631 +f 632 582 631 +f 583 582 632 +f 633 583 632 +f 584 583 633 +f 634 584 633 +f 585 584 634 +f 635 585 634 +f 586 585 635 +f 636 586 635 +f 587 586 636 +f 637 587 636 +f 588 587 637 +f 638 588 637 +f 589 588 638 +f 639 589 638 +f 590 589 639 +f 640 590 639 +f 591 590 640 +f 641 591 640 +f 592 591 641 +f 642 592 641 +f 593 592 642 +f 643 593 642 +f 594 593 643 +f 644 594 643 +f 595 594 644 +f 645 595 644 +f 596 595 645 +f 646 596 645 +f 597 596 646 +f 647 597 646 +f 598 597 647 +f 648 598 647 +f 599 598 648 +f 649 599 648 +f 600 599 649 +f 650 600 649 +f 602 601 651 +f 652 602 651 +f 603 602 652 +f 653 603 652 +f 604 603 653 +f 654 604 653 +f 605 604 654 +f 655 605 654 +f 606 605 655 +f 656 606 655 +f 607 606 656 +f 657 607 656 +f 608 607 657 +f 658 608 657 +f 609 608 658 +f 659 609 658 +f 610 609 659 +f 660 610 659 +f 611 610 660 +f 661 611 660 +f 612 611 661 +f 662 612 661 +f 613 612 662 +f 663 613 662 +f 614 613 663 +f 664 614 663 +f 615 614 664 +f 665 615 664 +f 616 615 665 +f 666 616 665 +f 617 616 666 +f 667 617 666 +f 618 617 667 +f 668 618 667 +f 619 618 668 +f 669 619 668 +f 620 619 669 +f 670 620 669 +f 621 620 670 +f 671 621 670 +f 622 621 671 +f 672 622 671 +f 623 622 672 +f 673 623 672 +f 624 623 673 +f 674 624 673 +f 625 624 674 +f 675 625 674 +f 626 625 675 +f 676 626 675 +f 627 626 676 +f 677 627 676 +f 628 627 677 +f 678 628 677 +f 629 628 678 +f 679 629 678 +f 630 629 679 +f 680 630 679 +f 631 630 680 +f 681 631 680 +f 632 631 681 +f 682 632 681 +f 633 632 682 +f 683 633 682 +f 634 633 683 +f 684 634 683 +f 635 634 684 +f 685 635 684 +f 636 635 685 +f 686 636 685 +f 637 636 686 +f 687 637 686 +f 638 637 687 +f 688 638 687 +f 639 638 688 +f 689 639 688 +f 640 639 689 +f 690 640 689 +f 641 640 690 +f 691 641 690 +f 642 641 691 +f 692 642 691 +f 643 642 692 +f 693 643 692 +f 644 643 693 +f 694 644 693 +f 645 644 694 +f 695 645 694 +f 646 645 695 +f 696 646 695 +f 647 646 696 +f 697 647 696 +f 648 647 697 +f 698 648 697 +f 649 648 698 +f 699 649 698 +f 650 649 699 +f 700 650 699 +f 652 651 701 +f 702 652 701 +f 653 652 702 +f 703 653 702 +f 654 653 703 +f 704 654 703 +f 655 654 704 +f 705 655 704 +f 656 655 705 +f 706 656 705 +f 657 656 706 +f 707 657 706 +f 658 657 707 +f 708 658 707 +f 659 658 708 +f 709 659 708 +f 660 659 709 +f 710 660 709 +f 661 660 710 +f 711 661 710 +f 662 661 711 +f 712 662 711 +f 663 662 712 +f 713 663 712 +f 664 663 713 +f 714 664 713 +f 665 664 714 +f 715 665 714 +f 666 665 715 +f 716 666 715 +f 667 666 716 +f 717 667 716 +f 668 667 717 +f 718 668 717 +f 669 668 718 +f 719 669 718 +f 670 669 719 +f 720 670 719 +f 671 670 720 +f 721 671 720 +f 672 671 721 +f 722 672 721 +f 673 672 722 +f 723 673 722 +f 674 673 723 +f 724 674 723 +f 675 674 724 +f 725 675 724 +f 676 675 725 +f 726 676 725 +f 677 676 726 +f 727 677 726 +f 678 677 727 +f 728 678 727 +f 679 678 728 +f 729 679 728 +f 680 679 729 +f 730 680 729 +f 681 680 730 +f 731 681 730 +f 682 681 731 +f 732 682 731 +f 683 682 732 +f 733 683 732 +f 684 683 733 +f 734 684 733 +f 685 684 734 +f 735 685 734 +f 686 685 735 +f 736 686 735 +f 687 686 736 +f 737 687 736 +f 688 687 737 +f 738 688 737 +f 689 688 738 +f 739 689 738 +f 690 689 739 +f 740 690 739 +f 691 690 740 +f 741 691 740 +f 692 691 741 +f 742 692 741 +f 693 692 742 +f 743 693 742 +f 694 693 743 +f 744 694 743 +f 695 694 744 +f 745 695 744 +f 696 695 745 +f 746 696 745 +f 697 696 746 +f 747 697 746 +f 698 697 747 +f 748 698 747 +f 699 698 748 +f 749 699 748 +f 700 699 749 +f 750 700 749 +f 702 701 751 +f 752 702 751 +f 703 702 752 +f 753 703 752 +f 704 703 753 +f 754 704 753 +f 705 704 754 +f 755 705 754 +f 706 705 755 +f 756 706 755 +f 707 706 756 +f 757 707 756 +f 708 707 757 +f 758 708 757 +f 709 708 758 +f 759 709 758 +f 710 709 759 +f 760 710 759 +f 711 710 760 +f 761 711 760 +f 712 711 761 +f 762 712 761 +f 713 712 762 +f 763 713 762 +f 714 713 763 +f 764 714 763 +f 715 714 764 +f 765 715 764 +f 716 715 765 +f 766 716 765 +f 717 716 766 +f 767 717 766 +f 718 717 767 +f 768 718 767 +f 719 718 768 +f 769 719 768 +f 720 719 769 +f 770 720 769 +f 721 720 770 +f 771 721 770 +f 722 721 771 +f 772 722 771 +f 723 722 772 +f 773 723 772 +f 724 723 773 +f 774 724 773 +f 725 724 774 +f 775 725 774 +f 726 725 775 +f 776 726 775 +f 727 726 776 +f 777 727 776 +f 728 727 777 +f 778 728 777 +f 729 728 778 +f 779 729 778 +f 730 729 779 +f 780 730 779 +f 731 730 780 +f 781 731 780 +f 732 731 781 +f 782 732 781 +f 733 732 782 +f 783 733 782 +f 734 733 783 +f 784 734 783 +f 735 734 784 +f 785 735 784 +f 736 735 785 +f 786 736 785 +f 737 736 786 +f 787 737 786 +f 738 737 787 +f 788 738 787 +f 739 738 788 +f 789 739 788 +f 740 739 789 +f 790 740 789 +f 741 740 790 +f 791 741 790 +f 742 741 791 +f 792 742 791 +f 743 742 792 +f 793 743 792 +f 744 743 793 +f 794 744 793 +f 745 744 794 +f 795 745 794 +f 746 745 795 +f 796 746 795 +f 747 746 796 +f 797 747 796 +f 748 747 797 +f 798 748 797 +f 749 748 798 +f 799 749 798 +f 750 749 799 +f 800 750 799 +f 752 751 801 +f 802 752 801 +f 753 752 802 +f 803 753 802 +f 754 753 803 +f 804 754 803 +f 755 754 804 +f 805 755 804 +f 756 755 805 +f 806 756 805 +f 757 756 806 +f 807 757 806 +f 758 757 807 +f 808 758 807 +f 759 758 808 +f 809 759 808 +f 760 759 809 +f 810 760 809 +f 761 760 810 +f 811 761 810 +f 762 761 811 +f 812 762 811 +f 763 762 812 +f 813 763 812 +f 764 763 813 +f 814 764 813 +f 765 764 814 +f 815 765 814 +f 766 765 815 +f 816 766 815 +f 767 766 816 +f 817 767 816 +f 768 767 817 +f 818 768 817 +f 769 768 818 +f 819 769 818 +f 770 769 819 +f 820 770 819 +f 771 770 820 +f 821 771 820 +f 772 771 821 +f 822 772 821 +f 773 772 822 +f 823 773 822 +f 774 773 823 +f 824 774 823 +f 775 774 824 +f 825 775 824 +f 776 775 825 +f 826 776 825 +f 777 776 826 +f 827 777 826 +f 778 777 827 +f 828 778 827 +f 779 778 828 +f 829 779 828 +f 780 779 829 +f 830 780 829 +f 781 780 830 +f 831 781 830 +f 782 781 831 +f 832 782 831 +f 783 782 832 +f 833 783 832 +f 784 783 833 +f 834 784 833 +f 785 784 834 +f 835 785 834 +f 786 785 835 +f 836 786 835 +f 787 786 836 +f 837 787 836 +f 788 787 837 +f 838 788 837 +f 789 788 838 +f 839 789 838 +f 790 789 839 +f 840 790 839 +f 791 790 840 +f 841 791 840 +f 792 791 841 +f 842 792 841 +f 793 792 842 +f 843 793 842 +f 794 793 843 +f 844 794 843 +f 795 794 844 +f 845 795 844 +f 796 795 845 +f 846 796 845 +f 797 796 846 +f 847 797 846 +f 798 797 847 +f 848 798 847 +f 799 798 848 +f 849 799 848 +f 800 799 849 +f 850 800 849 +f 802 801 851 +f 852 802 851 +f 803 802 852 +f 853 803 852 +f 804 803 853 +f 854 804 853 +f 805 804 854 +f 855 805 854 +f 806 805 855 +f 856 806 855 +f 807 806 856 +f 857 807 856 +f 808 807 857 +f 858 808 857 +f 809 808 858 +f 859 809 858 +f 810 809 859 +f 860 810 859 +f 811 810 860 +f 861 811 860 +f 812 811 861 +f 862 812 861 +f 813 812 862 +f 863 813 862 +f 814 813 863 +f 864 814 863 +f 815 814 864 +f 865 815 864 +f 816 815 865 +f 866 816 865 +f 817 816 866 +f 867 817 866 +f 818 817 867 +f 868 818 867 +f 819 818 868 +f 869 819 868 +f 820 819 869 +f 870 820 869 +f 821 820 870 +f 871 821 870 +f 822 821 871 +f 872 822 871 +f 823 822 872 +f 873 823 872 +f 824 823 873 +f 874 824 873 +f 825 824 874 +f 875 825 874 +f 826 825 875 +f 876 826 875 +f 827 826 876 +f 877 827 876 +f 828 827 877 +f 878 828 877 +f 829 828 878 +f 879 829 878 +f 830 829 879 +f 880 830 879 +f 831 830 880 +f 881 831 880 +f 832 831 881 +f 882 832 881 +f 833 832 882 +f 883 833 882 +f 834 833 883 +f 884 834 883 +f 835 834 884 +f 885 835 884 +f 836 835 885 +f 886 836 885 +f 837 836 886 +f 887 837 886 +f 838 837 887 +f 888 838 887 +f 839 838 888 +f 889 839 888 +f 840 839 889 +f 890 840 889 +f 841 840 890 +f 891 841 890 +f 842 841 891 +f 892 842 891 +f 843 842 892 +f 893 843 892 +f 844 843 893 +f 894 844 893 +f 845 844 894 +f 895 845 894 +f 846 845 895 +f 896 846 895 +f 847 846 896 +f 897 847 896 +f 848 847 897 +f 898 848 897 +f 849 848 898 +f 899 849 898 +f 850 849 899 +f 900 850 899 +f 852 851 901 +f 902 852 901 +f 853 852 902 +f 903 853 902 +f 854 853 903 +f 904 854 903 +f 855 854 904 +f 905 855 904 +f 856 855 905 +f 906 856 905 +f 857 856 906 +f 907 857 906 +f 858 857 907 +f 908 858 907 +f 859 858 908 +f 909 859 908 +f 860 859 909 +f 910 860 909 +f 861 860 910 +f 911 861 910 +f 862 861 911 +f 912 862 911 +f 863 862 912 +f 913 863 912 +f 864 863 913 +f 914 864 913 +f 865 864 914 +f 915 865 914 +f 866 865 915 +f 916 866 915 +f 867 866 916 +f 917 867 916 +f 868 867 917 +f 918 868 917 +f 869 868 918 +f 919 869 918 +f 870 869 919 +f 920 870 919 +f 871 870 920 +f 921 871 920 +f 872 871 921 +f 922 872 921 +f 873 872 922 +f 923 873 922 +f 874 873 923 +f 924 874 923 +f 875 874 924 +f 925 875 924 +f 876 875 925 +f 926 876 925 +f 877 876 926 +f 927 877 926 +f 878 877 927 +f 928 878 927 +f 879 878 928 +f 929 879 928 +f 880 879 929 +f 930 880 929 +f 881 880 930 +f 931 881 930 +f 882 881 931 +f 932 882 931 +f 883 882 932 +f 933 883 932 +f 884 883 933 +f 934 884 933 +f 885 884 934 +f 935 885 934 +f 886 885 935 +f 936 886 935 +f 887 886 936 +f 937 887 936 +f 888 887 937 +f 938 888 937 +f 889 888 938 +f 939 889 938 +f 890 889 939 +f 940 890 939 +f 891 890 940 +f 941 891 940 +f 892 891 941 +f 942 892 941 +f 893 892 942 +f 943 893 942 +f 894 893 943 +f 944 894 943 +f 895 894 944 +f 945 895 944 +f 896 895 945 +f 946 896 945 +f 897 896 946 +f 947 897 946 +f 898 897 947 +f 948 898 947 +f 899 898 948 +f 949 899 948 +f 900 899 949 +f 950 900 949 +f 902 901 951 +f 952 902 951 +f 903 902 952 +f 953 903 952 +f 904 903 953 +f 954 904 953 +f 905 904 954 +f 955 905 954 +f 906 905 955 +f 956 906 955 +f 907 906 956 +f 957 907 956 +f 908 907 957 +f 958 908 957 +f 909 908 958 +f 959 909 958 +f 910 909 959 +f 960 910 959 +f 911 910 960 +f 961 911 960 +f 912 911 961 +f 962 912 961 +f 913 912 962 +f 963 913 962 +f 914 913 963 +f 964 914 963 +f 915 914 964 +f 965 915 964 +f 916 915 965 +f 966 916 965 +f 917 916 966 +f 967 917 966 +f 918 917 967 +f 968 918 967 +f 919 918 968 +f 969 919 968 +f 920 919 969 +f 970 920 969 +f 921 920 970 +f 971 921 970 +f 922 921 971 +f 972 922 971 +f 923 922 972 +f 973 923 972 +f 924 923 973 +f 974 924 973 +f 925 924 974 +f 975 925 974 +f 926 925 975 +f 976 926 975 +f 927 926 976 +f 977 927 976 +f 928 927 977 +f 978 928 977 +f 929 928 978 +f 979 929 978 +f 930 929 979 +f 980 930 979 +f 931 930 980 +f 981 931 980 +f 932 931 981 +f 982 932 981 +f 933 932 982 +f 983 933 982 +f 934 933 983 +f 984 934 983 +f 935 934 984 +f 985 935 984 +f 936 935 985 +f 986 936 985 +f 937 936 986 +f 987 937 986 +f 938 937 987 +f 988 938 987 +f 939 938 988 +f 989 939 988 +f 940 939 989 +f 990 940 989 +f 941 940 990 +f 991 941 990 +f 942 941 991 +f 992 942 991 +f 943 942 992 +f 993 943 992 +f 944 943 993 +f 994 944 993 +f 945 944 994 +f 995 945 994 +f 946 945 995 +f 996 946 995 +f 947 946 996 +f 997 947 996 +f 948 947 997 +f 998 948 997 +f 949 948 998 +f 999 949 998 +f 950 949 999 +f 1000 950 999 +f 952 951 1001 +f 1002 952 1001 +f 953 952 1002 +f 1003 953 1002 +f 954 953 1003 +f 1004 954 1003 +f 955 954 1004 +f 1005 955 1004 +f 956 955 1005 +f 1006 956 1005 +f 957 956 1006 +f 1007 957 1006 +f 958 957 1007 +f 1008 958 1007 +f 959 958 1008 +f 1009 959 1008 +f 960 959 1009 +f 1010 960 1009 +f 961 960 1010 +f 1011 961 1010 +f 962 961 1011 +f 1012 962 1011 +f 963 962 1012 +f 1013 963 1012 +f 964 963 1013 +f 1014 964 1013 +f 965 964 1014 +f 1015 965 1014 +f 966 965 1015 +f 1016 966 1015 +f 967 966 1016 +f 1017 967 1016 +f 968 967 1017 +f 1018 968 1017 +f 969 968 1018 +f 1019 969 1018 +f 970 969 1019 +f 1020 970 1019 +f 971 970 1020 +f 1021 971 1020 +f 972 971 1021 +f 1022 972 1021 +f 973 972 1022 +f 1023 973 1022 +f 974 973 1023 +f 1024 974 1023 +f 975 974 1024 +f 1025 975 1024 +f 976 975 1025 +f 1026 976 1025 +f 977 976 1026 +f 1027 977 1026 +f 978 977 1027 +f 1028 978 1027 +f 979 978 1028 +f 1029 979 1028 +f 980 979 1029 +f 1030 980 1029 +f 981 980 1030 +f 1031 981 1030 +f 982 981 1031 +f 1032 982 1031 +f 983 982 1032 +f 1033 983 1032 +f 984 983 1033 +f 1034 984 1033 +f 985 984 1034 +f 1035 985 1034 +f 986 985 1035 +f 1036 986 1035 +f 987 986 1036 +f 1037 987 1036 +f 988 987 1037 +f 1038 988 1037 +f 989 988 1038 +f 1039 989 1038 +f 990 989 1039 +f 1040 990 1039 +f 991 990 1040 +f 1041 991 1040 +f 992 991 1041 +f 1042 992 1041 +f 993 992 1042 +f 1043 993 1042 +f 994 993 1043 +f 1044 994 1043 +f 995 994 1044 +f 1045 995 1044 +f 996 995 1045 +f 1046 996 1045 +f 997 996 1046 +f 1047 997 1046 +f 998 997 1047 +f 1048 998 1047 +f 999 998 1048 +f 1049 999 1048 +f 1000 999 1049 +f 1050 1000 1049 +f 1002 1001 1051 +f 1052 1002 1051 +f 1003 1002 1052 +f 1053 1003 1052 +f 1004 1003 1053 +f 1054 1004 1053 +f 1005 1004 1054 +f 1055 1005 1054 +f 1006 1005 1055 +f 1056 1006 1055 +f 1007 1006 1056 +f 1057 1007 1056 +f 1008 1007 1057 +f 1058 1008 1057 +f 1009 1008 1058 +f 1059 1009 1058 +f 1010 1009 1059 +f 1060 1010 1059 +f 1011 1010 1060 +f 1061 1011 1060 +f 1012 1011 1061 +f 1062 1012 1061 +f 1013 1012 1062 +f 1063 1013 1062 +f 1014 1013 1063 +f 1064 1014 1063 +f 1015 1014 1064 +f 1065 1015 1064 +f 1016 1015 1065 +f 1066 1016 1065 +f 1017 1016 1066 +f 1067 1017 1066 +f 1018 1017 1067 +f 1068 1018 1067 +f 1019 1018 1068 +f 1069 1019 1068 +f 1020 1019 1069 +f 1070 1020 1069 +f 1021 1020 1070 +f 1071 1021 1070 +f 1022 1021 1071 +f 1072 1022 1071 +f 1023 1022 1072 +f 1073 1023 1072 +f 1024 1023 1073 +f 1074 1024 1073 +f 1025 1024 1074 +f 1075 1025 1074 +f 1026 1025 1075 +f 1076 1026 1075 +f 1027 1026 1076 +f 1077 1027 1076 +f 1028 1027 1077 +f 1078 1028 1077 +f 1029 1028 1078 +f 1079 1029 1078 +f 1030 1029 1079 +f 1080 1030 1079 +f 1031 1030 1080 +f 1081 1031 1080 +f 1032 1031 1081 +f 1082 1032 1081 +f 1033 1032 1082 +f 1083 1033 1082 +f 1034 1033 1083 +f 1084 1034 1083 +f 1035 1034 1084 +f 1085 1035 1084 +f 1036 1035 1085 +f 1086 1036 1085 +f 1037 1036 1086 +f 1087 1037 1086 +f 1038 1037 1087 +f 1088 1038 1087 +f 1039 1038 1088 +f 1089 1039 1088 +f 1040 1039 1089 +f 1090 1040 1089 +f 1041 1040 1090 +f 1091 1041 1090 +f 1042 1041 1091 +f 1092 1042 1091 +f 1043 1042 1092 +f 1093 1043 1092 +f 1044 1043 1093 +f 1094 1044 1093 +f 1045 1044 1094 +f 1095 1045 1094 +f 1046 1045 1095 +f 1096 1046 1095 +f 1047 1046 1096 +f 1097 1047 1096 +f 1048 1047 1097 +f 1098 1048 1097 +f 1049 1048 1098 +f 1099 1049 1098 +f 1050 1049 1099 +f 1100 1050 1099 +f 1052 1051 1101 +f 1102 1052 1101 +f 1053 1052 1102 +f 1103 1053 1102 +f 1054 1053 1103 +f 1104 1054 1103 +f 1055 1054 1104 +f 1105 1055 1104 +f 1056 1055 1105 +f 1106 1056 1105 +f 1057 1056 1106 +f 1107 1057 1106 +f 1058 1057 1107 +f 1108 1058 1107 +f 1059 1058 1108 +f 1109 1059 1108 +f 1060 1059 1109 +f 1110 1060 1109 +f 1061 1060 1110 +f 1111 1061 1110 +f 1062 1061 1111 +f 1112 1062 1111 +f 1063 1062 1112 +f 1113 1063 1112 +f 1064 1063 1113 +f 1114 1064 1113 +f 1065 1064 1114 +f 1115 1065 1114 +f 1066 1065 1115 +f 1116 1066 1115 +f 1067 1066 1116 +f 1117 1067 1116 +f 1068 1067 1117 +f 1118 1068 1117 +f 1069 1068 1118 +f 1119 1069 1118 +f 1070 1069 1119 +f 1120 1070 1119 +f 1071 1070 1120 +f 1121 1071 1120 +f 1072 1071 1121 +f 1122 1072 1121 +f 1073 1072 1122 +f 1123 1073 1122 +f 1074 1073 1123 +f 1124 1074 1123 +f 1075 1074 1124 +f 1125 1075 1124 +f 1076 1075 1125 +f 1126 1076 1125 +f 1077 1076 1126 +f 1127 1077 1126 +f 1078 1077 1127 +f 1128 1078 1127 +f 1079 1078 1128 +f 1129 1079 1128 +f 1080 1079 1129 +f 1130 1080 1129 +f 1081 1080 1130 +f 1131 1081 1130 +f 1082 1081 1131 +f 1132 1082 1131 +f 1083 1082 1132 +f 1133 1083 1132 +f 1084 1083 1133 +f 1134 1084 1133 +f 1085 1084 1134 +f 1135 1085 1134 +f 1086 1085 1135 +f 1136 1086 1135 +f 1087 1086 1136 +f 1137 1087 1136 +f 1088 1087 1137 +f 1138 1088 1137 +f 1089 1088 1138 +f 1139 1089 1138 +f 1090 1089 1139 +f 1140 1090 1139 +f 1091 1090 1140 +f 1141 1091 1140 +f 1092 1091 1141 +f 1142 1092 1141 +f 1093 1092 1142 +f 1143 1093 1142 +f 1094 1093 1143 +f 1144 1094 1143 +f 1095 1094 1144 +f 1145 1095 1144 +f 1096 1095 1145 +f 1146 1096 1145 +f 1097 1096 1146 +f 1147 1097 1146 +f 1098 1097 1147 +f 1148 1098 1147 +f 1099 1098 1148 +f 1149 1099 1148 +f 1100 1099 1149 +f 1150 1100 1149 +f 1102 1101 1151 +f 1152 1102 1151 +f 1103 1102 1152 +f 1153 1103 1152 +f 1104 1103 1153 +f 1154 1104 1153 +f 1105 1104 1154 +f 1155 1105 1154 +f 1106 1105 1155 +f 1156 1106 1155 +f 1107 1106 1156 +f 1157 1107 1156 +f 1108 1107 1157 +f 1158 1108 1157 +f 1109 1108 1158 +f 1159 1109 1158 +f 1110 1109 1159 +f 1160 1110 1159 +f 1111 1110 1160 +f 1161 1111 1160 +f 1112 1111 1161 +f 1162 1112 1161 +f 1113 1112 1162 +f 1163 1113 1162 +f 1114 1113 1163 +f 1164 1114 1163 +f 1115 1114 1164 +f 1165 1115 1164 +f 1116 1115 1165 +f 1166 1116 1165 +f 1117 1116 1166 +f 1167 1117 1166 +f 1118 1117 1167 +f 1168 1118 1167 +f 1119 1118 1168 +f 1169 1119 1168 +f 1120 1119 1169 +f 1170 1120 1169 +f 1121 1120 1170 +f 1171 1121 1170 +f 1122 1121 1171 +f 1172 1122 1171 +f 1123 1122 1172 +f 1173 1123 1172 +f 1124 1123 1173 +f 1174 1124 1173 +f 1125 1124 1174 +f 1175 1125 1174 +f 1126 1125 1175 +f 1176 1126 1175 +f 1127 1126 1176 +f 1177 1127 1176 +f 1128 1127 1177 +f 1178 1128 1177 +f 1129 1128 1178 +f 1179 1129 1178 +f 1130 1129 1179 +f 1180 1130 1179 +f 1131 1130 1180 +f 1181 1131 1180 +f 1132 1131 1181 +f 1182 1132 1181 +f 1133 1132 1182 +f 1183 1133 1182 +f 1134 1133 1183 +f 1184 1134 1183 +f 1135 1134 1184 +f 1185 1135 1184 +f 1136 1135 1185 +f 1186 1136 1185 +f 1137 1136 1186 +f 1187 1137 1186 +f 1138 1137 1187 +f 1188 1138 1187 +f 1139 1138 1188 +f 1189 1139 1188 +f 1140 1139 1189 +f 1190 1140 1189 +f 1141 1140 1190 +f 1191 1141 1190 +f 1142 1141 1191 +f 1192 1142 1191 +f 1143 1142 1192 +f 1193 1143 1192 +f 1144 1143 1193 +f 1194 1144 1193 +f 1145 1144 1194 +f 1195 1145 1194 +f 1146 1145 1195 +f 1196 1146 1195 +f 1147 1146 1196 +f 1197 1147 1196 +f 1148 1147 1197 +f 1198 1148 1197 +f 1149 1148 1198 +f 1199 1149 1198 +f 1150 1149 1199 +f 1200 1150 1199 +f 1152 1151 1201 +f 1202 1152 1201 +f 1153 1152 1202 +f 1203 1153 1202 +f 1154 1153 1203 +f 1204 1154 1203 +f 1155 1154 1204 +f 1205 1155 1204 +f 1156 1155 1205 +f 1206 1156 1205 +f 1157 1156 1206 +f 1207 1157 1206 +f 1158 1157 1207 +f 1208 1158 1207 +f 1159 1158 1208 +f 1209 1159 1208 +f 1160 1159 1209 +f 1210 1160 1209 +f 1161 1160 1210 +f 1211 1161 1210 +f 1162 1161 1211 +f 1212 1162 1211 +f 1163 1162 1212 +f 1213 1163 1212 +f 1164 1163 1213 +f 1214 1164 1213 +f 1165 1164 1214 +f 1215 1165 1214 +f 1166 1165 1215 +f 1216 1166 1215 +f 1167 1166 1216 +f 1217 1167 1216 +f 1168 1167 1217 +f 1218 1168 1217 +f 1169 1168 1218 +f 1219 1169 1218 +f 1170 1169 1219 +f 1220 1170 1219 +f 1171 1170 1220 +f 1221 1171 1220 +f 1172 1171 1221 +f 1222 1172 1221 +f 1173 1172 1222 +f 1223 1173 1222 +f 1174 1173 1223 +f 1224 1174 1223 +f 1175 1174 1224 +f 1225 1175 1224 +f 1176 1175 1225 +f 1226 1176 1225 +f 1177 1176 1226 +f 1227 1177 1226 +f 1178 1177 1227 +f 1228 1178 1227 +f 1179 1178 1228 +f 1229 1179 1228 +f 1180 1179 1229 +f 1230 1180 1229 +f 1181 1180 1230 +f 1231 1181 1230 +f 1182 1181 1231 +f 1232 1182 1231 +f 1183 1182 1232 +f 1233 1183 1232 +f 1184 1183 1233 +f 1234 1184 1233 +f 1185 1184 1234 +f 1235 1185 1234 +f 1186 1185 1235 +f 1236 1186 1235 +f 1187 1186 1236 +f 1237 1187 1236 +f 1188 1187 1237 +f 1238 1188 1237 +f 1189 1188 1238 +f 1239 1189 1238 +f 1190 1189 1239 +f 1240 1190 1239 +f 1191 1190 1240 +f 1241 1191 1240 +f 1192 1191 1241 +f 1242 1192 1241 +f 1193 1192 1242 +f 1243 1193 1242 +f 1194 1193 1243 +f 1244 1194 1243 +f 1195 1194 1244 +f 1245 1195 1244 +f 1196 1195 1245 +f 1246 1196 1245 +f 1197 1196 1246 +f 1247 1197 1246 +f 1198 1197 1247 +f 1248 1198 1247 +f 1199 1198 1248 +f 1249 1199 1248 +f 1200 1199 1249 +f 1250 1200 1249 +f 1202 1201 1251 +f 1252 1202 1251 +f 1203 1202 1252 +f 1253 1203 1252 +f 1204 1203 1253 +f 1254 1204 1253 +f 1205 1204 1254 +f 1255 1205 1254 +f 1206 1205 1255 +f 1256 1206 1255 +f 1207 1206 1256 +f 1257 1207 1256 +f 1208 1207 1257 +f 1258 1208 1257 +f 1209 1208 1258 +f 1259 1209 1258 +f 1210 1209 1259 +f 1260 1210 1259 +f 1211 1210 1260 +f 1261 1211 1260 +f 1212 1211 1261 +f 1262 1212 1261 +f 1213 1212 1262 +f 1263 1213 1262 +f 1214 1213 1263 +f 1264 1214 1263 +f 1215 1214 1264 +f 1265 1215 1264 +f 1216 1215 1265 +f 1266 1216 1265 +f 1217 1216 1266 +f 1267 1217 1266 +f 1218 1217 1267 +f 1268 1218 1267 +f 1219 1218 1268 +f 1269 1219 1268 +f 1220 1219 1269 +f 1270 1220 1269 +f 1221 1220 1270 +f 1271 1221 1270 +f 1222 1221 1271 +f 1272 1222 1271 +f 1223 1222 1272 +f 1273 1223 1272 +f 1224 1223 1273 +f 1274 1224 1273 +f 1225 1224 1274 +f 1275 1225 1274 +f 1226 1225 1275 +f 1276 1226 1275 +f 1227 1226 1276 +f 1277 1227 1276 +f 1228 1227 1277 +f 1278 1228 1277 +f 1229 1228 1278 +f 1279 1229 1278 +f 1230 1229 1279 +f 1280 1230 1279 +f 1231 1230 1280 +f 1281 1231 1280 +f 1232 1231 1281 +f 1282 1232 1281 +f 1233 1232 1282 +f 1283 1233 1282 +f 1234 1233 1283 +f 1284 1234 1283 +f 1235 1234 1284 +f 1285 1235 1284 +f 1236 1235 1285 +f 1286 1236 1285 +f 1237 1236 1286 +f 1287 1237 1286 +f 1238 1237 1287 +f 1288 1238 1287 +f 1239 1238 1288 +f 1289 1239 1288 +f 1240 1239 1289 +f 1290 1240 1289 +f 1241 1240 1290 +f 1291 1241 1290 +f 1242 1241 1291 +f 1292 1242 1291 +f 1243 1242 1292 +f 1293 1243 1292 +f 1244 1243 1293 +f 1294 1244 1293 +f 1245 1244 1294 +f 1295 1245 1294 +f 1246 1245 1295 +f 1296 1246 1295 +f 1247 1246 1296 +f 1297 1247 1296 +f 1248 1247 1297 +f 1298 1248 1297 +f 1249 1248 1298 +f 1299 1249 1298 +f 1250 1249 1299 +f 1300 1250 1299 +f 1252 1251 1301 +f 1302 1252 1301 +f 1253 1252 1302 +f 1303 1253 1302 +f 1254 1253 1303 +f 1304 1254 1303 +f 1255 1254 1304 +f 1305 1255 1304 +f 1256 1255 1305 +f 1306 1256 1305 +f 1257 1256 1306 +f 1307 1257 1306 +f 1258 1257 1307 +f 1308 1258 1307 +f 1259 1258 1308 +f 1309 1259 1308 +f 1260 1259 1309 +f 1310 1260 1309 +f 1261 1260 1310 +f 1311 1261 1310 +f 1262 1261 1311 +f 1312 1262 1311 +f 1263 1262 1312 +f 1313 1263 1312 +f 1264 1263 1313 +f 1314 1264 1313 +f 1265 1264 1314 +f 1315 1265 1314 +f 1266 1265 1315 +f 1316 1266 1315 +f 1267 1266 1316 +f 1317 1267 1316 +f 1268 1267 1317 +f 1318 1268 1317 +f 1269 1268 1318 +f 1319 1269 1318 +f 1270 1269 1319 +f 1320 1270 1319 +f 1271 1270 1320 +f 1321 1271 1320 +f 1272 1271 1321 +f 1322 1272 1321 +f 1273 1272 1322 +f 1323 1273 1322 +f 1274 1273 1323 +f 1324 1274 1323 +f 1275 1274 1324 +f 1325 1275 1324 +f 1276 1275 1325 +f 1326 1276 1325 +f 1277 1276 1326 +f 1327 1277 1326 +f 1278 1277 1327 +f 1328 1278 1327 +f 1279 1278 1328 +f 1329 1279 1328 +f 1280 1279 1329 +f 1330 1280 1329 +f 1281 1280 1330 +f 1331 1281 1330 +f 1282 1281 1331 +f 1332 1282 1331 +f 1283 1282 1332 +f 1333 1283 1332 +f 1284 1283 1333 +f 1334 1284 1333 +f 1285 1284 1334 +f 1335 1285 1334 +f 1286 1285 1335 +f 1336 1286 1335 +f 1287 1286 1336 +f 1337 1287 1336 +f 1288 1287 1337 +f 1338 1288 1337 +f 1289 1288 1338 +f 1339 1289 1338 +f 1290 1289 1339 +f 1340 1290 1339 +f 1291 1290 1340 +f 1341 1291 1340 +f 1292 1291 1341 +f 1342 1292 1341 +f 1293 1292 1342 +f 1343 1293 1342 +f 1294 1293 1343 +f 1344 1294 1343 +f 1295 1294 1344 +f 1345 1295 1344 +f 1296 1295 1345 +f 1346 1296 1345 +f 1297 1296 1346 +f 1347 1297 1346 +f 1298 1297 1347 +f 1348 1298 1347 +f 1299 1298 1348 +f 1349 1299 1348 +f 1300 1299 1349 +f 1350 1300 1349 +f 1302 1301 1351 +f 1352 1302 1351 +f 1303 1302 1352 +f 1353 1303 1352 +f 1304 1303 1353 +f 1354 1304 1353 +f 1305 1304 1354 +f 1355 1305 1354 +f 1306 1305 1355 +f 1356 1306 1355 +f 1307 1306 1356 +f 1357 1307 1356 +f 1308 1307 1357 +f 1358 1308 1357 +f 1309 1308 1358 +f 1359 1309 1358 +f 1310 1309 1359 +f 1360 1310 1359 +f 1311 1310 1360 +f 1361 1311 1360 +f 1312 1311 1361 +f 1362 1312 1361 +f 1313 1312 1362 +f 1363 1313 1362 +f 1314 1313 1363 +f 1364 1314 1363 +f 1315 1314 1364 +f 1365 1315 1364 +f 1316 1315 1365 +f 1366 1316 1365 +f 1317 1316 1366 +f 1367 1317 1366 +f 1318 1317 1367 +f 1368 1318 1367 +f 1319 1318 1368 +f 1369 1319 1368 +f 1320 1319 1369 +f 1370 1320 1369 +f 1321 1320 1370 +f 1371 1321 1370 +f 1322 1321 1371 +f 1372 1322 1371 +f 1323 1322 1372 +f 1373 1323 1372 +f 1324 1323 1373 +f 1374 1324 1373 +f 1325 1324 1374 +f 1375 1325 1374 +f 1326 1325 1375 +f 1376 1326 1375 +f 1327 1326 1376 +f 1377 1327 1376 +f 1328 1327 1377 +f 1378 1328 1377 +f 1329 1328 1378 +f 1379 1329 1378 +f 1330 1329 1379 +f 1380 1330 1379 +f 1331 1330 1380 +f 1381 1331 1380 +f 1332 1331 1381 +f 1382 1332 1381 +f 1333 1332 1382 +f 1383 1333 1382 +f 1334 1333 1383 +f 1384 1334 1383 +f 1335 1334 1384 +f 1385 1335 1384 +f 1336 1335 1385 +f 1386 1336 1385 +f 1337 1336 1386 +f 1387 1337 1386 +f 1338 1337 1387 +f 1388 1338 1387 +f 1339 1338 1388 +f 1389 1339 1388 +f 1340 1339 1389 +f 1390 1340 1389 +f 1341 1340 1390 +f 1391 1341 1390 +f 1342 1341 1391 +f 1392 1342 1391 +f 1343 1342 1392 +f 1393 1343 1392 +f 1344 1343 1393 +f 1394 1344 1393 +f 1345 1344 1394 +f 1395 1345 1394 +f 1346 1345 1395 +f 1396 1346 1395 +f 1347 1346 1396 +f 1397 1347 1396 +f 1348 1347 1397 +f 1398 1348 1397 +f 1349 1348 1398 +f 1399 1349 1398 +f 1350 1349 1399 +f 1400 1350 1399 +f 1352 1351 1401 +f 1402 1352 1401 +f 1353 1352 1402 +f 1403 1353 1402 +f 1354 1353 1403 +f 1404 1354 1403 +f 1355 1354 1404 +f 1405 1355 1404 +f 1356 1355 1405 +f 1406 1356 1405 +f 1357 1356 1406 +f 1407 1357 1406 +f 1358 1357 1407 +f 1408 1358 1407 +f 1359 1358 1408 +f 1409 1359 1408 +f 1360 1359 1409 +f 1410 1360 1409 +f 1361 1360 1410 +f 1411 1361 1410 +f 1362 1361 1411 +f 1412 1362 1411 +f 1363 1362 1412 +f 1413 1363 1412 +f 1364 1363 1413 +f 1414 1364 1413 +f 1365 1364 1414 +f 1415 1365 1414 +f 1366 1365 1415 +f 1416 1366 1415 +f 1367 1366 1416 +f 1417 1367 1416 +f 1368 1367 1417 +f 1418 1368 1417 +f 1369 1368 1418 +f 1419 1369 1418 +f 1370 1369 1419 +f 1420 1370 1419 +f 1371 1370 1420 +f 1421 1371 1420 +f 1372 1371 1421 +f 1422 1372 1421 +f 1373 1372 1422 +f 1423 1373 1422 +f 1374 1373 1423 +f 1424 1374 1423 +f 1375 1374 1424 +f 1425 1375 1424 +f 1376 1375 1425 +f 1426 1376 1425 +f 1377 1376 1426 +f 1427 1377 1426 +f 1378 1377 1427 +f 1428 1378 1427 +f 1379 1378 1428 +f 1429 1379 1428 +f 1380 1379 1429 +f 1430 1380 1429 +f 1381 1380 1430 +f 1431 1381 1430 +f 1382 1381 1431 +f 1432 1382 1431 +f 1383 1382 1432 +f 1433 1383 1432 +f 1384 1383 1433 +f 1434 1384 1433 +f 1385 1384 1434 +f 1435 1385 1434 +f 1386 1385 1435 +f 1436 1386 1435 +f 1387 1386 1436 +f 1437 1387 1436 +f 1388 1387 1437 +f 1438 1388 1437 +f 1389 1388 1438 +f 1439 1389 1438 +f 1390 1389 1439 +f 1440 1390 1439 +f 1391 1390 1440 +f 1441 1391 1440 +f 1392 1391 1441 +f 1442 1392 1441 +f 1393 1392 1442 +f 1443 1393 1442 +f 1394 1393 1443 +f 1444 1394 1443 +f 1395 1394 1444 +f 1445 1395 1444 +f 1396 1395 1445 +f 1446 1396 1445 +f 1397 1396 1446 +f 1447 1397 1446 +f 1398 1397 1447 +f 1448 1398 1447 +f 1399 1398 1448 +f 1449 1399 1448 +f 1400 1399 1449 +f 1450 1400 1449 +f 1402 1401 1451 +f 1452 1402 1451 +f 1403 1402 1452 +f 1453 1403 1452 +f 1404 1403 1453 +f 1454 1404 1453 +f 1405 1404 1454 +f 1455 1405 1454 +f 1406 1405 1455 +f 1456 1406 1455 +f 1407 1406 1456 +f 1457 1407 1456 +f 1408 1407 1457 +f 1458 1408 1457 +f 1409 1408 1458 +f 1459 1409 1458 +f 1410 1409 1459 +f 1460 1410 1459 +f 1411 1410 1460 +f 1461 1411 1460 +f 1412 1411 1461 +f 1462 1412 1461 +f 1413 1412 1462 +f 1463 1413 1462 +f 1414 1413 1463 +f 1464 1414 1463 +f 1415 1414 1464 +f 1465 1415 1464 +f 1416 1415 1465 +f 1466 1416 1465 +f 1417 1416 1466 +f 1467 1417 1466 +f 1418 1417 1467 +f 1468 1418 1467 +f 1419 1418 1468 +f 1469 1419 1468 +f 1420 1419 1469 +f 1470 1420 1469 +f 1421 1420 1470 +f 1471 1421 1470 +f 1422 1421 1471 +f 1472 1422 1471 +f 1423 1422 1472 +f 1473 1423 1472 +f 1424 1423 1473 +f 1474 1424 1473 +f 1425 1424 1474 +f 1475 1425 1474 +f 1426 1425 1475 +f 1476 1426 1475 +f 1427 1426 1476 +f 1477 1427 1476 +f 1428 1427 1477 +f 1478 1428 1477 +f 1429 1428 1478 +f 1479 1429 1478 +f 1430 1429 1479 +f 1480 1430 1479 +f 1431 1430 1480 +f 1481 1431 1480 +f 1432 1431 1481 +f 1482 1432 1481 +f 1433 1432 1482 +f 1483 1433 1482 +f 1434 1433 1483 +f 1484 1434 1483 +f 1435 1434 1484 +f 1485 1435 1484 +f 1436 1435 1485 +f 1486 1436 1485 +f 1437 1436 1486 +f 1487 1437 1486 +f 1438 1437 1487 +f 1488 1438 1487 +f 1439 1438 1488 +f 1489 1439 1488 +f 1440 1439 1489 +f 1490 1440 1489 +f 1441 1440 1490 +f 1491 1441 1490 +f 1442 1441 1491 +f 1492 1442 1491 +f 1443 1442 1492 +f 1493 1443 1492 +f 1444 1443 1493 +f 1494 1444 1493 +f 1445 1444 1494 +f 1495 1445 1494 +f 1446 1445 1495 +f 1496 1446 1495 +f 1447 1446 1496 +f 1497 1447 1496 +f 1448 1447 1497 +f 1498 1448 1497 +f 1449 1448 1498 +f 1499 1449 1498 +f 1450 1449 1499 +f 1500 1450 1499 +f 1452 1451 1501 +f 1502 1452 1501 +f 1453 1452 1502 +f 1503 1453 1502 +f 1454 1453 1503 +f 1504 1454 1503 +f 1455 1454 1504 +f 1505 1455 1504 +f 1456 1455 1505 +f 1506 1456 1505 +f 1457 1456 1506 +f 1507 1457 1506 +f 1458 1457 1507 +f 1508 1458 1507 +f 1459 1458 1508 +f 1509 1459 1508 +f 1460 1459 1509 +f 1510 1460 1509 +f 1461 1460 1510 +f 1511 1461 1510 +f 1462 1461 1511 +f 1512 1462 1511 +f 1463 1462 1512 +f 1513 1463 1512 +f 1464 1463 1513 +f 1514 1464 1513 +f 1465 1464 1514 +f 1515 1465 1514 +f 1466 1465 1515 +f 1516 1466 1515 +f 1467 1466 1516 +f 1517 1467 1516 +f 1468 1467 1517 +f 1518 1468 1517 +f 1469 1468 1518 +f 1519 1469 1518 +f 1470 1469 1519 +f 1520 1470 1519 +f 1471 1470 1520 +f 1521 1471 1520 +f 1472 1471 1521 +f 1522 1472 1521 +f 1473 1472 1522 +f 1523 1473 1522 +f 1474 1473 1523 +f 1524 1474 1523 +f 1475 1474 1524 +f 1525 1475 1524 +f 1476 1475 1525 +f 1526 1476 1525 +f 1477 1476 1526 +f 1527 1477 1526 +f 1478 1477 1527 +f 1528 1478 1527 +f 1479 1478 1528 +f 1529 1479 1528 +f 1480 1479 1529 +f 1530 1480 1529 +f 1481 1480 1530 +f 1531 1481 1530 +f 1482 1481 1531 +f 1532 1482 1531 +f 1483 1482 1532 +f 1533 1483 1532 +f 1484 1483 1533 +f 1534 1484 1533 +f 1485 1484 1534 +f 1535 1485 1534 +f 1486 1485 1535 +f 1536 1486 1535 +f 1487 1486 1536 +f 1537 1487 1536 +f 1488 1487 1537 +f 1538 1488 1537 +f 1489 1488 1538 +f 1539 1489 1538 +f 1490 1489 1539 +f 1540 1490 1539 +f 1491 1490 1540 +f 1541 1491 1540 +f 1492 1491 1541 +f 1542 1492 1541 +f 1493 1492 1542 +f 1543 1493 1542 +f 1494 1493 1543 +f 1544 1494 1543 +f 1495 1494 1544 +f 1545 1495 1544 +f 1496 1495 1545 +f 1546 1496 1545 +f 1497 1496 1546 +f 1547 1497 1546 +f 1498 1497 1547 +f 1548 1498 1547 +f 1499 1498 1548 +f 1549 1499 1548 +f 1500 1499 1549 +f 1550 1500 1549 +f 1502 1501 1551 +f 1552 1502 1551 +f 1503 1502 1552 +f 1553 1503 1552 +f 1504 1503 1553 +f 1554 1504 1553 +f 1505 1504 1554 +f 1555 1505 1554 +f 1506 1505 1555 +f 1556 1506 1555 +f 1507 1506 1556 +f 1557 1507 1556 +f 1508 1507 1557 +f 1558 1508 1557 +f 1509 1508 1558 +f 1559 1509 1558 +f 1510 1509 1559 +f 1560 1510 1559 +f 1511 1510 1560 +f 1561 1511 1560 +f 1512 1511 1561 +f 1562 1512 1561 +f 1513 1512 1562 +f 1563 1513 1562 +f 1514 1513 1563 +f 1564 1514 1563 +f 1515 1514 1564 +f 1565 1515 1564 +f 1516 1515 1565 +f 1566 1516 1565 +f 1517 1516 1566 +f 1567 1517 1566 +f 1518 1517 1567 +f 1568 1518 1567 +f 1519 1518 1568 +f 1569 1519 1568 +f 1520 1519 1569 +f 1570 1520 1569 +f 1521 1520 1570 +f 1571 1521 1570 +f 1522 1521 1571 +f 1572 1522 1571 +f 1523 1522 1572 +f 1573 1523 1572 +f 1524 1523 1573 +f 1574 1524 1573 +f 1525 1524 1574 +f 1575 1525 1574 +f 1526 1525 1575 +f 1576 1526 1575 +f 1527 1526 1576 +f 1577 1527 1576 +f 1528 1527 1577 +f 1578 1528 1577 +f 1529 1528 1578 +f 1579 1529 1578 +f 1530 1529 1579 +f 1580 1530 1579 +f 1531 1530 1580 +f 1581 1531 1580 +f 1532 1531 1581 +f 1582 1532 1581 +f 1533 1532 1582 +f 1583 1533 1582 +f 1534 1533 1583 +f 1584 1534 1583 +f 1535 1534 1584 +f 1585 1535 1584 +f 1536 1535 1585 +f 1586 1536 1585 +f 1537 1536 1586 +f 1587 1537 1586 +f 1538 1537 1587 +f 1588 1538 1587 +f 1539 1538 1588 +f 1589 1539 1588 +f 1540 1539 1589 +f 1590 1540 1589 +f 1541 1540 1590 +f 1591 1541 1590 +f 1542 1541 1591 +f 1592 1542 1591 +f 1543 1542 1592 +f 1593 1543 1592 +f 1544 1543 1593 +f 1594 1544 1593 +f 1545 1544 1594 +f 1595 1545 1594 +f 1546 1545 1595 +f 1596 1546 1595 +f 1547 1546 1596 +f 1597 1547 1596 +f 1548 1547 1597 +f 1598 1548 1597 +f 1549 1548 1598 +f 1599 1549 1598 +f 1550 1549 1599 +f 1600 1550 1599 +f 1552 1551 1601 +f 1602 1552 1601 +f 1553 1552 1602 +f 1603 1553 1602 +f 1554 1553 1603 +f 1604 1554 1603 +f 1555 1554 1604 +f 1605 1555 1604 +f 1556 1555 1605 +f 1606 1556 1605 +f 1557 1556 1606 +f 1607 1557 1606 +f 1558 1557 1607 +f 1608 1558 1607 +f 1559 1558 1608 +f 1609 1559 1608 +f 1560 1559 1609 +f 1610 1560 1609 +f 1561 1560 1610 +f 1611 1561 1610 +f 1562 1561 1611 +f 1612 1562 1611 +f 1563 1562 1612 +f 1613 1563 1612 +f 1564 1563 1613 +f 1614 1564 1613 +f 1565 1564 1614 +f 1615 1565 1614 +f 1566 1565 1615 +f 1616 1566 1615 +f 1567 1566 1616 +f 1617 1567 1616 +f 1568 1567 1617 +f 1618 1568 1617 +f 1569 1568 1618 +f 1619 1569 1618 +f 1570 1569 1619 +f 1620 1570 1619 +f 1571 1570 1620 +f 1621 1571 1620 +f 1572 1571 1621 +f 1622 1572 1621 +f 1573 1572 1622 +f 1623 1573 1622 +f 1574 1573 1623 +f 1624 1574 1623 +f 1575 1574 1624 +f 1625 1575 1624 +f 1576 1575 1625 +f 1626 1576 1625 +f 1577 1576 1626 +f 1627 1577 1626 +f 1578 1577 1627 +f 1628 1578 1627 +f 1579 1578 1628 +f 1629 1579 1628 +f 1580 1579 1629 +f 1630 1580 1629 +f 1581 1580 1630 +f 1631 1581 1630 +f 1582 1581 1631 +f 1632 1582 1631 +f 1583 1582 1632 +f 1633 1583 1632 +f 1584 1583 1633 +f 1634 1584 1633 +f 1585 1584 1634 +f 1635 1585 1634 +f 1586 1585 1635 +f 1636 1586 1635 +f 1587 1586 1636 +f 1637 1587 1636 +f 1588 1587 1637 +f 1638 1588 1637 +f 1589 1588 1638 +f 1639 1589 1638 +f 1590 1589 1639 +f 1640 1590 1639 +f 1591 1590 1640 +f 1641 1591 1640 +f 1592 1591 1641 +f 1642 1592 1641 +f 1593 1592 1642 +f 1643 1593 1642 +f 1594 1593 1643 +f 1644 1594 1643 +f 1595 1594 1644 +f 1645 1595 1644 +f 1596 1595 1645 +f 1646 1596 1645 +f 1597 1596 1646 +f 1647 1597 1646 +f 1598 1597 1647 +f 1648 1598 1647 +f 1599 1598 1648 +f 1649 1599 1648 +f 1600 1599 1649 +f 1650 1600 1649 +f 1602 1601 1651 +f 1652 1602 1651 +f 1603 1602 1652 +f 1653 1603 1652 +f 1604 1603 1653 +f 1654 1604 1653 +f 1605 1604 1654 +f 1655 1605 1654 +f 1606 1605 1655 +f 1656 1606 1655 +f 1607 1606 1656 +f 1657 1607 1656 +f 1608 1607 1657 +f 1658 1608 1657 +f 1609 1608 1658 +f 1659 1609 1658 +f 1610 1609 1659 +f 1660 1610 1659 +f 1611 1610 1660 +f 1661 1611 1660 +f 1612 1611 1661 +f 1662 1612 1661 +f 1613 1612 1662 +f 1663 1613 1662 +f 1614 1613 1663 +f 1664 1614 1663 +f 1615 1614 1664 +f 1665 1615 1664 +f 1616 1615 1665 +f 1666 1616 1665 +f 1617 1616 1666 +f 1667 1617 1666 +f 1618 1617 1667 +f 1668 1618 1667 +f 1619 1618 1668 +f 1669 1619 1668 +f 1620 1619 1669 +f 1670 1620 1669 +f 1621 1620 1670 +f 1671 1621 1670 +f 1622 1621 1671 +f 1672 1622 1671 +f 1623 1622 1672 +f 1673 1623 1672 +f 1624 1623 1673 +f 1674 1624 1673 +f 1625 1624 1674 +f 1675 1625 1674 +f 1626 1625 1675 +f 1676 1626 1675 +f 1627 1626 1676 +f 1677 1627 1676 +f 1628 1627 1677 +f 1678 1628 1677 +f 1629 1628 1678 +f 1679 1629 1678 +f 1630 1629 1679 +f 1680 1630 1679 +f 1631 1630 1680 +f 1681 1631 1680 +f 1632 1631 1681 +f 1682 1632 1681 +f 1633 1632 1682 +f 1683 1633 1682 +f 1634 1633 1683 +f 1684 1634 1683 +f 1635 1634 1684 +f 1685 1635 1684 +f 1636 1635 1685 +f 1686 1636 1685 +f 1637 1636 1686 +f 1687 1637 1686 +f 1638 1637 1687 +f 1688 1638 1687 +f 1639 1638 1688 +f 1689 1639 1688 +f 1640 1639 1689 +f 1690 1640 1689 +f 1641 1640 1690 +f 1691 1641 1690 +f 1642 1641 1691 +f 1692 1642 1691 +f 1643 1642 1692 +f 1693 1643 1692 +f 1644 1643 1693 +f 1694 1644 1693 +f 1645 1644 1694 +f 1695 1645 1694 +f 1646 1645 1695 +f 1696 1646 1695 +f 1647 1646 1696 +f 1697 1647 1696 +f 1648 1647 1697 +f 1698 1648 1697 +f 1649 1648 1698 +f 1699 1649 1698 +f 1650 1649 1699 +f 1700 1650 1699 +f 1652 1651 1701 +f 1702 1652 1701 +f 1653 1652 1702 +f 1703 1653 1702 +f 1654 1653 1703 +f 1704 1654 1703 +f 1655 1654 1704 +f 1705 1655 1704 +f 1656 1655 1705 +f 1706 1656 1705 +f 1657 1656 1706 +f 1707 1657 1706 +f 1658 1657 1707 +f 1708 1658 1707 +f 1659 1658 1708 +f 1709 1659 1708 +f 1660 1659 1709 +f 1710 1660 1709 +f 1661 1660 1710 +f 1711 1661 1710 +f 1662 1661 1711 +f 1712 1662 1711 +f 1663 1662 1712 +f 1713 1663 1712 +f 1664 1663 1713 +f 1714 1664 1713 +f 1665 1664 1714 +f 1715 1665 1714 +f 1666 1665 1715 +f 1716 1666 1715 +f 1667 1666 1716 +f 1717 1667 1716 +f 1668 1667 1717 +f 1718 1668 1717 +f 1669 1668 1718 +f 1719 1669 1718 +f 1670 1669 1719 +f 1720 1670 1719 +f 1671 1670 1720 +f 1721 1671 1720 +f 1672 1671 1721 +f 1722 1672 1721 +f 1673 1672 1722 +f 1723 1673 1722 +f 1674 1673 1723 +f 1724 1674 1723 +f 1675 1674 1724 +f 1725 1675 1724 +f 1676 1675 1725 +f 1726 1676 1725 +f 1677 1676 1726 +f 1727 1677 1726 +f 1678 1677 1727 +f 1728 1678 1727 +f 1679 1678 1728 +f 1729 1679 1728 +f 1680 1679 1729 +f 1730 1680 1729 +f 1681 1680 1730 +f 1731 1681 1730 +f 1682 1681 1731 +f 1732 1682 1731 +f 1683 1682 1732 +f 1733 1683 1732 +f 1684 1683 1733 +f 1734 1684 1733 +f 1685 1684 1734 +f 1735 1685 1734 +f 1686 1685 1735 +f 1736 1686 1735 +f 1687 1686 1736 +f 1737 1687 1736 +f 1688 1687 1737 +f 1738 1688 1737 +f 1689 1688 1738 +f 1739 1689 1738 +f 1690 1689 1739 +f 1740 1690 1739 +f 1691 1690 1740 +f 1741 1691 1740 +f 1692 1691 1741 +f 1742 1692 1741 +f 1693 1692 1742 +f 1743 1693 1742 +f 1694 1693 1743 +f 1744 1694 1743 +f 1695 1694 1744 +f 1745 1695 1744 +f 1696 1695 1745 +f 1746 1696 1745 +f 1697 1696 1746 +f 1747 1697 1746 +f 1698 1697 1747 +f 1748 1698 1747 +f 1699 1698 1748 +f 1749 1699 1748 +f 1700 1699 1749 +f 1750 1700 1749 +f 1702 1701 1751 +f 1752 1702 1751 +f 1703 1702 1752 +f 1753 1703 1752 +f 1704 1703 1753 +f 1754 1704 1753 +f 1705 1704 1754 +f 1755 1705 1754 +f 1706 1705 1755 +f 1756 1706 1755 +f 1707 1706 1756 +f 1757 1707 1756 +f 1708 1707 1757 +f 1758 1708 1757 +f 1709 1708 1758 +f 1759 1709 1758 +f 1710 1709 1759 +f 1760 1710 1759 +f 1711 1710 1760 +f 1761 1711 1760 +f 1712 1711 1761 +f 1762 1712 1761 +f 1713 1712 1762 +f 1763 1713 1762 +f 1714 1713 1763 +f 1764 1714 1763 +f 1715 1714 1764 +f 1765 1715 1764 +f 1716 1715 1765 +f 1766 1716 1765 +f 1717 1716 1766 +f 1767 1717 1766 +f 1718 1717 1767 +f 1768 1718 1767 +f 1719 1718 1768 +f 1769 1719 1768 +f 1720 1719 1769 +f 1770 1720 1769 +f 1721 1720 1770 +f 1771 1721 1770 +f 1722 1721 1771 +f 1772 1722 1771 +f 1723 1722 1772 +f 1773 1723 1772 +f 1724 1723 1773 +f 1774 1724 1773 +f 1725 1724 1774 +f 1775 1725 1774 +f 1726 1725 1775 +f 1776 1726 1775 +f 1727 1726 1776 +f 1777 1727 1776 +f 1728 1727 1777 +f 1778 1728 1777 +f 1729 1728 1778 +f 1779 1729 1778 +f 1730 1729 1779 +f 1780 1730 1779 +f 1731 1730 1780 +f 1781 1731 1780 +f 1732 1731 1781 +f 1782 1732 1781 +f 1733 1732 1782 +f 1783 1733 1782 +f 1734 1733 1783 +f 1784 1734 1783 +f 1735 1734 1784 +f 1785 1735 1784 +f 1736 1735 1785 +f 1786 1736 1785 +f 1737 1736 1786 +f 1787 1737 1786 +f 1738 1737 1787 +f 1788 1738 1787 +f 1739 1738 1788 +f 1789 1739 1788 +f 1740 1739 1789 +f 1790 1740 1789 +f 1741 1740 1790 +f 1791 1741 1790 +f 1742 1741 1791 +f 1792 1742 1791 +f 1743 1742 1792 +f 1793 1743 1792 +f 1744 1743 1793 +f 1794 1744 1793 +f 1745 1744 1794 +f 1795 1745 1794 +f 1746 1745 1795 +f 1796 1746 1795 +f 1747 1746 1796 +f 1797 1747 1796 +f 1748 1747 1797 +f 1798 1748 1797 +f 1749 1748 1798 +f 1799 1749 1798 +f 1750 1749 1799 +f 1800 1750 1799 +f 1752 1751 1801 +f 1802 1752 1801 +f 1753 1752 1802 +f 1803 1753 1802 +f 1754 1753 1803 +f 1804 1754 1803 +f 1755 1754 1804 +f 1805 1755 1804 +f 1756 1755 1805 +f 1806 1756 1805 +f 1757 1756 1806 +f 1807 1757 1806 +f 1758 1757 1807 +f 1808 1758 1807 +f 1759 1758 1808 +f 1809 1759 1808 +f 1760 1759 1809 +f 1810 1760 1809 +f 1761 1760 1810 +f 1811 1761 1810 +f 1762 1761 1811 +f 1812 1762 1811 +f 1763 1762 1812 +f 1813 1763 1812 +f 1764 1763 1813 +f 1814 1764 1813 +f 1765 1764 1814 +f 1815 1765 1814 +f 1766 1765 1815 +f 1816 1766 1815 +f 1767 1766 1816 +f 1817 1767 1816 +f 1768 1767 1817 +f 1818 1768 1817 +f 1769 1768 1818 +f 1819 1769 1818 +f 1770 1769 1819 +f 1820 1770 1819 +f 1771 1770 1820 +f 1821 1771 1820 +f 1772 1771 1821 +f 1822 1772 1821 +f 1773 1772 1822 +f 1823 1773 1822 +f 1774 1773 1823 +f 1824 1774 1823 +f 1775 1774 1824 +f 1825 1775 1824 +f 1776 1775 1825 +f 1826 1776 1825 +f 1777 1776 1826 +f 1827 1777 1826 +f 1778 1777 1827 +f 1828 1778 1827 +f 1779 1778 1828 +f 1829 1779 1828 +f 1780 1779 1829 +f 1830 1780 1829 +f 1781 1780 1830 +f 1831 1781 1830 +f 1782 1781 1831 +f 1832 1782 1831 +f 1783 1782 1832 +f 1833 1783 1832 +f 1784 1783 1833 +f 1834 1784 1833 +f 1785 1784 1834 +f 1835 1785 1834 +f 1786 1785 1835 +f 1836 1786 1835 +f 1787 1786 1836 +f 1837 1787 1836 +f 1788 1787 1837 +f 1838 1788 1837 +f 1789 1788 1838 +f 1839 1789 1838 +f 1790 1789 1839 +f 1840 1790 1839 +f 1791 1790 1840 +f 1841 1791 1840 +f 1792 1791 1841 +f 1842 1792 1841 +f 1793 1792 1842 +f 1843 1793 1842 +f 1794 1793 1843 +f 1844 1794 1843 +f 1795 1794 1844 +f 1845 1795 1844 +f 1796 1795 1845 +f 1846 1796 1845 +f 1797 1796 1846 +f 1847 1797 1846 +f 1798 1797 1847 +f 1848 1798 1847 +f 1799 1798 1848 +f 1849 1799 1848 +f 1800 1799 1849 +f 1850 1800 1849 +f 1802 1801 1851 +f 1852 1802 1851 +f 1803 1802 1852 +f 1853 1803 1852 +f 1804 1803 1853 +f 1854 1804 1853 +f 1805 1804 1854 +f 1855 1805 1854 +f 1806 1805 1855 +f 1856 1806 1855 +f 1807 1806 1856 +f 1857 1807 1856 +f 1808 1807 1857 +f 1858 1808 1857 +f 1809 1808 1858 +f 1859 1809 1858 +f 1810 1809 1859 +f 1860 1810 1859 +f 1811 1810 1860 +f 1861 1811 1860 +f 1812 1811 1861 +f 1862 1812 1861 +f 1813 1812 1862 +f 1863 1813 1862 +f 1814 1813 1863 +f 1864 1814 1863 +f 1815 1814 1864 +f 1865 1815 1864 +f 1816 1815 1865 +f 1866 1816 1865 +f 1817 1816 1866 +f 1867 1817 1866 +f 1818 1817 1867 +f 1868 1818 1867 +f 1819 1818 1868 +f 1869 1819 1868 +f 1820 1819 1869 +f 1870 1820 1869 +f 1821 1820 1870 +f 1871 1821 1870 +f 1822 1821 1871 +f 1872 1822 1871 +f 1823 1822 1872 +f 1873 1823 1872 +f 1824 1823 1873 +f 1874 1824 1873 +f 1825 1824 1874 +f 1875 1825 1874 +f 1826 1825 1875 +f 1876 1826 1875 +f 1827 1826 1876 +f 1877 1827 1876 +f 1828 1827 1877 +f 1878 1828 1877 +f 1829 1828 1878 +f 1879 1829 1878 +f 1830 1829 1879 +f 1880 1830 1879 +f 1831 1830 1880 +f 1881 1831 1880 +f 1832 1831 1881 +f 1882 1832 1881 +f 1833 1832 1882 +f 1883 1833 1882 +f 1834 1833 1883 +f 1884 1834 1883 +f 1835 1834 1884 +f 1885 1835 1884 +f 1836 1835 1885 +f 1886 1836 1885 +f 1837 1836 1886 +f 1887 1837 1886 +f 1838 1837 1887 +f 1888 1838 1887 +f 1839 1838 1888 +f 1889 1839 1888 +f 1840 1839 1889 +f 1890 1840 1889 +f 1841 1840 1890 +f 1891 1841 1890 +f 1842 1841 1891 +f 1892 1842 1891 +f 1843 1842 1892 +f 1893 1843 1892 +f 1844 1843 1893 +f 1894 1844 1893 +f 1845 1844 1894 +f 1895 1845 1894 +f 1846 1845 1895 +f 1896 1846 1895 +f 1847 1846 1896 +f 1897 1847 1896 +f 1848 1847 1897 +f 1898 1848 1897 +f 1849 1848 1898 +f 1899 1849 1898 +f 1850 1849 1899 +f 1900 1850 1899 +f 1852 1851 1901 +f 1902 1852 1901 +f 1853 1852 1902 +f 1903 1853 1902 +f 1854 1853 1903 +f 1904 1854 1903 +f 1855 1854 1904 +f 1905 1855 1904 +f 1856 1855 1905 +f 1906 1856 1905 +f 1857 1856 1906 +f 1907 1857 1906 +f 1858 1857 1907 +f 1908 1858 1907 +f 1859 1858 1908 +f 1909 1859 1908 +f 1860 1859 1909 +f 1910 1860 1909 +f 1861 1860 1910 +f 1911 1861 1910 +f 1862 1861 1911 +f 1912 1862 1911 +f 1863 1862 1912 +f 1913 1863 1912 +f 1864 1863 1913 +f 1914 1864 1913 +f 1865 1864 1914 +f 1915 1865 1914 +f 1866 1865 1915 +f 1916 1866 1915 +f 1867 1866 1916 +f 1917 1867 1916 +f 1868 1867 1917 +f 1918 1868 1917 +f 1869 1868 1918 +f 1919 1869 1918 +f 1870 1869 1919 +f 1920 1870 1919 +f 1871 1870 1920 +f 1921 1871 1920 +f 1872 1871 1921 +f 1922 1872 1921 +f 1873 1872 1922 +f 1923 1873 1922 +f 1874 1873 1923 +f 1924 1874 1923 +f 1875 1874 1924 +f 1925 1875 1924 +f 1876 1875 1925 +f 1926 1876 1925 +f 1877 1876 1926 +f 1927 1877 1926 +f 1878 1877 1927 +f 1928 1878 1927 +f 1879 1878 1928 +f 1929 1879 1928 +f 1880 1879 1929 +f 1930 1880 1929 +f 1881 1880 1930 +f 1931 1881 1930 +f 1882 1881 1931 +f 1932 1882 1931 +f 1883 1882 1932 +f 1933 1883 1932 +f 1884 1883 1933 +f 1934 1884 1933 +f 1885 1884 1934 +f 1935 1885 1934 +f 1886 1885 1935 +f 1936 1886 1935 +f 1887 1886 1936 +f 1937 1887 1936 +f 1888 1887 1937 +f 1938 1888 1937 +f 1889 1888 1938 +f 1939 1889 1938 +f 1890 1889 1939 +f 1940 1890 1939 +f 1891 1890 1940 +f 1941 1891 1940 +f 1892 1891 1941 +f 1942 1892 1941 +f 1893 1892 1942 +f 1943 1893 1942 +f 1894 1893 1943 +f 1944 1894 1943 +f 1895 1894 1944 +f 1945 1895 1944 +f 1896 1895 1945 +f 1946 1896 1945 +f 1897 1896 1946 +f 1947 1897 1946 +f 1898 1897 1947 +f 1948 1898 1947 +f 1899 1898 1948 +f 1949 1899 1948 +f 1900 1899 1949 +f 1950 1900 1949 +f 1902 1901 1951 +f 1952 1902 1951 +f 1903 1902 1952 +f 1953 1903 1952 +f 1904 1903 1953 +f 1954 1904 1953 +f 1905 1904 1954 +f 1955 1905 1954 +f 1906 1905 1955 +f 1956 1906 1955 +f 1907 1906 1956 +f 1957 1907 1956 +f 1908 1907 1957 +f 1958 1908 1957 +f 1909 1908 1958 +f 1959 1909 1958 +f 1910 1909 1959 +f 1960 1910 1959 +f 1911 1910 1960 +f 1961 1911 1960 +f 1912 1911 1961 +f 1962 1912 1961 +f 1913 1912 1962 +f 1963 1913 1962 +f 1914 1913 1963 +f 1964 1914 1963 +f 1915 1914 1964 +f 1965 1915 1964 +f 1916 1915 1965 +f 1966 1916 1965 +f 1917 1916 1966 +f 1967 1917 1966 +f 1918 1917 1967 +f 1968 1918 1967 +f 1919 1918 1968 +f 1969 1919 1968 +f 1920 1919 1969 +f 1970 1920 1969 +f 1921 1920 1970 +f 1971 1921 1970 +f 1922 1921 1971 +f 1972 1922 1971 +f 1923 1922 1972 +f 1973 1923 1972 +f 1924 1923 1973 +f 1974 1924 1973 +f 1925 1924 1974 +f 1975 1925 1974 +f 1926 1925 1975 +f 1976 1926 1975 +f 1927 1926 1976 +f 1977 1927 1976 +f 1928 1927 1977 +f 1978 1928 1977 +f 1929 1928 1978 +f 1979 1929 1978 +f 1930 1929 1979 +f 1980 1930 1979 +f 1931 1930 1980 +f 1981 1931 1980 +f 1932 1931 1981 +f 1982 1932 1981 +f 1933 1932 1982 +f 1983 1933 1982 +f 1934 1933 1983 +f 1984 1934 1983 +f 1935 1934 1984 +f 1985 1935 1984 +f 1936 1935 1985 +f 1986 1936 1985 +f 1937 1936 1986 +f 1987 1937 1986 +f 1938 1937 1987 +f 1988 1938 1987 +f 1939 1938 1988 +f 1989 1939 1988 +f 1940 1939 1989 +f 1990 1940 1989 +f 1941 1940 1990 +f 1991 1941 1990 +f 1942 1941 1991 +f 1992 1942 1991 +f 1943 1942 1992 +f 1993 1943 1992 +f 1944 1943 1993 +f 1994 1944 1993 +f 1945 1944 1994 +f 1995 1945 1994 +f 1946 1945 1995 +f 1996 1946 1995 +f 1947 1946 1996 +f 1997 1947 1996 +f 1948 1947 1997 +f 1998 1948 1997 +f 1949 1948 1998 +f 1999 1949 1998 +f 1950 1949 1999 +f 2000 1950 1999 +f 1952 1951 2001 +f 2002 1952 2001 +f 1953 1952 2002 +f 2003 1953 2002 +f 1954 1953 2003 +f 2004 1954 2003 +f 1955 1954 2004 +f 2005 1955 2004 +f 1956 1955 2005 +f 2006 1956 2005 +f 1957 1956 2006 +f 2007 1957 2006 +f 1958 1957 2007 +f 2008 1958 2007 +f 1959 1958 2008 +f 2009 1959 2008 +f 1960 1959 2009 +f 2010 1960 2009 +f 1961 1960 2010 +f 2011 1961 2010 +f 1962 1961 2011 +f 2012 1962 2011 +f 1963 1962 2012 +f 2013 1963 2012 +f 1964 1963 2013 +f 2014 1964 2013 +f 1965 1964 2014 +f 2015 1965 2014 +f 1966 1965 2015 +f 2016 1966 2015 +f 1967 1966 2016 +f 2017 1967 2016 +f 1968 1967 2017 +f 2018 1968 2017 +f 1969 1968 2018 +f 2019 1969 2018 +f 1970 1969 2019 +f 2020 1970 2019 +f 1971 1970 2020 +f 2021 1971 2020 +f 1972 1971 2021 +f 2022 1972 2021 +f 1973 1972 2022 +f 2023 1973 2022 +f 1974 1973 2023 +f 2024 1974 2023 +f 1975 1974 2024 +f 2025 1975 2024 +f 1976 1975 2025 +f 2026 1976 2025 +f 1977 1976 2026 +f 2027 1977 2026 +f 1978 1977 2027 +f 2028 1978 2027 +f 1979 1978 2028 +f 2029 1979 2028 +f 1980 1979 2029 +f 2030 1980 2029 +f 1981 1980 2030 +f 2031 1981 2030 +f 1982 1981 2031 +f 2032 1982 2031 +f 1983 1982 2032 +f 2033 1983 2032 +f 1984 1983 2033 +f 2034 1984 2033 +f 1985 1984 2034 +f 2035 1985 2034 +f 1986 1985 2035 +f 2036 1986 2035 +f 1987 1986 2036 +f 2037 1987 2036 +f 1988 1987 2037 +f 2038 1988 2037 +f 1989 1988 2038 +f 2039 1989 2038 +f 1990 1989 2039 +f 2040 1990 2039 +f 1991 1990 2040 +f 2041 1991 2040 +f 1992 1991 2041 +f 2042 1992 2041 +f 1993 1992 2042 +f 2043 1993 2042 +f 1994 1993 2043 +f 2044 1994 2043 +f 1995 1994 2044 +f 2045 1995 2044 +f 1996 1995 2045 +f 2046 1996 2045 +f 1997 1996 2046 +f 2047 1997 2046 +f 1998 1997 2047 +f 2048 1998 2047 +f 1999 1998 2048 +f 2049 1999 2048 +f 2000 1999 2049 +f 2050 2000 2049 +f 2002 2001 2051 +f 2052 2002 2051 +f 2003 2002 2052 +f 2053 2003 2052 +f 2004 2003 2053 +f 2054 2004 2053 +f 2005 2004 2054 +f 2055 2005 2054 +f 2006 2005 2055 +f 2056 2006 2055 +f 2007 2006 2056 +f 2057 2007 2056 +f 2008 2007 2057 +f 2058 2008 2057 +f 2009 2008 2058 +f 2059 2009 2058 +f 2010 2009 2059 +f 2060 2010 2059 +f 2011 2010 2060 +f 2061 2011 2060 +f 2012 2011 2061 +f 2062 2012 2061 +f 2013 2012 2062 +f 2063 2013 2062 +f 2014 2013 2063 +f 2064 2014 2063 +f 2015 2014 2064 +f 2065 2015 2064 +f 2016 2015 2065 +f 2066 2016 2065 +f 2017 2016 2066 +f 2067 2017 2066 +f 2018 2017 2067 +f 2068 2018 2067 +f 2019 2018 2068 +f 2069 2019 2068 +f 2020 2019 2069 +f 2070 2020 2069 +f 2021 2020 2070 +f 2071 2021 2070 +f 2022 2021 2071 +f 2072 2022 2071 +f 2023 2022 2072 +f 2073 2023 2072 +f 2024 2023 2073 +f 2074 2024 2073 +f 2025 2024 2074 +f 2075 2025 2074 +f 2026 2025 2075 +f 2076 2026 2075 +f 2027 2026 2076 +f 2077 2027 2076 +f 2028 2027 2077 +f 2078 2028 2077 +f 2029 2028 2078 +f 2079 2029 2078 +f 2030 2029 2079 +f 2080 2030 2079 +f 2031 2030 2080 +f 2081 2031 2080 +f 2032 2031 2081 +f 2082 2032 2081 +f 2033 2032 2082 +f 2083 2033 2082 +f 2034 2033 2083 +f 2084 2034 2083 +f 2035 2034 2084 +f 2085 2035 2084 +f 2036 2035 2085 +f 2086 2036 2085 +f 2037 2036 2086 +f 2087 2037 2086 +f 2038 2037 2087 +f 2088 2038 2087 +f 2039 2038 2088 +f 2089 2039 2088 +f 2040 2039 2089 +f 2090 2040 2089 +f 2041 2040 2090 +f 2091 2041 2090 +f 2042 2041 2091 +f 2092 2042 2091 +f 2043 2042 2092 +f 2093 2043 2092 +f 2044 2043 2093 +f 2094 2044 2093 +f 2045 2044 2094 +f 2095 2045 2094 +f 2046 2045 2095 +f 2096 2046 2095 +f 2047 2046 2096 +f 2097 2047 2096 +f 2048 2047 2097 +f 2098 2048 2097 +f 2049 2048 2098 +f 2099 2049 2098 +f 2050 2049 2099 +f 2100 2050 2099 +f 2052 2051 2101 +f 2102 2052 2101 +f 2053 2052 2102 +f 2103 2053 2102 +f 2054 2053 2103 +f 2104 2054 2103 +f 2055 2054 2104 +f 2105 2055 2104 +f 2056 2055 2105 +f 2106 2056 2105 +f 2057 2056 2106 +f 2107 2057 2106 +f 2058 2057 2107 +f 2108 2058 2107 +f 2059 2058 2108 +f 2109 2059 2108 +f 2060 2059 2109 +f 2110 2060 2109 +f 2061 2060 2110 +f 2111 2061 2110 +f 2062 2061 2111 +f 2112 2062 2111 +f 2063 2062 2112 +f 2113 2063 2112 +f 2064 2063 2113 +f 2114 2064 2113 +f 2065 2064 2114 +f 2115 2065 2114 +f 2066 2065 2115 +f 2116 2066 2115 +f 2067 2066 2116 +f 2117 2067 2116 +f 2068 2067 2117 +f 2118 2068 2117 +f 2069 2068 2118 +f 2119 2069 2118 +f 2070 2069 2119 +f 2120 2070 2119 +f 2071 2070 2120 +f 2121 2071 2120 +f 2072 2071 2121 +f 2122 2072 2121 +f 2073 2072 2122 +f 2123 2073 2122 +f 2074 2073 2123 +f 2124 2074 2123 +f 2075 2074 2124 +f 2125 2075 2124 +f 2076 2075 2125 +f 2126 2076 2125 +f 2077 2076 2126 +f 2127 2077 2126 +f 2078 2077 2127 +f 2128 2078 2127 +f 2079 2078 2128 +f 2129 2079 2128 +f 2080 2079 2129 +f 2130 2080 2129 +f 2081 2080 2130 +f 2131 2081 2130 +f 2082 2081 2131 +f 2132 2082 2131 +f 2083 2082 2132 +f 2133 2083 2132 +f 2084 2083 2133 +f 2134 2084 2133 +f 2085 2084 2134 +f 2135 2085 2134 +f 2086 2085 2135 +f 2136 2086 2135 +f 2087 2086 2136 +f 2137 2087 2136 +f 2088 2087 2137 +f 2138 2088 2137 +f 2089 2088 2138 +f 2139 2089 2138 +f 2090 2089 2139 +f 2140 2090 2139 +f 2091 2090 2140 +f 2141 2091 2140 +f 2092 2091 2141 +f 2142 2092 2141 +f 2093 2092 2142 +f 2143 2093 2142 +f 2094 2093 2143 +f 2144 2094 2143 +f 2095 2094 2144 +f 2145 2095 2144 +f 2096 2095 2145 +f 2146 2096 2145 +f 2097 2096 2146 +f 2147 2097 2146 +f 2098 2097 2147 +f 2148 2098 2147 +f 2099 2098 2148 +f 2149 2099 2148 +f 2100 2099 2149 +f 2150 2100 2149 +f 2102 2101 2151 +f 2152 2102 2151 +f 2103 2102 2152 +f 2153 2103 2152 +f 2104 2103 2153 +f 2154 2104 2153 +f 2105 2104 2154 +f 2155 2105 2154 +f 2106 2105 2155 +f 2156 2106 2155 +f 2107 2106 2156 +f 2157 2107 2156 +f 2108 2107 2157 +f 2158 2108 2157 +f 2109 2108 2158 +f 2159 2109 2158 +f 2110 2109 2159 +f 2160 2110 2159 +f 2111 2110 2160 +f 2161 2111 2160 +f 2112 2111 2161 +f 2162 2112 2161 +f 2113 2112 2162 +f 2163 2113 2162 +f 2114 2113 2163 +f 2164 2114 2163 +f 2115 2114 2164 +f 2165 2115 2164 +f 2116 2115 2165 +f 2166 2116 2165 +f 2117 2116 2166 +f 2167 2117 2166 +f 2118 2117 2167 +f 2168 2118 2167 +f 2119 2118 2168 +f 2169 2119 2168 +f 2120 2119 2169 +f 2170 2120 2169 +f 2121 2120 2170 +f 2171 2121 2170 +f 2122 2121 2171 +f 2172 2122 2171 +f 2123 2122 2172 +f 2173 2123 2172 +f 2124 2123 2173 +f 2174 2124 2173 +f 2125 2124 2174 +f 2175 2125 2174 +f 2126 2125 2175 +f 2176 2126 2175 +f 2127 2126 2176 +f 2177 2127 2176 +f 2128 2127 2177 +f 2178 2128 2177 +f 2129 2128 2178 +f 2179 2129 2178 +f 2130 2129 2179 +f 2180 2130 2179 +f 2131 2130 2180 +f 2181 2131 2180 +f 2132 2131 2181 +f 2182 2132 2181 +f 2133 2132 2182 +f 2183 2133 2182 +f 2134 2133 2183 +f 2184 2134 2183 +f 2135 2134 2184 +f 2185 2135 2184 +f 2136 2135 2185 +f 2186 2136 2185 +f 2137 2136 2186 +f 2187 2137 2186 +f 2138 2137 2187 +f 2188 2138 2187 +f 2139 2138 2188 +f 2189 2139 2188 +f 2140 2139 2189 +f 2190 2140 2189 +f 2141 2140 2190 +f 2191 2141 2190 +f 2142 2141 2191 +f 2192 2142 2191 +f 2143 2142 2192 +f 2193 2143 2192 +f 2144 2143 2193 +f 2194 2144 2193 +f 2145 2144 2194 +f 2195 2145 2194 +f 2146 2145 2195 +f 2196 2146 2195 +f 2147 2146 2196 +f 2197 2147 2196 +f 2148 2147 2197 +f 2198 2148 2197 +f 2149 2148 2198 +f 2199 2149 2198 +f 2150 2149 2199 +f 2200 2150 2199 +f 2152 2151 2201 +f 2202 2152 2201 +f 2153 2152 2202 +f 2203 2153 2202 +f 2154 2153 2203 +f 2204 2154 2203 +f 2155 2154 2204 +f 2205 2155 2204 +f 2156 2155 2205 +f 2206 2156 2205 +f 2157 2156 2206 +f 2207 2157 2206 +f 2158 2157 2207 +f 2208 2158 2207 +f 2159 2158 2208 +f 2209 2159 2208 +f 2160 2159 2209 +f 2210 2160 2209 +f 2161 2160 2210 +f 2211 2161 2210 +f 2162 2161 2211 +f 2212 2162 2211 +f 2163 2162 2212 +f 2213 2163 2212 +f 2164 2163 2213 +f 2214 2164 2213 +f 2165 2164 2214 +f 2215 2165 2214 +f 2166 2165 2215 +f 2216 2166 2215 +f 2167 2166 2216 +f 2217 2167 2216 +f 2168 2167 2217 +f 2218 2168 2217 +f 2169 2168 2218 +f 2219 2169 2218 +f 2170 2169 2219 +f 2220 2170 2219 +f 2171 2170 2220 +f 2221 2171 2220 +f 2172 2171 2221 +f 2222 2172 2221 +f 2173 2172 2222 +f 2223 2173 2222 +f 2174 2173 2223 +f 2224 2174 2223 +f 2175 2174 2224 +f 2225 2175 2224 +f 2176 2175 2225 +f 2226 2176 2225 +f 2177 2176 2226 +f 2227 2177 2226 +f 2178 2177 2227 +f 2228 2178 2227 +f 2179 2178 2228 +f 2229 2179 2228 +f 2180 2179 2229 +f 2230 2180 2229 +f 2181 2180 2230 +f 2231 2181 2230 +f 2182 2181 2231 +f 2232 2182 2231 +f 2183 2182 2232 +f 2233 2183 2232 +f 2184 2183 2233 +f 2234 2184 2233 +f 2185 2184 2234 +f 2235 2185 2234 +f 2186 2185 2235 +f 2236 2186 2235 +f 2187 2186 2236 +f 2237 2187 2236 +f 2188 2187 2237 +f 2238 2188 2237 +f 2189 2188 2238 +f 2239 2189 2238 +f 2190 2189 2239 +f 2240 2190 2239 +f 2191 2190 2240 +f 2241 2191 2240 +f 2192 2191 2241 +f 2242 2192 2241 +f 2193 2192 2242 +f 2243 2193 2242 +f 2194 2193 2243 +f 2244 2194 2243 +f 2195 2194 2244 +f 2245 2195 2244 +f 2196 2195 2245 +f 2246 2196 2245 +f 2197 2196 2246 +f 2247 2197 2246 +f 2198 2197 2247 +f 2248 2198 2247 +f 2199 2198 2248 +f 2249 2199 2248 +f 2200 2199 2249 +f 2250 2200 2249 +f 2202 2201 2251 +f 2252 2202 2251 +f 2203 2202 2252 +f 2253 2203 2252 +f 2204 2203 2253 +f 2254 2204 2253 +f 2205 2204 2254 +f 2255 2205 2254 +f 2206 2205 2255 +f 2256 2206 2255 +f 2207 2206 2256 +f 2257 2207 2256 +f 2208 2207 2257 +f 2258 2208 2257 +f 2209 2208 2258 +f 2259 2209 2258 +f 2210 2209 2259 +f 2260 2210 2259 +f 2211 2210 2260 +f 2261 2211 2260 +f 2212 2211 2261 +f 2262 2212 2261 +f 2213 2212 2262 +f 2263 2213 2262 +f 2214 2213 2263 +f 2264 2214 2263 +f 2215 2214 2264 +f 2265 2215 2264 +f 2216 2215 2265 +f 2266 2216 2265 +f 2217 2216 2266 +f 2267 2217 2266 +f 2218 2217 2267 +f 2268 2218 2267 +f 2219 2218 2268 +f 2269 2219 2268 +f 2220 2219 2269 +f 2270 2220 2269 +f 2221 2220 2270 +f 2271 2221 2270 +f 2222 2221 2271 +f 2272 2222 2271 +f 2223 2222 2272 +f 2273 2223 2272 +f 2224 2223 2273 +f 2274 2224 2273 +f 2225 2224 2274 +f 2275 2225 2274 +f 2226 2225 2275 +f 2276 2226 2275 +f 2227 2226 2276 +f 2277 2227 2276 +f 2228 2227 2277 +f 2278 2228 2277 +f 2229 2228 2278 +f 2279 2229 2278 +f 2230 2229 2279 +f 2280 2230 2279 +f 2231 2230 2280 +f 2281 2231 2280 +f 2232 2231 2281 +f 2282 2232 2281 +f 2233 2232 2282 +f 2283 2233 2282 +f 2234 2233 2283 +f 2284 2234 2283 +f 2235 2234 2284 +f 2285 2235 2284 +f 2236 2235 2285 +f 2286 2236 2285 +f 2237 2236 2286 +f 2287 2237 2286 +f 2238 2237 2287 +f 2288 2238 2287 +f 2239 2238 2288 +f 2289 2239 2288 +f 2240 2239 2289 +f 2290 2240 2289 +f 2241 2240 2290 +f 2291 2241 2290 +f 2242 2241 2291 +f 2292 2242 2291 +f 2243 2242 2292 +f 2293 2243 2292 +f 2244 2243 2293 +f 2294 2244 2293 +f 2245 2244 2294 +f 2295 2245 2294 +f 2246 2245 2295 +f 2296 2246 2295 +f 2247 2246 2296 +f 2297 2247 2296 +f 2248 2247 2297 +f 2298 2248 2297 +f 2249 2248 2298 +f 2299 2249 2298 +f 2250 2249 2299 +f 2300 2250 2299 +f 2252 2251 2301 +f 2302 2252 2301 +f 2253 2252 2302 +f 2303 2253 2302 +f 2254 2253 2303 +f 2304 2254 2303 +f 2255 2254 2304 +f 2305 2255 2304 +f 2256 2255 2305 +f 2306 2256 2305 +f 2257 2256 2306 +f 2307 2257 2306 +f 2258 2257 2307 +f 2308 2258 2307 +f 2259 2258 2308 +f 2309 2259 2308 +f 2260 2259 2309 +f 2310 2260 2309 +f 2261 2260 2310 +f 2311 2261 2310 +f 2262 2261 2311 +f 2312 2262 2311 +f 2263 2262 2312 +f 2313 2263 2312 +f 2264 2263 2313 +f 2314 2264 2313 +f 2265 2264 2314 +f 2315 2265 2314 +f 2266 2265 2315 +f 2316 2266 2315 +f 2267 2266 2316 +f 2317 2267 2316 +f 2268 2267 2317 +f 2318 2268 2317 +f 2269 2268 2318 +f 2319 2269 2318 +f 2270 2269 2319 +f 2320 2270 2319 +f 2271 2270 2320 +f 2321 2271 2320 +f 2272 2271 2321 +f 2322 2272 2321 +f 2273 2272 2322 +f 2323 2273 2322 +f 2274 2273 2323 +f 2324 2274 2323 +f 2275 2274 2324 +f 2325 2275 2324 +f 2276 2275 2325 +f 2326 2276 2325 +f 2277 2276 2326 +f 2327 2277 2326 +f 2278 2277 2327 +f 2328 2278 2327 +f 2279 2278 2328 +f 2329 2279 2328 +f 2280 2279 2329 +f 2330 2280 2329 +f 2281 2280 2330 +f 2331 2281 2330 +f 2282 2281 2331 +f 2332 2282 2331 +f 2283 2282 2332 +f 2333 2283 2332 +f 2284 2283 2333 +f 2334 2284 2333 +f 2285 2284 2334 +f 2335 2285 2334 +f 2286 2285 2335 +f 2336 2286 2335 +f 2287 2286 2336 +f 2337 2287 2336 +f 2288 2287 2337 +f 2338 2288 2337 +f 2289 2288 2338 +f 2339 2289 2338 +f 2290 2289 2339 +f 2340 2290 2339 +f 2291 2290 2340 +f 2341 2291 2340 +f 2292 2291 2341 +f 2342 2292 2341 +f 2293 2292 2342 +f 2343 2293 2342 +f 2294 2293 2343 +f 2344 2294 2343 +f 2295 2294 2344 +f 2345 2295 2344 +f 2296 2295 2345 +f 2346 2296 2345 +f 2297 2296 2346 +f 2347 2297 2346 +f 2298 2297 2347 +f 2348 2298 2347 +f 2299 2298 2348 +f 2349 2299 2348 +f 2300 2299 2349 +f 2350 2300 2349 +f 2302 2301 2351 +f 2352 2302 2351 +f 2303 2302 2352 +f 2353 2303 2352 +f 2304 2303 2353 +f 2354 2304 2353 +f 2305 2304 2354 +f 2355 2305 2354 +f 2306 2305 2355 +f 2356 2306 2355 +f 2307 2306 2356 +f 2357 2307 2356 +f 2308 2307 2357 +f 2358 2308 2357 +f 2309 2308 2358 +f 2359 2309 2358 +f 2310 2309 2359 +f 2360 2310 2359 +f 2311 2310 2360 +f 2361 2311 2360 +f 2312 2311 2361 +f 2362 2312 2361 +f 2313 2312 2362 +f 2363 2313 2362 +f 2314 2313 2363 +f 2364 2314 2363 +f 2315 2314 2364 +f 2365 2315 2364 +f 2316 2315 2365 +f 2366 2316 2365 +f 2317 2316 2366 +f 2367 2317 2366 +f 2318 2317 2367 +f 2368 2318 2367 +f 2319 2318 2368 +f 2369 2319 2368 +f 2320 2319 2369 +f 2370 2320 2369 +f 2321 2320 2370 +f 2371 2321 2370 +f 2322 2321 2371 +f 2372 2322 2371 +f 2323 2322 2372 +f 2373 2323 2372 +f 2324 2323 2373 +f 2374 2324 2373 +f 2325 2324 2374 +f 2375 2325 2374 +f 2326 2325 2375 +f 2376 2326 2375 +f 2327 2326 2376 +f 2377 2327 2376 +f 2328 2327 2377 +f 2378 2328 2377 +f 2329 2328 2378 +f 2379 2329 2378 +f 2330 2329 2379 +f 2380 2330 2379 +f 2331 2330 2380 +f 2381 2331 2380 +f 2332 2331 2381 +f 2382 2332 2381 +f 2333 2332 2382 +f 2383 2333 2382 +f 2334 2333 2383 +f 2384 2334 2383 +f 2335 2334 2384 +f 2385 2335 2384 +f 2336 2335 2385 +f 2386 2336 2385 +f 2337 2336 2386 +f 2387 2337 2386 +f 2338 2337 2387 +f 2388 2338 2387 +f 2339 2338 2388 +f 2389 2339 2388 +f 2340 2339 2389 +f 2390 2340 2389 +f 2341 2340 2390 +f 2391 2341 2390 +f 2342 2341 2391 +f 2392 2342 2391 +f 2343 2342 2392 +f 2393 2343 2392 +f 2344 2343 2393 +f 2394 2344 2393 +f 2345 2344 2394 +f 2395 2345 2394 +f 2346 2345 2395 +f 2396 2346 2395 +f 2347 2346 2396 +f 2397 2347 2396 +f 2348 2347 2397 +f 2398 2348 2397 +f 2349 2348 2398 +f 2399 2349 2398 +f 2350 2349 2399 +f 2400 2350 2399 +f 2352 2351 2401 +f 2402 2352 2401 +f 2353 2352 2402 +f 2403 2353 2402 +f 2354 2353 2403 +f 2404 2354 2403 +f 2355 2354 2404 +f 2405 2355 2404 +f 2356 2355 2405 +f 2406 2356 2405 +f 2357 2356 2406 +f 2407 2357 2406 +f 2358 2357 2407 +f 2408 2358 2407 +f 2359 2358 2408 +f 2409 2359 2408 +f 2360 2359 2409 +f 2410 2360 2409 +f 2361 2360 2410 +f 2411 2361 2410 +f 2362 2361 2411 +f 2412 2362 2411 +f 2363 2362 2412 +f 2413 2363 2412 +f 2364 2363 2413 +f 2414 2364 2413 +f 2365 2364 2414 +f 2415 2365 2414 +f 2366 2365 2415 +f 2416 2366 2415 +f 2367 2366 2416 +f 2417 2367 2416 +f 2368 2367 2417 +f 2418 2368 2417 +f 2369 2368 2418 +f 2419 2369 2418 +f 2370 2369 2419 +f 2420 2370 2419 +f 2371 2370 2420 +f 2421 2371 2420 +f 2372 2371 2421 +f 2422 2372 2421 +f 2373 2372 2422 +f 2423 2373 2422 +f 2374 2373 2423 +f 2424 2374 2423 +f 2375 2374 2424 +f 2425 2375 2424 +f 2376 2375 2425 +f 2426 2376 2425 +f 2377 2376 2426 +f 2427 2377 2426 +f 2378 2377 2427 +f 2428 2378 2427 +f 2379 2378 2428 +f 2429 2379 2428 +f 2380 2379 2429 +f 2430 2380 2429 +f 2381 2380 2430 +f 2431 2381 2430 +f 2382 2381 2431 +f 2432 2382 2431 +f 2383 2382 2432 +f 2433 2383 2432 +f 2384 2383 2433 +f 2434 2384 2433 +f 2385 2384 2434 +f 2435 2385 2434 +f 2386 2385 2435 +f 2436 2386 2435 +f 2387 2386 2436 +f 2437 2387 2436 +f 2388 2387 2437 +f 2438 2388 2437 +f 2389 2388 2438 +f 2439 2389 2438 +f 2390 2389 2439 +f 2440 2390 2439 +f 2391 2390 2440 +f 2441 2391 2440 +f 2392 2391 2441 +f 2442 2392 2441 +f 2393 2392 2442 +f 2443 2393 2442 +f 2394 2393 2443 +f 2444 2394 2443 +f 2395 2394 2444 +f 2445 2395 2444 +f 2396 2395 2445 +f 2446 2396 2445 +f 2397 2396 2446 +f 2447 2397 2446 +f 2398 2397 2447 +f 2448 2398 2447 +f 2399 2398 2448 +f 2449 2399 2448 +f 2400 2399 2449 +f 2450 2400 2449 +f 2402 2401 2451 +f 2452 2402 2451 +f 2403 2402 2452 +f 2453 2403 2452 +f 2404 2403 2453 +f 2454 2404 2453 +f 2405 2404 2454 +f 2455 2405 2454 +f 2406 2405 2455 +f 2456 2406 2455 +f 2407 2406 2456 +f 2457 2407 2456 +f 2408 2407 2457 +f 2458 2408 2457 +f 2409 2408 2458 +f 2459 2409 2458 +f 2410 2409 2459 +f 2460 2410 2459 +f 2411 2410 2460 +f 2461 2411 2460 +f 2412 2411 2461 +f 2462 2412 2461 +f 2413 2412 2462 +f 2463 2413 2462 +f 2414 2413 2463 +f 2464 2414 2463 +f 2415 2414 2464 +f 2465 2415 2464 +f 2416 2415 2465 +f 2466 2416 2465 +f 2417 2416 2466 +f 2467 2417 2466 +f 2418 2417 2467 +f 2468 2418 2467 +f 2419 2418 2468 +f 2469 2419 2468 +f 2420 2419 2469 +f 2470 2420 2469 +f 2421 2420 2470 +f 2471 2421 2470 +f 2422 2421 2471 +f 2472 2422 2471 +f 2423 2422 2472 +f 2473 2423 2472 +f 2424 2423 2473 +f 2474 2424 2473 +f 2425 2424 2474 +f 2475 2425 2474 +f 2426 2425 2475 +f 2476 2426 2475 +f 2427 2426 2476 +f 2477 2427 2476 +f 2428 2427 2477 +f 2478 2428 2477 +f 2429 2428 2478 +f 2479 2429 2478 +f 2430 2429 2479 +f 2480 2430 2479 +f 2431 2430 2480 +f 2481 2431 2480 +f 2432 2431 2481 +f 2482 2432 2481 +f 2433 2432 2482 +f 2483 2433 2482 +f 2434 2433 2483 +f 2484 2434 2483 +f 2435 2434 2484 +f 2485 2435 2484 +f 2436 2435 2485 +f 2486 2436 2485 +f 2437 2436 2486 +f 2487 2437 2486 +f 2438 2437 2487 +f 2488 2438 2487 +f 2439 2438 2488 +f 2489 2439 2488 +f 2440 2439 2489 +f 2490 2440 2489 +f 2441 2440 2490 +f 2491 2441 2490 +f 2442 2441 2491 +f 2492 2442 2491 +f 2443 2442 2492 +f 2493 2443 2492 +f 2444 2443 2493 +f 2494 2444 2493 +f 2445 2444 2494 +f 2495 2445 2494 +f 2446 2445 2495 +f 2496 2446 2495 +f 2447 2446 2496 +f 2497 2447 2496 +f 2448 2447 2497 +f 2498 2448 2497 +f 2449 2448 2498 +f 2499 2449 2498 +f 2450 2449 2499 +f 2500 2450 2499 +f 2501 2503 2502 +f 2504 2505 2506 +f 2501 2505 2504 +f 2505 2501 2502 +f 2502 2506 2505 +f 2506 2502 2503 +f 2503 2504 2506 +f 2504 2503 2501 +f 2507 2509 2508 +f 2510 2511 2512 +f 2507 2511 2510 +f 2511 2507 2508 +f 2508 2512 2511 +f 2512 2508 2509 +f 2509 2510 2512 +f 2510 2509 2507 +f 2513 2515 2514 +f 2516 2517 2518 +f 2513 2517 2516 +f 2517 2513 2514 +f 2514 2518 2517 +f 2518 2514 2515 +f 2515 2516 2518 +f 2516 2515 2513 +f 2519 2521 2520 +f 2522 2523 2524 +f 2519 2523 2522 +f 2523 2519 2520 +f 2520 2524 2523 +f 2524 2520 2521 +f 2521 2522 2524 +f 2522 2521 2519 +f 2525 2527 2526 +f 2528 2529 2530 +f 2525 2529 2528 +f 2529 2525 2526 +f 2526 2530 2529 +f 2530 2526 2527 +f 2527 2528 2530 +f 2528 2527 2525 +f 2531 2533 2532 +f 2534 2535 2536 +f 2531 2535 2534 +f 2535 2531 2532 +f 2532 2536 2535 +f 2536 2532 2533 +f 2533 2534 2536 +f 2534 2533 2531 +f 2537 2539 2538 +f 2540 2541 2542 +f 2537 2541 2540 +f 2541 2537 2538 +f 2538 2542 2541 +f 2542 2538 2539 +f 2539 2540 2542 +f 2540 2539 2537 +f 2543 2545 2544 +f 2546 2547 2548 +f 2543 2547 2546 +f 2547 2543 2544 +f 2544 2548 2547 +f 2548 2544 2545 +f 2545 2546 2548 +f 2546 2545 2543 +f 2549 2551 2550 +f 2552 2553 2554 +f 2549 2553 2552 +f 2553 2549 2550 +f 2550 2554 2553 +f 2554 2550 2551 +f 2551 2552 2554 +f 2552 2551 2549 +f 2555 2557 2556 +f 2558 2559 2560 +f 2555 2559 2558 +f 2559 2555 2556 +f 2556 2560 2559 +f 2560 2556 2557 +f 2557 2558 2560 +f 2558 2557 2555 +f 2561 2563 2562 +f 2564 2565 2566 +f 2561 2565 2564 +f 2565 2561 2562 +f 2562 2566 2565 +f 2566 2562 2563 +f 2563 2564 2566 +f 2564 2563 2561 +f 2567 2569 2568 +f 2570 2571 2572 +f 2567 2571 2570 +f 2571 2567 2568 +f 2568 2572 2571 +f 2572 2568 2569 +f 2569 2570 2572 +f 2570 2569 2567 +f 2573 2575 2574 +f 2576 2577 2578 +f 2573 2577 2576 +f 2577 2573 2574 +f 2574 2578 2577 +f 2578 2574 2575 +f 2575 2576 2578 +f 2576 2575 2573 +f 2579 2581 2580 +f 2582 2583 2584 +f 2579 2583 2582 +f 2583 2579 2580 +f 2580 2584 2583 +f 2584 2580 2581 +f 2581 2582 2584 +f 2582 2581 2579 +f 2585 2587 2586 +f 2588 2589 2590 +f 2585 2589 2588 +f 2589 2585 2586 +f 2586 2590 2589 +f 2590 2586 2587 +f 2587 2588 2590 +f 2588 2587 2585 +f 2591 2593 2592 +f 2594 2595 2596 +f 2591 2595 2594 +f 2595 2591 2592 +f 2592 2596 2595 +f 2596 2592 2593 +f 2593 2594 2596 +f 2594 2593 2591 +f 2597 2599 2598 +f 2600 2601 2602 +f 2597 2601 2600 +f 2601 2597 2598 +f 2598 2602 2601 +f 2602 2598 2599 +f 2599 2600 2602 +f 2600 2599 2597 +f 2603 2605 2604 +f 2606 2607 2608 +f 2603 2607 2606 +f 2607 2603 2604 +f 2604 2608 2607 +f 2608 2604 2605 +f 2605 2606 2608 +f 2606 2605 2603 +f 2609 2611 2610 +f 2612 2613 2614 +f 2609 2613 2612 +f 2613 2609 2610 +f 2610 2614 2613 +f 2614 2610 2611 +f 2611 2612 2614 +f 2612 2611 2609 +f 2615 2617 2616 +f 2618 2619 2620 +f 2615 2619 2618 +f 2619 2615 2616 +f 2616 2620 2619 +f 2620 2616 2617 +f 2617 2618 2620 +f 2618 2617 2615 +f 2621 2623 2622 +f 2624 2625 2626 +f 2621 2625 2624 +f 2625 2621 2622 +f 2622 2626 2625 +f 2626 2622 2623 +f 2623 2624 2626 +f 2624 2623 2621 +f 2627 2629 2628 +f 2630 2631 2632 +f 2627 2631 2630 +f 2631 2627 2628 +f 2628 2632 2631 +f 2632 2628 2629 +f 2629 2630 2632 +f 2630 2629 2627 +f 2633 2635 2634 +f 2636 2637 2638 +f 2633 2637 2636 +f 2637 2633 2634 +f 2634 2638 2637 +f 2638 2634 2635 +f 2635 2636 2638 +f 2636 2635 2633 +f 2639 2641 2640 +f 2642 2643 2644 +f 2639 2643 2642 +f 2643 2639 2640 +f 2640 2644 2643 +f 2644 2640 2641 +f 2641 2642 2644 +f 2642 2641 2639 +f 2645 2647 2646 +f 2648 2649 2650 +f 2645 2649 2648 +f 2649 2645 2646 +f 2646 2650 2649 +f 2650 2646 2647 +f 2647 2648 2650 +f 2648 2647 2645 +f 2651 2653 2652 +f 2654 2655 2656 +f 2651 2655 2654 +f 2655 2651 2652 +f 2652 2656 2655 +f 2656 2652 2653 +f 2653 2654 2656 +f 2654 2653 2651 +f 2657 2659 2658 +f 2660 2661 2662 +f 2657 2661 2660 +f 2661 2657 2658 +f 2658 2662 2661 +f 2662 2658 2659 +f 2659 2660 2662 +f 2660 2659 2657 +f 2663 2665 2664 +f 2666 2667 2668 +f 2663 2667 2666 +f 2667 2663 2664 +f 2664 2668 2667 +f 2668 2664 2665 +f 2665 2666 2668 +f 2666 2665 2663 +f 2669 2671 2670 +f 2672 2673 2674 +f 2669 2673 2672 +f 2673 2669 2670 +f 2670 2674 2673 +f 2674 2670 2671 +f 2671 2672 2674 +f 2672 2671 2669 +f 2675 2677 2676 +f 2678 2679 2680 +f 2675 2679 2678 +f 2679 2675 2676 +f 2676 2680 2679 +f 2680 2676 2677 +f 2677 2678 2680 +f 2678 2677 2675 +f 2681 2683 2682 +f 2684 2685 2686 +f 2681 2685 2684 +f 2685 2681 2682 +f 2682 2686 2685 +f 2686 2682 2683 +f 2683 2684 2686 +f 2684 2683 2681 +f 2687 2689 2688 +f 2690 2691 2692 +f 2687 2691 2690 +f 2691 2687 2688 +f 2688 2692 2691 +f 2692 2688 2689 +f 2689 2690 2692 +f 2690 2689 2687 +f 2693 2695 2694 +f 2696 2697 2698 +f 2693 2697 2696 +f 2697 2693 2694 +f 2694 2698 2697 +f 2698 2694 2695 +f 2695 2696 2698 +f 2696 2695 2693 +f 2699 2701 2700 +f 2702 2703 2704 +f 2699 2703 2702 +f 2703 2699 2700 +f 2700 2704 2703 +f 2704 2700 2701 +f 2701 2702 2704 +f 2702 2701 2699 +f 2705 2707 2706 +f 2708 2709 2710 +f 2705 2709 2708 +f 2709 2705 2706 +f 2706 2710 2709 +f 2710 2706 2707 +f 2707 2708 2710 +f 2708 2707 2705 +f 2711 2713 2712 +f 2714 2715 2716 +f 2711 2715 2714 +f 2715 2711 2712 +f 2712 2716 2715 +f 2716 2712 2713 +f 2713 2714 2716 +f 2714 2713 2711 +f 2717 2719 2718 +f 2720 2721 2722 +f 2717 2721 2720 +f 2721 2717 2718 +f 2718 2722 2721 +f 2722 2718 2719 +f 2719 2720 2722 +f 2720 2719 2717 +f 2723 2725 2724 +f 2726 2727 2728 +f 2723 2727 2726 +f 2727 2723 2724 +f 2724 2728 2727 +f 2728 2724 2725 +f 2725 2726 2728 +f 2726 2725 2723 +f 2729 2731 2730 +f 2732 2733 2734 +f 2729 2733 2732 +f 2733 2729 2730 +f 2730 2734 2733 +f 2734 2730 2731 +f 2731 2732 2734 +f 2732 2731 2729 +f 2735 2737 2736 +f 2738 2739 2740 +f 2735 2739 2738 +f 2739 2735 2736 +f 2736 2740 2739 +f 2740 2736 2737 +f 2737 2738 2740 +f 2738 2737 2735 +f 2741 2743 2742 +f 2744 2745 2746 +f 2741 2745 2744 +f 2745 2741 2742 +f 2742 2746 2745 +f 2746 2742 2743 +f 2743 2744 2746 +f 2744 2743 2741 +f 2747 2749 2748 +f 2750 2751 2752 +f 2747 2751 2750 +f 2751 2747 2748 +f 2748 2752 2751 +f 2752 2748 2749 +f 2749 2750 2752 +f 2750 2749 2747 +f 2753 2755 2754 +f 2756 2757 2758 +f 2753 2757 2756 +f 2757 2753 2754 +f 2754 2758 2757 +f 2758 2754 2755 +f 2755 2756 2758 +f 2756 2755 2753 +f 2759 2761 2760 +f 2762 2763 2764 +f 2759 2763 2762 +f 2763 2759 2760 +f 2760 2764 2763 +f 2764 2760 2761 +f 2761 2762 2764 +f 2762 2761 2759 +f 2765 2767 2766 +f 2768 2769 2770 +f 2765 2769 2768 +f 2769 2765 2766 +f 2766 2770 2769 +f 2770 2766 2767 +f 2767 2768 2770 +f 2768 2767 2765 +f 2771 2773 2772 +f 2774 2775 2776 +f 2771 2775 2774 +f 2775 2771 2772 +f 2772 2776 2775 +f 2776 2772 2773 +f 2773 2774 2776 +f 2774 2773 2771 +f 2777 2779 2778 +f 2780 2781 2782 +f 2777 2781 2780 +f 2781 2777 2778 +f 2778 2782 2781 +f 2782 2778 2779 +f 2779 2780 2782 +f 2780 2779 2777 +f 2783 2785 2784 +f 2786 2787 2788 +f 2783 2787 2786 +f 2787 2783 2784 +f 2784 2788 2787 +f 2788 2784 2785 +f 2785 2786 2788 +f 2786 2785 2783 +f 2789 2791 2790 +f 2792 2793 2794 +f 2789 2793 2792 +f 2793 2789 2790 +f 2790 2794 2793 +f 2794 2790 2791 +f 2791 2792 2794 +f 2792 2791 2789 +f 2795 2797 2796 +f 2798 2799 2800 +f 2795 2799 2798 +f 2799 2795 2796 +f 2796 2800 2799 +f 2800 2796 2797 +f 2797 2798 2800 +f 2798 2797 2795 From 33a9eb1cce2aaff9e02217ca14667e78a2a33497 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Sat, 16 Mar 2019 14:58:34 +0100 Subject: [PATCH 42/99] Document detail triangle edge flags --- DebugUtils/Source/DetourDebugDraw.cpp | 4 +++- Detour/Include/DetourNavMesh.h | 17 ++++++++++++++++- Recast/Source/RecastMeshDetail.cpp | 3 ++- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/DebugUtils/Source/DetourDebugDraw.cpp b/DebugUtils/Source/DetourDebugDraw.cpp index dd4bad3f..4ca0581c 100644 --- a/DebugUtils/Source/DetourDebugDraw.cpp +++ b/DebugUtils/Source/DetourDebugDraw.cpp @@ -101,7 +101,9 @@ static void drawPolyBoundaries(duDebugDraw* dd, const dtMeshTile* tile, } for (int m = 0, n = 2; m < 3; n=m++) { - if (((t[3] >> (n*2)) & 0x3) == 0) continue; // Skip inner detail edges. + if ((dtGetDetailTriEdgeFlags(t[3], n) & DT_DETAIL_EDGE_BOUNDARY) == 0) + continue; + if (distancePtLine2d(tv[n],v0,v1) < thr && distancePtLine2d(tv[m],v0,v1) < thr) { diff --git a/Detour/Include/DetourNavMesh.h b/Detour/Include/DetourNavMesh.h index 09b778f5..42affe28 100644 --- a/Detour/Include/DetourNavMesh.h +++ b/Detour/Include/DetourNavMesh.h @@ -130,6 +130,11 @@ enum dtRaycastOptions DT_RAYCAST_USE_COSTS = 0x01, ///< Raycast should calculate movement cost along the ray and fill RaycastHit::cost }; +enum dtDetailTriEdgeFlags +{ + DT_DETAIL_EDGE_BOUNDARY = 0x01, ///< Detail triangle edge is part of the poly boundary +}; + /// Limit raycasting during any angle pahfinding /// The limit is given as a multiple of the character radius @@ -287,7 +292,8 @@ struct dtMeshTile /// The detail mesh's unique vertices. [(x, y, z) * dtMeshHeader::detailVertCount] float* detailVerts; - /// The detail mesh's triangles. [(vertA, vertB, vertC) * dtMeshHeader::detailTriCount] + /// The detail mesh's triangles. [(vertA, vertB, vertC, triFlags) * dtMeshHeader::detailTriCount]. + /// See dtDetailTriEdgeFlags and dtGetDetailTriEdgeFlags. unsigned char* detailTris; /// The tile bounding volume nodes. [Size: dtMeshHeader::bvNodeCount] @@ -305,6 +311,15 @@ private: dtMeshTile& operator=(const dtMeshTile&); }; +/// Get flags for edge in detail triangle. +/// @param triFlags[in] The flags for the triangle (last component of detail vertices above). +/// @param edgeIndex[in] The index of the first vertex of the edge. For instance, if 0, +/// returns flags for edge AB. +inline int dtGetDetailTriEdgeFlags(unsigned char triFlags, int edgeIndex) +{ + return (triFlags >> (edgeIndex * 2)) & 0x3; +} + /// Configuration parameters used to define multi-tile navigation meshes. /// The values are used to allocate space during the initialization of a navigation mesh. /// @see dtNavMesh::init() diff --git a/Recast/Source/RecastMeshDetail.cpp b/Recast/Source/RecastMeshDetail.cpp index 68ab726a..9a423cab 100644 --- a/Recast/Source/RecastMeshDetail.cpp +++ b/Recast/Source/RecastMeshDetail.cpp @@ -1141,7 +1141,8 @@ static void getHeightData(rcContext* ctx, const rcCompactHeightfield& chf, static unsigned char getEdgeFlags(const float* va, const float* vb, const float* vpoly, const int npoly) { - // Return true if edge (va,vb) is part of the polygon. + // The flag returned by this function matches dtDetailTriEdgeFlags in Detour. + // Figure out if edge (va,vb) is part of the polygon boundary. static const float thrSqr = rcSqr(0.001f); for (int i = 0, j = npoly-1; i < npoly; j=i++) { From 54bb0943e5174a71eeeca11919920f685760a4f0 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Sat, 16 Mar 2019 15:03:50 +0100 Subject: [PATCH 43/99] Make closestPointOnPoly and getPolyHeight more robust * Make dtClosestHeightPointTriangle use no epsilon (except for checking denominator) * When point is outside poly, directly find closest height from boundary detail edges instead of first interpolating and then finding height from detail tris. * When point is inside poly, if all dtClosestHeightPointTriangle queries failed, then find height from detail edges. This should only happen if the point is right on top of an internal detail tri edge. --- Detour/Include/DetourNavMesh.h | 2 + Detour/Source/DetourCommon.cpp | 18 +-- Detour/Source/DetourNavMesh.cpp | 160 +++++++++++++++++++-------- Detour/Source/DetourNavMeshQuery.cpp | 45 +++----- 4 files changed, 135 insertions(+), 90 deletions(-) diff --git a/Detour/Include/DetourNavMesh.h b/Detour/Include/DetourNavMesh.h index 42affe28..98293c49 100644 --- a/Detour/Include/DetourNavMesh.h +++ b/Detour/Include/DetourNavMesh.h @@ -651,6 +651,8 @@ private: /// Find nearest polygon within a tile. dtPolyRef findNearestPolyInTile(const dtMeshTile* tile, const float* center, const float* halfExtents, float* nearestPt) const; + /// Returns whether position is over the poly and the height at the position if so. + bool getPolyHeight(const dtMeshTile* tile, const dtPoly* poly, const float* pos, float* height) const; /// Returns closest point on polygon. void closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest, bool* posOverPoly) const; diff --git a/Detour/Source/DetourCommon.cpp b/Detour/Source/DetourCommon.cpp index 3886f14b..b89d7512 100644 --- a/Detour/Source/DetourCommon.cpp +++ b/Detour/Source/DetourCommon.cpp @@ -203,14 +203,18 @@ void dtCalcPolyCenter(float* tc, const unsigned short* idx, int nidx, const floa bool dtClosestHeightPointTriangle(const float* p, const float* a, const float* b, const float* c, float& h) { + const float EPS = 1e-6f; float v0[3], v1[3], v2[3]; - dtVsub(v0, c,a); - dtVsub(v1, b,a); - dtVsub(v2, p,a); + dtVsub(v0, c, a); + dtVsub(v1, b, a); + dtVsub(v2, p, a); // Compute scaled barycentric coordinates float denom = v0[0] * v1[2] - v0[2] * v1[0]; + if (fabsf(denom) < EPS) + return false; + float u = v1[2] * v2[0] - v1[0] * v2[2]; float v = v0[0] * v2[2] - v0[2] * v2[0]; @@ -220,13 +224,9 @@ bool dtClosestHeightPointTriangle(const float* p, const float* a, const float* b v = -v; } - // The (sloppy) epsilon is needed to allow to get height of points which - // are interpolated along the edges of the triangles. - float epsilon = - 1e-4f * denom; - // If point lies inside the triangle, return interpolated ycoord. - if (u >= epsilon && v >= epsilon && (u+v) <= denom - epsilon) { - h = a[1] + (v0[1]*u + v1[1]*v) / denom; + if (u >= 0.0f && v >= 0.0f && (u + v) <= denom) { + h = a[1] + (v0[1] * u + v1[1] * v) / denom; return true; } return false; diff --git a/Detour/Source/DetourNavMesh.cpp b/Detour/Source/DetourNavMesh.cpp index 17df26d8..a655d1d8 100644 --- a/Detour/Source/DetourNavMesh.cpp +++ b/Detour/Source/DetourNavMesh.cpp @@ -616,63 +616,84 @@ void dtNavMesh::baseOffMeshLinks(dtMeshTile* tile) } } -void dtNavMesh::closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest, bool* posOverPoly) const +namespace { - const dtMeshTile* tile = 0; - const dtPoly* poly = 0; - getTileAndPolyByRefUnsafe(ref, &tile, &poly); - - // Off-mesh connections don't have detail polygons. - if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) + template + void closestPointOnDetailEdges(const dtMeshTile* tile, const dtPoly* poly, const float* pos, float* closest) { - const float* v0 = &tile->verts[poly->verts[0]*3]; - const float* v1 = &tile->verts[poly->verts[1]*3]; - const float d0 = dtVdist(pos, v0); - const float d1 = dtVdist(pos, v1); - const float u = d0 / (d0+d1); - dtVlerp(closest, v0, v1, u); - if (posOverPoly) - *posOverPoly = false; - return; + const unsigned int ip = (unsigned int)(poly - tile->polys); + const dtPolyDetail* pd = &tile->detailMeshes[ip]; + + float dmin = FLT_MAX; + float tmin = 0; + const float* pmin = 0; + const float* pmax = 0; + + for (int i = 0; i < pd->triCount; i++) + { + const unsigned char* tris = &tile->detailTris[(pd->triBase + i) * 4]; + const int ANY_BOUNDARY_EDGE = + (DT_DETAIL_EDGE_BOUNDARY << 0) | + (DT_DETAIL_EDGE_BOUNDARY << 2) | + (DT_DETAIL_EDGE_BOUNDARY << 4); + if (onlyBoundary && (tris[3] & ANY_BOUNDARY_EDGE) == 0) + continue; + + const float* v[3]; + for (int j = 0; j < 3; ++j) + { + if (tris[j] < poly->vertCount) + v[j] = &tile->verts[poly->verts[tris[j]] * 3]; + else + v[j] = &tile->detailVerts[(pd->vertBase + (tris[j] - poly->vertCount)) * 3]; + } + + for (int k = 0, j = 2; k < 3; j = k++) + { + if ((dtGetDetailTriEdgeFlags(tris[3], j) & DT_DETAIL_EDGE_BOUNDARY) == 0 && + (onlyBoundary || tris[j] < tris[k])) + { + // Only looking at boundary edges and this is internal, or + // this is an inner edge that we will see again or have already seen. + continue; + } + + float t; + float d = dtDistancePtSegSqr2D(pos, v[j], v[k], t); + if (d < dmin) + { + dmin = d; + tmin = t; + pmin = v[j]; + pmax = v[k]; + } + } + } + + dtVlerp(closest, pmin, pmax, tmin); } - +} + +bool dtNavMesh::getPolyHeight(const dtMeshTile* tile, const dtPoly* poly, const float* pos, float* height) const +{ + // Off-mesh connections do not have detail polys and getting height + // over them does not make sense. + if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) + return false; + const unsigned int ip = (unsigned int)(poly - tile->polys); const dtPolyDetail* pd = &tile->detailMeshes[ip]; - // Clamp point to be inside the polygon. float verts[DT_VERTS_PER_POLYGON*3]; - float edged[DT_VERTS_PER_POLYGON]; - float edget[DT_VERTS_PER_POLYGON]; const int nv = poly->vertCount; for (int i = 0; i < nv; ++i) dtVcopy(&verts[i*3], &tile->verts[poly->verts[i]*3]); - dtVcopy(closest, pos); - if (!dtDistancePtPolyEdgesSqr(pos, verts, nv, edged, edget)) - { - // Point is outside the polygon, dtClamp to nearest edge. - float dmin = edged[0]; - int imin = 0; - for (int i = 1; i < nv; ++i) - { - if (edged[i] < dmin) - { - dmin = edged[i]; - imin = i; - } - } - const float* va = &verts[imin*3]; - const float* vb = &verts[((imin+1)%nv)*3]; - dtVlerp(closest, va, vb, edget[imin]); - - if (posOverPoly) - *posOverPoly = false; - } - else - { - if (posOverPoly) - *posOverPoly = true; - } + if (!dtPointInPolygon(pos, verts, nv)) + return false; + + if (!height) + return true; // Find height at the location. for (int j = 0; j < pd->triCount; ++j) @@ -687,12 +708,53 @@ void dtNavMesh::closestPointOnPoly(dtPolyRef ref, const float* pos, float* close v[k] = &tile->detailVerts[(pd->vertBase+(t[k]-poly->vertCount))*3]; } float h; - if (dtClosestHeightPointTriangle(closest, v[0], v[1], v[2], h)) + if (dtClosestHeightPointTriangle(pos, v[0], v[1], v[2], h)) { - closest[1] = h; - break; + *height = h; + return true; } } + + // If all triangle checks failed above (can happen with degenerate triangles + // or larger floating point values) the point is on an edge, so just select + // closest. This should almost never happen so the extra iteration here is + // ok. + float closest[3]; + closestPointOnDetailEdges(tile, poly, pos, closest); + *height = closest[1]; + return true; +} + +void dtNavMesh::closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest, bool* posOverPoly) const +{ + const dtMeshTile* tile = 0; + const dtPoly* poly = 0; + getTileAndPolyByRefUnsafe(ref, &tile, &poly); + + dtVcopy(closest, pos); + if (getPolyHeight(tile, poly, pos, &closest[1])) + { + if (posOverPoly) + *posOverPoly = true; + return; + } + + if (posOverPoly) + *posOverPoly = false; + + // Off-mesh connections don't have detail polygons. + if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) + { + const float* v0 = &tile->verts[poly->verts[0]*3]; + const float* v1 = &tile->verts[poly->verts[1]*3]; + float t; + dtDistancePtSegSqr2D(pos, v0, v1, t); + dtVlerp(closest, v0, v1, t); + return; + } + + // Outside poly that is not an offmesh connection. + closestPointOnDetailEdges(tile, poly, pos, closest); } dtPolyRef dtNavMesh::findNearestPolyInTile(const dtMeshTile* tile, diff --git a/Detour/Source/DetourNavMeshQuery.cpp b/Detour/Source/DetourNavMeshQuery.cpp index 520f915a..839ee1e8 100644 --- a/Detour/Source/DetourNavMeshQuery.cpp +++ b/Detour/Source/DetourNavMeshQuery.cpp @@ -588,7 +588,7 @@ dtStatus dtNavMeshQuery::closestPointOnPolyBoundary(dtPolyRef ref, const float* /// @par /// -/// Will return #DT_FAILURE if the provided position is outside the xz-bounds +/// Will return #DT_FAILURE | DT_INVALID_PARAM if the provided position is outside the xz-bounds /// of the polygon. /// dtStatus dtNavMeshQuery::getPolyHeight(dtPolyRef ref, const float* pos, float* height) const @@ -602,44 +602,25 @@ dtStatus dtNavMeshQuery::getPolyHeight(dtPolyRef ref, const float* pos, float* h if (!pos || !dtVisfinite2D(pos)) return DT_FAILURE | DT_INVALID_PARAM; - + + // We used to return success for offmesh connections, but the + // getPolyHeight in DetourNavMesh does not do this, so special + // case it here. if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) { const float* v0 = &tile->verts[poly->verts[0]*3]; const float* v1 = &tile->verts[poly->verts[1]*3]; - const float d0 = dtVdist2D(pos, v0); - const float d1 = dtVdist2D(pos, v1); - const float u = d0 / (d0+d1); + float t; + dtDistancePtSegSqr2D(pos, v0, v1, t); if (height) - *height = v0[1] + (v1[1] - v0[1]) * u; + *height = v0[1] + (v1[1] - v0[1])*t; + return DT_SUCCESS; } - else - { - const unsigned int ip = (unsigned int)(poly - tile->polys); - const dtPolyDetail* pd = &tile->detailMeshes[ip]; - for (int j = 0; j < pd->triCount; ++j) - { - const unsigned char* t = &tile->detailTris[(pd->triBase+j)*4]; - const float* v[3]; - for (int k = 0; k < 3; ++k) - { - if (t[k] < poly->vertCount) - v[k] = &tile->verts[poly->verts[t[k]]*3]; - else - v[k] = &tile->detailVerts[(pd->vertBase+(t[k]-poly->vertCount))*3]; - } - float h; - if (dtClosestHeightPointTriangle(pos, v[0], v[1], v[2], h)) - { - if (height) - *height = h; - return DT_SUCCESS; - } - } - } - - return DT_FAILURE | DT_INVALID_PARAM; + + return m_nav->getPolyHeight(tile, poly, pos, height) + ? DT_SUCCESS + : DT_FAILURE | DT_INVALID_PARAM; } class dtFindNearestPolyQuery : public dtPolyQuery From c40188c796f089f89a42e0b939d934178dbcfc5c Mon Sep 17 00:00:00 2001 From: cypheron <--author=cypheron cyberfriday@protonmail.com> Date: Thu, 9 May 2019 01:49:54 +0200 Subject: [PATCH 44/99] Permit false positive compile errors (gcc8) --- RecastDemo/premake5.lua | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/RecastDemo/premake5.lua b/RecastDemo/premake5.lua index d2a6343f..8c218ddb 100644 --- a/RecastDemo/premake5.lua +++ b/RecastDemo/premake5.lua @@ -73,6 +73,12 @@ project "Detour" "../Detour/Include/*.h", "../Detour/Source/*.cpp" } + -- linux library cflags and libs + configuration { "linux", "gmake" } + buildoptions { + "-Wno-error=class-memaccess" + } + project "DetourCrowd" language "C++" @@ -148,7 +154,10 @@ project "RecastDemo" buildoptions { "`pkg-config --cflags sdl2`", "`pkg-config --cflags gl`", - "`pkg-config --cflags glu`" + "`pkg-config --cflags glu`", + "-Wno-ignored-qualifiers", + "-Wno-error=class-memaccess" + } linkoptions { "`pkg-config --libs sdl2`", From 57610fa6ef31b39020231906f8c5d40eaa8294ae Mon Sep 17 00:00:00 2001 From: "Icecream95 (gitlab.com/icecream95)" Date: Mon, 21 Oct 2019 17:39:28 +1300 Subject: [PATCH 45/99] Explicitly define winding as being signed --- Recast/Source/RecastContour.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Recast/Source/RecastContour.cpp b/Recast/Source/RecastContour.cpp index 277ab015..6574c11b 100644 --- a/Recast/Source/RecastContour.cpp +++ b/Recast/Source/RecastContour.cpp @@ -1009,7 +1009,7 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf, if (cset.nconts > 0) { // Calculate winding of all polygons. - rcScopedDelete winding((char*)rcAlloc(sizeof(char)*cset.nconts, RC_ALLOC_TEMP)); + rcScopedDelete winding((signed char*)rcAlloc(sizeof(signed char)*cset.nconts, RC_ALLOC_TEMP)); if (!winding) { ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'hole' (%d).", cset.nconts); From df27e4eb1a4ade9912f8b7d75c25769a3193dbd0 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Mon, 11 May 2020 17:54:25 +0200 Subject: [PATCH 46/99] Fix build -Wno-error does not ignore unknown warnings on old GCC versions causing problems when using it for new warnings. On the other hand, -Wno-X will ignore unknown warnings, so we can use this instead. Fix #403 Fix #413 --- RecastDemo/premake5.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RecastDemo/premake5.lua b/RecastDemo/premake5.lua index 8c218ddb..3f8b75b5 100644 --- a/RecastDemo/premake5.lua +++ b/RecastDemo/premake5.lua @@ -76,7 +76,7 @@ project "Detour" -- linux library cflags and libs configuration { "linux", "gmake" } buildoptions { - "-Wno-error=class-memaccess" + "-Wno-class-memaccess" } @@ -156,7 +156,7 @@ project "RecastDemo" "`pkg-config --cflags gl`", "`pkg-config --cflags glu`", "-Wno-ignored-qualifiers", - "-Wno-error=class-memaccess" + "-Wno-class-memaccess" } linkoptions { From eecb7f45ecb92d79328cf54719266d8739bdbfb4 Mon Sep 17 00:00:00 2001 From: Dig-Doug Date: Wed, 25 Sep 2019 06:20:46 -0700 Subject: [PATCH 47/99] Updates the README instructions for linux The binary has to be run from the bin directory, otherwise the DroidSans font can't be found and shows the error "Could not init GUI renderer.": https://github.com/recastnavigation/recastnavigation/blob/master/RecastDemo/Source/main.cpp#L129 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index afa03689..d9e191fe 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ RecastDemo uses [premake5](http://premake.github.io/) to build platform specific - Install SDL2 and its dependencies according to your distro's guidelines. - run `premake5 gmake` from the `RecastDemo` folder. - `cd Build/gmake` then `make` -- Run `RecastDemo\Bin\RecastDemo` +- `cd RecastDemo/Bin` and then run `./RecastDemo` #### OSX From 9337e124182697de93acb656ef25766486738807 Mon Sep 17 00:00:00 2001 From: Alexey Sokolov Date: Thu, 24 Sep 2020 09:08:03 +0100 Subject: [PATCH 48/99] Several fixes for make install (#437) * Don't install test * Install to the correct directory (e.g. lib64) * Install includes to /usr/include/recastnavigation --- CMakeLists.txt | 2 ++ DebugUtils/CMakeLists.txt | 7 ++++--- Detour/CMakeLists.txt | 7 ++++--- DetourCrowd/CMakeLists.txt | 7 ++++--- DetourTileCache/CMakeLists.txt | 7 ++++--- Recast/CMakeLists.txt | 7 ++++--- Tests/CMakeLists.txt | 2 -- 7 files changed, 22 insertions(+), 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d23859df..54a38d80 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,8 @@ option(RECASTNAVIGATION_TESTS "Build tests" ON) option(RECASTNAVIGATION_EXAMPLES "Build examples" ON) option(RECASTNAVIGATION_STATIC "Build static libraries" ON) +include(GNUInstallDirs) + add_subdirectory(DebugUtils) add_subdirectory(Detour) add_subdirectory(DetourCrowd) diff --git a/DebugUtils/CMakeLists.txt b/DebugUtils/CMakeLists.txt index 8b6a3fcf..a1d9decd 100644 --- a/DebugUtils/CMakeLists.txt +++ b/DebugUtils/CMakeLists.txt @@ -26,10 +26,11 @@ set_target_properties(DebugUtils PROPERTIES ) install(TARGETS DebugUtils - ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT library ) file(GLOB INCLUDES Include/*.h) -install(FILES ${INCLUDES} DESTINATION include) +install(FILES ${INCLUDES} DESTINATION + ${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation) diff --git a/Detour/CMakeLists.txt b/Detour/CMakeLists.txt index de88111d..fc21f441 100644 --- a/Detour/CMakeLists.txt +++ b/Detour/CMakeLists.txt @@ -20,10 +20,11 @@ set_target_properties(Detour PROPERTIES ) install(TARGETS Detour - ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT library ) file(GLOB INCLUDES Include/*.h) -install(FILES ${INCLUDES} DESTINATION include) +install(FILES ${INCLUDES} DESTINATION + ${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation) diff --git a/DetourCrowd/CMakeLists.txt b/DetourCrowd/CMakeLists.txt index 73cdf7ce..18f6f7ef 100644 --- a/DetourCrowd/CMakeLists.txt +++ b/DetourCrowd/CMakeLists.txt @@ -24,10 +24,11 @@ set_target_properties(DetourCrowd PROPERTIES ) install(TARGETS DetourCrowd - ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT library ) file(GLOB INCLUDES Include/*.h) -install(FILES ${INCLUDES} DESTINATION include) +install(FILES ${INCLUDES} DESTINATION + ${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation) diff --git a/DetourTileCache/CMakeLists.txt b/DetourTileCache/CMakeLists.txt index 121b8edc..9a0aa171 100644 --- a/DetourTileCache/CMakeLists.txt +++ b/DetourTileCache/CMakeLists.txt @@ -25,10 +25,11 @@ set_target_properties(DetourTileCache PROPERTIES install(TARGETS DetourTileCache - ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT library ) file(GLOB INCLUDES Include/*.h) -install(FILES ${INCLUDES} DESTINATION include) +install(FILES ${INCLUDES} DESTINATION + ${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation) diff --git a/Recast/CMakeLists.txt b/Recast/CMakeLists.txt index 5e843762..f8402e37 100644 --- a/Recast/CMakeLists.txt +++ b/Recast/CMakeLists.txt @@ -20,10 +20,11 @@ set_target_properties(Recast PROPERTIES ) install(TARGETS Recast - ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT library ) file(GLOB INCLUDES Include/*.h) -install(FILES ${INCLUDES} DESTINATION include) +install(FILES ${INCLUDES} DESTINATION + ${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation) diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 7c174696..a98ccbf1 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -8,5 +8,3 @@ add_executable(Tests ${TESTS_SOURCES}) add_dependencies(Tests Recast Detour) target_link_libraries(Tests Recast Detour) add_test(Tests Tests) - -install(TARGETS Tests RUNTIME DESTINATION bin) From e679507845df6d2d008829b5bbee008f4b3f60af Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Fri, 30 Oct 2020 10:41:54 +0100 Subject: [PATCH 49/99] Improve debug builds for MSVC (#443) * Add -d suffix for debug libraries * Export PDB files for each Debug library --- DebugUtils/CMakeLists.txt | 4 ++++ Detour/CMakeLists.txt | 4 ++++ DetourCrowd/CMakeLists.txt | 4 ++++ DetourTileCache/CMakeLists.txt | 4 ++++ Recast/CMakeLists.txt | 4 ++++ 5 files changed, 20 insertions(+) diff --git a/DebugUtils/CMakeLists.txt b/DebugUtils/CMakeLists.txt index a1d9decd..30fe6261 100644 --- a/DebugUtils/CMakeLists.txt +++ b/DebugUtils/CMakeLists.txt @@ -7,6 +7,7 @@ else() endif() add_library(RecastNavigation::DebugUtils ALIAS DebugUtils) +set_target_properties(DebugUtils PROPERTIES DEBUG_POSTFIX -d) set(DebugUtils_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Include") @@ -23,6 +24,8 @@ target_link_libraries(DebugUtils set_target_properties(DebugUtils PROPERTIES SOVERSION ${SOVERSION} VERSION ${VERSION} + COMPILE_PDB_OUTPUT_DIRECTORY . + COMPILE_PDB_NAME "DebugUtils-d" ) install(TARGETS DebugUtils @@ -34,3 +37,4 @@ install(TARGETS DebugUtils file(GLOB INCLUDES Include/*.h) install(FILES ${INCLUDES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation) +install(FILES "$/DebugUtils-d.pdb" CONFIGURATIONS "Debug" DESTINATION "lib") diff --git a/Detour/CMakeLists.txt b/Detour/CMakeLists.txt index fc21f441..74f9fc06 100644 --- a/Detour/CMakeLists.txt +++ b/Detour/CMakeLists.txt @@ -7,6 +7,7 @@ else() endif() add_library(RecastNavigation::Detour ALIAS Detour) +set_target_properties(Detour PROPERTIES DEBUG_POSTFIX -d) set(Detour_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Include") @@ -17,6 +18,8 @@ target_include_directories(Detour PUBLIC set_target_properties(Detour PROPERTIES SOVERSION ${SOVERSION} VERSION ${VERSION} + COMPILE_PDB_OUTPUT_DIRECTORY . + COMPILE_PDB_NAME "Detour-d" ) install(TARGETS Detour @@ -28,3 +31,4 @@ install(TARGETS Detour file(GLOB INCLUDES Include/*.h) install(FILES ${INCLUDES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation) +install(FILES "$/Detour-d.pdb" CONFIGURATIONS "Debug" DESTINATION "lib") diff --git a/DetourCrowd/CMakeLists.txt b/DetourCrowd/CMakeLists.txt index 18f6f7ef..4ea8d8a4 100644 --- a/DetourCrowd/CMakeLists.txt +++ b/DetourCrowd/CMakeLists.txt @@ -7,6 +7,7 @@ else () endif () add_library(RecastNavigation::DetourCrowd ALIAS DetourCrowd) +set_target_properties(DetourCrowd PROPERTIES DEBUG_POSTFIX -d) set(DetourCrowd_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Include") @@ -21,6 +22,8 @@ target_link_libraries(DetourCrowd set_target_properties(DetourCrowd PROPERTIES SOVERSION ${SOVERSION} VERSION ${VERSION} + COMPILE_PDB_OUTPUT_DIRECTORY . + COMPILE_PDB_NAME "DetourCrowd-d" ) install(TARGETS DetourCrowd @@ -32,3 +35,4 @@ install(TARGETS DetourCrowd file(GLOB INCLUDES Include/*.h) install(FILES ${INCLUDES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation) +install(FILES "$/DetourCrowd-d.pdb" CONFIGURATIONS "Debug" DESTINATION "lib") diff --git a/DetourTileCache/CMakeLists.txt b/DetourTileCache/CMakeLists.txt index 9a0aa171..7a8936b1 100644 --- a/DetourTileCache/CMakeLists.txt +++ b/DetourTileCache/CMakeLists.txt @@ -7,6 +7,7 @@ else () endif () add_library(RecastNavigation::DetourTileCache ALIAS DetourTileCache) +set_target_properties(DetourTileCache PROPERTIES DEBUG_POSTFIX -d) set(DetourTileCache_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Include") @@ -21,6 +22,8 @@ target_link_libraries(DetourTileCache set_target_properties(DetourTileCache PROPERTIES SOVERSION ${SOVERSION} VERSION ${VERSION} + COMPILE_PDB_OUTPUT_DIRECTORY . + COMPILE_PDB_NAME "DetourTileCache-d" ) @@ -33,3 +36,4 @@ install(TARGETS DetourTileCache file(GLOB INCLUDES Include/*.h) install(FILES ${INCLUDES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation) +install(FILES "$/DetourTileCache-d.pdb" CONFIGURATIONS "Debug" DESTINATION "lib") diff --git a/Recast/CMakeLists.txt b/Recast/CMakeLists.txt index f8402e37..60a20364 100644 --- a/Recast/CMakeLists.txt +++ b/Recast/CMakeLists.txt @@ -7,6 +7,7 @@ else () endif () add_library(RecastNavigation::Recast ALIAS Recast) +set_target_properties(Recast PROPERTIES DEBUG_POSTFIX -d) set(Recast_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Include") @@ -17,6 +18,8 @@ target_include_directories(Recast PUBLIC set_target_properties(Recast PROPERTIES SOVERSION ${SOVERSION} VERSION ${VERSION} + COMPILE_PDB_OUTPUT_DIRECTORY . + COMPILE_PDB_NAME "Recast-d" ) install(TARGETS Recast @@ -28,3 +31,4 @@ install(TARGETS Recast file(GLOB INCLUDES Include/*.h) install(FILES ${INCLUDES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation) +install(FILES "$/Recast-d.pdb" CONFIGURATIONS "Debug" DESTINATION "lib") From 8db2f62d7a67a2bacf8a2e966e4710a023fcdf77 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Fri, 30 Oct 2020 10:40:09 +0100 Subject: [PATCH 50/99] Fix compiler error due to memset on type with non-trivial constructors Fix #446 --- RecastDemo/Include/TestCase.h | 4 ++++ RecastDemo/Source/TestCase.cpp | 6 ++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/RecastDemo/Include/TestCase.h b/RecastDemo/Include/TestCase.h index f61cc856..49dcb234 100644 --- a/RecastDemo/Include/TestCase.h +++ b/RecastDemo/Include/TestCase.h @@ -34,6 +34,10 @@ class TestCase { Test() : type(), + spos(), + epos(), + nspos(), + nepos(), radius(0), includeFlags(0), excludeFlags(0), diff --git a/RecastDemo/Source/TestCase.cpp b/RecastDemo/Source/TestCase.cpp index cf1214a7..5ce42623 100644 --- a/RecastDemo/Source/TestCase.cpp +++ b/RecastDemo/Source/TestCase.cpp @@ -154,8 +154,7 @@ bool TestCase::load(const std::string& filePath) else if (row[0] == 'p' && row[1] == 'f') { // Pathfind test. - Test* test = new Test; - memset(test, 0, sizeof(Test)); + Test* test = new Test(); test->type = TEST_PATHFIND; test->expand = false; test->next = m_tests; @@ -168,8 +167,7 @@ bool TestCase::load(const std::string& filePath) else if (row[0] == 'r' && row[1] == 'c') { // Pathfind test. - Test* test = new Test; - memset(test, 0, sizeof(Test)); + Test* test = new Test(); test->type = TEST_RAYCAST; test->expand = false; test->next = m_tests; From 65b314a44e92d5e07d943e7523455ad4d391dfaa Mon Sep 17 00:00:00 2001 From: Yoann Potinet Date: Fri, 7 Aug 2020 13:06:10 -0400 Subject: [PATCH 51/99] Fix shared library build --- CMakeLists.txt | 5 ++++- DebugUtils/CMakeLists.txt | 8 ++------ Detour/CMakeLists.txt | 8 ++------ DetourCrowd/CMakeLists.txt | 8 ++------ DetourTileCache/CMakeLists.txt | 8 ++------ Recast/CMakeLists.txt | 8 ++------ 6 files changed, 14 insertions(+), 31 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 54a38d80..b8af6d54 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,7 +9,10 @@ SET(VERSION 1.0.0) option(RECASTNAVIGATION_DEMO "Build demo" ON) option(RECASTNAVIGATION_TESTS "Build tests" ON) option(RECASTNAVIGATION_EXAMPLES "Build examples" ON) -option(RECASTNAVIGATION_STATIC "Build static libraries" ON) + +if(MSVC AND BUILD_SHARED_LIBS) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) +endif() include(GNUInstallDirs) diff --git a/DebugUtils/CMakeLists.txt b/DebugUtils/CMakeLists.txt index 30fe6261..21d8f8f9 100644 --- a/DebugUtils/CMakeLists.txt +++ b/DebugUtils/CMakeLists.txt @@ -1,10 +1,5 @@ file(GLOB SOURCES Source/*.cpp) - -if (RECASTNAVIGATION_STATIC) - add_library(DebugUtils STATIC ${SOURCES}) -else() - add_library(DebugUtils SHARED ${SOURCES}) -endif() +add_library(DebugUtils ${SOURCES}) add_library(RecastNavigation::DebugUtils ALIAS DebugUtils) set_target_properties(DebugUtils PROPERTIES DEBUG_POSTFIX -d) @@ -29,6 +24,7 @@ set_target_properties(DebugUtils PROPERTIES ) install(TARGETS DebugUtils + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT library diff --git a/Detour/CMakeLists.txt b/Detour/CMakeLists.txt index 74f9fc06..5cb47ec0 100644 --- a/Detour/CMakeLists.txt +++ b/Detour/CMakeLists.txt @@ -1,10 +1,5 @@ file(GLOB SOURCES Source/*.cpp) - -if(RECASTNAVIGATION_STATIC) - add_library(Detour STATIC ${SOURCES}) -else() - add_library(Detour SHARED ${SOURCES}) -endif() +add_library(Detour ${SOURCES}) add_library(RecastNavigation::Detour ALIAS Detour) set_target_properties(Detour PROPERTIES DEBUG_POSTFIX -d) @@ -23,6 +18,7 @@ set_target_properties(Detour PROPERTIES ) install(TARGETS Detour + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT library diff --git a/DetourCrowd/CMakeLists.txt b/DetourCrowd/CMakeLists.txt index 4ea8d8a4..d0e186be 100644 --- a/DetourCrowd/CMakeLists.txt +++ b/DetourCrowd/CMakeLists.txt @@ -1,10 +1,5 @@ file(GLOB SOURCES Source/*.cpp) - -if (RECASTNAVIGATION_STATIC) - add_library(DetourCrowd STATIC ${SOURCES}) -else () - add_library(DetourCrowd SHARED ${SOURCES}) -endif () +add_library(DetourCrowd ${SOURCES}) add_library(RecastNavigation::DetourCrowd ALIAS DetourCrowd) set_target_properties(DetourCrowd PROPERTIES DEBUG_POSTFIX -d) @@ -27,6 +22,7 @@ set_target_properties(DetourCrowd PROPERTIES ) install(TARGETS DetourCrowd + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT library diff --git a/DetourTileCache/CMakeLists.txt b/DetourTileCache/CMakeLists.txt index 7a8936b1..3703ebb9 100644 --- a/DetourTileCache/CMakeLists.txt +++ b/DetourTileCache/CMakeLists.txt @@ -1,10 +1,5 @@ file(GLOB SOURCES Source/*.cpp) - -if (RECASTNAVIGATION_STATIC) - add_library(DetourTileCache STATIC ${SOURCES}) -else () - add_library(DetourTileCache SHARED ${SOURCES}) -endif () +add_library(DetourTileCache ${SOURCES}) add_library(RecastNavigation::DetourTileCache ALIAS DetourTileCache) set_target_properties(DetourTileCache PROPERTIES DEBUG_POSTFIX -d) @@ -28,6 +23,7 @@ set_target_properties(DetourTileCache PROPERTIES install(TARGETS DetourTileCache + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT library diff --git a/Recast/CMakeLists.txt b/Recast/CMakeLists.txt index 60a20364..36065446 100644 --- a/Recast/CMakeLists.txt +++ b/Recast/CMakeLists.txt @@ -1,10 +1,5 @@ file(GLOB SOURCES Source/*.cpp) - -if (RECASTNAVIGATION_STATIC) - add_library(Recast STATIC ${SOURCES}) -else () - add_library(Recast SHARED ${SOURCES}) -endif () +add_library(Recast ${SOURCES}) add_library(RecastNavigation::Recast ALIAS Recast) set_target_properties(Recast PROPERTIES DEBUG_POSTFIX -d) @@ -23,6 +18,7 @@ set_target_properties(Recast PROPERTIES ) install(TARGETS Recast + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT library From 24230e8e8dde374507174cefa877b218b03447e5 Mon Sep 17 00:00:00 2001 From: Kyeongho Park Date: Tue, 17 Nov 2020 19:46:52 +0900 Subject: [PATCH 52/99] Bugfix/chunkytrimesh subdivide maxnodes (#451) * minor redundant: dir * bugfix: ChunkyTriMesh nodes array: out of bounds --- RecastDemo/Source/ChunkyTriMesh.cpp | 2 +- RecastDemo/Source/InputGeom.cpp | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/RecastDemo/Source/ChunkyTriMesh.cpp b/RecastDemo/Source/ChunkyTriMesh.cpp index 2991d4ff..242bf285 100644 --- a/RecastDemo/Source/ChunkyTriMesh.cpp +++ b/RecastDemo/Source/ChunkyTriMesh.cpp @@ -83,7 +83,7 @@ static void subdivide(BoundsItem* items, int nitems, int imin, int imax, int tri int inum = imax - imin; int icur = curNode; - if (curNode > maxNodes) + if (curNode >= maxNodes) return; rcChunkyTriMeshNode& node = nodes[curNode++]; diff --git a/RecastDemo/Source/InputGeom.cpp b/RecastDemo/Source/InputGeom.cpp index a5f9a0d1..fc6af820 100644 --- a/RecastDemo/Source/InputGeom.cpp +++ b/RecastDemo/Source/InputGeom.cpp @@ -424,9 +424,6 @@ static bool isectSegAABB(const float* sp, const float* sq, bool InputGeom::raycastMesh(float* src, float* dst, float& tmin) { - float dir[3]; - rcVsub(dir, dst, src); - // Prune hit ray. float btmin, btmax; if (!isectSegAABB(src, dst, m_meshBMin, m_meshBMax, btmin, btmax)) From 6624e7aef5e15df11cb2f5673574df8e4c96af6a Mon Sep 17 00:00:00 2001 From: ZhihuiLiu <1085438567@qq.com> Date: Fri, 13 Nov 2020 12:08:02 +0800 Subject: [PATCH 53/99] fix an index error --- DetourCrowd/Source/DetourCrowd.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/DetourCrowd/Source/DetourCrowd.cpp b/DetourCrowd/Source/DetourCrowd.cpp index 1e76e40c..3f0311f7 100644 --- a/DetourCrowd/Source/DetourCrowd.cpp +++ b/DetourCrowd/Source/DetourCrowd.cpp @@ -1409,12 +1409,14 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug) } // Update agents using off-mesh connection. - for (int i = 0; i < m_maxAgents; ++i) + for (int i = 0; i < nagents; ++i) { - dtCrowdAgentAnimation* anim = &m_agentAnims[i]; + dtCrowdAgent* ag = agents[i]; + const int idx = (int)(ag - m_agents); + dtCrowdAgentAnimation* anim = &m_agentAnims[idx]; if (!anim->active) continue; - dtCrowdAgent* ag = agents[i]; + anim->t += dt; if (anim->t > anim->tmax) From 9dc88fcab98b2213e88f7bbe5a3354ffc70c59bc Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Thu, 19 Nov 2020 10:13:30 +0100 Subject: [PATCH 54/99] Validate constraint: #polygons per tile (#449) * Validate constraint: #polygons per tile When adding a tile to a NavMesh, ensure that the number of polygons in that tile fit in the poly ID address space. * Improve dtNavMeshParams field documentation --- Detour/Include/DetourNavMesh.h | 4 ++-- Detour/Source/DetourNavMesh.cpp | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Detour/Include/DetourNavMesh.h b/Detour/Include/DetourNavMesh.h index 98293c49..9ac1dc8d 100644 --- a/Detour/Include/DetourNavMesh.h +++ b/Detour/Include/DetourNavMesh.h @@ -329,8 +329,8 @@ struct dtNavMeshParams float orig[3]; ///< The world space origin of the navigation mesh's tile space. [(x, y, z)] float tileWidth; ///< The width of each tile. (Along the x-axis.) float tileHeight; ///< The height of each tile. (Along the z-axis.) - int maxTiles; ///< The maximum number of tiles the navigation mesh can contain. - int maxPolys; ///< The maximum number of polygons each tile can contain. + int maxTiles; ///< The maximum number of tiles the navigation mesh can contain. This and maxPolys are used to calculate how many bits are needed to identify tiles and polygons uniquely. + int maxPolys; ///< The maximum number of polygons each tile can contain. This and maxTiles are used to calculate how many bits are needed to identify tiles and polygons uniquely. }; /// A navigation mesh based on tiles of convex polygons. diff --git a/Detour/Source/DetourNavMesh.cpp b/Detour/Source/DetourNavMesh.cpp index a655d1d8..b119cd54 100644 --- a/Detour/Source/DetourNavMesh.cpp +++ b/Detour/Source/DetourNavMesh.cpp @@ -914,6 +914,13 @@ dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags, return DT_FAILURE | DT_WRONG_MAGIC; if (header->version != DT_NAVMESH_VERSION) return DT_FAILURE | DT_WRONG_VERSION; + +#ifndef DT_POLYREF64 + // Do not allow adding more polygons than specified in the NavMesh's maxPolys constraint. + // Otherwise, the poly ID cannot be represented with the given number of bits. + if (m_polyBits < dtIlog2(dtNextPow2((unsigned int)header->polyCount))) + return DT_FAILURE | DT_INVALID_PARAM; +#endif // Make sure the location is free. if (getTileAt(header->x, header->y, header->layer)) From ec05c9e17c31bb5528ca0daf06974ed5fb9fd18c Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 19 Nov 2020 14:23:59 +0100 Subject: [PATCH 55/99] Update build instructions for Windows --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d9e191fe..3e1d3ada 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ RecastDemo uses [premake5](http://premake.github.io/) to build platform specific #### Windows - Grab the latest SDL2 development library release from [here](https://www.libsdl.org/download-2.0.php) and unzip it `RecastDemo\Contrib`. Rename the SDL folder such that the path `RecastDemo\Contrib\SDL\lib\x86` is valid. -- Run `"premake5" vs2015` from the `RecastDemo` folder +- Run `"premake5" vs2019` from the `RecastDemo` folder - Open the solution, build, and run. ### Running Unit tests From c32297c2f9532a55a11c00f22ec763d771d3dae5 Mon Sep 17 00:00:00 2001 From: Rubaka Date: Thu, 19 Nov 2020 18:27:26 +0300 Subject: [PATCH 56/99] Fixed run RecastDemo on MVS --- RecastDemo/CMakeLists.txt | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/RecastDemo/CMakeLists.txt b/RecastDemo/CMakeLists.txt index a6a1a733..b379b27b 100644 --- a/RecastDemo/CMakeLists.txt +++ b/RecastDemo/CMakeLists.txt @@ -18,6 +18,7 @@ include_directories(../DetourCrowd/Include) include_directories(../DetourTileCache/Include) include_directories(../Recast/Include) include_directories(Include) +include_directories(Contrib/SDL/include) if (WIN32) add_executable(RecastDemo WIN32 ${SOURCES}) @@ -27,14 +28,31 @@ else() add_executable(RecastDemo ${SOURCES}) endif() -file(COPY Bin/Meshes DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) -file(COPY Bin/TestCases DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) -file(COPY Bin/DroidSans.ttf DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) - -if (WIN32) - file(COPY "${SDL2_RUNTIME_LIBRARY}" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) +if( WIN32 ) + if ( "${CMAKE_MAKE_PROGRAM}" MATCHES "MSBuild" ) + add_custom_command(TARGET RecastDemo + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy "${SDL2_RUNTIME_LIBRARY}" ${CMAKE_BINARY_DIR}/RecastDemo/$(ConfigurationName)/ + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/Bin/Meshes ${CMAKE_BINARY_DIR}/RecastDemo/$(ConfigurationName)/Meshes + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/Bin/TestCases ${CMAKE_BINARY_DIR}/RecastDemo/$(ConfigurationName)/TestCases + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/Bin/DroidSans.ttf ${CMAKE_BINARY_DIR}/RecastDemo/$(ConfigurationName)/ + ) + elseif ( MINGW ) + add_custom_command(TARGET RecastDemo + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy "${SDL2_RUNTIME_LIBRARY}" ${CMAKE_BINARY_DIR}/RecastDemo/ + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/Bin/Meshes ${CMAKE_BINARY_DIR}/RecastDemo/Meshes + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/Bin/TestCases ${CMAKE_BINARY_DIR}/RecastDemo/TestCases + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/Bin/DroidSans.ttf ${CMAKE_BINARY_DIR}/RecastDemo/ + ) + endif() +else() + file(COPY Bin/Meshes DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) + file(COPY Bin/TestCases DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) + file(COPY Bin/DroidSans.ttf DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) endif() + add_dependencies(RecastDemo DebugUtils Detour DetourCrowd DetourTileCache Recast) target_link_libraries(RecastDemo ${OPENGL_LIBRARIES} SDL2::SDL2main DebugUtils Detour DetourCrowd DetourTileCache Recast) From cf4f3e15a85775db643381f32623cfde40720db9 Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Sat, 23 Jan 2021 13:25:41 +0100 Subject: [PATCH 57/99] Poly query API: expose distance and isOverPoly (#448) * Add dtNavMeshQuery.findNearestPoly() overload with parameters: distance, isOverPoly * dtNavMeshQuery::findNearestPoly(): improve documentation, avoid code duplication * Make code C++98-compatible * Remove distance parameter from dtNavMeshQuery::queryPolygons() --- Detour/Include/DetourNavMeshQuery.h | 22 +++++++++++++++++++--- Detour/Source/DetourNavMeshQuery.cpp | 18 +++++++++++++++++- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/Detour/Include/DetourNavMeshQuery.h b/Detour/Include/DetourNavMeshQuery.h index 0b40371b..08b70a8f 100644 --- a/Detour/Include/DetourNavMeshQuery.h +++ b/Detour/Include/DetourNavMeshQuery.h @@ -313,15 +313,31 @@ public: ///@{ /// Finds the polygon nearest to the specified center point. + /// [opt] means the specified parameter can be a null pointer, in that case the output parameter will not be set. + /// /// @param[in] center The center of the search box. [(x, y, z)] - /// @param[in] halfExtents The search distance along each axis. [(x, y, z)] + /// @param[in] halfExtents The search distance along each axis. [(x, y, z)] /// @param[in] filter The polygon filter to apply to the query. - /// @param[out] nearestRef The reference id of the nearest polygon. - /// @param[out] nearestPt The nearest point on the polygon. [opt] [(x, y, z)] + /// @param[out] nearestRef The reference id of the nearest polygon. Will be set to 0 if no polygon is found. + /// @param[out] nearestPt The nearest point on the polygon. Unchanged if no polygon is found. [opt] [(x, y, z)] /// @returns The status flags for the query. dtStatus findNearestPoly(const float* center, const float* halfExtents, const dtQueryFilter* filter, dtPolyRef* nearestRef, float* nearestPt) const; + + /// Finds the polygon nearest to the specified center point. + /// [opt] means the specified parameter can be a null pointer, in that case the output parameter will not be set. + /// + /// @param[in] center The center of the search box. [(x, y, z)] + /// @param[in] halfExtents The search distance along each axis. [(x, y, z)] + /// @param[in] filter The polygon filter to apply to the query. + /// @param[out] nearestRef The reference id of the nearest polygon. Will be set to 0 if no polygon is found. + /// @param[out] nearestPt The nearest point on the polygon. Unchanged if no polygon is found. [opt] [(x, y, z)] + /// @param[out] isOverPoly Set to true if the point's X/Z coordinate lies inside the polygon, false otherwise. Unchanged if no polygon is found. [opt] + /// @returns The status flags for the query. + dtStatus findNearestPoly(const float* center, const float* halfExtents, + const dtQueryFilter* filter, + dtPolyRef* nearestRef, float* nearestPt, bool* isOverPoly) const; /// Finds polygons that overlap the search box. /// @param[in] center The center of the search box. [(x, y, z)] diff --git a/Detour/Source/DetourNavMeshQuery.cpp b/Detour/Source/DetourNavMeshQuery.cpp index 839ee1e8..8ea09c82 100644 --- a/Detour/Source/DetourNavMeshQuery.cpp +++ b/Detour/Source/DetourNavMeshQuery.cpp @@ -630,15 +630,17 @@ class dtFindNearestPolyQuery : public dtPolyQuery float m_nearestDistanceSqr; dtPolyRef m_nearestRef; float m_nearestPoint[3]; + bool m_overPoly; public: dtFindNearestPolyQuery(const dtNavMeshQuery* query, const float* center) - : m_query(query), m_center(center), m_nearestDistanceSqr(FLT_MAX), m_nearestRef(0), m_nearestPoint() + : m_query(query), m_center(center), m_nearestDistanceSqr(FLT_MAX), m_nearestRef(0), m_nearestPoint(), m_overPoly(false) { } dtPolyRef nearestRef() const { return m_nearestRef; } const float* nearestPoint() const { return m_nearestPoint; } + bool isOverPoly() const { return m_overPoly; } void process(const dtMeshTile* tile, dtPoly** polys, dtPolyRef* refs, int count) { @@ -672,6 +674,7 @@ public: m_nearestDistanceSqr = d; m_nearestRef = ref; + m_overPoly = posOverPoly; } } } @@ -686,6 +689,15 @@ public: dtStatus dtNavMeshQuery::findNearestPoly(const float* center, const float* halfExtents, const dtQueryFilter* filter, dtPolyRef* nearestRef, float* nearestPt) const +{ + return findNearestPoly(center, halfExtents, filter, nearestRef, nearestPt, NULL); +} + +// If center and nearestPt point to an equal position, isOverPoly will be true; +// however there's also a special case of climb height inside the polygon (see dtFindNearestPolyQuery) +dtStatus dtNavMeshQuery::findNearestPoly(const float* center, const float* halfExtents, + const dtQueryFilter* filter, + dtPolyRef* nearestRef, float* nearestPt, bool* isOverPoly) const { dtAssert(m_nav); @@ -704,7 +716,11 @@ dtStatus dtNavMeshQuery::findNearestPoly(const float* center, const float* halfE // Only override nearestPt if we actually found a poly so the nearest point // is valid. if (nearestPt && *nearestRef) + { dtVcopy(nearestPt, query.nearestPoint()); + if (isOverPoly) + *isOverPoly = query.isOverPoly(); + } return DT_SUCCESS; } From e75adf86f91eb3082220085e42dda62679f9a3ea Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Mon, 15 Feb 2021 19:27:21 +0800 Subject: [PATCH 58/99] Avoid reallocs of temp vectors (#467) * Use clear() instead of resize(0) for temp vectors Nicer style * rcVectorBase::resize: Avoid realloc on grow When capacity is sufficient, do not realloc the whole buffer * rcVector: Grow exponentially on resize --- Recast/Include/RecastAlloc.h | 47 +++++++++++++++++++++--------- Recast/Source/RecastContour.cpp | 4 +-- Recast/Source/RecastMeshDetail.cpp | 16 +++++----- Recast/Source/RecastRegion.cpp | 10 +++---- 4 files changed, 49 insertions(+), 28 deletions(-) diff --git a/Recast/Include/RecastAlloc.h b/Recast/Include/RecastAlloc.h index e436af9a..20e8dd1b 100644 --- a/Recast/Include/RecastAlloc.h +++ b/Recast/Include/RecastAlloc.h @@ -106,6 +106,8 @@ class rcVectorBase { // Creates an array of the given size, copies all of this vector's data into it, and returns it. T* allocate_and_copy(rcSizeType size); void resize_impl(rcSizeType size, const T* value); + // Requires: min_capacity > m_cap. + rcSizeType get_new_capacity(rcSizeType min_capacity); public: typedef rcSizeType size_type; typedef T value_type; @@ -196,8 +198,7 @@ void rcVectorBase::push_back(const T& value) { return; } - rcAssert(RC_SIZE_MAX / 2 >= m_size); - rcSizeType new_cap = m_size ? 2*m_size : 1; + const rcSizeType new_cap = get_new_capacity(m_cap + 1); T* data = allocate_and_copy(new_cap); // construct between allocate and destroy+free in case value is // in this vector. @@ -208,25 +209,44 @@ void rcVectorBase::push_back(const T& value) { rcFree(m_data); m_data = data; } + +template +rcSizeType rcVectorBase::get_new_capacity(rcSizeType min_capacity) { + rcAssert(min_capacity <= RC_SIZE_MAX); + if (rcUnlikely(m_cap >= RC_SIZE_MAX / 2)) + return RC_SIZE_MAX; + return 2 * m_cap > min_capacity ? 2 * m_cap : min_capacity; +} + template void rcVectorBase::resize_impl(rcSizeType size, const T* value) { if (size < m_size) { destroy_range(size, m_size); m_size = size; } else if (size > m_size) { - T* new_data = allocate_and_copy(size); - // We defer deconstructing/freeing old data until after constructing - // new elements in case "value" is there. - if (value) { - construct_range(new_data + m_size, new_data + size, *value); + if (size <= m_cap) { + if (value) { + construct_range(m_data + m_size, m_data + size, *value); + } else { + construct_range(m_data + m_size, m_data + size); + } + m_size = size; } else { - construct_range(new_data + m_size, new_data + size); + const rcSizeType new_cap = get_new_capacity(size); + T* new_data = allocate_and_copy(new_cap); + // We defer deconstructing/freeing old data until after constructing + // new elements in case "value" is there. + if (value) { + construct_range(new_data + m_size, new_data + size, *value); + } else { + construct_range(new_data + m_size, new_data + size); + } + destroy_range(0, m_size); + rcFree(m_data); + m_data = new_data; + m_cap = new_cap; + m_size = size; } - destroy_range(0, m_size); - rcFree(m_data); - m_data = new_data; - m_cap = size; - m_size = size; } } template @@ -303,6 +323,7 @@ public: rcIntArray(int n) : m_impl(n, 0) {} void push(int item) { m_impl.push_back(item); } void resize(int size) { m_impl.resize(size); } + void clear() { m_impl.clear(); } int pop() { int v = m_impl.back(); diff --git a/Recast/Source/RecastContour.cpp b/Recast/Source/RecastContour.cpp index 6574c11b..1293d4fb 100644 --- a/Recast/Source/RecastContour.cpp +++ b/Recast/Source/RecastContour.cpp @@ -921,8 +921,8 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf, continue; const unsigned char area = chf.areas[i]; - verts.resize(0); - simplified.resize(0); + verts.clear(); + simplified.clear(); ctx->startTimer(RC_TIMER_BUILD_CONTOURS_TRACE); walkContour(x, y, i, chf, flags, verts); diff --git a/Recast/Source/RecastMeshDetail.cpp b/Recast/Source/RecastMeshDetail.cpp index 9a423cab..1999200c 100644 --- a/Recast/Source/RecastMeshDetail.cpp +++ b/Recast/Source/RecastMeshDetail.cpp @@ -653,8 +653,8 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin, for (int i = 0; i < nin; ++i) rcVcopy(&verts[i*3], &in[i*3]); - edges.resize(0); - tris.resize(0); + edges.clear(); + tris.clear(); const float cs = chf.cs; const float ics = 1.0f/cs; @@ -803,7 +803,7 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin, int x1 = (int)ceilf(bmax[0]/sampleDist); int z0 = (int)floorf(bmin[2]/sampleDist); int z1 = (int)ceilf(bmax[2]/sampleDist); - samples.resize(0); + samples.clear(); for (int z = z0; z < z1; ++z) { for (int x = x0; x < x1; ++x) @@ -864,8 +864,8 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin, // Create new triangulation. // TODO: Incremental add instead of full rebuild. - edges.resize(0); - tris.resize(0); + edges.clear(); + tris.clear(); delaunayHull(ctx, nverts, verts, nhull, hull, tris, edges); } } @@ -935,7 +935,7 @@ static void seedArrayWithPolyCenter(rcContext* ctx, const rcCompactHeightfield& pcy /= npoly; // Use seeds array as a stack for DFS - array.resize(0); + array.clear(); array.push(startCellX); array.push(startCellY); array.push(startSpanIndex); @@ -1001,7 +1001,7 @@ static void seedArrayWithPolyCenter(rcContext* ctx, const rcCompactHeightfield& rcSwap(dirs[directDir], dirs[3]); } - array.resize(0); + array.clear(); // getHeightData seeds are given in coordinates with borders array.push(cx+bs); array.push(cy+bs); @@ -1030,7 +1030,7 @@ static void getHeightData(rcContext* ctx, const rcCompactHeightfield& chf, // Note: Reads to the compact heightfield are offset by border size (bs) // since border size offset is already removed from the polymesh vertices. - queue.resize(0); + queue.clear(); // Set all heights to RC_UNSET_HEIGHT. memset(hp.data, 0xff, sizeof(unsigned short)*hp.width*hp.height); diff --git a/Recast/Source/RecastRegion.cpp b/Recast/Source/RecastRegion.cpp index e1fc0ee7..48318688 100644 --- a/Recast/Source/RecastRegion.cpp +++ b/Recast/Source/RecastRegion.cpp @@ -650,7 +650,7 @@ static bool mergeRegions(rcRegion& rega, rcRegion& regb) return false; // Merge neighbours. - rega.connections.resize(0); + rega.connections.clear(); for (int i = 0, ni = acon.size(); i < ni-1; ++i) rega.connections.push(acon[(insa+1+i) % ni]); @@ -876,8 +876,8 @@ static bool mergeAndFilterRegions(rcContext* ctx, int minRegionArea, int mergeRe // Also keep track of the regions connects to a tile border. bool connectsToBorder = false; int spanCount = 0; - stack.resize(0); - trace.resize(0); + stack.clear(); + trace.clear(); reg.visited = true; stack.push(i); @@ -1068,7 +1068,7 @@ static bool mergeAndFilterLayerRegions(rcContext* ctx, int minRegionArea, { const rcCompactCell& c = chf.cells[x+y*w]; - lregs.resize(0); + lregs.clear(); for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) { @@ -1139,7 +1139,7 @@ static bool mergeAndFilterLayerRegions(rcContext* ctx, int minRegionArea, // Start search. root.id = layerId; - stack.resize(0); + stack.clear(); stack.push(i); while (stack.size() > 0) From 1f25f9cc29ae56fa23531af01abef375a186aa2d Mon Sep 17 00:00:00 2001 From: elsid Date: Wed, 3 Mar 2021 17:33:44 +0100 Subject: [PATCH 59/99] Install .pdb files only with MSVC --- DebugUtils/CMakeLists.txt | 4 +++- Detour/CMakeLists.txt | 4 +++- DetourCrowd/CMakeLists.txt | 4 +++- DetourTileCache/CMakeLists.txt | 4 +++- Recast/CMakeLists.txt | 4 +++- 5 files changed, 15 insertions(+), 5 deletions(-) diff --git a/DebugUtils/CMakeLists.txt b/DebugUtils/CMakeLists.txt index 21d8f8f9..14b36a9a 100644 --- a/DebugUtils/CMakeLists.txt +++ b/DebugUtils/CMakeLists.txt @@ -33,4 +33,6 @@ install(TARGETS DebugUtils file(GLOB INCLUDES Include/*.h) install(FILES ${INCLUDES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation) -install(FILES "$/DebugUtils-d.pdb" CONFIGURATIONS "Debug" DESTINATION "lib") +if(MSVC) + install(FILES "$/DebugUtils-d.pdb" CONFIGURATIONS "Debug" DESTINATION "lib") +endif() diff --git a/Detour/CMakeLists.txt b/Detour/CMakeLists.txt index 5cb47ec0..da1a195c 100644 --- a/Detour/CMakeLists.txt +++ b/Detour/CMakeLists.txt @@ -27,4 +27,6 @@ install(TARGETS Detour file(GLOB INCLUDES Include/*.h) install(FILES ${INCLUDES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation) -install(FILES "$/Detour-d.pdb" CONFIGURATIONS "Debug" DESTINATION "lib") +if(MSVC) + install(FILES "$/Detour-d.pdb" CONFIGURATIONS "Debug" DESTINATION "lib") +endif() diff --git a/DetourCrowd/CMakeLists.txt b/DetourCrowd/CMakeLists.txt index d0e186be..60b8e1a7 100644 --- a/DetourCrowd/CMakeLists.txt +++ b/DetourCrowd/CMakeLists.txt @@ -31,4 +31,6 @@ install(TARGETS DetourCrowd file(GLOB INCLUDES Include/*.h) install(FILES ${INCLUDES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation) -install(FILES "$/DetourCrowd-d.pdb" CONFIGURATIONS "Debug" DESTINATION "lib") +if(MSVC) + install(FILES "$/DetourCrowd-d.pdb" CONFIGURATIONS "Debug" DESTINATION "lib") +endif() diff --git a/DetourTileCache/CMakeLists.txt b/DetourTileCache/CMakeLists.txt index 3703ebb9..cf2d437a 100644 --- a/DetourTileCache/CMakeLists.txt +++ b/DetourTileCache/CMakeLists.txt @@ -32,4 +32,6 @@ install(TARGETS DetourTileCache file(GLOB INCLUDES Include/*.h) install(FILES ${INCLUDES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation) -install(FILES "$/DetourTileCache-d.pdb" CONFIGURATIONS "Debug" DESTINATION "lib") +if(MSVC) + install(FILES "$/DetourTileCache-d.pdb" CONFIGURATIONS "Debug" DESTINATION "lib") +endif() diff --git a/Recast/CMakeLists.txt b/Recast/CMakeLists.txt index 36065446..f25cab89 100644 --- a/Recast/CMakeLists.txt +++ b/Recast/CMakeLists.txt @@ -27,4 +27,6 @@ install(TARGETS Recast file(GLOB INCLUDES Include/*.h) install(FILES ${INCLUDES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation) -install(FILES "$/Recast-d.pdb" CONFIGURATIONS "Debug" DESTINATION "lib") +if(MSVC) + install(FILES "$/Recast-d.pdb" CONFIGURATIONS "Debug" DESTINATION "lib") +endif() From 63a4ee4c1e696a5a2a0577f3351104b570bfdd48 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Fri, 5 Mar 2021 17:24:04 +0100 Subject: [PATCH 60/99] Add version and pkgconfig (#474) * Add version and pkgconfig * simply versioning Co-authored-by: Bret Curtis --- CMakeLists.txt | 23 ++++++++++++++++++++++- DebugUtils/CMakeLists.txt | 2 +- Detour/CMakeLists.txt | 2 +- DetourCrowd/CMakeLists.txt | 2 +- DetourTileCache/CMakeLists.txt | 2 +- Recast/CMakeLists.txt | 2 +- recastnavigation.pc.in | 10 ++++++++++ version.h.in | 3 +++ 8 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 recastnavigation.pc.in create mode 100644 version.h.in diff --git a/CMakeLists.txt b/CMakeLists.txt index b8af6d54..c3121c6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,8 @@ project(RecastNavigation) # lib versions SET(SOVERSION 1) -SET(VERSION 1.0.0) +set(LIB_VERSION 1.5.1) +string(REPLACE "." "," LIB_VERSION_NUM "${LIB_VERSION}.0") option(RECASTNAVIGATION_DEMO "Build demo" ON) option(RECASTNAVIGATION_TESTS "Build tests" ON) @@ -16,6 +17,26 @@ endif() include(GNUInstallDirs) +configure_file( + "${RecastNavigation_SOURCE_DIR}/version.h.in" + "${RecastNavigation_BINARY_DIR}/version.h") +install(FILES "${RecastNavigation_BINARY_DIR}/version.h" DESTINATION + ${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation) + +# Needed for recastnavigation.pc.in +set(prefix ${CMAKE_INSTALL_PREFIX}) +set(exec_prefix "\${prefix}") +set(libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}") +set(bindir "\${exec_prefix}/${CMAKE_INSTALL_BINDIR}") +set(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") +set(PACKAGE_VERSION "${LIB_VERSION}") +configure_file( + "${RecastNavigation_SOURCE_DIR}/recastnavigation.pc.in" + "${RecastNavigation_BINARY_DIR}/recastnavigation.pc" + @ONLY) +install(FILES "${RecastNavigation_BINARY_DIR}/recastnavigation.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") + add_subdirectory(DebugUtils) add_subdirectory(Detour) add_subdirectory(DetourCrowd) diff --git a/DebugUtils/CMakeLists.txt b/DebugUtils/CMakeLists.txt index 14b36a9a..f6325c4a 100644 --- a/DebugUtils/CMakeLists.txt +++ b/DebugUtils/CMakeLists.txt @@ -18,7 +18,7 @@ target_link_libraries(DebugUtils set_target_properties(DebugUtils PROPERTIES SOVERSION ${SOVERSION} - VERSION ${VERSION} + VERSION ${LIB_VERSION} COMPILE_PDB_OUTPUT_DIRECTORY . COMPILE_PDB_NAME "DebugUtils-d" ) diff --git a/Detour/CMakeLists.txt b/Detour/CMakeLists.txt index da1a195c..a3ca8085 100644 --- a/Detour/CMakeLists.txt +++ b/Detour/CMakeLists.txt @@ -12,7 +12,7 @@ target_include_directories(Detour PUBLIC set_target_properties(Detour PROPERTIES SOVERSION ${SOVERSION} - VERSION ${VERSION} + VERSION ${LIB_VERSION} COMPILE_PDB_OUTPUT_DIRECTORY . COMPILE_PDB_NAME "Detour-d" ) diff --git a/DetourCrowd/CMakeLists.txt b/DetourCrowd/CMakeLists.txt index 60b8e1a7..1a5d2243 100644 --- a/DetourCrowd/CMakeLists.txt +++ b/DetourCrowd/CMakeLists.txt @@ -16,7 +16,7 @@ target_link_libraries(DetourCrowd set_target_properties(DetourCrowd PROPERTIES SOVERSION ${SOVERSION} - VERSION ${VERSION} + VERSION ${LIB_VERSION} COMPILE_PDB_OUTPUT_DIRECTORY . COMPILE_PDB_NAME "DetourCrowd-d" ) diff --git a/DetourTileCache/CMakeLists.txt b/DetourTileCache/CMakeLists.txt index cf2d437a..7b835560 100644 --- a/DetourTileCache/CMakeLists.txt +++ b/DetourTileCache/CMakeLists.txt @@ -16,7 +16,7 @@ target_link_libraries(DetourTileCache set_target_properties(DetourTileCache PROPERTIES SOVERSION ${SOVERSION} - VERSION ${VERSION} + VERSION ${LIB_VERSION} COMPILE_PDB_OUTPUT_DIRECTORY . COMPILE_PDB_NAME "DetourTileCache-d" ) diff --git a/Recast/CMakeLists.txt b/Recast/CMakeLists.txt index f25cab89..de88b811 100644 --- a/Recast/CMakeLists.txt +++ b/Recast/CMakeLists.txt @@ -12,7 +12,7 @@ target_include_directories(Recast PUBLIC set_target_properties(Recast PROPERTIES SOVERSION ${SOVERSION} - VERSION ${VERSION} + VERSION ${LIB_VERSION} COMPILE_PDB_OUTPUT_DIRECTORY . COMPILE_PDB_NAME "Recast-d" ) diff --git a/recastnavigation.pc.in b/recastnavigation.pc.in new file mode 100644 index 00000000..7f806fd4 --- /dev/null +++ b/recastnavigation.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: RecastNavigation +Description: RecastNavigation is a cross-platform navigation mesh construction toolset for games +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lRecast -lDetour -lDebugUtils -lDetourCrowd -lDetourTileCache +Cflags: -I${includedir} -I${includedir}/recastnavigation @PKG_CONFIG_CFLAGS@ \ No newline at end of file diff --git a/version.h.in b/version.h.in new file mode 100644 index 00000000..5e6e16c1 --- /dev/null +++ b/version.h.in @@ -0,0 +1,3 @@ +/* Define to the library version */ +#define RECASTNAV_VERSION "${LIB_VERSION}" +#define RECASTNAV_VERSION_NUM ${LIB_VERSION_NUM} From c5cbd53024c8a9d8d097a4371215e3342d2fdc87 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Fri, 5 Mar 2021 17:24:41 +0100 Subject: [PATCH 61/99] Fix MacOS builds on Travis (#475) * use defaults where possible, simplify .travis.yml and fix macos builds while we are at it * de-duplicate code * specifically ask for xcode12.2 --- .travis.yml | 40 ++++++++++------------------------------ 1 file changed, 10 insertions(+), 30 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0e63abad..6cd8bbc2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,6 @@ language: cpp +os: linux +dist: bionic branches: only: - master @@ -9,55 +11,33 @@ sudo: false addons: apt: - sources: - - ubuntu-toolchain-r-test - - llvm-toolchain-xenial-7 - packages: [ cmake, clang-7, clang-tools-7, gcc-8, g++-8, libsdl2-dev ] + packages: [ build-essential, cmake, clang, clang-tools, libsdl2-dev ] matrix: include: - - name: Recastnavigation (all) on MacOS xcode9.4 + - name: Recastnavigation on MacOS using XCode os: osx - osx_image: xcode9.4 + osx_image: xcode12.2 before_install: - brew update - brew install sdl2 if: branch != coverity_scan - - name: Recastnavigation on Ubuntu Xenial GCC-5 - os: linux - dist: xenial - sudo: required + - name: Recastnavigation on Ubuntu GCC if: branch != coverity_scan - - name: Recastnavigation on Ubuntu Xenial GCC-8 - os: linux - dist: xenial - sudo: required - env: - - MATRIX_EVAL="CC=gcc-8 && CXX=g++-8" - if: branch != coverity_scan - - name: Recastnavigation on Ubuntu Xenial GCC-5 using Premake5 - os: linux - dist: xenial - sudo: required + - name: Recastnavigation on Ubuntu GCC using Premake5 if: branch != coverity_scan before_install: - wget https://github.com/premake/premake-core/releases/download/v5.0.0-alpha12/premake-5.0.0-alpha12-linux.tar.gz -O premake.tar.gz - tar -xf premake.tar.gz env: - PREMAKE=1 - - name: Recastnavigation on Ubuntu Xenial Clang-7 with Static Analysis - os: linux - dist: xenial - sudo: required + - name: Recastnavigation on Ubuntu Clang with Static Analysis env: - - MATRIX_EVAL="CC=clang-7 && CXX=clang++-7" - - ANALYZE="scan-build-7 --force-analyze-debug-code --use-cc clang-7 --use-c++ clang++-7" + - MATRIX_EVAL="CC=clang && CXX=clang++" + - ANALYZE="scan-build --force-analyze-debug-code --use-cc clang --use-c++ clang++" if: branch != coverity_scan compiler: clang - name: Recastnavigation Coverity Scan - os: linux - dist: xenial - sudo: required if: branch = coverity_scan before_script: From 5a870d427e47abd4a8e4ce58a95582ec049434d5 Mon Sep 17 00:00:00 2001 From: Prin_E Date: Thu, 17 Mar 2022 01:53:48 +0900 Subject: [PATCH 62/99] Fixed RecastDemo crash in macOS 10.11+ * Set hints to always use OpenGL render driver (instead of Metal driver) * Compile error fix in Xcode 13 --- Recast/Include/RecastAlloc.h | 2 +- RecastDemo/Source/main.cpp | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Recast/Include/RecastAlloc.h b/Recast/Include/RecastAlloc.h index 20e8dd1b..071278d6 100644 --- a/Recast/Include/RecastAlloc.h +++ b/Recast/Include/RecastAlloc.h @@ -22,7 +22,7 @@ #include #include -#include +#include "RecastAssert.h" /// Provides hint values to the memory allocator on how long the /// memory is expected to be used. diff --git a/RecastDemo/Source/main.cpp b/RecastDemo/Source/main.cpp index e9f584df..270c2af3 100644 --- a/RecastDemo/Source/main.cpp +++ b/RecastDemo/Source/main.cpp @@ -78,6 +78,9 @@ int main(int /*argc*/, char** /*argv*/) return -1; } + // Use OpenGL render driver. + SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl"); + // Enable depth buffer. SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); From c02d6fa81eb8e332b932186ebd5641f249f46fe8 Mon Sep 17 00:00:00 2001 From: Brian Swenson Date: Wed, 1 Jun 2022 17:29:09 -0400 Subject: [PATCH 63/99] -Wunused-but-set-variable flagged variables removed --- DetourTileCache/Source/DetourTileCacheBuilder.cpp | 2 -- Recast/Source/RecastMesh.cpp | 2 -- 2 files changed, 4 deletions(-) diff --git a/DetourTileCache/Source/DetourTileCacheBuilder.cpp b/DetourTileCache/Source/DetourTileCacheBuilder.cpp index 21a59f1a..05e34acd 100644 --- a/DetourTileCache/Source/DetourTileCacheBuilder.cpp +++ b/DetourTileCache/Source/DetourTileCacheBuilder.cpp @@ -1399,7 +1399,6 @@ static void pushBack(unsigned short v, unsigned short* arr, int& an) static bool canRemoveVertex(dtTileCachePolyMesh& mesh, const unsigned short rem) { // Count number of polygons to remove. - int numRemovedVerts = 0; int numTouchedVerts = 0; int numRemainingEdges = 0; for (int i = 0; i < mesh.npolys; ++i) @@ -1419,7 +1418,6 @@ static bool canRemoveVertex(dtTileCachePolyMesh& mesh, const unsigned short rem) } if (numRemoved) { - numRemovedVerts += numRemoved; numRemainingEdges += numVerts-(numRemoved+1); } } diff --git a/Recast/Source/RecastMesh.cpp b/Recast/Source/RecastMesh.cpp index e99eaebb..ea09ee1d 100644 --- a/Recast/Source/RecastMesh.cpp +++ b/Recast/Source/RecastMesh.cpp @@ -566,7 +566,6 @@ static bool canRemoveVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned sho const int nvp = mesh.nvp; // Count number of polygons to remove. - int numRemovedVerts = 0; int numTouchedVerts = 0; int numRemainingEdges = 0; for (int i = 0; i < mesh.npolys; ++i) @@ -586,7 +585,6 @@ static bool canRemoveVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned sho } if (numRemoved) { - numRemovedVerts += numRemoved; numRemainingEdges += numVerts-(numRemoved+1); } } From c187b7e88bfc922bca2175e3751881bdf378ff0d Mon Sep 17 00:00:00 2001 From: Graham Pentheny Date: Fri, 3 Jun 2022 02:56:44 -0400 Subject: [PATCH 64/99] Removed broken IssueStats badges from README.md --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 3e1d3ada..2ea200af 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,6 @@ Recast & Detour [![Travis (Linux) Build Status](https://travis-ci.org/recastnavigation/recastnavigation.svg?branch=master)](https://travis-ci.org/recastnavigation/recastnavigation) [![Appveyor (Windows) Build Status](https://ci.appveyor.com/api/projects/status/20w84u25b3f8h179/branch/master?svg=true)](https://ci.appveyor.com/project/recastnavigation/recastnavigation/branch/master) -[![Issue Stats](http://www.issuestats.com/github/recastnavigation/recastnavigation/badge/pr?style=flat)](http://www.issuestats.com/github/recastnavigation/recastnavigation) -[![Issue Stats](http://www.issuestats.com/github/recastnavigation/recastnavigation/badge/issue?style=flat)](http://www.issuestats.com/github/recastnavigation/recastnavigation) - ![screenshot of a navmesh baked with the sample program](/RecastDemo/screenshot.png?raw=true) ## Recast From 2164a272e5487534c1f63f6ace8cb30ca9683280 Mon Sep 17 00:00:00 2001 From: Graham Pentheny Date: Thu, 6 Oct 2022 22:48:29 -0400 Subject: [PATCH 65/99] Updated array size in docstring for dtMeshTile::verts Thanks to @pineappleKID for pointing this out! https://github.com/recastnavigation/recastnavigation/issues/535 --- Detour/Include/DetourNavMesh.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Detour/Include/DetourNavMesh.h b/Detour/Include/DetourNavMesh.h index 9ac1dc8d..cb5414f5 100644 --- a/Detour/Include/DetourNavMesh.h +++ b/Detour/Include/DetourNavMesh.h @@ -285,7 +285,7 @@ struct dtMeshTile unsigned int linksFreeList; ///< Index to the next free link. dtMeshHeader* header; ///< The tile header. dtPoly* polys; ///< The tile polygons. [Size: dtMeshHeader::polyCount] - float* verts; ///< The tile vertices. [Size: dtMeshHeader::vertCount] + float* verts; ///< The tile vertices. [(x, y, z) * dtMeshHeader::vertCount] dtLink* links; ///< The tile links. [Size: dtMeshHeader::maxLinkCount] dtPolyDetail* detailMeshes; ///< The tile's detail sub-meshes. [Size: dtMeshHeader::detailMeshCount] From 3a1924e3b03a19dbddd1a0af64dea27bf09c339a Mon Sep 17 00:00:00 2001 From: ZA139 <40553487+ZA139@users.noreply.github.com> Date: Sun, 9 Oct 2022 21:11:34 +0800 Subject: [PATCH 66/99] Correction the comment of dtVperp2D The return value should be the perp dot product rather than the dot product. --- Detour/Include/DetourCommon.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Detour/Include/DetourCommon.h b/Detour/Include/DetourCommon.h index 113e8c33..d578ebe5 100644 --- a/Detour/Include/DetourCommon.h +++ b/Detour/Include/DetourCommon.h @@ -319,7 +319,7 @@ inline float dtVdot2D(const float* u, const float* v) /// Derives the xz-plane 2D perp product of the two vectors. (uz*vx - ux*vz) /// @param[in] u The LHV vector [(x, y, z)] /// @param[in] v The RHV vector [(x, y, z)] -/// @return The dot product on the xz-plane. +/// @return The prep dot product on the xz-plane. /// /// The vectors are projected onto the xz-plane, so the y-values are ignored. inline float dtVperp2D(const float* u, const float* v) From 314e94b692fe6c5df1de703527c8774a473526b7 Mon Sep 17 00:00:00 2001 From: ZA139 <40553487+ZA139@users.noreply.github.com> Date: Mon, 10 Oct 2022 09:29:52 +0800 Subject: [PATCH 67/99] Fix the typo though of dtVperp2D function comment fix typo though --- Detour/Include/DetourCommon.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Detour/Include/DetourCommon.h b/Detour/Include/DetourCommon.h index d578ebe5..9d247fe5 100644 --- a/Detour/Include/DetourCommon.h +++ b/Detour/Include/DetourCommon.h @@ -319,7 +319,7 @@ inline float dtVdot2D(const float* u, const float* v) /// Derives the xz-plane 2D perp product of the two vectors. (uz*vx - ux*vz) /// @param[in] u The LHV vector [(x, y, z)] /// @param[in] v The RHV vector [(x, y, z)] -/// @return The prep dot product on the xz-plane. +/// @return The perp dot product on the xz-plane. /// /// The vectors are projected onto the xz-plane, so the y-values are ignored. inline float dtVperp2D(const float* u, const float* v) From 30edaf442c87e870e71a0e4509a96281418b1af8 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 11 Oct 2022 09:31:06 +0400 Subject: [PATCH 68/99] Fix -Wextra-semi GCC warnings by removing redundant semicolons --- Recast/Include/RecastAlloc.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Recast/Include/RecastAlloc.h b/Recast/Include/RecastAlloc.h index 071278d6..8b166d73 100644 --- a/Recast/Include/RecastAlloc.h +++ b/Recast/Include/RecastAlloc.h @@ -112,7 +112,7 @@ class rcVectorBase { typedef rcSizeType size_type; typedef T value_type; - rcVectorBase() : m_size(0), m_cap(0), m_data(0) {}; + rcVectorBase() : m_size(0), m_cap(0), m_data(0) {} rcVectorBase(const rcVectorBase& other) : m_size(0), m_cap(0), m_data(0) { assign(other.begin(), other.end()); } explicit rcVectorBase(rcSizeType count) : m_size(0), m_cap(0), m_data(0) { resize(count); } rcVectorBase(rcSizeType count, const T& value) : m_size(0), m_cap(0), m_data(0) { resize(count, value); } @@ -142,8 +142,8 @@ class rcVectorBase { const T& front() const { rcAssert(m_size); return m_data[0]; } T& front() { rcAssert(m_size); return m_data[0]; } - const T& back() const { rcAssert(m_size); return m_data[m_size - 1]; }; - T& back() { rcAssert(m_size); return m_data[m_size - 1]; }; + const T& back() const { rcAssert(m_size); return m_data[m_size - 1]; } + T& back() { rcAssert(m_size); return m_data[m_size - 1]; } const T* data() const { return m_data; } T* data() { return m_data; } From a425d03b0e41021193059df70e0a64363b71286b Mon Sep 17 00:00:00 2001 From: grdowns Date: Tue, 10 Sep 2019 12:11:28 -0700 Subject: [PATCH 69/99] Add vcpkg installation instructions --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 2ea200af..6f8fae90 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,12 @@ Recast is accompanied with Detour, path-finding and spatial reasoning toolkit. Y Detour offers simple static navigation mesh which is suitable for many simple cases, as well as tiled navigation mesh which allows you to plug in and out pieces of the mesh. The tiled mesh allows you to create systems where you stream new navigation data in and out as the player progresses the level, or you may regenerate tiles as the world changes. +## Installation through vcpkg + +If you are using the [vcpkg](https://github.com/Microsoft/vcpkg/) dependency manager you can download and install Recast with a single command: +``` +vcpkg install recast +``` ## Recast Demo From 9df3e2e0a6e37651f876fd1960ce91687ed2d48f Mon Sep 17 00:00:00 2001 From: Anton Matveev Date: Fri, 17 Apr 2020 12:57:31 +0500 Subject: [PATCH 70/99] Fix rounding error due to precision loss after optimization. --- Detour/Source/DetourNavMesh.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Detour/Source/DetourNavMesh.cpp b/Detour/Source/DetourNavMesh.cpp index b119cd54..2240526e 100644 --- a/Detour/Source/DetourNavMesh.cpp +++ b/Detour/Source/DetourNavMesh.cpp @@ -433,8 +433,8 @@ void dtNavMesh::connectExtLinks(dtMeshTile* tile, dtMeshTile* target, int side) float tmax = (neia[k*2+1]-va[2]) / (vb[2]-va[2]); if (tmin > tmax) dtSwap(tmin,tmax); - link->bmin = (unsigned char)(dtClamp(tmin, 0.0f, 1.0f)*255.0f); - link->bmax = (unsigned char)(dtClamp(tmax, 0.0f, 1.0f)*255.0f); + link->bmin = (unsigned char)roundf(dtClamp(tmin, 0.0f, 1.0f)*255.0f); + link->bmax = (unsigned char)roundf(dtClamp(tmax, 0.0f, 1.0f)*255.0f); } else if (dir == 2 || dir == 6) { @@ -442,8 +442,8 @@ void dtNavMesh::connectExtLinks(dtMeshTile* tile, dtMeshTile* target, int side) float tmax = (neia[k*2+1]-va[0]) / (vb[0]-va[0]); if (tmin > tmax) dtSwap(tmin,tmax); - link->bmin = (unsigned char)(dtClamp(tmin, 0.0f, 1.0f)*255.0f); - link->bmax = (unsigned char)(dtClamp(tmax, 0.0f, 1.0f)*255.0f); + link->bmin = (unsigned char)roundf(dtClamp(tmin, 0.0f, 1.0f)*255.0f); + link->bmax = (unsigned char)roundf(dtClamp(tmax, 0.0f, 1.0f)*255.0f); } } } From 0af470fe748466d66c56a134785340bd2906ed97 Mon Sep 17 00:00:00 2001 From: EfveZombie <34877029+EfveZombie@users.noreply.github.com> Date: Thu, 13 Oct 2022 17:27:06 +0800 Subject: [PATCH 71/99] fix: use closestPointOnPoly instead of getPolyHeight in dtNavMeshQuery::findRandomPoint (#560) --- Detour/Source/DetourNavMeshQuery.cpp | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/Detour/Source/DetourNavMeshQuery.cpp b/Detour/Source/DetourNavMeshQuery.cpp index 8ea09c82..b48618f0 100644 --- a/Detour/Source/DetourNavMeshQuery.cpp +++ b/Detour/Source/DetourNavMeshQuery.cpp @@ -301,11 +301,7 @@ dtStatus dtNavMeshQuery::findRandomPoint(const dtQueryFilter* filter, float (*fr float pt[3]; dtRandomPointInConvexPoly(verts, poly->vertCount, areas, s, t, pt); - float h = 0.0f; - dtStatus status = getPolyHeight(polyRef, pt, &h); - if (dtStatusFailed(status)) - return status; - pt[1] = h; + closestPointOnPoly(polyRef, pt, pt, NULL); dtVcopy(randomPt, pt); *randomRef = polyRef; @@ -488,16 +484,12 @@ dtStatus dtNavMeshQuery::findRandomPointAroundCircle(dtPolyRef startRef, const f float pt[3]; dtRandomPointInConvexPoly(verts, randomPoly->vertCount, areas, s, t, pt); - float h = 0.0f; - dtStatus stat = getPolyHeight(randomPolyRef, pt, &h); - if (dtStatusFailed(status)) - return stat; - pt[1] = h; + closestPointOnPoly(randomPolyRef, pt, pt, NULL); dtVcopy(randomPt, pt); *randomRef = randomPolyRef; - return DT_SUCCESS; + return status; } From 4e233f22b432e77dd9a3e2e5b990750d88d4c465 Mon Sep 17 00:00:00 2001 From: Graham Pentheny Date: Wed, 12 Oct 2022 00:55:38 -0400 Subject: [PATCH 72/99] Added configuration protips from Mikko's blog to a FAQ --- Docs/Conceptual/FAQ.md | 49 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 Docs/Conceptual/FAQ.md diff --git a/Docs/Conceptual/FAQ.md b/Docs/Conceptual/FAQ.md new file mode 100644 index 00000000..232aef6e --- /dev/null +++ b/Docs/Conceptual/FAQ.md @@ -0,0 +1,49 @@ +# Configuring Recast with rcConfig +The following describes best practices for configuring Recast. They're broken down by the fields in `rcConfig` and are ordered by the sequence in which you should consider them. + +## Determine Agent Size +First you should decide the size of your agent's logical cylinder. If your game world uses meters as units, a reasonable starting point for a human-sized agent might be a radius of `0.4` and a height of `2.0`. + +## cs +The voxelization cell size `cs` defines the voxel size along both axes of the ground plane: x and z in Recast. This value is usually derived from the character radius `r`. A recommended starting value for `cs` is either `r/2` or `r/3`. Smaller values of `cs` will increase rasterization resolution and navmesh detail, but total generation time will increase exponentially. In outdoor environments, `r/2` is often good enough. For indoor scenes with tight spaces you might want the extra precision, so a value of `r/3` or smaller may give better results. + +The initial instinct is to reduce this value to something very close to zero to maximize the detail of the generated navmesh. This quickly becomes a case of diminishing returns, however. Beyond a certain point there's usually not much perceptable difference in the generated navmesh, but huge increases in generation time. This hinders your ability to quickly iterate on level designs and provides little benefit. The general recommendation here is to use as large a value for `cs` as you can get away with. + +## ch +The voxelization cell height `ch` is defined separately in order to allow greater precision in height tests. A good starting point for `ch` is half the `cs` value. Smaller `ch` values ensure that the navmesh properly connects areas that are only separated by a small curb or ditch. If small holes are generated in your navmesh around where there are discontinuities in height (for example, stairs or curbs), you may want to decrease the cell height value to increase the vertical rasterization precision of Recast. + +## walkableHeight +This value defines the height `h` of the agent in voxels. This value should be calculated as `ceil(h / ch)`. Note this is based on `ch` not `cs` since it's a height value. + +## walkableClimb +The `walkableClimb` value defines the maximum height of ledges and steps that the agent can walk up. Given a designer-defined `maxClimb` distance in world units, this value should be calculated as `ceil(maxClimb / ch)`. Again, note that this is using `ch` not `cs` because it's a height-based value. + +## walkableRadius +The parameter `walkableRadius` defines the agent radius in voxels. Most often, this value should be calculated as `ceil(r/cs)`. Note this is based on `cs` since the agent radius is always parallel to the ground plane. + +If the `walkableRadius` value is greater than zero, the edges of the navmesh will be pushed away from all obstacles by this amount. + +A non-zero `walkableRadius` allows for much simpler runtime navmesh collision checks. The game only needs to check that the center point of the agent is contained within a navmesh polygon. Without this erosion, runtime navigation checks need to collide the geometric projection onto the navmesh of agent's logical cylinder with the boundary edges of the navmesh polygons. + +Navmesh erosion is performed on the voxelized world representation, so some precision can be lost there. This step allows simpler checks at runtime. + +If you want to have tight-fitting navmesh, or want to reuse the same navmesh for multiple agents with differing radii, you can use a `walkableRadius` value of zero. + +## walkableSlopeAngle +The parameter `walkableSlopeAngle` is to filter out areas of the world where the ground slope would be too steep for an agent to traverse. This value is defined as a maximum angle in degrees that the surface normal of a polgyon can differ from the world's up vector. This value must be within the range `[0, 90]`. + +## maxEdgeLen +In certain cases, long outer edges may decrease the quality of the resulting triangulation, creating very long thin triangles. This can sometimes be remedied by limiting the maximum edge length, causing the problematic long edges to be broken up into smaller segments. + +The parameter `maxEdgeLen` defines the maximum edge length and is defined in terms of voxels. A good value for `maxEdgeLen` is something like `walkableRadius * 8`. A good way to adjust this value is to first set it really high and see if your data creates long edges. If it does, decrease `maxEdgeLen` until you find the largest value which improves the resulting tesselation. + +## maxSimplificationError +When the rasterized areas are converted back to a vectorized representation, the `maxSimplificationError` describes how loosely the simplification is done. The simplification process uses the [Ramer–Douglas-Peucker algorithm](https://en.wikipedia.org/wiki/Ramer–Douglas–Peucker_algorithm), and this value describes the max deviation in voxels. + +Good values for `maxSimplificationError` are in the range `[1.1, 1.5]`. A value of `1.3` is a good starting point and usually yields good results. If the value is less than `1.1`, some sawtoothing starts to appear at the generated edges. If the value is more than `1.5`, the mesh simplification starts to cut some corners it shouldn't. + +## minRegionSize +Watershed partitioning is really prone to noise in the input distance field. In order to get nicer areas, the areas are merged and small disconnected areas are removed after the water shed partitioning. The parameter `minRegionSize` describes the minimum isolated region size that is still kept. A region is removed if the number of voxels in the region is less than the square of `minRegionSize`. + +## mergeRegionSize +The triangulation process works best with small, localized voxel regions. The parameter `mergeRegionSize` controls the maximum voxel area of a region that is allowed to be merged with another region. If you see small patches missing here and there, you could lower the minRegionSize. From c14a5a58977ae8aea7bc02d0e498bfdf4d13201b Mon Sep 17 00:00:00 2001 From: Graham Pentheny Date: Tue, 25 Oct 2022 19:47:43 -0400 Subject: [PATCH 73/99] Ignore generated dSYM debug symbols archive on mac --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 98f17e4b..1e20e602 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,9 @@ RecastDemo/Bin/Tests # Build directory RecastDemo/Build +# XCode debug symbols archive +RecastDemo/Bin/*.dSYM + # Ignore meshes RecastDemo/Bin/Meshes/* From 96dc90b6ff93007a82c2325ee696bfb32b642cc0 Mon Sep 17 00:00:00 2001 From: Graham Pentheny Date: Tue, 25 Oct 2022 19:43:55 -0400 Subject: [PATCH 74/99] Fix deprecation warnings when using premake5 The premake script generates some deprecation warnings when generating with premake5: - "configuration" has been deprecated in favor of "filter" - "solution" was renamed to "workspace" --- RecastDemo/premake5.lua | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/RecastDemo/premake5.lua b/RecastDemo/premake5.lua index 3f8b75b5..f6fdca6a 100644 --- a/RecastDemo/premake5.lua +++ b/RecastDemo/premake5.lua @@ -6,7 +6,7 @@ local action = _ACTION or "" local todir = "Build/" .. action -solution "recastnavigation" +workspace "recastnavigation" configurations { "Debug", "Release" @@ -15,27 +15,27 @@ solution "recastnavigation" location (todir) floatingpoint "Fast" - symbols "On" exceptionhandling "Off" rtti "Off" + symbols "On" flags { "FatalCompileWarnings" } -- debug configs - configuration "Debug*" + filter "configurations:Debug" defines { "DEBUG" } targetdir ( todir .. "/lib/Debug" ) -- release configs - configuration "Release*" + filter "configurations:Release" defines { "NDEBUG" } optimize "On" targetdir ( todir .. "/lib/Release" ) - configuration "not windows" + filter "system:not windows" warnings "Extra" -- windows specific - configuration "windows" + filter "system:windows" platforms { "Win32", "Win64" } defines { "WIN32", "_WINDOWS", "_CRT_SECURE_NO_WARNINGS", "_HAS_EXCEPTIONS=0" } -- warnings "Extra" uses /W4 which is too aggressive for us, so use W3 instead. @@ -58,7 +58,7 @@ project "DebugUtils" "../DetourTileCache/Include", "../Recast/Include" } - files { + files { "../DebugUtils/Include/*.h", "../DebugUtils/Source/*.cpp" } @@ -74,12 +74,11 @@ project "Detour" "../Detour/Source/*.cpp" } -- linux library cflags and libs - configuration { "linux", "gmake" } - buildoptions { + filter {"system:linux", "action:gmake"} + buildoptions { "-Wno-class-memaccess" } - project "DetourCrowd" language "C++" kind "StaticLib" @@ -150,7 +149,7 @@ project "RecastDemo" targetdir "Bin" -- linux library cflags and libs - configuration { "linux", "gmake" } + filter {"system:linux", "action:gmake"} buildoptions { "`pkg-config --cflags sdl2`", "`pkg-config --cflags gl`", @@ -166,7 +165,7 @@ project "RecastDemo" } -- windows library cflags and libs - configuration { "windows" } + filter "system:windows" includedirs { "../RecastDemo/Contrib/SDL/include" } libdirs { "../RecastDemo/Contrib/SDL/lib/%{cfg.architecture:gsub('x86_64', 'x64')}" } debugdir "../RecastDemo/Bin/" @@ -182,7 +181,7 @@ project "RecastDemo" } -- mac includes and libs - configuration { "macosx" } + filter "system:macosx" kind "ConsoleApp" -- xcode4 failes to run the project if using WindowedApp includedirs { "/Library/Frameworks/SDL2.framework/Headers" } links { @@ -209,7 +208,7 @@ project "Tests" "../Tests/Recast", "../Tests", } - files { + files { "../Tests/*.h", "../Tests/*.hpp", "../Tests/*.cpp", @@ -232,7 +231,7 @@ project "Tests" targetdir "Bin" -- linux library cflags and libs - configuration { "linux", "gmake" } + filter {"system:linux", "action:gmake"} buildoptions { "`pkg-config --cflags sdl2`", "`pkg-config --cflags gl`", @@ -246,7 +245,7 @@ project "Tests" } -- windows library cflags and libs - configuration { "windows" } + filter "system:windows" includedirs { "../RecastDemo/Contrib/SDL/include" } libdirs { "../RecastDemo/Contrib/SDL/lib/%{cfg.architecture:gsub('x86_64', 'x64')}" } debugdir "../RecastDemo/Bin/" @@ -258,7 +257,7 @@ project "Tests" } -- mac includes and libs - configuration { "macosx" } + filter "system:macosx" kind "ConsoleApp" includedirs { "/Library/Frameworks/SDL2.framework/Headers" } links { From 0d1cbd3d6755712325f3c6278542174df5dd9cb8 Mon Sep 17 00:00:00 2001 From: Graham Pentheny Date: Sun, 16 Oct 2022 11:59:25 -0400 Subject: [PATCH 75/99] Don't explicitly create a GL context since SDL_CreateWindowAndRenderer does it internally. Having two GL contexts causes rendering problems on Ubuntu. Fixes #521 --- RecastDemo/Source/main.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/RecastDemo/Source/main.cpp b/RecastDemo/Source/main.cpp index 270c2af3..1a31e75f 100644 --- a/RecastDemo/Source/main.cpp +++ b/RecastDemo/Source/main.cpp @@ -127,7 +127,6 @@ int main(int /*argc*/, char** /*argv*/) } SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); - SDL_GL_CreateContext(window); if (!imguiRenderGLInit("DroidSans.ttf")) { From d576e71b256d8ba853c522d365ff08e2a5e45d76 Mon Sep 17 00:00:00 2001 From: Graham Pentheny Date: Sat, 29 Oct 2022 13:43:50 -0400 Subject: [PATCH 76/99] Disable class-memaccess error to fix builds on Ubuntu with newer versions of GCC Based on SgtVincent's suggestion here: https://github.com/recastnavigation/recastnavigation/issues/497#issuecomment-1293318731 Fixes #497 --- RecastDemo/premake5.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RecastDemo/premake5.lua b/RecastDemo/premake5.lua index f6fdca6a..c9a9f23c 100644 --- a/RecastDemo/premake5.lua +++ b/RecastDemo/premake5.lua @@ -76,7 +76,7 @@ project "Detour" -- linux library cflags and libs filter {"system:linux", "action:gmake"} buildoptions { - "-Wno-class-memaccess" + "-Wno-error=class-memaccess" } project "DetourCrowd" @@ -155,7 +155,7 @@ project "RecastDemo" "`pkg-config --cflags gl`", "`pkg-config --cflags glu`", "-Wno-ignored-qualifiers", - "-Wno-class-memaccess" + "-Wno-error=class-memaccess" } linkoptions { From 7dfa9849ca4f725991916cccd16a704c4d167b98 Mon Sep 17 00:00:00 2001 From: Graham Pentheny Date: Sat, 29 Oct 2022 14:13:46 -0400 Subject: [PATCH 77/99] Updated Catch2 testing library to v3.1.1 https://github.com/catchorg/Catch2 Updated tests with new API changes as well. --- RecastDemo/premake5.lua | 4 + Tests/Contrib/Catch/catch_amalgamated.cpp | 10395 +++++++++++++++++ Tests/Contrib/Catch/catch_amalgamated.hpp | 12260 ++++++++++++++++++++ Tests/Detour/Tests_Detour.cpp | 20 +- Tests/Recast/Tests_Recast.cpp | 150 +- Tests/catch.hpp | 11689 ------------------- Tests/main.cpp | 2 +- 7 files changed, 22745 insertions(+), 11775 deletions(-) create mode 100644 Tests/Contrib/Catch/catch_amalgamated.cpp create mode 100644 Tests/Contrib/Catch/catch_amalgamated.hpp delete mode 100644 Tests/catch.hpp diff --git a/RecastDemo/premake5.lua b/RecastDemo/premake5.lua index c9a9f23c..08a2dbbd 100644 --- a/RecastDemo/premake5.lua +++ b/RecastDemo/premake5.lua @@ -19,6 +19,7 @@ workspace "recastnavigation" rtti "Off" symbols "On" flags { "FatalCompileWarnings" } + cppdialect "C++98" -- debug configs filter "configurations:Debug" @@ -193,6 +194,7 @@ project "RecastDemo" project "Tests" language "C++" kind "ConsoleApp" + cppdialect "C++20" -- Catch requires newer C++ features -- Catch requires RTTI and exceptions exceptionhandling "On" @@ -207,6 +209,7 @@ project "Tests" "../Recast/Source", "../Tests/Recast", "../Tests", + "../Tests/Contrib/Catch" } files { "../Tests/*.h", @@ -216,6 +219,7 @@ project "Tests" "../Tests/Recast/*.cpp", "../Tests/Detour/*.h", "../Tests/Detour/*.cpp", + "../Tests/Contrib/Catch/*.cpp" } -- project dependencies diff --git a/Tests/Contrib/Catch/catch_amalgamated.cpp b/Tests/Contrib/Catch/catch_amalgamated.cpp new file mode 100644 index 00000000..1f5527a4 --- /dev/null +++ b/Tests/Contrib/Catch/catch_amalgamated.cpp @@ -0,0 +1,10395 @@ +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + +// Catch v3.1.1 +// Generated: 2022-10-17 18:47:22.400176 +// ---------------------------------------------------------- +// This file is an amalgamation of multiple different files. +// You probably shouldn't edit it directly. +// ---------------------------------------------------------- + +#include "catch_amalgamated.hpp" + + +#ifndef CATCH_WINDOWS_H_PROXY_HPP_INCLUDED +#define CATCH_WINDOWS_H_PROXY_HPP_INCLUDED + + +#if defined(CATCH_PLATFORM_WINDOWS) + +// We might end up with the define made globally through the compiler, +// and we don't want to trigger warnings for this +#if !defined(NOMINMAX) +# define NOMINMAX +#endif +#if !defined(WIN32_LEAN_AND_MEAN) +# define WIN32_LEAN_AND_MEAN +#endif + +#include + +#endif // defined(CATCH_PLATFORM_WINDOWS) + +#endif // CATCH_WINDOWS_H_PROXY_HPP_INCLUDED + + + + +namespace Catch { + namespace Benchmark { + namespace Detail { + ChronometerConcept::~ChronometerConcept() = default; + } // namespace Detail + } // namespace Benchmark +} // namespace Catch + + + + +namespace Catch { + namespace Benchmark { + namespace Detail { + BenchmarkFunction::callable::~callable() = default; + } // namespace Detail + } // namespace Benchmark +} // namespace Catch + + + +#include + +namespace Catch { + namespace Benchmark { + namespace Detail { + struct optimized_away_error : std::exception { + const char* what() const noexcept override; + }; + + const char* optimized_away_error::what() const noexcept { + return "could not measure benchmark, maybe it was optimized away"; + } + + void throw_optimized_away_error() { + Catch::throw_exception(optimized_away_error{}); + } + + } // namespace Detail + } // namespace Benchmark +} // namespace Catch + + +// Adapted from donated nonius code. + + + +#include +#include +#include +#include + + +#if defined(CATCH_CONFIG_USE_ASYNC) +#include +#endif + +namespace { + +using Catch::Benchmark::Detail::sample; + + template + sample resample(URng& rng, unsigned int resamples, std::vector::iterator first, std::vector::iterator last, Estimator& estimator) { + auto n = static_cast(last - first); + std::uniform_int_distribution dist(0, n - 1); + + sample out; + out.reserve(resamples); + std::generate_n(std::back_inserter(out), resamples, [n, first, &estimator, &dist, &rng] { + std::vector resampled; + resampled.reserve(n); + std::generate_n(std::back_inserter(resampled), n, [first, &dist, &rng] { return first[static_cast(dist(rng))]; }); + return estimator(resampled.begin(), resampled.end()); + }); + std::sort(out.begin(), out.end()); + return out; + } + + + double erf_inv(double x) { + // Code accompanying the article "Approximating the erfinv function" in GPU Computing Gems, Volume 2 + double w, p; + + w = -log((1.0 - x) * (1.0 + x)); + + if (w < 6.250000) { + w = w - 3.125000; + p = -3.6444120640178196996e-21; + p = -1.685059138182016589e-19 + p * w; + p = 1.2858480715256400167e-18 + p * w; + p = 1.115787767802518096e-17 + p * w; + p = -1.333171662854620906e-16 + p * w; + p = 2.0972767875968561637e-17 + p * w; + p = 6.6376381343583238325e-15 + p * w; + p = -4.0545662729752068639e-14 + p * w; + p = -8.1519341976054721522e-14 + p * w; + p = 2.6335093153082322977e-12 + p * w; + p = -1.2975133253453532498e-11 + p * w; + p = -5.4154120542946279317e-11 + p * w; + p = 1.051212273321532285e-09 + p * w; + p = -4.1126339803469836976e-09 + p * w; + p = -2.9070369957882005086e-08 + p * w; + p = 4.2347877827932403518e-07 + p * w; + p = -1.3654692000834678645e-06 + p * w; + p = -1.3882523362786468719e-05 + p * w; + p = 0.0001867342080340571352 + p * w; + p = -0.00074070253416626697512 + p * w; + p = -0.0060336708714301490533 + p * w; + p = 0.24015818242558961693 + p * w; + p = 1.6536545626831027356 + p * w; + } else if (w < 16.000000) { + w = sqrt(w) - 3.250000; + p = 2.2137376921775787049e-09; + p = 9.0756561938885390979e-08 + p * w; + p = -2.7517406297064545428e-07 + p * w; + p = 1.8239629214389227755e-08 + p * w; + p = 1.5027403968909827627e-06 + p * w; + p = -4.013867526981545969e-06 + p * w; + p = 2.9234449089955446044e-06 + p * w; + p = 1.2475304481671778723e-05 + p * w; + p = -4.7318229009055733981e-05 + p * w; + p = 6.8284851459573175448e-05 + p * w; + p = 2.4031110387097893999e-05 + p * w; + p = -0.0003550375203628474796 + p * w; + p = 0.00095328937973738049703 + p * w; + p = -0.0016882755560235047313 + p * w; + p = 0.0024914420961078508066 + p * w; + p = -0.0037512085075692412107 + p * w; + p = 0.005370914553590063617 + p * w; + p = 1.0052589676941592334 + p * w; + p = 3.0838856104922207635 + p * w; + } else { + w = sqrt(w) - 5.000000; + p = -2.7109920616438573243e-11; + p = -2.5556418169965252055e-10 + p * w; + p = 1.5076572693500548083e-09 + p * w; + p = -3.7894654401267369937e-09 + p * w; + p = 7.6157012080783393804e-09 + p * w; + p = -1.4960026627149240478e-08 + p * w; + p = 2.9147953450901080826e-08 + p * w; + p = -6.7711997758452339498e-08 + p * w; + p = 2.2900482228026654717e-07 + p * w; + p = -9.9298272942317002539e-07 + p * w; + p = 4.5260625972231537039e-06 + p * w; + p = -1.9681778105531670567e-05 + p * w; + p = 7.5995277030017761139e-05 + p * w; + p = -0.00021503011930044477347 + p * w; + p = -0.00013871931833623122026 + p * w; + p = 1.0103004648645343977 + p * w; + p = 4.8499064014085844221 + p * w; + } + return p * x; + } + + double standard_deviation(std::vector::iterator first, std::vector::iterator last) { + auto m = Catch::Benchmark::Detail::mean(first, last); + double variance = std::accumulate( first, + last, + 0., + [m]( double a, double b ) { + double diff = b - m; + return a + diff * diff; + } ) / + ( last - first ); + return std::sqrt( variance ); + } + +} + +namespace Catch { + namespace Benchmark { + namespace Detail { + +#if defined( __GNUC__ ) || defined( __clang__ ) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + bool directCompare( double lhs, double rhs ) { return lhs == rhs; } +#if defined( __GNUC__ ) || defined( __clang__ ) +# pragma GCC diagnostic pop +#endif + + double weighted_average_quantile(int k, int q, std::vector::iterator first, std::vector::iterator last) { + auto count = last - first; + double idx = (count - 1) * k / static_cast(q); + int j = static_cast(idx); + double g = idx - j; + std::nth_element(first, first + j, last); + auto xj = first[j]; + if ( directCompare( g, 0 ) ) { + return xj; + } + + auto xj1 = *std::min_element(first + (j + 1), last); + return xj + g * (xj1 - xj); + } + + + double erfc_inv(double x) { + return erf_inv(1.0 - x); + } + + double normal_quantile(double p) { + static const double ROOT_TWO = std::sqrt(2.0); + + double result = 0.0; + assert(p >= 0 && p <= 1); + if (p < 0 || p > 1) { + return result; + } + + result = -erfc_inv(2.0 * p); + // result *= normal distribution standard deviation (1.0) * sqrt(2) + result *= /*sd * */ ROOT_TWO; + // result += normal disttribution mean (0) + return result; + } + + + double outlier_variance(Estimate mean, Estimate stddev, int n) { + double sb = stddev.point; + double mn = mean.point / n; + double mg_min = mn / 2.; + double sg = (std::min)(mg_min / 4., sb / std::sqrt(n)); + double sg2 = sg * sg; + double sb2 = sb * sb; + + auto c_max = [n, mn, sb2, sg2](double x) -> double { + double k = mn - x; + double d = k * k; + double nd = n * d; + double k0 = -n * nd; + double k1 = sb2 - n * sg2 + nd; + double det = k1 * k1 - 4 * sg2 * k0; + return static_cast(-2. * k0 / (k1 + std::sqrt(det))); + }; + + auto var_out = [n, sb2, sg2](double c) { + double nc = n - c; + return (nc / n) * (sb2 - nc * sg2); + }; + + return (std::min)(var_out(1), var_out((std::min)(c_max(0.), c_max(mg_min)))) / sb2; + } + + + bootstrap_analysis analyse_samples(double confidence_level, unsigned int n_resamples, std::vector::iterator first, std::vector::iterator last) { + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS + static std::random_device entropy; + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + + auto n = static_cast(last - first); // seriously, one can't use integral types without hell in C++ + + auto mean = &Detail::mean::iterator>; + auto stddev = &standard_deviation; + +#if defined(CATCH_CONFIG_USE_ASYNC) + auto Estimate = [=](double(*f)(std::vector::iterator, std::vector::iterator)) { + auto seed = entropy(); + return std::async(std::launch::async, [=] { + std::mt19937 rng(seed); + auto resampled = resample(rng, n_resamples, first, last, f); + return bootstrap(confidence_level, first, last, resampled, f); + }); + }; + + auto mean_future = Estimate(mean); + auto stddev_future = Estimate(stddev); + + auto mean_estimate = mean_future.get(); + auto stddev_estimate = stddev_future.get(); +#else + auto Estimate = [=](double(*f)(std::vector::iterator, std::vector::iterator)) { + auto seed = entropy(); + std::mt19937 rng(seed); + auto resampled = resample(rng, n_resamples, first, last, f); + return bootstrap(confidence_level, first, last, resampled, f); + }; + + auto mean_estimate = Estimate(mean); + auto stddev_estimate = Estimate(stddev); +#endif // CATCH_USE_ASYNC + + double outlier_variance = Detail::outlier_variance(mean_estimate, stddev_estimate, n); + + return { mean_estimate, stddev_estimate, outlier_variance }; + } + } // namespace Detail + } // namespace Benchmark +} // namespace Catch + + + +#include +#include + +namespace { + +// Performs equivalent check of std::fabs(lhs - rhs) <= margin +// But without the subtraction to allow for INFINITY in comparison +bool marginComparison(double lhs, double rhs, double margin) { + return (lhs + margin >= rhs) && (rhs + margin >= lhs); +} + +} + +namespace Catch { + + Approx::Approx ( double value ) + : m_epsilon( std::numeric_limits::epsilon()*100. ), + m_margin( 0.0 ), + m_scale( 0.0 ), + m_value( value ) + {} + + Approx Approx::custom() { + return Approx( 0 ); + } + + Approx Approx::operator-() const { + auto temp(*this); + temp.m_value = -temp.m_value; + return temp; + } + + + std::string Approx::toString() const { + ReusableStringStream rss; + rss << "Approx( " << ::Catch::Detail::stringify( m_value ) << " )"; + return rss.str(); + } + + bool Approx::equalityComparisonImpl(const double other) const { + // First try with fixed margin, then compute margin based on epsilon, scale and Approx's value + // Thanks to Richard Harris for his help refining the scaled margin value + return marginComparison(m_value, other, m_margin) + || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(std::isinf(m_value)? 0 : m_value))); + } + + void Approx::setMargin(double newMargin) { + CATCH_ENFORCE(newMargin >= 0, + "Invalid Approx::margin: " << newMargin << '.' + << " Approx::Margin has to be non-negative."); + m_margin = newMargin; + } + + void Approx::setEpsilon(double newEpsilon) { + CATCH_ENFORCE(newEpsilon >= 0 && newEpsilon <= 1.0, + "Invalid Approx::epsilon: " << newEpsilon << '.' + << " Approx::epsilon has to be in [0, 1]"); + m_epsilon = newEpsilon; + } + +namespace literals { + Approx operator "" _a(long double val) { + return Approx(val); + } + Approx operator "" _a(unsigned long long val) { + return Approx(val); + } +} // end namespace literals + +std::string StringMaker::convert(Catch::Approx const& value) { + return value.toString(); +} + +} // end namespace Catch + + + +namespace Catch { + + AssertionResultData::AssertionResultData(ResultWas::OfType _resultType, LazyExpression const & _lazyExpression): + lazyExpression(_lazyExpression), + resultType(_resultType) {} + + std::string AssertionResultData::reconstructExpression() const { + + if( reconstructedExpression.empty() ) { + if( lazyExpression ) { + ReusableStringStream rss; + rss << lazyExpression; + reconstructedExpression = rss.str(); + } + } + return reconstructedExpression; + } + + AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data ) + : m_info( info ), + m_resultData( data ) + {} + + // Result was a success + bool AssertionResult::succeeded() const { + return Catch::isOk( m_resultData.resultType ); + } + + // Result was a success, or failure is suppressed + bool AssertionResult::isOk() const { + return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition ); + } + + ResultWas::OfType AssertionResult::getResultType() const { + return m_resultData.resultType; + } + + bool AssertionResult::hasExpression() const { + return !m_info.capturedExpression.empty(); + } + + bool AssertionResult::hasMessage() const { + return !m_resultData.message.empty(); + } + + std::string AssertionResult::getExpression() const { + // Possibly overallocating by 3 characters should be basically free + std::string expr; expr.reserve(m_info.capturedExpression.size() + 3); + if (isFalseTest(m_info.resultDisposition)) { + expr += "!("; + } + expr += m_info.capturedExpression; + if (isFalseTest(m_info.resultDisposition)) { + expr += ')'; + } + return expr; + } + + std::string AssertionResult::getExpressionInMacro() const { + std::string expr; + if( m_info.macroName.empty() ) + expr = static_cast(m_info.capturedExpression); + else { + expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 ); + expr += m_info.macroName; + expr += "( "; + expr += m_info.capturedExpression; + expr += " )"; + } + return expr; + } + + bool AssertionResult::hasExpandedExpression() const { + return hasExpression() && getExpandedExpression() != getExpression(); + } + + std::string AssertionResult::getExpandedExpression() const { + std::string expr = m_resultData.reconstructExpression(); + return expr.empty() + ? getExpression() + : expr; + } + + StringRef AssertionResult::getMessage() const { + return m_resultData.message; + } + SourceLineInfo AssertionResult::getSourceInfo() const { + return m_info.lineInfo; + } + + StringRef AssertionResult::getTestMacroName() const { + return m_info.macroName; + } + +} // end namespace Catch + + + +namespace { + bool provideBazelReporterOutput() { +#if defined(CATCH_CONFIG_BAZEL_SUPPORT) + return true; +#elif defined(CATCH_PLATFORM_WINDOWS_UWP) + // UWP does not support environment variables + return false; +#else + +# if defined( _MSC_VER ) + // On Windows getenv throws a warning as there is no input validation, + // since the switch is hardcoded, this should not be an issue. +# pragma warning( push ) +# pragma warning( disable : 4996 ) +# endif + + return std::getenv( "BAZEL_TEST" ) != nullptr; + +# if defined( _MSC_VER ) +# pragma warning( pop ) +# endif +#endif + } +} + +namespace Catch { + + bool operator==( ProcessedReporterSpec const& lhs, + ProcessedReporterSpec const& rhs ) { + return lhs.name == rhs.name && + lhs.outputFilename == rhs.outputFilename && + lhs.colourMode == rhs.colourMode && + lhs.customOptions == rhs.customOptions; + } + + Config::Config( ConfigData const& data ): + m_data( data ) { + // We need to trim filter specs to avoid trouble with superfluous + // whitespace (esp. important for bdd macros, as those are manually + // aligned with whitespace). + + for (auto& elem : m_data.testsOrTags) { + elem = trim(elem); + } + for (auto& elem : m_data.sectionsToRun) { + elem = trim(elem); + } + + + TestSpecParser parser(ITagAliasRegistry::get()); + if (!m_data.testsOrTags.empty()) { + m_hasTestFilters = true; + for (auto const& testOrTags : m_data.testsOrTags) { + parser.parse(testOrTags); + } + } + m_testSpec = parser.testSpec(); + + + // Insert the default reporter if user hasn't asked for a specfic one + if ( m_data.reporterSpecifications.empty() ) { + m_data.reporterSpecifications.push_back( { +#if defined( CATCH_CONFIG_DEFAULT_REPORTER ) + CATCH_CONFIG_DEFAULT_REPORTER, +#else + "console", +#endif + {}, {}, {} + } ); + } + +#if !defined(CATCH_PLATFORM_WINDOWS_UWP) + if(provideBazelReporterOutput()){ + // Register a JUnit reporter for Bazel. Bazel sets an environment + // variable with the path to XML output. If this file is written to + // during test, Bazel will not generate a default XML output. + // This allows the XML output file to contain higher level of detail + // than what is possible otherwise. +# if defined( _MSC_VER ) + // On Windows getenv throws a warning as there is no input validation, + // since the key is hardcoded, this should not be an issue. +# pragma warning( push ) +# pragma warning( disable : 4996 ) +# endif + const auto bazelOutputFilePtr = std::getenv( "XML_OUTPUT_FILE" ); +# if defined( _MSC_VER ) +# pragma warning( pop ) +# endif + if ( bazelOutputFilePtr != nullptr ) { + m_data.reporterSpecifications.push_back( + { "junit", std::string( bazelOutputFilePtr ), {}, {} } ); + } + } +#endif + + // We now fixup the reporter specs to handle default output spec, + // default colour spec, etc + bool defaultOutputUsed = false; + for ( auto const& reporterSpec : m_data.reporterSpecifications ) { + // We do the default-output check separately, while always + // using the default output below to make the code simpler + // and avoid superfluous copies. + if ( reporterSpec.outputFile().none() ) { + CATCH_ENFORCE( !defaultOutputUsed, + "Internal error: cannot use default output for " + "multiple reporters" ); + defaultOutputUsed = true; + } + + m_processedReporterSpecs.push_back( ProcessedReporterSpec{ + reporterSpec.name(), + reporterSpec.outputFile() ? *reporterSpec.outputFile() + : data.defaultOutputFilename, + reporterSpec.colourMode().valueOr( data.defaultColourMode ), + reporterSpec.customOptions() } ); + } + } + + Config::~Config() = default; + + + bool Config::listTests() const { return m_data.listTests; } + bool Config::listTags() const { return m_data.listTags; } + bool Config::listReporters() const { return m_data.listReporters; } + bool Config::listListeners() const { return m_data.listListeners; } + + std::vector const& Config::getTestsOrTags() const { return m_data.testsOrTags; } + std::vector const& Config::getSectionsToRun() const { return m_data.sectionsToRun; } + + std::vector const& Config::getReporterSpecs() const { + return m_data.reporterSpecifications; + } + + std::vector const& + Config::getProcessedReporterSpecs() const { + return m_processedReporterSpecs; + } + + TestSpec const& Config::testSpec() const { return m_testSpec; } + bool Config::hasTestFilters() const { return m_hasTestFilters; } + + bool Config::showHelp() const { return m_data.showHelp; } + + // IConfig interface + bool Config::allowThrows() const { return !m_data.noThrow; } + StringRef Config::name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } + bool Config::includeSuccessfulResults() const { return m_data.showSuccessfulTests; } + bool Config::warnAboutMissingAssertions() const { + return !!( m_data.warnings & WarnAbout::NoAssertions ); + } + bool Config::warnAboutUnmatchedTestSpecs() const { + return !!( m_data.warnings & WarnAbout::UnmatchedTestSpec ); + } + bool Config::zeroTestsCountAsSuccess() const { return m_data.allowZeroTests; } + ShowDurations Config::showDurations() const { return m_data.showDurations; } + double Config::minDuration() const { return m_data.minDuration; } + TestRunOrder Config::runOrder() const { return m_data.runOrder; } + uint32_t Config::rngSeed() const { return m_data.rngSeed; } + unsigned int Config::shardCount() const { return m_data.shardCount; } + unsigned int Config::shardIndex() const { return m_data.shardIndex; } + ColourMode Config::defaultColourMode() const { return m_data.defaultColourMode; } + bool Config::shouldDebugBreak() const { return m_data.shouldDebugBreak; } + int Config::abortAfter() const { return m_data.abortAfter; } + bool Config::showInvisibles() const { return m_data.showInvisibles; } + Verbosity Config::verbosity() const { return m_data.verbosity; } + + bool Config::skipBenchmarks() const { return m_data.skipBenchmarks; } + bool Config::benchmarkNoAnalysis() const { return m_data.benchmarkNoAnalysis; } + unsigned int Config::benchmarkSamples() const { return m_data.benchmarkSamples; } + double Config::benchmarkConfidenceInterval() const { return m_data.benchmarkConfidenceInterval; } + unsigned int Config::benchmarkResamples() const { return m_data.benchmarkResamples; } + std::chrono::milliseconds Config::benchmarkWarmupTime() const { return std::chrono::milliseconds(m_data.benchmarkWarmupTime); } + +} // end namespace Catch + + + + + +namespace Catch { + std::uint32_t getSeed() { + return getCurrentContext().getConfig()->rngSeed(); + } +} + + + +#include +#include + +namespace Catch { + + //////////////////////////////////////////////////////////////////////////// + + + ScopedMessage::ScopedMessage( MessageBuilder const& builder ): + m_info( builder.m_info ) { + m_info.message = builder.m_stream.str(); + getResultCapture().pushScopedMessage( m_info ); + } + + ScopedMessage::ScopedMessage( ScopedMessage&& old ) noexcept: + m_info( CATCH_MOVE( old.m_info ) ) { + old.m_moved = true; + } + + ScopedMessage::~ScopedMessage() { + if ( !uncaught_exceptions() && !m_moved ){ + getResultCapture().popScopedMessage(m_info); + } + } + + + Capturer::Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ) { + auto trimmed = [&] (size_t start, size_t end) { + while (names[start] == ',' || isspace(static_cast(names[start]))) { + ++start; + } + while (names[end] == ',' || isspace(static_cast(names[end]))) { + --end; + } + return names.substr(start, end - start + 1); + }; + auto skipq = [&] (size_t start, char quote) { + for (auto i = start + 1; i < names.size() ; ++i) { + if (names[i] == quote) + return i; + if (names[i] == '\\') + ++i; + } + CATCH_INTERNAL_ERROR("CAPTURE parsing encountered unmatched quote"); + }; + + size_t start = 0; + std::stack openings; + for (size_t pos = 0; pos < names.size(); ++pos) { + char c = names[pos]; + switch (c) { + case '[': + case '{': + case '(': + // It is basically impossible to disambiguate between + // comparison and start of template args in this context +// case '<': + openings.push(c); + break; + case ']': + case '}': + case ')': +// case '>': + openings.pop(); + break; + case '"': + case '\'': + pos = skipq(pos, c); + break; + case ',': + if (start != pos && openings.empty()) { + m_messages.emplace_back(macroName, lineInfo, resultType); + m_messages.back().message = static_cast(trimmed(start, pos)); + m_messages.back().message += " := "; + start = pos; + } + } + } + assert(openings.empty() && "Mismatched openings"); + m_messages.emplace_back(macroName, lineInfo, resultType); + m_messages.back().message = static_cast(trimmed(start, names.size() - 1)); + m_messages.back().message += " := "; + } + Capturer::~Capturer() { + if ( !uncaught_exceptions() ){ + assert( m_captured == m_messages.size() ); + for( size_t i = 0; i < m_captured; ++i ) + m_resultCapture.popScopedMessage( m_messages[i] ); + } + } + + void Capturer::captureValue( size_t index, std::string const& value ) { + assert( index < m_messages.size() ); + m_messages[index].message += value; + m_resultCapture.pushScopedMessage( m_messages[index] ); + m_captured++; + } + +} // end namespace Catch + + + + +namespace Catch { + + namespace { + + class RegistryHub : public IRegistryHub, + public IMutableRegistryHub, + private Detail::NonCopyable { + + public: // IRegistryHub + RegistryHub() = default; + IReporterRegistry const& getReporterRegistry() const override { + return m_reporterRegistry; + } + ITestCaseRegistry const& getTestCaseRegistry() const override { + return m_testCaseRegistry; + } + IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const override { + return m_exceptionTranslatorRegistry; + } + ITagAliasRegistry const& getTagAliasRegistry() const override { + return m_tagAliasRegistry; + } + StartupExceptionRegistry const& getStartupExceptionRegistry() const override { + return m_exceptionRegistry; + } + + public: // IMutableRegistryHub + void registerReporter( std::string const& name, IReporterFactoryPtr factory ) override { + m_reporterRegistry.registerReporter( name, CATCH_MOVE(factory) ); + } + void registerListener( Detail::unique_ptr factory ) override { + m_reporterRegistry.registerListener( CATCH_MOVE(factory) ); + } + void registerTest( Detail::unique_ptr&& testInfo, Detail::unique_ptr&& invoker ) override { + m_testCaseRegistry.registerTest( CATCH_MOVE(testInfo), CATCH_MOVE(invoker) ); + } + void registerTranslator( Detail::unique_ptr&& translator ) override { + m_exceptionTranslatorRegistry.registerTranslator( CATCH_MOVE(translator) ); + } + void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) override { + m_tagAliasRegistry.add( alias, tag, lineInfo ); + } + void registerStartupException() noexcept override { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + m_exceptionRegistry.add(std::current_exception()); +#else + CATCH_INTERNAL_ERROR("Attempted to register active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); +#endif + } + IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() override { + return m_enumValuesRegistry; + } + + private: + TestRegistry m_testCaseRegistry; + ReporterRegistry m_reporterRegistry; + ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; + TagAliasRegistry m_tagAliasRegistry; + StartupExceptionRegistry m_exceptionRegistry; + Detail::EnumValuesRegistry m_enumValuesRegistry; + }; + } + + using RegistryHubSingleton = Singleton; + + IRegistryHub const& getRegistryHub() { + return RegistryHubSingleton::get(); + } + IMutableRegistryHub& getMutableRegistryHub() { + return RegistryHubSingleton::getMutable(); + } + void cleanUp() { + cleanupSingletons(); + cleanUpContext(); + } + std::string translateActiveException() { + return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); + } + + +} // end namespace Catch + + + +#include +#include +#include +#include + +namespace Catch { + + namespace { + const int MaxExitCode = 255; + + IEventListenerPtr createReporter(std::string const& reporterName, ReporterConfig&& config) { + auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, CATCH_MOVE(config)); + CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << '\''); + + return reporter; + } + + IEventListenerPtr prepareReporters(Config const* config) { + if (Catch::getRegistryHub().getReporterRegistry().getListeners().empty() + && config->getProcessedReporterSpecs().size() == 1) { + auto const& spec = config->getProcessedReporterSpecs()[0]; + return createReporter( + spec.name, + ReporterConfig( config, + makeStream( spec.outputFilename ), + spec.colourMode, + spec.customOptions ) ); + } + + auto multi = Detail::make_unique(config); + + auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners(); + for (auto const& listener : listeners) { + multi->addListener(listener->create(config)); + } + + for ( auto const& reporterSpec : config->getProcessedReporterSpecs() ) { + multi->addReporter( createReporter( + reporterSpec.name, + ReporterConfig( config, + makeStream( reporterSpec.outputFilename ), + reporterSpec.colourMode, + reporterSpec.customOptions ) ) ); + } + + return multi; + } + + class TestGroup { + public: + explicit TestGroup(IEventListenerPtr&& reporter, Config const* config): + m_reporter(reporter.get()), + m_config{config}, + m_context{config, CATCH_MOVE(reporter)} { + + assert( m_config->testSpec().getInvalidSpecs().empty() && + "Invalid test specs should be handled before running tests" ); + + auto const& allTestCases = getAllTestCasesSorted(*m_config); + auto const& testSpec = m_config->testSpec(); + if ( !testSpec.hasFilters() ) { + for ( auto const& test : allTestCases ) { + if ( !test.getTestCaseInfo().isHidden() ) { + m_tests.emplace( &test ); + } + } + } else { + m_matches = + testSpec.matchesByFilter( allTestCases, *m_config ); + for ( auto const& match : m_matches ) { + m_tests.insert( match.tests.begin(), + match.tests.end() ); + } + } + + m_tests = createShard(m_tests, m_config->shardCount(), m_config->shardIndex()); + } + + Totals execute() { + Totals totals; + for (auto const& testCase : m_tests) { + if (!m_context.aborting()) + totals += m_context.runTest(*testCase); + else + m_reporter->skipTest(testCase->getTestCaseInfo()); + } + + for (auto const& match : m_matches) { + if (match.tests.empty()) { + m_unmatchedTestSpecs = true; + m_reporter->noMatchingTestCases( match.name ); + } + } + + return totals; + } + + bool hadUnmatchedTestSpecs() const { + return m_unmatchedTestSpecs; + } + + + private: + IEventListener* m_reporter; + Config const* m_config; + RunContext m_context; + std::set m_tests; + TestSpec::Matches m_matches; + bool m_unmatchedTestSpecs = false; + }; + + void applyFilenamesAsTags() { + for (auto const& testInfo : getRegistryHub().getTestCaseRegistry().getAllInfos()) { + testInfo->addFilenameTag(); + } + } + + } // anon namespace + + Session::Session() { + static bool alreadyInstantiated = false; + if( alreadyInstantiated ) { + CATCH_TRY { CATCH_INTERNAL_ERROR( "Only one instance of Catch::Session can ever be used" ); } + CATCH_CATCH_ALL { getMutableRegistryHub().registerStartupException(); } + } + + // There cannot be exceptions at startup in no-exception mode. +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + const auto& exceptions = getRegistryHub().getStartupExceptionRegistry().getExceptions(); + if ( !exceptions.empty() ) { + config(); + getCurrentMutableContext().setConfig(m_config.get()); + + m_startupExceptions = true; + auto errStream = makeStream( "%stderr" ); + auto colourImpl = makeColourImpl( + ColourMode::PlatformDefault, errStream.get() ); + auto guard = colourImpl->guardColour( Colour::Red ); + errStream->stream() << "Errors occurred during startup!" << '\n'; + // iterate over all exceptions and notify user + for ( const auto& ex_ptr : exceptions ) { + try { + std::rethrow_exception(ex_ptr); + } catch ( std::exception const& ex ) { + errStream->stream() << TextFlow::Column( ex.what() ).indent(2) << '\n'; + } + } + } +#endif + + alreadyInstantiated = true; + m_cli = makeCommandLineParser( m_configData ); + } + Session::~Session() { + Catch::cleanUp(); + } + + void Session::showHelp() const { + Catch::cout() + << "\nCatch2 v" << libraryVersion() << '\n' + << m_cli << '\n' + << "For more detailed usage please see the project docs\n\n" << std::flush; + } + void Session::libIdentify() { + Catch::cout() + << std::left << std::setw(16) << "description: " << "A Catch2 test executable\n" + << std::left << std::setw(16) << "category: " << "testframework\n" + << std::left << std::setw(16) << "framework: " << "Catch2\n" + << std::left << std::setw(16) << "version: " << libraryVersion() << '\n' << std::flush; + } + + int Session::applyCommandLine( int argc, char const * const * argv ) { + if( m_startupExceptions ) + return 1; + + auto result = m_cli.parse( Clara::Args( argc, argv ) ); + + if( !result ) { + config(); + getCurrentMutableContext().setConfig(m_config.get()); + auto errStream = makeStream( "%stderr" ); + auto colour = makeColourImpl( ColourMode::PlatformDefault, errStream.get() ); + + errStream->stream() + << colour->guardColour( Colour::Red ) + << "\nError(s) in input:\n" + << TextFlow::Column( result.errorMessage() ).indent( 2 ) + << "\n\n"; + errStream->stream() << "Run with -? for usage\n\n" << std::flush; + return MaxExitCode; + } + + if( m_configData.showHelp ) + showHelp(); + if( m_configData.libIdentify ) + libIdentify(); + + m_config.reset(); + return 0; + } + +#if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE) + int Session::applyCommandLine( int argc, wchar_t const * const * argv ) { + + char **utf8Argv = new char *[ argc ]; + + for ( int i = 0; i < argc; ++i ) { + int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, nullptr, 0, nullptr, nullptr ); + + utf8Argv[ i ] = new char[ bufSize ]; + + WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, nullptr, nullptr ); + } + + int returnCode = applyCommandLine( argc, utf8Argv ); + + for ( int i = 0; i < argc; ++i ) + delete [] utf8Argv[ i ]; + + delete [] utf8Argv; + + return returnCode; + } +#endif + + void Session::useConfigData( ConfigData const& configData ) { + m_configData = configData; + m_config.reset(); + } + + int Session::run() { + if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeStart ) != 0 ) { + Catch::cout() << "...waiting for enter/ return before starting\n" << std::flush; + static_cast(std::getchar()); + } + int exitCode = runInternal(); + if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) { + Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << '\n' << std::flush; + static_cast(std::getchar()); + } + return exitCode; + } + + Clara::Parser const& Session::cli() const { + return m_cli; + } + void Session::cli( Clara::Parser const& newParser ) { + m_cli = newParser; + } + ConfigData& Session::configData() { + return m_configData; + } + Config& Session::config() { + if( !m_config ) + m_config = Detail::make_unique( m_configData ); + return *m_config; + } + + int Session::runInternal() { + if( m_startupExceptions ) + return 1; + + if (m_configData.showHelp || m_configData.libIdentify) { + return 0; + } + + if ( m_configData.shardIndex >= m_configData.shardCount ) { + Catch::cerr() << "The shard count (" << m_configData.shardCount + << ") must be greater than the shard index (" + << m_configData.shardIndex << ")\n" + << std::flush; + return 1; + } + + CATCH_TRY { + config(); // Force config to be constructed + + seedRng( *m_config ); + + if (m_configData.filenamesAsTags) { + applyFilenamesAsTags(); + } + + // Set up global config instance before we start calling into other functions + getCurrentMutableContext().setConfig(m_config.get()); + + // Create reporter(s) so we can route listings through them + auto reporter = prepareReporters(m_config.get()); + + auto const& invalidSpecs = m_config->testSpec().getInvalidSpecs(); + if ( !invalidSpecs.empty() ) { + for ( auto const& spec : invalidSpecs ) { + reporter->reportInvalidTestSpec( spec ); + } + return 1; + } + + + // Handle list request + if (list(*reporter, *m_config)) { + return 0; + } + + TestGroup tests { CATCH_MOVE(reporter), m_config.get() }; + auto const totals = tests.execute(); + + if ( tests.hadUnmatchedTestSpecs() + && m_config->warnAboutUnmatchedTestSpecs() ) { + return 3; + } + + if ( totals.testCases.total() == 0 + && !m_config->zeroTestsCountAsSuccess() ) { + return 2; + } + + // Note that on unices only the lower 8 bits are usually used, clamping + // the return value to 255 prevents false negative when some multiple + // of 256 tests has failed + return (std::min) (MaxExitCode, static_cast(totals.assertions.failed)); + } +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + catch( std::exception& ex ) { + Catch::cerr() << ex.what() << '\n' << std::flush; + return MaxExitCode; + } +#endif + } + +} // end namespace Catch + + + + +namespace Catch { + + RegistrarForTagAliases::RegistrarForTagAliases(char const* alias, char const* tag, SourceLineInfo const& lineInfo) { + CATCH_TRY { + getMutableRegistryHub().registerTagAlias(alias, tag, lineInfo); + } CATCH_CATCH_ALL { + // Do not throw when constructing global objects, instead register the exception to be processed later + getMutableRegistryHub().registerStartupException(); + } + } + +} + + + +#include +#include +#include + +namespace Catch { + + namespace { + using TCP_underlying_type = uint8_t; + static_assert(sizeof(TestCaseProperties) == sizeof(TCP_underlying_type), + "The size of the TestCaseProperties is different from the assumed size"); + + TestCaseProperties operator|(TestCaseProperties lhs, TestCaseProperties rhs) { + return static_cast( + static_cast(lhs) | static_cast(rhs) + ); + } + + TestCaseProperties& operator|=(TestCaseProperties& lhs, TestCaseProperties rhs) { + lhs = static_cast( + static_cast(lhs) | static_cast(rhs) + ); + return lhs; + } + + TestCaseProperties operator&(TestCaseProperties lhs, TestCaseProperties rhs) { + return static_cast( + static_cast(lhs) & static_cast(rhs) + ); + } + + bool applies(TestCaseProperties tcp) { + static_assert(static_cast(TestCaseProperties::None) == 0, + "TestCaseProperties::None must be equal to 0"); + return tcp != TestCaseProperties::None; + } + + TestCaseProperties parseSpecialTag( StringRef tag ) { + if( !tag.empty() && tag[0] == '.' ) + return TestCaseProperties::IsHidden; + else if( tag == "!throws"_sr ) + return TestCaseProperties::Throws; + else if( tag == "!shouldfail"_sr ) + return TestCaseProperties::ShouldFail; + else if( tag == "!mayfail"_sr ) + return TestCaseProperties::MayFail; + else if( tag == "!nonportable"_sr ) + return TestCaseProperties::NonPortable; + else if( tag == "!benchmark"_sr ) + return TestCaseProperties::Benchmark | TestCaseProperties::IsHidden; + else + return TestCaseProperties::None; + } + bool isReservedTag( StringRef tag ) { + return parseSpecialTag( tag ) == TestCaseProperties::None + && tag.size() > 0 + && !std::isalnum( static_cast(tag[0]) ); + } + void enforceNotReservedTag( StringRef tag, SourceLineInfo const& _lineInfo ) { + CATCH_ENFORCE( !isReservedTag(tag), + "Tag name: [" << tag << "] is not allowed.\n" + << "Tag names starting with non alphanumeric characters are reserved\n" + << _lineInfo ); + } + + std::string makeDefaultName() { + static size_t counter = 0; + return "Anonymous test case " + std::to_string(++counter); + } + + StringRef extractFilenamePart(StringRef filename) { + size_t lastDot = filename.size(); + while (lastDot > 0 && filename[lastDot - 1] != '.') { + --lastDot; + } + --lastDot; + + size_t nameStart = lastDot; + while (nameStart > 0 && filename[nameStart - 1] != '/' && filename[nameStart - 1] != '\\') { + --nameStart; + } + + return filename.substr(nameStart, lastDot - nameStart); + } + + // Returns the upper bound on size of extra tags ([#file]+[.]) + size_t sizeOfExtraTags(StringRef filepath) { + // [.] is 3, [#] is another 3 + const size_t extras = 3 + 3; + return extractFilenamePart(filepath).size() + extras; + } + } // end unnamed namespace + + bool operator<( Tag const& lhs, Tag const& rhs ) { + Detail::CaseInsensitiveLess cmp; + return cmp( lhs.original, rhs.original ); + } + bool operator==( Tag const& lhs, Tag const& rhs ) { + Detail::CaseInsensitiveEqualTo cmp; + return cmp( lhs.original, rhs.original ); + } + + Detail::unique_ptr + makeTestCaseInfo(StringRef _className, + NameAndTags const& nameAndTags, + SourceLineInfo const& _lineInfo ) { + return Detail::make_unique(_className, nameAndTags, _lineInfo); + } + + TestCaseInfo::TestCaseInfo(StringRef _className, + NameAndTags const& _nameAndTags, + SourceLineInfo const& _lineInfo): + name( _nameAndTags.name.empty() ? makeDefaultName() : _nameAndTags.name ), + className( _className ), + lineInfo( _lineInfo ) + { + StringRef originalTags = _nameAndTags.tags; + // We need to reserve enough space to store all of the tags + // (including optional hidden tag and filename tag) + auto requiredSize = originalTags.size() + sizeOfExtraTags(_lineInfo.file); + backingTags.reserve(requiredSize); + + // We cannot copy the tags directly, as we need to normalize + // some tags, so that [.foo] is copied as [.][foo]. + size_t tagStart = 0; + size_t tagEnd = 0; + bool inTag = false; + for (size_t idx = 0; idx < originalTags.size(); ++idx) { + auto c = originalTags[idx]; + if (c == '[') { + assert(!inTag); + inTag = true; + tagStart = idx; + } + if (c == ']') { + assert(inTag); + inTag = false; + tagEnd = idx; + assert(tagStart < tagEnd); + + // We need to check the tag for special meanings, copy + // it over to backing storage and actually reference the + // backing storage in the saved tags + StringRef tagStr = originalTags.substr(tagStart+1, tagEnd - tagStart - 1); + CATCH_ENFORCE(!tagStr.empty(), "Empty tags are not allowed"); + enforceNotReservedTag(tagStr, lineInfo); + properties |= parseSpecialTag(tagStr); + // When copying a tag to the backing storage, we need to + // check if it is a merged hide tag, such as [.foo], and + // if it is, we need to handle it as if it was [foo]. + if (tagStr.size() > 1 && tagStr[0] == '.') { + tagStr = tagStr.substr(1, tagStr.size() - 1); + } + // We skip over dealing with the [.] tag, as we will add + // it later unconditionally and then sort and unique all + // the tags. + internalAppendTag(tagStr); + } + (void)inTag; // Silence "set-but-unused" warning in release mode. + } + // Add [.] if relevant + if (isHidden()) { + internalAppendTag("."_sr); + } + + // Sort and prepare tags + std::sort(begin(tags), end(tags)); + tags.erase(std::unique(begin(tags), end(tags)), + end(tags)); + } + + bool TestCaseInfo::isHidden() const { + return applies( properties & TestCaseProperties::IsHidden ); + } + bool TestCaseInfo::throws() const { + return applies( properties & TestCaseProperties::Throws ); + } + bool TestCaseInfo::okToFail() const { + return applies( properties & (TestCaseProperties::ShouldFail | TestCaseProperties::MayFail ) ); + } + bool TestCaseInfo::expectedToFail() const { + return applies( properties & (TestCaseProperties::ShouldFail) ); + } + + void TestCaseInfo::addFilenameTag() { + std::string combined("#"); + combined += extractFilenamePart(lineInfo.file); + internalAppendTag(combined); + } + + std::string TestCaseInfo::tagsAsString() const { + std::string ret; + // '[' and ']' per tag + std::size_t full_size = 2 * tags.size(); + for (const auto& tag : tags) { + full_size += tag.original.size(); + } + ret.reserve(full_size); + for (const auto& tag : tags) { + ret.push_back('['); + ret += tag.original; + ret.push_back(']'); + } + + return ret; + } + + void TestCaseInfo::internalAppendTag(StringRef tagStr) { + backingTags += '['; + const auto backingStart = backingTags.size(); + backingTags += tagStr; + const auto backingEnd = backingTags.size(); + backingTags += ']'; + tags.emplace_back(StringRef(backingTags.c_str() + backingStart, backingEnd - backingStart)); + } + + bool operator<( TestCaseInfo const& lhs, TestCaseInfo const& rhs ) { + // We want to avoid redoing the string comparisons multiple times, + // so we store the result of a three-way comparison before using + // it in the actual comparison logic. + const auto cmpName = lhs.name.compare( rhs.name ); + if ( cmpName != 0 ) { + return cmpName < 0; + } + const auto cmpClassName = lhs.className.compare( rhs.className ); + if ( cmpClassName != 0 ) { + return cmpClassName < 0; + } + return lhs.tags < rhs.tags; + } + + TestCaseInfo const& TestCaseHandle::getTestCaseInfo() const { + return *m_info; + } + +} // end namespace Catch + + + +#include +#include +#include + +namespace Catch { + + TestSpec::Pattern::Pattern( std::string const& name ) + : m_name( name ) + {} + + TestSpec::Pattern::~Pattern() = default; + + std::string const& TestSpec::Pattern::name() const { + return m_name; + } + + + TestSpec::NamePattern::NamePattern( std::string const& name, std::string const& filterString ) + : Pattern( filterString ) + , m_wildcardPattern( toLower( name ), CaseSensitive::No ) + {} + + bool TestSpec::NamePattern::matches( TestCaseInfo const& testCase ) const { + return m_wildcardPattern.matches( testCase.name ); + } + + + TestSpec::TagPattern::TagPattern( std::string const& tag, std::string const& filterString ) + : Pattern( filterString ) + , m_tag( tag ) + {} + + bool TestSpec::TagPattern::matches( TestCaseInfo const& testCase ) const { + return std::find( begin( testCase.tags ), + end( testCase.tags ), + Tag( m_tag ) ) != end( testCase.tags ); + } + + bool TestSpec::Filter::matches( TestCaseInfo const& testCase ) const { + bool should_use = !testCase.isHidden(); + for (auto const& pattern : m_required) { + should_use = true; + if (!pattern->matches(testCase)) { + return false; + } + } + for (auto const& pattern : m_forbidden) { + if (pattern->matches(testCase)) { + return false; + } + } + return should_use; + } + + std::string TestSpec::Filter::name() const { + std::string name; + for (auto const& p : m_required) { + name += p->name(); + } + for (auto const& p : m_forbidden) { + name += p->name(); + } + return name; + } + + + bool TestSpec::hasFilters() const { + return !m_filters.empty(); + } + + bool TestSpec::matches( TestCaseInfo const& testCase ) const { + return std::any_of( m_filters.begin(), m_filters.end(), [&]( Filter const& f ){ return f.matches( testCase ); } ); + } + + TestSpec::Matches TestSpec::matchesByFilter( std::vector const& testCases, IConfig const& config ) const + { + Matches matches( m_filters.size() ); + std::transform( m_filters.begin(), m_filters.end(), matches.begin(), [&]( Filter const& filter ){ + std::vector currentMatches; + for( auto const& test : testCases ) + if( isThrowSafe( test, config ) && filter.matches( test.getTestCaseInfo() ) ) + currentMatches.emplace_back( &test ); + return FilterMatch{ filter.name(), currentMatches }; + } ); + return matches; + } + + const TestSpec::vectorStrings& TestSpec::getInvalidSpecs() const { + return m_invalidSpecs; + } + +} + + + +#include + +namespace Catch { + + namespace { + static auto getCurrentNanosecondsSinceEpoch() -> uint64_t { + return std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + } + } // end unnamed namespace + + void Timer::start() { + m_nanoseconds = getCurrentNanosecondsSinceEpoch(); + } + auto Timer::getElapsedNanoseconds() const -> uint64_t { + return getCurrentNanosecondsSinceEpoch() - m_nanoseconds; + } + auto Timer::getElapsedMicroseconds() const -> uint64_t { + return getElapsedNanoseconds()/1000; + } + auto Timer::getElapsedMilliseconds() const -> unsigned int { + return static_cast(getElapsedMicroseconds()/1000); + } + auto Timer::getElapsedSeconds() const -> double { + return getElapsedMicroseconds()/1000000.0; + } + + +} // namespace Catch + + + + +#include +#include + +namespace Catch { + +namespace Detail { + + namespace { + const int hexThreshold = 255; + + struct Endianness { + enum Arch { Big, Little }; + + static Arch which() { + int one = 1; + // If the lowest byte we read is non-zero, we can assume + // that little endian format is used. + auto value = *reinterpret_cast(&one); + return value ? Little : Big; + } + }; + + template + std::string fpToString(T value, int precision) { + if (Catch::isnan(value)) { + return "nan"; + } + + ReusableStringStream rss; + rss << std::setprecision(precision) + << std::fixed + << value; + std::string d = rss.str(); + std::size_t i = d.find_last_not_of('0'); + if (i != std::string::npos && i != d.size() - 1) { + if (d[i] == '.') + i++; + d = d.substr(0, i + 1); + } + return d; + } + } // end unnamed namespace + + std::string convertIntoString(StringRef string, bool escape_invisibles) { + std::string ret; + // This is enough for the "don't escape invisibles" case, and a good + // lower bound on the "escape invisibles" case. + ret.reserve(string.size() + 2); + + if (!escape_invisibles) { + ret += '"'; + ret += string; + ret += '"'; + return ret; + } + + ret += '"'; + for (char c : string) { + switch (c) { + case '\r': + ret.append("\\r"); + break; + case '\n': + ret.append("\\n"); + break; + case '\t': + ret.append("\\t"); + break; + case '\f': + ret.append("\\f"); + break; + default: + ret.push_back(c); + break; + } + } + ret += '"'; + + return ret; + } + + std::string convertIntoString(StringRef string) { + return convertIntoString(string, getCurrentContext().getConfig()->showInvisibles()); + } + + std::string rawMemoryToString( const void *object, std::size_t size ) { + // Reverse order for little endian architectures + int i = 0, end = static_cast( size ), inc = 1; + if( Endianness::which() == Endianness::Little ) { + i = end-1; + end = inc = -1; + } + + unsigned char const *bytes = static_cast(object); + ReusableStringStream rss; + rss << "0x" << std::setfill('0') << std::hex; + for( ; i != end; i += inc ) + rss << std::setw(2) << static_cast(bytes[i]); + return rss.str(); + } +} // end Detail namespace + + + +//// ======================================================= //// +// +// Out-of-line defs for full specialization of StringMaker +// +//// ======================================================= //// + +std::string StringMaker::convert(const std::string& str) { + return Detail::convertIntoString( str ); +} + +#ifdef CATCH_CONFIG_CPP17_STRING_VIEW +std::string StringMaker::convert(std::string_view str) { + return Detail::convertIntoString( StringRef( str.data(), str.size() ) ); +} +#endif + +std::string StringMaker::convert(char const* str) { + if (str) { + return Detail::convertIntoString( str ); + } else { + return{ "{null string}" }; + } +} +std::string StringMaker::convert(char* str) { + if (str) { + return Detail::convertIntoString( str ); + } else { + return{ "{null string}" }; + } +} + +#ifdef CATCH_CONFIG_WCHAR +std::string StringMaker::convert(const std::wstring& wstr) { + std::string s; + s.reserve(wstr.size()); + for (auto c : wstr) { + s += (c <= 0xff) ? static_cast(c) : '?'; + } + return ::Catch::Detail::stringify(s); +} + +# ifdef CATCH_CONFIG_CPP17_STRING_VIEW +std::string StringMaker::convert(std::wstring_view str) { + return StringMaker::convert(std::wstring(str)); +} +# endif + +std::string StringMaker::convert(wchar_t const * str) { + if (str) { + return ::Catch::Detail::stringify(std::wstring{ str }); + } else { + return{ "{null string}" }; + } +} +std::string StringMaker::convert(wchar_t * str) { + if (str) { + return ::Catch::Detail::stringify(std::wstring{ str }); + } else { + return{ "{null string}" }; + } +} +#endif + +#if defined(CATCH_CONFIG_CPP17_BYTE) +#include +std::string StringMaker::convert(std::byte value) { + return ::Catch::Detail::stringify(std::to_integer(value)); +} +#endif // defined(CATCH_CONFIG_CPP17_BYTE) + +std::string StringMaker::convert(int value) { + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(long value) { + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(long long value) { + ReusableStringStream rss; + rss << value; + if (value > Detail::hexThreshold) { + rss << " (0x" << std::hex << value << ')'; + } + return rss.str(); +} + +std::string StringMaker::convert(unsigned int value) { + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(unsigned long value) { + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(unsigned long long value) { + ReusableStringStream rss; + rss << value; + if (value > Detail::hexThreshold) { + rss << " (0x" << std::hex << value << ')'; + } + return rss.str(); +} + +std::string StringMaker::convert(signed char value) { + if (value == '\r') { + return "'\\r'"; + } else if (value == '\f') { + return "'\\f'"; + } else if (value == '\n') { + return "'\\n'"; + } else if (value == '\t') { + return "'\\t'"; + } else if ('\0' <= value && value < ' ') { + return ::Catch::Detail::stringify(static_cast(value)); + } else { + char chstr[] = "' '"; + chstr[1] = value; + return chstr; + } +} +std::string StringMaker::convert(char c) { + return ::Catch::Detail::stringify(static_cast(c)); +} +std::string StringMaker::convert(unsigned char c) { + return ::Catch::Detail::stringify(static_cast(c)); +} + +int StringMaker::precision = 5; + +std::string StringMaker::convert(float value) { + return Detail::fpToString(value, precision) + 'f'; +} + +int StringMaker::precision = 10; + +std::string StringMaker::convert(double value) { + return Detail::fpToString(value, precision); +} + +} // end namespace Catch + + + +namespace Catch { + + Counts Counts::operator - ( Counts const& other ) const { + Counts diff; + diff.passed = passed - other.passed; + diff.failed = failed - other.failed; + diff.failedButOk = failedButOk - other.failedButOk; + return diff; + } + + Counts& Counts::operator += ( Counts const& other ) { + passed += other.passed; + failed += other.failed; + failedButOk += other.failedButOk; + return *this; + } + + std::uint64_t Counts::total() const { + return passed + failed + failedButOk; + } + bool Counts::allPassed() const { + return failed == 0 && failedButOk == 0; + } + bool Counts::allOk() const { + return failed == 0; + } + + Totals Totals::operator - ( Totals const& other ) const { + Totals diff; + diff.assertions = assertions - other.assertions; + diff.testCases = testCases - other.testCases; + return diff; + } + + Totals& Totals::operator += ( Totals const& other ) { + assertions += other.assertions; + testCases += other.testCases; + return *this; + } + + Totals Totals::delta( Totals const& prevTotals ) const { + Totals diff = *this - prevTotals; + if( diff.assertions.failed > 0 ) + ++diff.testCases.failed; + else if( diff.assertions.failedButOk > 0 ) + ++diff.testCases.failedButOk; + else + ++diff.testCases.passed; + return diff; + } + +} + + +#include + +namespace Catch { + + Version::Version + ( unsigned int _majorVersion, + unsigned int _minorVersion, + unsigned int _patchNumber, + char const * const _branchName, + unsigned int _buildNumber ) + : majorVersion( _majorVersion ), + minorVersion( _minorVersion ), + patchNumber( _patchNumber ), + branchName( _branchName ), + buildNumber( _buildNumber ) + {} + + std::ostream& operator << ( std::ostream& os, Version const& version ) { + os << version.majorVersion << '.' + << version.minorVersion << '.' + << version.patchNumber; + // branchName is never null -> 0th char is \0 if it is empty + if (version.branchName[0]) { + os << '-' << version.branchName + << '.' << version.buildNumber; + } + return os; + } + + Version const& libraryVersion() { + static Version version( 3, 1, 1, "", 0 ); + return version; + } + +} + + + + +namespace Catch { + + const char* GeneratorException::what() const noexcept { + return m_msg; + } + +} // end namespace Catch + + + + +namespace Catch { + + IGeneratorTracker::~IGeneratorTracker() = default; + +namespace Generators { + +namespace Detail { + + [[noreturn]] + void throw_generator_exception(char const* msg) { + Catch::throw_exception(GeneratorException{ msg }); + } +} // end namespace Detail + + GeneratorUntypedBase::~GeneratorUntypedBase() = default; + + auto acquireGeneratorTracker(StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { + return getResultCapture().acquireGeneratorTracker( generatorName, lineInfo ); + } + +} // namespace Generators +} // namespace Catch + + + + + +std::uint32_t Catch::Generators::Detail::getSeed() { return sharedRng()(); } + + + + +namespace Catch { + IResultCapture::~IResultCapture() = default; +} + + + + +namespace Catch { + IConfig::~IConfig() = default; +} + + + + +namespace Catch { + IExceptionTranslator::~IExceptionTranslator() = default; + IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() = default; +} + + + +#include + +namespace Catch { + namespace Generators { + + bool GeneratorUntypedBase::countedNext() { + auto ret = next(); + if ( ret ) { + m_stringReprCache.clear(); + ++m_currentElementIndex; + } + return ret; + } + + StringRef GeneratorUntypedBase::currentElementAsString() const { + if ( m_stringReprCache.empty() ) { + m_stringReprCache = stringifyImpl(); + } + return m_stringReprCache; + } + + } // namespace Generators +} // namespace Catch + + + + +namespace Catch { + IRegistryHub::~IRegistryHub() = default; + IMutableRegistryHub::~IMutableRegistryHub() = default; +} + + + +#include +#include +#include + +namespace Catch { + + ReporterConfig::ReporterConfig( + IConfig const* _fullConfig, + Detail::unique_ptr _stream, + ColourMode colourMode, + std::map customOptions ): + m_stream( CATCH_MOVE(_stream) ), + m_fullConfig( _fullConfig ), + m_colourMode( colourMode ), + m_customOptions( CATCH_MOVE( customOptions ) ) {} + + Detail::unique_ptr ReporterConfig::takeStream() && { + assert( m_stream ); + return CATCH_MOVE( m_stream ); + } + IConfig const * ReporterConfig::fullConfig() const { return m_fullConfig; } + ColourMode ReporterConfig::colourMode() const { return m_colourMode; } + + std::map const& + ReporterConfig::customOptions() const { + return m_customOptions; + } + + ReporterConfig::~ReporterConfig() = default; + + AssertionStats::AssertionStats( AssertionResult const& _assertionResult, + std::vector const& _infoMessages, + Totals const& _totals ) + : assertionResult( _assertionResult ), + infoMessages( _infoMessages ), + totals( _totals ) + { + assertionResult.m_resultData.lazyExpression.m_transientExpression = _assertionResult.m_resultData.lazyExpression.m_transientExpression; + + if( assertionResult.hasMessage() ) { + // Copy message into messages list. + // !TBD This should have been done earlier, somewhere + MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() ); + builder << assertionResult.getMessage(); + builder.m_info.message = builder.m_stream.str(); + + infoMessages.push_back( builder.m_info ); + } + } + + SectionStats::SectionStats( SectionInfo const& _sectionInfo, + Counts const& _assertions, + double _durationInSeconds, + bool _missingAssertions ) + : sectionInfo( _sectionInfo ), + assertions( _assertions ), + durationInSeconds( _durationInSeconds ), + missingAssertions( _missingAssertions ) + {} + + + TestCaseStats::TestCaseStats( TestCaseInfo const& _testInfo, + Totals const& _totals, + std::string const& _stdOut, + std::string const& _stdErr, + bool _aborting ) + : testInfo( &_testInfo ), + totals( _totals ), + stdOut( _stdOut ), + stdErr( _stdErr ), + aborting( _aborting ) + {} + + + TestRunStats::TestRunStats( TestRunInfo const& _runInfo, + Totals const& _totals, + bool _aborting ) + : runInfo( _runInfo ), + totals( _totals ), + aborting( _aborting ) + {} + + IEventListener::~IEventListener() = default; + +} // end namespace Catch + + + + +namespace Catch { + IReporterFactory::~IReporterFactory() = default; + EventListenerFactory::~EventListenerFactory() = default; +} + + + + +namespace Catch { + IReporterRegistry::~IReporterRegistry() = default; +} + + + + +namespace Catch { + ITestInvoker::~ITestInvoker() = default; + ITestCaseRegistry::~ITestCaseRegistry() = default; +} + + + +namespace Catch { + + AssertionHandler::AssertionHandler + ( StringRef macroName, + SourceLineInfo const& lineInfo, + StringRef capturedExpression, + ResultDisposition::Flags resultDisposition ) + : m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition }, + m_resultCapture( getResultCapture() ) + {} + + void AssertionHandler::handleExpr( ITransientExpression const& expr ) { + m_resultCapture.handleExpr( m_assertionInfo, expr, m_reaction ); + } + void AssertionHandler::handleMessage(ResultWas::OfType resultType, StringRef message) { + m_resultCapture.handleMessage( m_assertionInfo, resultType, message, m_reaction ); + } + + auto AssertionHandler::allowThrows() const -> bool { + return getCurrentContext().getConfig()->allowThrows(); + } + + void AssertionHandler::complete() { + setCompleted(); + if( m_reaction.shouldDebugBreak ) { + + // If you find your debugger stopping you here then go one level up on the + // call-stack for the code that caused it (typically a failed assertion) + + // (To go back to the test and change execution, jump over the throw, next) + CATCH_BREAK_INTO_DEBUGGER(); + } + if (m_reaction.shouldThrow) { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + throw Catch::TestFailureException(); +#else + CATCH_ERROR( "Test failure requires aborting test!" ); +#endif + } + } + void AssertionHandler::setCompleted() { + m_completed = true; + } + + void AssertionHandler::handleUnexpectedInflightException() { + m_resultCapture.handleUnexpectedInflightException( m_assertionInfo, Catch::translateActiveException(), m_reaction ); + } + + void AssertionHandler::handleExceptionThrownAsExpected() { + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); + } + void AssertionHandler::handleExceptionNotThrownAsExpected() { + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); + } + + void AssertionHandler::handleUnexpectedExceptionNotThrown() { + m_resultCapture.handleUnexpectedExceptionNotThrown( m_assertionInfo, m_reaction ); + } + + void AssertionHandler::handleThrowingCallSkipped() { + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); + } + + // This is the overload that takes a string and infers the Equals matcher from it + // The more general overload, that takes any string matcher, is in catch_capture_matchers.cpp + void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef matcherString ) { + handleExceptionMatchExpr( handler, Matchers::Equals( str ), matcherString ); + } + +} // namespace Catch + + + + +#include + +namespace Catch { + namespace Detail { + + bool CaseInsensitiveLess::operator()( StringRef lhs, + StringRef rhs ) const { + return std::lexicographical_compare( + lhs.begin(), lhs.end(), + rhs.begin(), rhs.end(), + []( char l, char r ) { return toLower( l ) < toLower( r ); } ); + } + + bool + CaseInsensitiveEqualTo::operator()( StringRef lhs, + StringRef rhs ) const { + return std::equal( + lhs.begin(), lhs.end(), + rhs.begin(), rhs.end(), + []( char l, char r ) { return toLower( l ) == toLower( r ); } ); + } + + } // namespace Detail +} // namespace Catch + + + + +#include +#include + +namespace { + bool isOptPrefix( char c ) { + return c == '-' +#ifdef CATCH_PLATFORM_WINDOWS + || c == '/' +#endif + ; + } + + std::string normaliseOpt( std::string const& optName ) { +#ifdef CATCH_PLATFORM_WINDOWS + if ( optName[0] == '/' ) + return "-" + optName.substr( 1 ); + else +#endif + return optName; + } + +} // namespace + +namespace Catch { + namespace Clara { + namespace Detail { + + void TokenStream::loadBuffer() { + m_tokenBuffer.clear(); + + // Skip any empty strings + while ( it != itEnd && it->empty() ) { + ++it; + } + + if ( it != itEnd ) { + auto const& next = *it; + if ( isOptPrefix( next[0] ) ) { + auto delimiterPos = next.find_first_of( " :=" ); + if ( delimiterPos != std::string::npos ) { + m_tokenBuffer.push_back( + { TokenType::Option, + next.substr( 0, delimiterPos ) } ); + m_tokenBuffer.push_back( + { TokenType::Argument, + next.substr( delimiterPos + 1 ) } ); + } else { + if ( next[1] != '-' && next.size() > 2 ) { + std::string opt = "- "; + for ( size_t i = 1; i < next.size(); ++i ) { + opt[1] = next[i]; + m_tokenBuffer.push_back( + { TokenType::Option, opt } ); + } + } else { + m_tokenBuffer.push_back( + { TokenType::Option, next } ); + } + } + } else { + m_tokenBuffer.push_back( + { TokenType::Argument, next } ); + } + } + } + + TokenStream::TokenStream( Args const& args ): + TokenStream( args.m_args.begin(), args.m_args.end() ) {} + + TokenStream::TokenStream( Iterator it_, Iterator itEnd_ ): + it( it_ ), itEnd( itEnd_ ) { + loadBuffer(); + } + + TokenStream& TokenStream::operator++() { + if ( m_tokenBuffer.size() >= 2 ) { + m_tokenBuffer.erase( m_tokenBuffer.begin() ); + } else { + if ( it != itEnd ) + ++it; + loadBuffer(); + } + return *this; + } + + ParserResult convertInto( std::string const& source, + std::string& target ) { + target = source; + return ParserResult::ok( ParseResultType::Matched ); + } + + ParserResult convertInto( std::string const& source, + bool& target ) { + std::string srcLC = toLower( source ); + + if ( srcLC == "y" || srcLC == "1" || srcLC == "true" || + srcLC == "yes" || srcLC == "on" ) { + target = true; + } else if ( srcLC == "n" || srcLC == "0" || srcLC == "false" || + srcLC == "no" || srcLC == "off" ) { + target = false; + } else { + return ParserResult::runtimeError( + "Expected a boolean value but did not recognise: '" + + source + '\'' ); + } + return ParserResult::ok( ParseResultType::Matched ); + } + + size_t ParserBase::cardinality() const { return 1; } + + InternalParseResult ParserBase::parse( Args const& args ) const { + return parse( args.exeName(), TokenStream( args ) ); + } + + ParseState::ParseState( ParseResultType type, + TokenStream const& remainingTokens ): + m_type( type ), m_remainingTokens( remainingTokens ) {} + + ParserResult BoundFlagRef::setFlag( bool flag ) { + m_ref = flag; + return ParserResult::ok( ParseResultType::Matched ); + } + + ResultBase::~ResultBase() = default; + + bool BoundRef::isContainer() const { return false; } + + bool BoundRef::isFlag() const { return false; } + + bool BoundFlagRefBase::isFlag() const { return true; } + +} // namespace Detail + + Detail::InternalParseResult Arg::parse(std::string const&, + Detail::TokenStream const& tokens) const { + auto validationResult = validate(); + if (!validationResult) + return Detail::InternalParseResult(validationResult); + + auto remainingTokens = tokens; + auto const& token = *remainingTokens; + if (token.type != Detail::TokenType::Argument) + return Detail::InternalParseResult::ok(Detail::ParseState( + ParseResultType::NoMatch, remainingTokens)); + + assert(!m_ref->isFlag()); + auto valueRef = + static_cast(m_ref.get()); + + auto result = valueRef->setValue(remainingTokens->token); + if (!result) + return Detail::InternalParseResult(result); + else + return Detail::InternalParseResult::ok(Detail::ParseState( + ParseResultType::Matched, ++remainingTokens)); + } + + Opt::Opt(bool& ref) : + ParserRefImpl(std::make_shared(ref)) {} + + std::vector Opt::getHelpColumns() const { + std::ostringstream oss; + bool first = true; + for (auto const& opt : m_optNames) { + if (first) + first = false; + else + oss << ", "; + oss << opt; + } + if (!m_hint.empty()) + oss << " <" << m_hint << '>'; + return { { oss.str(), m_description } }; + } + + bool Opt::isMatch(std::string const& optToken) const { + auto normalisedToken = normaliseOpt(optToken); + for (auto const& name : m_optNames) { + if (normaliseOpt(name) == normalisedToken) + return true; + } + return false; + } + + Detail::InternalParseResult Opt::parse(std::string const&, + Detail::TokenStream const& tokens) const { + auto validationResult = validate(); + if (!validationResult) + return Detail::InternalParseResult(validationResult); + + auto remainingTokens = tokens; + if (remainingTokens && + remainingTokens->type == Detail::TokenType::Option) { + auto const& token = *remainingTokens; + if (isMatch(token.token)) { + if (m_ref->isFlag()) { + auto flagRef = + static_cast( + m_ref.get()); + auto result = flagRef->setFlag(true); + if (!result) + return Detail::InternalParseResult(result); + if (result.value() == + ParseResultType::ShortCircuitAll) + return Detail::InternalParseResult::ok(Detail::ParseState( + result.value(), remainingTokens)); + } else { + auto valueRef = + static_cast( + m_ref.get()); + ++remainingTokens; + if (!remainingTokens) + return Detail::InternalParseResult::runtimeError( + "Expected argument following " + + token.token); + auto const& argToken = *remainingTokens; + if (argToken.type != Detail::TokenType::Argument) + return Detail::InternalParseResult::runtimeError( + "Expected argument following " + + token.token); + const auto result = valueRef->setValue(argToken.token); + if (!result) + return Detail::InternalParseResult(result); + if (result.value() == + ParseResultType::ShortCircuitAll) + return Detail::InternalParseResult::ok(Detail::ParseState( + result.value(), remainingTokens)); + } + return Detail::InternalParseResult::ok(Detail::ParseState( + ParseResultType::Matched, ++remainingTokens)); + } + } + return Detail::InternalParseResult::ok( + Detail::ParseState(ParseResultType::NoMatch, remainingTokens)); + } + + Detail::Result Opt::validate() const { + if (m_optNames.empty()) + return Detail::Result::logicError("No options supplied to Opt"); + for (auto const& name : m_optNames) { + if (name.empty()) + return Detail::Result::logicError( + "Option name cannot be empty"); +#ifdef CATCH_PLATFORM_WINDOWS + if (name[0] != '-' && name[0] != '/') + return Detail::Result::logicError( + "Option name must begin with '-' or '/'"); +#else + if (name[0] != '-') + return Detail::Result::logicError( + "Option name must begin with '-'"); +#endif + } + return ParserRefImpl::validate(); + } + + ExeName::ExeName() : + m_name(std::make_shared("")) {} + + ExeName::ExeName(std::string& ref) : ExeName() { + m_ref = std::make_shared>(ref); + } + + Detail::InternalParseResult + ExeName::parse(std::string const&, + Detail::TokenStream const& tokens) const { + return Detail::InternalParseResult::ok( + Detail::ParseState(ParseResultType::NoMatch, tokens)); + } + + ParserResult ExeName::set(std::string const& newName) { + auto lastSlash = newName.find_last_of("\\/"); + auto filename = (lastSlash == std::string::npos) + ? newName + : newName.substr(lastSlash + 1); + + *m_name = filename; + if (m_ref) + return m_ref->setValue(filename); + else + return ParserResult::ok(ParseResultType::Matched); + } + + + + + Parser& Parser::operator|=( Parser const& other ) { + m_options.insert( m_options.end(), + other.m_options.begin(), + other.m_options.end() ); + m_args.insert( + m_args.end(), other.m_args.begin(), other.m_args.end() ); + return *this; + } + + std::vector Parser::getHelpColumns() const { + std::vector cols; + for ( auto const& o : m_options ) { + auto childCols = o.getHelpColumns(); + cols.insert( cols.end(), childCols.begin(), childCols.end() ); + } + return cols; + } + + void Parser::writeToStream( std::ostream& os ) const { + if ( !m_exeName.name().empty() ) { + os << "usage:\n" + << " " << m_exeName.name() << ' '; + bool required = true, first = true; + for ( auto const& arg : m_args ) { + if ( first ) + first = false; + else + os << ' '; + if ( arg.isOptional() && required ) { + os << '['; + required = false; + } + os << '<' << arg.hint() << '>'; + if ( arg.cardinality() == 0 ) + os << " ... "; + } + if ( !required ) + os << ']'; + if ( !m_options.empty() ) + os << " options"; + os << "\n\nwhere options are:\n"; + } + + auto rows = getHelpColumns(); + size_t consoleWidth = CATCH_CONFIG_CONSOLE_WIDTH; + size_t optWidth = 0; + for ( auto const& cols : rows ) + optWidth = ( std::max )( optWidth, cols.left.size() + 2 ); + + optWidth = ( std::min )( optWidth, consoleWidth / 2 ); + + for ( auto const& cols : rows ) { + auto row = TextFlow::Column( cols.left ) + .width( optWidth ) + .indent( 2 ) + + TextFlow::Spacer( 4 ) + + TextFlow::Column( cols.right ) + .width( consoleWidth - 7 - optWidth ); + os << row << '\n'; + } + } + + Detail::Result Parser::validate() const { + for ( auto const& opt : m_options ) { + auto result = opt.validate(); + if ( !result ) + return result; + } + for ( auto const& arg : m_args ) { + auto result = arg.validate(); + if ( !result ) + return result; + } + return Detail::Result::ok(); + } + + Detail::InternalParseResult + Parser::parse( std::string const& exeName, + Detail::TokenStream const& tokens ) const { + + struct ParserInfo { + ParserBase const* parser = nullptr; + size_t count = 0; + }; + std::vector parseInfos; + parseInfos.reserve( m_options.size() + m_args.size() ); + for ( auto const& opt : m_options ) { + parseInfos.push_back( { &opt, 0 } ); + } + for ( auto const& arg : m_args ) { + parseInfos.push_back( { &arg, 0 } ); + } + + m_exeName.set( exeName ); + + auto result = Detail::InternalParseResult::ok( + Detail::ParseState( ParseResultType::NoMatch, tokens ) ); + while ( result.value().remainingTokens() ) { + bool tokenParsed = false; + + for ( auto& parseInfo : parseInfos ) { + if ( parseInfo.parser->cardinality() == 0 || + parseInfo.count < parseInfo.parser->cardinality() ) { + result = parseInfo.parser->parse( + exeName, result.value().remainingTokens() ); + if ( !result ) + return result; + if ( result.value().type() != + ParseResultType::NoMatch ) { + tokenParsed = true; + ++parseInfo.count; + break; + } + } + } + + if ( result.value().type() == ParseResultType::ShortCircuitAll ) + return result; + if ( !tokenParsed ) + return Detail::InternalParseResult::runtimeError( + "Unrecognised token: " + + result.value().remainingTokens()->token ); + } + // !TBD Check missing required options + return result; + } + + Args::Args(int argc, char const* const* argv) : + m_exeName(argv[0]), m_args(argv + 1, argv + argc) {} + + Args::Args(std::initializer_list args) : + m_exeName(*args.begin()), + m_args(args.begin() + 1, args.end()) {} + + + Help::Help( bool& showHelpFlag ): + Opt( [&]( bool flag ) { + showHelpFlag = flag; + return ParserResult::ok( ParseResultType::ShortCircuitAll ); + } ) { + static_cast ( *this )( + "display usage information" )["-?"]["-h"]["--help"] + .optional(); + } + + } // namespace Clara +} // namespace Catch + + + + +#include +#include + +namespace Catch { + + Clara::Parser makeCommandLineParser( ConfigData& config ) { + + using namespace Clara; + + auto const setWarning = [&]( std::string const& warning ) { + if ( warning == "NoAssertions" ) { + config.warnings = static_cast(config.warnings | WarnAbout::NoAssertions); + return ParserResult::ok( ParseResultType::Matched ); + } else if ( warning == "UnmatchedTestSpec" ) { + config.warnings = static_cast(config.warnings | WarnAbout::UnmatchedTestSpec); + return ParserResult::ok( ParseResultType::Matched ); + } + + return ParserResult ::runtimeError( + "Unrecognised warning option: '" + warning + '\'' ); + }; + auto const loadTestNamesFromFile = [&]( std::string const& filename ) { + std::ifstream f( filename.c_str() ); + if( !f.is_open() ) + return ParserResult::runtimeError( "Unable to load input file: '" + filename + '\'' ); + + std::string line; + while( std::getline( f, line ) ) { + line = trim(line); + if( !line.empty() && !startsWith( line, '#' ) ) { + if( !startsWith( line, '"' ) ) + line = '"' + line + '"'; + config.testsOrTags.push_back( line ); + config.testsOrTags.emplace_back( "," ); + } + } + //Remove comma in the end + if(!config.testsOrTags.empty()) + config.testsOrTags.erase( config.testsOrTags.end()-1 ); + + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setTestOrder = [&]( std::string const& order ) { + if( startsWith( "declared", order ) ) + config.runOrder = TestRunOrder::Declared; + else if( startsWith( "lexical", order ) ) + config.runOrder = TestRunOrder::LexicographicallySorted; + else if( startsWith( "random", order ) ) + config.runOrder = TestRunOrder::Randomized; + else + return ParserResult::runtimeError( "Unrecognised ordering: '" + order + '\'' ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setRngSeed = [&]( std::string const& seed ) { + if( seed == "time" ) { + config.rngSeed = generateRandomSeed(GenerateFrom::Time); + return ParserResult::ok(ParseResultType::Matched); + } else if (seed == "random-device") { + config.rngSeed = generateRandomSeed(GenerateFrom::RandomDevice); + return ParserResult::ok(ParseResultType::Matched); + } + + CATCH_TRY { + std::size_t parsedTo = 0; + unsigned long parsedSeed = std::stoul(seed, &parsedTo, 0); + if (parsedTo != seed.size()) { + return ParserResult::runtimeError("Could not parse '" + seed + "' as seed"); + } + + // TODO: Ideally we could parse unsigned int directly, + // but the stdlib doesn't provide helper for that + // type. After this is refactored to use fixed size + // type, we should check the parsed value is in range + // of the underlying type. + config.rngSeed = static_cast(parsedSeed); + return ParserResult::ok(ParseResultType::Matched); + } CATCH_CATCH_ANON(std::exception const&) { + return ParserResult::runtimeError("Could not parse '" + seed + "' as seed"); + } + }; + auto const setDefaultColourMode = [&]( std::string const& colourMode ) { + Optional maybeMode = Catch::Detail::stringToColourMode(toLower( colourMode )); + if ( !maybeMode ) { + return ParserResult::runtimeError( + "colour mode must be one of: default, ansi, win32, " + "or none. '" + + colourMode + "' is not recognised" ); + } + auto mode = *maybeMode; + if ( !isColourImplAvailable( mode ) ) { + return ParserResult::runtimeError( + "colour mode '" + colourMode + + "' is not supported in this binary" ); + } + config.defaultColourMode = mode; + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setWaitForKeypress = [&]( std::string const& keypress ) { + auto keypressLc = toLower( keypress ); + if (keypressLc == "never") + config.waitForKeypress = WaitForKeypress::Never; + else if( keypressLc == "start" ) + config.waitForKeypress = WaitForKeypress::BeforeStart; + else if( keypressLc == "exit" ) + config.waitForKeypress = WaitForKeypress::BeforeExit; + else if( keypressLc == "both" ) + config.waitForKeypress = WaitForKeypress::BeforeStartAndExit; + else + return ParserResult::runtimeError( "keypress argument must be one of: never, start, exit or both. '" + keypress + "' not recognised" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setVerbosity = [&]( std::string const& verbosity ) { + auto lcVerbosity = toLower( verbosity ); + if( lcVerbosity == "quiet" ) + config.verbosity = Verbosity::Quiet; + else if( lcVerbosity == "normal" ) + config.verbosity = Verbosity::Normal; + else if( lcVerbosity == "high" ) + config.verbosity = Verbosity::High; + else + return ParserResult::runtimeError( "Unrecognised verbosity, '" + verbosity + '\'' ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setReporter = [&]( std::string const& userReporterSpec ) { + if ( userReporterSpec.empty() ) { + return ParserResult::runtimeError( "Received empty reporter spec." ); + } + + Optional parsed = + parseReporterSpec( userReporterSpec ); + if ( !parsed ) { + return ParserResult::runtimeError( + "Could not parse reporter spec '" + userReporterSpec + + "'" ); + } + + auto const& reporterSpec = *parsed; + + IReporterRegistry::FactoryMap const& factories = + getRegistryHub().getReporterRegistry().getFactories(); + auto result = factories.find( reporterSpec.name() ); + + if ( result == factories.end() ) { + return ParserResult::runtimeError( + "Unrecognized reporter, '" + reporterSpec.name() + + "'. Check available with --list-reporters" ); + } + + + const bool hadOutputFile = reporterSpec.outputFile().some(); + config.reporterSpecifications.push_back( CATCH_MOVE( *parsed ) ); + // It would be enough to check this only once at the very end, but + // there is not a place where we could call this check, so do it + // every time it could fail. For valid inputs, this is still called + // at most once. + if (!hadOutputFile) { + int n_reporters_without_file = 0; + for (auto const& spec : config.reporterSpecifications) { + if (spec.outputFile().none()) { + n_reporters_without_file++; + } + } + if (n_reporters_without_file > 1) { + return ParserResult::runtimeError( "Only one reporter may have unspecified output file." ); + } + } + + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setShardCount = [&]( std::string const& shardCount ) { + CATCH_TRY{ + std::size_t parsedTo = 0; + int64_t parsedCount = std::stoll(shardCount, &parsedTo, 0); + if (parsedTo != shardCount.size()) { + return ParserResult::runtimeError("Could not parse '" + shardCount + "' as shard count"); + } + if (parsedCount <= 0) { + return ParserResult::runtimeError("Shard count must be a positive number"); + } + + config.shardCount = static_cast(parsedCount); + return ParserResult::ok(ParseResultType::Matched); + } CATCH_CATCH_ANON(std::exception const&) { + return ParserResult::runtimeError("Could not parse '" + shardCount + "' as shard count"); + } + }; + + auto const setShardIndex = [&](std::string const& shardIndex) { + CATCH_TRY{ + std::size_t parsedTo = 0; + int64_t parsedIndex = std::stoll(shardIndex, &parsedTo, 0); + if (parsedTo != shardIndex.size()) { + return ParserResult::runtimeError("Could not parse '" + shardIndex + "' as shard index"); + } + if (parsedIndex < 0) { + return ParserResult::runtimeError("Shard index must be a non-negative number"); + } + + config.shardIndex = static_cast(parsedIndex); + return ParserResult::ok(ParseResultType::Matched); + } CATCH_CATCH_ANON(std::exception const&) { + return ParserResult::runtimeError("Could not parse '" + shardIndex + "' as shard index"); + } + }; + + + auto cli + = ExeName( config.processName ) + | Help( config.showHelp ) + | Opt( config.showSuccessfulTests ) + ["-s"]["--success"] + ( "include successful tests in output" ) + | Opt( config.shouldDebugBreak ) + ["-b"]["--break"] + ( "break into debugger on failure" ) + | Opt( config.noThrow ) + ["-e"]["--nothrow"] + ( "skip exception tests" ) + | Opt( config.showInvisibles ) + ["-i"]["--invisibles"] + ( "show invisibles (tabs, newlines)" ) + | Opt( config.defaultOutputFilename, "filename" ) + ["-o"]["--out"] + ( "default output filename" ) + | Opt( accept_many, setReporter, "name[::key=value]*" ) + ["-r"]["--reporter"] + ( "reporter to use (defaults to console)" ) + | Opt( config.name, "name" ) + ["-n"]["--name"] + ( "suite name" ) + | Opt( [&]( bool ){ config.abortAfter = 1; } ) + ["-a"]["--abort"] + ( "abort at first failure" ) + | Opt( [&]( int x ){ config.abortAfter = x; }, "no. failures" ) + ["-x"]["--abortx"] + ( "abort after x failures" ) + | Opt( accept_many, setWarning, "warning name" ) + ["-w"]["--warn"] + ( "enable warnings" ) + | Opt( [&]( bool flag ) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, "yes|no" ) + ["-d"]["--durations"] + ( "show test durations" ) + | Opt( config.minDuration, "seconds" ) + ["-D"]["--min-duration"] + ( "show test durations for tests taking at least the given number of seconds" ) + | Opt( loadTestNamesFromFile, "filename" ) + ["-f"]["--input-file"] + ( "load test names to run from a file" ) + | Opt( config.filenamesAsTags ) + ["-#"]["--filenames-as-tags"] + ( "adds a tag for the filename" ) + | Opt( config.sectionsToRun, "section name" ) + ["-c"]["--section"] + ( "specify section to run" ) + | Opt( setVerbosity, "quiet|normal|high" ) + ["-v"]["--verbosity"] + ( "set output verbosity" ) + | Opt( config.listTests ) + ["--list-tests"] + ( "list all/matching test cases" ) + | Opt( config.listTags ) + ["--list-tags"] + ( "list all/matching tags" ) + | Opt( config.listReporters ) + ["--list-reporters"] + ( "list all available reporters" ) + | Opt( config.listListeners ) + ["--list-listeners"] + ( "list all listeners" ) + | Opt( setTestOrder, "decl|lex|rand" ) + ["--order"] + ( "test case order (defaults to decl)" ) + | Opt( setRngSeed, "'time'|'random-device'|number" ) + ["--rng-seed"] + ( "set a specific seed for random numbers" ) + | Opt( setDefaultColourMode, "ansi|win32|none|default" ) + ["--colour-mode"] + ( "what color mode should be used as default" ) + | Opt( config.libIdentify ) + ["--libidentify"] + ( "report name and version according to libidentify standard" ) + | Opt( setWaitForKeypress, "never|start|exit|both" ) + ["--wait-for-keypress"] + ( "waits for a keypress before exiting" ) + | Opt( config.skipBenchmarks) + ["--skip-benchmarks"] + ( "disable running benchmarks") + | Opt( config.benchmarkSamples, "samples" ) + ["--benchmark-samples"] + ( "number of samples to collect (default: 100)" ) + | Opt( config.benchmarkResamples, "resamples" ) + ["--benchmark-resamples"] + ( "number of resamples for the bootstrap (default: 100000)" ) + | Opt( config.benchmarkConfidenceInterval, "confidence interval" ) + ["--benchmark-confidence-interval"] + ( "confidence interval for the bootstrap (between 0 and 1, default: 0.95)" ) + | Opt( config.benchmarkNoAnalysis ) + ["--benchmark-no-analysis"] + ( "perform only measurements; do not perform any analysis" ) + | Opt( config.benchmarkWarmupTime, "benchmarkWarmupTime" ) + ["--benchmark-warmup-time"] + ( "amount of time in milliseconds spent on warming up each test (default: 100)" ) + | Opt( setShardCount, "shard count" ) + ["--shard-count"] + ( "split the tests to execute into this many groups" ) + | Opt( setShardIndex, "shard index" ) + ["--shard-index"] + ( "index of the group of tests to execute (see --shard-count)" ) | + Opt( config.allowZeroTests ) + ["--allow-running-no-tests"] + ( "Treat 'No tests run' as a success" ) + | Arg( config.testsOrTags, "test name|pattern|tags" ) + ( "which test or tests to use" ); + + return cli; + } + +} // end namespace Catch + + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +#endif + + + +#include +#include +#include + +namespace Catch { + + ColourImpl::~ColourImpl() = default; + + ColourImpl::ColourGuard ColourImpl::guardColour( Colour::Code colourCode ) { + return ColourGuard(colourCode, this ); + } + + void ColourImpl::ColourGuard::engageImpl( std::ostream& stream ) { + assert( &stream == &m_colourImpl->m_stream->stream() && + "Engaging colour guard for different stream than used by the " + "parent colour implementation" ); + static_cast( stream ); + + m_engaged = true; + m_colourImpl->use( m_code ); + } + + ColourImpl::ColourGuard::ColourGuard( Colour::Code code, + ColourImpl const* colour ): + m_colourImpl( colour ), m_code( code ) { + } + ColourImpl::ColourGuard::ColourGuard( ColourGuard&& rhs ) noexcept: + m_colourImpl( rhs.m_colourImpl ), + m_code( rhs.m_code ), + m_engaged( rhs.m_engaged ) { + rhs.m_engaged = false; + } + ColourImpl::ColourGuard& + ColourImpl::ColourGuard::operator=( ColourGuard&& rhs ) noexcept { + using std::swap; + swap( m_colourImpl, rhs.m_colourImpl ); + swap( m_code, rhs.m_code ); + swap( m_engaged, rhs.m_engaged ); + + return *this; + } + ColourImpl::ColourGuard::~ColourGuard() { + if ( m_engaged ) { + m_colourImpl->use( Colour::None ); + } + } + + ColourImpl::ColourGuard& + ColourImpl::ColourGuard::engage( std::ostream& stream ) & { + engageImpl( stream ); + return *this; + } + + ColourImpl::ColourGuard&& + ColourImpl::ColourGuard::engage( std::ostream& stream ) && { + engageImpl( stream ); + return CATCH_MOVE(*this); + } + + namespace { + //! A do-nothing implementation of colour, used as fallback for unknown + //! platforms, and when the user asks to deactivate all colours. + class NoColourImpl : public ColourImpl { + public: + NoColourImpl( IStream* stream ): ColourImpl( stream ) {} + + private: + void use( Colour::Code ) const override {} + }; + } // namespace + + +} // namespace Catch + + +#if defined ( CATCH_CONFIG_COLOUR_WIN32 ) ///////////////////////////////////////// + +namespace Catch { +namespace { + + class Win32ColourImpl : public ColourImpl { + public: + Win32ColourImpl(IStream* stream): + ColourImpl(stream) { + CONSOLE_SCREEN_BUFFER_INFO csbiInfo; + GetConsoleScreenBufferInfo( GetStdHandle( STD_OUTPUT_HANDLE ), + &csbiInfo ); + originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY ); + originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY ); + } + + static bool useImplementationForStream(IStream const& stream) { + // Win32 text colour APIs can only be used on console streams + // We cannot check that the output hasn't been redirected, + // so we just check that the original stream is console stream. + return stream.isConsole(); + } + + private: + void use( Colour::Code _colourCode ) const override { + switch( _colourCode ) { + case Colour::None: return setTextAttribute( originalForegroundAttributes ); + case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); + case Colour::Red: return setTextAttribute( FOREGROUND_RED ); + case Colour::Green: return setTextAttribute( FOREGROUND_GREEN ); + case Colour::Blue: return setTextAttribute( FOREGROUND_BLUE ); + case Colour::Cyan: return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN ); + case Colour::Yellow: return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN ); + case Colour::Grey: return setTextAttribute( 0 ); + + case Colour::LightGrey: return setTextAttribute( FOREGROUND_INTENSITY ); + case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED ); + case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN ); + case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); + case Colour::BrightYellow: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN ); + + case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); + + default: + CATCH_ERROR( "Unknown colour requested" ); + } + } + + void setTextAttribute( WORD _textAttribute ) const { + SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), + _textAttribute | + originalBackgroundAttributes ); + } + WORD originalForegroundAttributes; + WORD originalBackgroundAttributes; + }; + +} // end anon namespace +} // end namespace Catch + +#endif // Windows/ ANSI/ None + + +#if defined( CATCH_PLATFORM_LINUX ) || defined( CATCH_PLATFORM_MAC ) +# define CATCH_INTERNAL_HAS_ISATTY +# include +#endif + +namespace Catch { +namespace { + + class ANSIColourImpl : public ColourImpl { + public: + ANSIColourImpl( IStream* stream ): ColourImpl( stream ) {} + + static bool useImplementationForStream(IStream const& stream) { + // This is kinda messy due to trying to support a bunch of + // different platforms at once. + // The basic idea is that if we are asked to do autodetection (as + // opposed to being told to use posixy colours outright), then we + // only want to use the colours if we are writing to console. + // However, console might be redirected, so we make an attempt at + // checking for that on platforms where we know how to do that. + bool useColour = stream.isConsole(); +#if defined( CATCH_INTERNAL_HAS_ISATTY ) && \ + !( defined( __DJGPP__ ) && defined( __STRICT_ANSI__ ) ) + ErrnoGuard _; // for isatty + useColour = useColour && isatty( STDOUT_FILENO ); +# endif +# if defined( CATCH_PLATFORM_MAC ) || defined( CATCH_PLATFORM_IPHONE ) + useColour = useColour && !isDebuggerActive(); +# endif + + return useColour; + } + + private: + void use( Colour::Code _colourCode ) const override { + auto setColour = [&out = + m_stream->stream()]( char const* escapeCode ) { + // The escape sequence must be flushed to console, otherwise + // if stdin and stderr are intermixed, we'd get accidentally + // coloured output. + out << '\033' << escapeCode << std::flush; + }; + switch( _colourCode ) { + case Colour::None: + case Colour::White: return setColour( "[0m" ); + case Colour::Red: return setColour( "[0;31m" ); + case Colour::Green: return setColour( "[0;32m" ); + case Colour::Blue: return setColour( "[0;34m" ); + case Colour::Cyan: return setColour( "[0;36m" ); + case Colour::Yellow: return setColour( "[0;33m" ); + case Colour::Grey: return setColour( "[1;30m" ); + + case Colour::LightGrey: return setColour( "[0;37m" ); + case Colour::BrightRed: return setColour( "[1;31m" ); + case Colour::BrightGreen: return setColour( "[1;32m" ); + case Colour::BrightWhite: return setColour( "[1;37m" ); + case Colour::BrightYellow: return setColour( "[1;33m" ); + + case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); + default: CATCH_INTERNAL_ERROR( "Unknown colour requested" ); + } + } + }; + +} // end anon namespace +} // end namespace Catch + +namespace Catch { + + Detail::unique_ptr makeColourImpl( ColourMode implSelection, + IStream* stream ) { +#if defined( CATCH_CONFIG_COLOUR_WIN32 ) + if ( implSelection == ColourMode::Win32 ) { + return Detail::make_unique( stream ); + } +#endif + if ( implSelection == ColourMode::ANSI ) { + return Detail::make_unique( stream ); + } + if ( implSelection == ColourMode::None ) { + return Detail::make_unique( stream ); + } + + if ( implSelection == ColourMode::PlatformDefault) { +#if defined( CATCH_CONFIG_COLOUR_WIN32 ) + if ( Win32ColourImpl::useImplementationForStream( *stream ) ) { + return Detail::make_unique( stream ); + } +#endif + if ( ANSIColourImpl::useImplementationForStream( *stream ) ) { + return Detail::make_unique( stream ); + } + return Detail::make_unique( stream ); + } + + CATCH_ERROR( "Could not create colour impl for selection " << static_cast(implSelection) ); + } + + bool isColourImplAvailable( ColourMode colourSelection ) { + switch ( colourSelection ) { +#if defined( CATCH_CONFIG_COLOUR_WIN32 ) + case ColourMode::Win32: +#endif + case ColourMode::ANSI: + case ColourMode::None: + case ColourMode::PlatformDefault: + return true; + default: + return false; + } + } + + +} // end namespace Catch + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif + + + + +namespace Catch { + + class Context : public IMutableContext, private Detail::NonCopyable { + + public: // IContext + IResultCapture* getResultCapture() override { + return m_resultCapture; + } + + IConfig const* getConfig() const override { + return m_config; + } + + ~Context() override; + + public: // IMutableContext + void setResultCapture( IResultCapture* resultCapture ) override { + m_resultCapture = resultCapture; + } + void setConfig( IConfig const* config ) override { + m_config = config; + } + + friend IMutableContext& getCurrentMutableContext(); + + private: + IConfig const* m_config = nullptr; + IResultCapture* m_resultCapture = nullptr; + }; + + IMutableContext *IMutableContext::currentContext = nullptr; + + void IMutableContext::createContext() + { + currentContext = new Context(); + } + + void cleanUpContext() { + delete IMutableContext::currentContext; + IMutableContext::currentContext = nullptr; + } + IContext::~IContext() = default; + IMutableContext::~IMutableContext() = default; + Context::~Context() = default; + + + SimplePcg32& sharedRng() { + static SimplePcg32 s_rng; + return s_rng; + } + +} + + + + + +#include + +#if defined(CATCH_CONFIG_ANDROID_LOGWRITE) +#include + + namespace Catch { + void writeToDebugConsole( std::string const& text ) { + __android_log_write( ANDROID_LOG_DEBUG, "Catch", text.c_str() ); + } + } + +#elif defined(CATCH_PLATFORM_WINDOWS) + + namespace Catch { + void writeToDebugConsole( std::string const& text ) { + ::OutputDebugStringA( text.c_str() ); + } + } + +#else + + namespace Catch { + void writeToDebugConsole( std::string const& text ) { + // !TBD: Need a version for Mac/ XCode and other IDEs + Catch::cout() << text; + } + } + +#endif // Platform + + + +#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE) + +# include +# include +# include +# include +# include + +#ifdef __apple_build_version__ + // These headers will only compile with AppleClang (XCode) + // For other compilers (Clang, GCC, ... ) we need to exclude them +# include +#endif + + namespace Catch { + #ifdef __apple_build_version__ + // The following function is taken directly from the following technical note: + // https://developer.apple.com/library/archive/qa/qa1361/_index.html + + // Returns true if the current process is being debugged (either + // running under the debugger or has a debugger attached post facto). + bool isDebuggerActive(){ + int mib[4]; + struct kinfo_proc info; + std::size_t size; + + // Initialize the flags so that, if sysctl fails for some bizarre + // reason, we get a predictable result. + + info.kp_proc.p_flag = 0; + + // Initialize mib, which tells sysctl the info we want, in this case + // we're looking for information about a specific process ID. + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); + + // Call sysctl. + + size = sizeof(info); + if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0) != 0 ) { + Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n\n" << std::flush; + return false; + } + + // We're being debugged if the P_TRACED flag is set. + + return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); + } + #else + bool isDebuggerActive() { + // We need to find another way to determine this for non-appleclang compilers on macOS + return false; + } + #endif + } // namespace Catch + +#elif defined(CATCH_PLATFORM_LINUX) + #include + #include + + namespace Catch{ + // The standard POSIX way of detecting a debugger is to attempt to + // ptrace() the process, but this needs to be done from a child and not + // this process itself to still allow attaching to this process later + // if wanted, so is rather heavy. Under Linux we have the PID of the + // "debugger" (which doesn't need to be gdb, of course, it could also + // be strace, for example) in /proc/$PID/status, so just get it from + // there instead. + bool isDebuggerActive(){ + // Libstdc++ has a bug, where std::ifstream sets errno to 0 + // This way our users can properly assert over errno values + ErrnoGuard guard; + std::ifstream in("/proc/self/status"); + for( std::string line; std::getline(in, line); ) { + static const int PREFIX_LEN = 11; + if( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) { + // We're traced if the PID is not 0 and no other PID starts + // with 0 digit, so it's enough to check for just a single + // character. + return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; + } + } + + return false; + } + } // namespace Catch +#elif defined(_MSC_VER) + extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); + namespace Catch { + bool isDebuggerActive() { + return IsDebuggerPresent() != 0; + } + } +#elif defined(__MINGW32__) + extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); + namespace Catch { + bool isDebuggerActive() { + return IsDebuggerPresent() != 0; + } + } +#else + namespace Catch { + bool isDebuggerActive() { return false; } + } +#endif // Platform + + + + +namespace Catch { + + ITransientExpression::~ITransientExpression() = default; + + void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ) { + if( lhs.size() + rhs.size() < 40 && + lhs.find('\n') == std::string::npos && + rhs.find('\n') == std::string::npos ) + os << lhs << ' ' << op << ' ' << rhs; + else + os << lhs << '\n' << op << '\n' << rhs; + } +} + + + +#include + + +namespace Catch { +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS_CUSTOM_HANDLER) + [[noreturn]] + void throw_exception(std::exception const& e) { + Catch::cerr() << "Catch will terminate because it needed to throw an exception.\n" + << "The message was: " << e.what() << '\n'; + std::terminate(); + } +#endif + + [[noreturn]] + void throw_logic_error(std::string const& msg) { + throw_exception(std::logic_error(msg)); + } + + [[noreturn]] + void throw_domain_error(std::string const& msg) { + throw_exception(std::domain_error(msg)); + } + + [[noreturn]] + void throw_runtime_error(std::string const& msg) { + throw_exception(std::runtime_error(msg)); + } + + + +} // namespace Catch; + + + +#include + +namespace Catch { + + IMutableEnumValuesRegistry::~IMutableEnumValuesRegistry() = default; + + namespace Detail { + + namespace { + // Extracts the actual name part of an enum instance + // In other words, it returns the Blue part of Bikeshed::Colour::Blue + StringRef extractInstanceName(StringRef enumInstance) { + // Find last occurrence of ":" + size_t name_start = enumInstance.size(); + while (name_start > 0 && enumInstance[name_start - 1] != ':') { + --name_start; + } + return enumInstance.substr(name_start, enumInstance.size() - name_start); + } + } + + std::vector parseEnums( StringRef enums ) { + auto enumValues = splitStringRef( enums, ',' ); + std::vector parsed; + parsed.reserve( enumValues.size() ); + for( auto const& enumValue : enumValues ) { + parsed.push_back(trim(extractInstanceName(enumValue))); + } + return parsed; + } + + EnumInfo::~EnumInfo() {} + + StringRef EnumInfo::lookup( int value ) const { + for( auto const& valueToName : m_values ) { + if( valueToName.first == value ) + return valueToName.second; + } + return "{** unexpected enum value **}"_sr; + } + + Catch::Detail::unique_ptr makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector const& values ) { + auto enumInfo = Catch::Detail::make_unique(); + enumInfo->m_name = enumName; + enumInfo->m_values.reserve( values.size() ); + + const auto valueNames = Catch::Detail::parseEnums( allValueNames ); + assert( valueNames.size() == values.size() ); + std::size_t i = 0; + for( auto value : values ) + enumInfo->m_values.emplace_back(value, valueNames[i++]); + + return enumInfo; + } + + EnumInfo const& EnumValuesRegistry::registerEnum( StringRef enumName, StringRef allValueNames, std::vector const& values ) { + m_enumInfos.push_back(makeEnumInfo(enumName, allValueNames, values)); + return *m_enumInfos.back(); + } + + } // Detail +} // Catch + + + + + +#include + +namespace Catch { + ErrnoGuard::ErrnoGuard():m_oldErrno(errno){} + ErrnoGuard::~ErrnoGuard() { errno = m_oldErrno; } +} + + + +namespace Catch { + + ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() { + } + + void ExceptionTranslatorRegistry::registerTranslator( Detail::unique_ptr&& translator ) { + m_translators.push_back( CATCH_MOVE( translator ) ); + } + +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + std::string ExceptionTranslatorRegistry::translateActiveException() const { + // Compiling a mixed mode project with MSVC means that CLR + // exceptions will be caught in (...) as well. However, these do + // do not fill-in std::current_exception and thus lead to crash + // when attempting rethrow. + // /EHa switch also causes structured exceptions to be caught + // here, but they fill-in current_exception properly, so + // at worst the output should be a little weird, instead of + // causing a crash. + if ( std::current_exception() == nullptr ) { + return "Non C++ exception. Possibly a CLR exception."; + } + + // First we try user-registered translators. If none of them can + // handle the exception, it will be rethrown handled by our defaults. + try { + return tryTranslators(); + } + // To avoid having to handle TFE explicitly everywhere, we just + // rethrow it so that it goes back up the caller. + catch( TestFailureException& ) { + std::rethrow_exception(std::current_exception()); + } + catch( std::exception const& ex ) { + return ex.what(); + } + catch( std::string const& msg ) { + return msg; + } + catch( const char* msg ) { + return msg; + } + catch(...) { + return "Unknown exception"; + } + } + + std::string ExceptionTranslatorRegistry::tryTranslators() const { + if (m_translators.empty()) { + std::rethrow_exception(std::current_exception()); + } else { + return m_translators[0]->translate(m_translators.begin() + 1, m_translators.end()); + } + } + +#else // ^^ Exceptions are enabled // Exceptions are disabled vv + std::string ExceptionTranslatorRegistry::translateActiveException() const { + CATCH_INTERNAL_ERROR("Attempted to translate active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); + } + + std::string ExceptionTranslatorRegistry::tryTranslators() const { + CATCH_INTERNAL_ERROR("Attempted to use exception translators under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); + } +#endif + + +} + + + +/** \file + * This file provides platform specific implementations of FatalConditionHandler + * + * This means that there is a lot of conditional compilation, and platform + * specific code. Currently, Catch2 supports a dummy handler (if no + * handler is desired), and 2 platform specific handlers: + * * Windows' SEH + * * POSIX signals + * + * Consequently, various pieces of code below are compiled if either of + * the platform specific handlers is enabled, or if none of them are + * enabled. It is assumed that both cannot be enabled at the same time, + * and doing so should cause a compilation error. + * + * If another platform specific handler is added, the compile guards + * below will need to be updated taking these assumptions into account. + */ + + + +#include + +#if !defined( CATCH_CONFIG_WINDOWS_SEH ) && !defined( CATCH_CONFIG_POSIX_SIGNALS ) + +namespace Catch { + + // If neither SEH nor signal handling is required, the handler impls + // do not have to do anything, and can be empty. + void FatalConditionHandler::engage_platform() {} + void FatalConditionHandler::disengage_platform() noexcept {} + FatalConditionHandler::FatalConditionHandler() = default; + FatalConditionHandler::~FatalConditionHandler() = default; + +} // end namespace Catch + +#endif // !CATCH_CONFIG_WINDOWS_SEH && !CATCH_CONFIG_POSIX_SIGNALS + +#if defined( CATCH_CONFIG_WINDOWS_SEH ) && defined( CATCH_CONFIG_POSIX_SIGNALS ) +#error "Inconsistent configuration: Windows' SEH handling and POSIX signals cannot be enabled at the same time" +#endif // CATCH_CONFIG_WINDOWS_SEH && CATCH_CONFIG_POSIX_SIGNALS + +#if defined( CATCH_CONFIG_WINDOWS_SEH ) || defined( CATCH_CONFIG_POSIX_SIGNALS ) + +namespace { + //! Signals fatal error message to the run context + void reportFatal( char const * const message ) { + Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message ); + } + + //! Minimal size Catch2 needs for its own fatal error handling. + //! Picked empirically, so it might not be sufficient on all + //! platforms, and for all configurations. + constexpr std::size_t minStackSizeForErrors = 32 * 1024; +} // end unnamed namespace + +#endif // CATCH_CONFIG_WINDOWS_SEH || CATCH_CONFIG_POSIX_SIGNALS + +#if defined( CATCH_CONFIG_WINDOWS_SEH ) + +namespace Catch { + + struct SignalDefs { DWORD id; const char* name; }; + + // There is no 1-1 mapping between signals and windows exceptions. + // Windows can easily distinguish between SO and SigSegV, + // but SigInt, SigTerm, etc are handled differently. + static SignalDefs signalDefs[] = { + { EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal" }, + { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" }, + { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" }, + { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" }, + }; + + static LONG CALLBACK topLevelExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo) { + for (auto const& def : signalDefs) { + if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) { + reportFatal(def.name); + } + } + // If its not an exception we care about, pass it along. + // This stops us from eating debugger breaks etc. + return EXCEPTION_CONTINUE_SEARCH; + } + + // Since we do not support multiple instantiations, we put these + // into global variables and rely on cleaning them up in outlined + // constructors/destructors + static LPTOP_LEVEL_EXCEPTION_FILTER previousTopLevelExceptionFilter = nullptr; + + + // For MSVC, we reserve part of the stack memory for handling + // memory overflow structured exception. + FatalConditionHandler::FatalConditionHandler() { + ULONG guaranteeSize = static_cast(minStackSizeForErrors); + if (!SetThreadStackGuarantee(&guaranteeSize)) { + // We do not want to fully error out, because needing + // the stack reserve should be rare enough anyway. + Catch::cerr() + << "Failed to reserve piece of stack." + << " Stack overflows will not be reported successfully."; + } + } + + // We do not attempt to unset the stack guarantee, because + // Windows does not support lowering the stack size guarantee. + FatalConditionHandler::~FatalConditionHandler() = default; + + + void FatalConditionHandler::engage_platform() { + // Register as a the top level exception filter. + previousTopLevelExceptionFilter = SetUnhandledExceptionFilter(topLevelExceptionFilter); + } + + void FatalConditionHandler::disengage_platform() noexcept { + if (SetUnhandledExceptionFilter(previousTopLevelExceptionFilter) != topLevelExceptionFilter) { + Catch::cerr() + << "Unexpected SEH unhandled exception filter on disengage." + << " The filter was restored, but might be rolled back unexpectedly."; + } + previousTopLevelExceptionFilter = nullptr; + } + +} // end namespace Catch + +#endif // CATCH_CONFIG_WINDOWS_SEH + +#if defined( CATCH_CONFIG_POSIX_SIGNALS ) + +#include + +namespace Catch { + + struct SignalDefs { + int id; + const char* name; + }; + + static SignalDefs signalDefs[] = { + { SIGINT, "SIGINT - Terminal interrupt signal" }, + { SIGILL, "SIGILL - Illegal instruction signal" }, + { SIGFPE, "SIGFPE - Floating point error signal" }, + { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, + { SIGTERM, "SIGTERM - Termination request signal" }, + { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } + }; + +// Older GCCs trigger -Wmissing-field-initializers for T foo = {} +// which is zero initialization, but not explicit. We want to avoid +// that. +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif + + static char* altStackMem = nullptr; + static std::size_t altStackSize = 0; + static stack_t oldSigStack{}; + static struct sigaction oldSigActions[sizeof(signalDefs) / sizeof(SignalDefs)]{}; + + static void restorePreviousSignalHandlers() noexcept { + // We set signal handlers back to the previous ones. Hopefully + // nobody overwrote them in the meantime, and doesn't expect + // their signal handlers to live past ours given that they + // installed them after ours.. + for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { + sigaction(signalDefs[i].id, &oldSigActions[i], nullptr); + } + // Return the old stack + sigaltstack(&oldSigStack, nullptr); + } + + static void handleSignal( int sig ) { + char const * name = ""; + for (auto const& def : signalDefs) { + if (sig == def.id) { + name = def.name; + break; + } + } + // We need to restore previous signal handlers and let them do + // their thing, so that the users can have the debugger break + // when a signal is raised, and so on. + restorePreviousSignalHandlers(); + reportFatal( name ); + raise( sig ); + } + + FatalConditionHandler::FatalConditionHandler() { + assert(!altStackMem && "Cannot initialize POSIX signal handler when one already exists"); + if (altStackSize == 0) { + altStackSize = std::max(static_cast(SIGSTKSZ), minStackSizeForErrors); + } + altStackMem = new char[altStackSize](); + } + + FatalConditionHandler::~FatalConditionHandler() { + delete[] altStackMem; + // We signal that another instance can be constructed by zeroing + // out the pointer. + altStackMem = nullptr; + } + + void FatalConditionHandler::engage_platform() { + stack_t sigStack; + sigStack.ss_sp = altStackMem; + sigStack.ss_size = altStackSize; + sigStack.ss_flags = 0; + sigaltstack(&sigStack, &oldSigStack); + struct sigaction sa = { }; + + sa.sa_handler = handleSignal; + sa.sa_flags = SA_ONSTACK; + for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) { + sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); + } + } + +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + + + void FatalConditionHandler::disengage_platform() noexcept { + restorePreviousSignalHandlers(); + } + +} // end namespace Catch + +#endif // CATCH_CONFIG_POSIX_SIGNALS + + + + +#include + +namespace Catch { + namespace Detail { + + uint32_t convertToBits(float f) { + static_assert(sizeof(float) == sizeof(uint32_t), "Important ULP matcher assumption violated"); + uint32_t i; + std::memcpy(&i, &f, sizeof(f)); + return i; + } + + uint64_t convertToBits(double d) { + static_assert(sizeof(double) == sizeof(uint64_t), "Important ULP matcher assumption violated"); + uint64_t i; + std::memcpy(&i, &d, sizeof(d)); + return i; + } + + } // end namespace Detail +} // end namespace Catch + + + + + +#include +#include +#include +#include + +namespace Catch { + + Catch::IStream::~IStream() = default; + +namespace Detail { + namespace { + template + class StreamBufImpl : public std::streambuf { + char data[bufferSize]; + WriterF m_writer; + + public: + StreamBufImpl() { + setp( data, data + sizeof(data) ); + } + + ~StreamBufImpl() noexcept override { + StreamBufImpl::sync(); + } + + private: + int overflow( int c ) override { + sync(); + + if( c != EOF ) { + if( pbase() == epptr() ) + m_writer( std::string( 1, static_cast( c ) ) ); + else + sputc( static_cast( c ) ); + } + return 0; + } + + int sync() override { + if( pbase() != pptr() ) { + m_writer( std::string( pbase(), static_cast( pptr() - pbase() ) ) ); + setp( pbase(), epptr() ); + } + return 0; + } + }; + + /////////////////////////////////////////////////////////////////////////// + + struct OutputDebugWriter { + + void operator()( std::string const& str ) { + if ( !str.empty() ) { + writeToDebugConsole( str ); + } + } + }; + + /////////////////////////////////////////////////////////////////////////// + + class FileStream : public IStream { + std::ofstream m_ofs; + public: + FileStream( std::string const& filename ) { + m_ofs.open( filename.c_str() ); + CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << '\'' ); + m_ofs << std::unitbuf; + } + ~FileStream() override = default; + public: // IStream + std::ostream& stream() override { + return m_ofs; + } + }; + + /////////////////////////////////////////////////////////////////////////// + + class CoutStream : public IStream { + std::ostream m_os; + public: + // Store the streambuf from cout up-front because + // cout may get redirected when running tests + CoutStream() : m_os( Catch::cout().rdbuf() ) {} + ~CoutStream() override = default; + + public: // IStream + std::ostream& stream() override { return m_os; } + bool isConsole() const override { return true; } + }; + + class CerrStream : public IStream { + std::ostream m_os; + + public: + // Store the streambuf from cerr up-front because + // cout may get redirected when running tests + CerrStream(): m_os( Catch::cerr().rdbuf() ) {} + ~CerrStream() override = default; + + public: // IStream + std::ostream& stream() override { return m_os; } + bool isConsole() const override { return true; } + }; + + /////////////////////////////////////////////////////////////////////////// + + class DebugOutStream : public IStream { + Detail::unique_ptr> m_streamBuf; + std::ostream m_os; + public: + DebugOutStream() + : m_streamBuf( Detail::make_unique>() ), + m_os( m_streamBuf.get() ) + {} + + ~DebugOutStream() override = default; + + public: // IStream + std::ostream& stream() override { return m_os; } + }; + + } // unnamed namespace +} // namespace Detail + + /////////////////////////////////////////////////////////////////////////// + + auto makeStream( std::string const& filename ) -> Detail::unique_ptr { + if ( filename.empty() || filename == "-" ) { + return Detail::make_unique(); + } + if( filename[0] == '%' ) { + if ( filename == "%debug" ) { + return Detail::make_unique(); + } else if ( filename == "%stderr" ) { + return Detail::make_unique(); + } else if ( filename == "%stdout" ) { + return Detail::make_unique(); + } else { + CATCH_ERROR( "Unrecognised stream: '" << filename << '\'' ); + } + } + return Detail::make_unique( filename ); + } + +} + + + + +namespace Catch { + + auto operator << (std::ostream& os, LazyExpression const& lazyExpr) -> std::ostream& { + if (lazyExpr.m_isNegated) + os << '!'; + + if (lazyExpr) { + if (lazyExpr.m_isNegated && lazyExpr.m_transientExpression->isBinaryExpression()) + os << '(' << *lazyExpr.m_transientExpression << ')'; + else + os << *lazyExpr.m_transientExpression; + } else { + os << "{** error - unchecked empty expression requested **}"; + } + return os; + } + +} // namespace Catch + + + + +#ifdef CATCH_CONFIG_WINDOWS_CRTDBG +#include + +namespace Catch { + + LeakDetector::LeakDetector() { + int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + flag |= _CRTDBG_LEAK_CHECK_DF; + flag |= _CRTDBG_ALLOC_MEM_DF; + _CrtSetDbgFlag(flag); + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); + // Change this to leaking allocation's number to break there + _CrtSetBreakAlloc(-1); + } +} + +#else // ^^ Windows crt debug heap enabled // Windows crt debug heap disabled vv + + Catch::LeakDetector::LeakDetector() {} + +#endif // CATCH_CONFIG_WINDOWS_CRTDBG + +Catch::LeakDetector::~LeakDetector() { + Catch::cleanUp(); +} + + + + + +namespace Catch { + namespace { + + void listTests(IEventListener& reporter, IConfig const& config) { + auto const& testSpec = config.testSpec(); + auto matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config); + reporter.listTests(matchedTestCases); + } + + void listTags(IEventListener& reporter, IConfig const& config) { + auto const& testSpec = config.testSpec(); + std::vector matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config); + + std::map tagCounts; + for (auto const& testCase : matchedTestCases) { + for (auto const& tagName : testCase.getTestCaseInfo().tags) { + auto it = tagCounts.find(tagName.original); + if (it == tagCounts.end()) + it = tagCounts.insert(std::make_pair(tagName.original, TagInfo())).first; + it->second.add(tagName.original); + } + } + + std::vector infos; infos.reserve(tagCounts.size()); + for (auto& tagc : tagCounts) { + infos.push_back(CATCH_MOVE(tagc.second)); + } + + reporter.listTags(infos); + } + + void listReporters(IEventListener& reporter) { + std::vector descriptions; + + IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); + descriptions.reserve(factories.size()); + for (auto const& fac : factories) { + descriptions.push_back({ fac.first, fac.second->getDescription() }); + } + + reporter.listReporters(descriptions); + } + + void listListeners(IEventListener& reporter) { + std::vector descriptions; + + auto const& factories = + getRegistryHub().getReporterRegistry().getListeners(); + descriptions.reserve( factories.size() ); + for ( auto const& fac : factories ) { + descriptions.push_back( { fac->getName(), fac->getDescription() } ); + } + + reporter.listListeners( descriptions ); + } + + } // end anonymous namespace + + void TagInfo::add( StringRef spelling ) { + ++count; + spellings.insert( spelling ); + } + + std::string TagInfo::all() const { + // 2 per tag for brackets '[' and ']' + size_t size = spellings.size() * 2; + for (auto const& spelling : spellings) { + size += spelling.size(); + } + + std::string out; out.reserve(size); + for (auto const& spelling : spellings) { + out += '['; + out += spelling; + out += ']'; + } + return out; + } + + bool list( IEventListener& reporter, Config const& config ) { + bool listed = false; + if (config.listTests()) { + listed = true; + listTests(reporter, config); + } + if (config.listTags()) { + listed = true; + listTags(reporter, config); + } + if (config.listReporters()) { + listed = true; + listReporters(reporter); + } + if ( config.listListeners() ) { + listed = true; + listListeners( reporter ); + } + return listed; + } + +} // end namespace Catch + + + +namespace Catch { + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS + static LeakDetector leakDetector; + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +} + +// Allow users of amalgamated .cpp file to remove our main and provide their own. +#if !defined(CATCH_AMALGAMATED_CUSTOM_MAIN) + +#if defined(CATCH_CONFIG_WCHAR) && defined(CATCH_PLATFORM_WINDOWS) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN) +// Standard C/C++ Win32 Unicode wmain entry point +extern "C" int __cdecl wmain (int argc, wchar_t * argv[], wchar_t * []) { +#else +// Standard C/C++ main entry point +int main (int argc, char * argv[]) { +#endif + + // We want to force the linker not to discard the global variable + // and its constructor, as it (optionally) registers leak detector + (void)&Catch::leakDetector; + + return Catch::Session().run( argc, argv ); +} + +#endif // !defined(CATCH_AMALGAMATED_CUSTOM_MAIN + + + + +namespace Catch { + + MessageInfo::MessageInfo( StringRef _macroName, + SourceLineInfo const& _lineInfo, + ResultWas::OfType _type ) + : macroName( _macroName ), + lineInfo( _lineInfo ), + type( _type ), + sequence( ++globalCount ) + {} + + // This may need protecting if threading support is added + unsigned int MessageInfo::globalCount = 0; + +} // end namespace Catch + + + +#include +#include +#include + +#if defined(CATCH_CONFIG_NEW_CAPTURE) + #if defined(_MSC_VER) + #include //_dup and _dup2 + #define dup _dup + #define dup2 _dup2 + #define fileno _fileno + #else + #include // dup and dup2 + #endif +#endif + + +namespace Catch { + + RedirectedStream::RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream ) + : m_originalStream( originalStream ), + m_redirectionStream( redirectionStream ), + m_prevBuf( m_originalStream.rdbuf() ) + { + m_originalStream.rdbuf( m_redirectionStream.rdbuf() ); + } + + RedirectedStream::~RedirectedStream() { + m_originalStream.rdbuf( m_prevBuf ); + } + + RedirectedStdOut::RedirectedStdOut() : m_cout( Catch::cout(), m_rss.get() ) {} + auto RedirectedStdOut::str() const -> std::string { return m_rss.str(); } + + RedirectedStdErr::RedirectedStdErr() + : m_cerr( Catch::cerr(), m_rss.get() ), + m_clog( Catch::clog(), m_rss.get() ) + {} + auto RedirectedStdErr::str() const -> std::string { return m_rss.str(); } + + RedirectedStreams::RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr) + : m_redirectedCout(redirectedCout), + m_redirectedCerr(redirectedCerr) + {} + + RedirectedStreams::~RedirectedStreams() { + m_redirectedCout += m_redirectedStdOut.str(); + m_redirectedCerr += m_redirectedStdErr.str(); + } + +#if defined(CATCH_CONFIG_NEW_CAPTURE) + +#if defined(_MSC_VER) + TempFile::TempFile() { + if (tmpnam_s(m_buffer)) { + CATCH_RUNTIME_ERROR("Could not get a temp filename"); + } + if (fopen_s(&m_file, m_buffer, "w+")) { + char buffer[100]; + if (strerror_s(buffer, errno)) { + CATCH_RUNTIME_ERROR("Could not translate errno to a string"); + } + CATCH_RUNTIME_ERROR("Could not open the temp file: '" << m_buffer << "' because: " << buffer); + } + } +#else + TempFile::TempFile() { + m_file = std::tmpfile(); + if (!m_file) { + CATCH_RUNTIME_ERROR("Could not create a temp file."); + } + } + +#endif + + TempFile::~TempFile() { + // TBD: What to do about errors here? + std::fclose(m_file); + // We manually create the file on Windows only, on Linux + // it will be autodeleted +#if defined(_MSC_VER) + std::remove(m_buffer); +#endif + } + + + FILE* TempFile::getFile() { + return m_file; + } + + std::string TempFile::getContents() { + std::stringstream sstr; + char buffer[100] = {}; + std::rewind(m_file); + while (std::fgets(buffer, sizeof(buffer), m_file)) { + sstr << buffer; + } + return sstr.str(); + } + + OutputRedirect::OutputRedirect(std::string& stdout_dest, std::string& stderr_dest) : + m_originalStdout(dup(1)), + m_originalStderr(dup(2)), + m_stdoutDest(stdout_dest), + m_stderrDest(stderr_dest) { + dup2(fileno(m_stdoutFile.getFile()), 1); + dup2(fileno(m_stderrFile.getFile()), 2); + } + + OutputRedirect::~OutputRedirect() { + Catch::cout() << std::flush; + fflush(stdout); + // Since we support overriding these streams, we flush cerr + // even though std::cerr is unbuffered + Catch::cerr() << std::flush; + Catch::clog() << std::flush; + fflush(stderr); + + dup2(m_originalStdout, 1); + dup2(m_originalStderr, 2); + + m_stdoutDest += m_stdoutFile.getContents(); + m_stderrDest += m_stderrFile.getContents(); + } + +#endif // CATCH_CONFIG_NEW_CAPTURE + +} // namespace Catch + +#if defined(CATCH_CONFIG_NEW_CAPTURE) + #if defined(_MSC_VER) + #undef dup + #undef dup2 + #undef fileno + #endif +#endif + + + + +#include + +namespace Catch { + +#if !defined(CATCH_CONFIG_POLYFILL_ISNAN) + bool isnan(float f) { + return std::isnan(f); + } + bool isnan(double d) { + return std::isnan(d); + } +#else + // For now we only use this for embarcadero + bool isnan(float f) { + return std::_isnan(f); + } + bool isnan(double d) { + return std::_isnan(d); + } +#endif + +} // end namespace Catch + + + +namespace Catch { + +namespace { + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4146) // we negate uint32 during the rotate +#endif + // Safe rotr implementation thanks to John Regehr + uint32_t rotate_right(uint32_t val, uint32_t count) { + const uint32_t mask = 31; + count &= mask; + return (val >> count) | (val << (-count & mask)); + } + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +} + + + SimplePcg32::SimplePcg32(result_type seed_) { + seed(seed_); + } + + + void SimplePcg32::seed(result_type seed_) { + m_state = 0; + (*this)(); + m_state += seed_; + (*this)(); + } + + void SimplePcg32::discard(uint64_t skip) { + // We could implement this to run in O(log n) steps, but this + // should suffice for our use case. + for (uint64_t s = 0; s < skip; ++s) { + static_cast((*this)()); + } + } + + SimplePcg32::result_type SimplePcg32::operator()() { + // prepare the output value + const uint32_t xorshifted = static_cast(((m_state >> 18u) ^ m_state) >> 27u); + const auto output = rotate_right(xorshifted, m_state >> 59u); + + // advance state + m_state = m_state * 6364136223846793005ULL + s_inc; + + return output; + } + + bool operator==(SimplePcg32 const& lhs, SimplePcg32 const& rhs) { + return lhs.m_state == rhs.m_state; + } + + bool operator!=(SimplePcg32 const& lhs, SimplePcg32 const& rhs) { + return lhs.m_state != rhs.m_state; + } +} + + + + + +#include +#include + +namespace Catch { + + std::uint32_t generateRandomSeed( GenerateFrom from ) { + switch ( from ) { + case GenerateFrom::Time: + return static_cast( std::time( nullptr ) ); + + case GenerateFrom::Default: + case GenerateFrom::RandomDevice: + // In theory, a platform could have random_device that returns just + // 16 bits. That is still some randomness, so we don't care too much + return static_cast( std::random_device{}() ); + + default: + CATCH_ERROR("Unknown generation method"); + } + } + +} // end namespace Catch + + + + +namespace Catch { + + ReporterRegistry::ReporterRegistry() { + // Because it is impossible to move out of initializer list, + // we have to add the elements manually + m_factories["Automake"] = Detail::make_unique>(); + m_factories["compact"] = Detail::make_unique>(); + m_factories["console"] = Detail::make_unique>(); + m_factories["JUnit"] = Detail::make_unique>(); + m_factories["SonarQube"] = Detail::make_unique>(); + m_factories["TAP"] = Detail::make_unique>(); + m_factories["TeamCity"] = Detail::make_unique>(); + m_factories["XML"] = Detail::make_unique>(); + } + + ReporterRegistry::~ReporterRegistry() = default; + + + IEventListenerPtr ReporterRegistry::create( std::string const& name, ReporterConfig&& config ) const { + auto it = m_factories.find( name ); + if( it == m_factories.end() ) + return nullptr; + return it->second->create( CATCH_MOVE(config) ); + } + + void ReporterRegistry::registerReporter( std::string const& name, IReporterFactoryPtr factory ) { + CATCH_ENFORCE( name.find( "::" ) == name.npos, + "'::' is not allowed in reporter name: '" + name + '\'' ); + auto ret = m_factories.emplace(name, CATCH_MOVE(factory)); + CATCH_ENFORCE( ret.second, "reporter using '" + name + "' as name was already registered" ); + } + void ReporterRegistry::registerListener( + Detail::unique_ptr factory ) { + m_listeners.push_back( CATCH_MOVE(factory) ); + } + + IReporterRegistry::FactoryMap const& ReporterRegistry::getFactories() const { + return m_factories; + } + IReporterRegistry::Listeners const& ReporterRegistry::getListeners() const { + return m_listeners; + } + +} + + + + + +#include + +namespace Catch { + + namespace { + struct kvPair { + StringRef key, value; + }; + + kvPair splitKVPair(StringRef kvString) { + auto splitPos = static_cast( std::distance( + kvString.begin(), + std::find( kvString.begin(), kvString.end(), '=' ) ) ); + + return { kvString.substr( 0, splitPos ), + kvString.substr( splitPos + 1, kvString.size() ) }; + } + } + + namespace Detail { + std::vector splitReporterSpec( StringRef reporterSpec ) { + static constexpr auto separator = "::"; + static constexpr size_t separatorSize = 2; + + size_t separatorPos = 0; + auto findNextSeparator = [&reporterSpec]( size_t startPos ) { + static_assert( + separatorSize == 2, + "The code below currently assumes 2 char separator" ); + + auto currentPos = startPos; + do { + while ( currentPos < reporterSpec.size() && + reporterSpec[currentPos] != separator[0] ) { + ++currentPos; + } + if ( currentPos + 1 < reporterSpec.size() && + reporterSpec[currentPos + 1] == separator[1] ) { + return currentPos; + } + ++currentPos; + } while ( currentPos < reporterSpec.size() ); + + return static_cast( -1 ); + }; + + std::vector parts; + + while ( separatorPos < reporterSpec.size() ) { + const auto nextSeparator = findNextSeparator( separatorPos ); + parts.push_back( static_cast( reporterSpec.substr( + separatorPos, nextSeparator - separatorPos ) ) ); + + if ( nextSeparator == static_cast( -1 ) ) { + break; + } + separatorPos = nextSeparator + separatorSize; + } + + // Handle a separator at the end. + // This is not a valid spec, but we want to do validation in a + // centralized place + if ( separatorPos == reporterSpec.size() ) { + parts.emplace_back(); + } + + return parts; + } + + Optional stringToColourMode( StringRef colourMode ) { + if ( colourMode == "default" ) { + return ColourMode::PlatformDefault; + } else if ( colourMode == "ansi" ) { + return ColourMode::ANSI; + } else if ( colourMode == "win32" ) { + return ColourMode::Win32; + } else if ( colourMode == "none" ) { + return ColourMode::None; + } else { + return {}; + } + } + } // namespace Detail + + + bool operator==( ReporterSpec const& lhs, ReporterSpec const& rhs ) { + return lhs.m_name == rhs.m_name && + lhs.m_outputFileName == rhs.m_outputFileName && + lhs.m_colourMode == rhs.m_colourMode && + lhs.m_customOptions == rhs.m_customOptions; + } + + Optional parseReporterSpec( StringRef reporterSpec ) { + auto parts = Detail::splitReporterSpec( reporterSpec ); + + assert( parts.size() > 0 && "Split should never return empty vector" ); + + std::map kvPairs; + Optional outputFileName; + Optional colourMode; + + // First part is always reporter name, so we skip it + for ( size_t i = 1; i < parts.size(); ++i ) { + auto kv = splitKVPair( parts[i] ); + auto key = kv.key, value = kv.value; + + if ( key.empty() || value.empty() ) { + return {}; + } else if ( key[0] == 'X' ) { + // This is a reporter-specific option, we don't check these + // apart from basic sanity checks + if ( key.size() == 1 ) { + return {}; + } + + auto ret = kvPairs.emplace( std::string(kv.key), std::string(kv.value) ); + if ( !ret.second ) { + // Duplicated key. We might want to handle this differently, + // e.g. by overwriting the existing value? + return {}; + } + } else if ( key == "out" ) { + // Duplicated key + if ( outputFileName ) { + return {}; + } + outputFileName = static_cast( value ); + } else if ( key == "colour-mode" ) { + // Duplicated key + if ( colourMode ) { + return {}; + } + colourMode = Detail::stringToColourMode( value ); + // Parsing failed + if ( !colourMode ) { + return {}; + } + } else { + // Unrecognized option + return {}; + } + } + + return ReporterSpec{ CATCH_MOVE( parts[0] ), + CATCH_MOVE( outputFileName ), + CATCH_MOVE( colourMode ), + CATCH_MOVE( kvPairs ) }; + } + +ReporterSpec::ReporterSpec( + std::string name, + Optional outputFileName, + Optional colourMode, + std::map customOptions ): + m_name( CATCH_MOVE( name ) ), + m_outputFileName( CATCH_MOVE( outputFileName ) ), + m_colourMode( CATCH_MOVE( colourMode ) ), + m_customOptions( CATCH_MOVE( customOptions ) ) {} + +} // namespace Catch + + + +namespace Catch { + + bool isOk( ResultWas::OfType resultType ) { + return ( resultType & ResultWas::FailureBit ) == 0; + } + bool isJustInfo( int flags ) { + return flags == ResultWas::Info; + } + + ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) { + return static_cast( static_cast( lhs ) | static_cast( rhs ) ); + } + + bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } + bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; } + +} // end namespace Catch + + + +#include +#include +#include + +namespace Catch { + + // This class encapsulates the idea of a pool of ostringstreams that can be reused. + struct StringStreams { + std::vector> m_streams; + std::vector m_unused; + std::ostringstream m_referenceStream; // Used for copy state/ flags from + + auto add() -> std::size_t { + if( m_unused.empty() ) { + m_streams.push_back( Detail::make_unique() ); + return m_streams.size()-1; + } + else { + auto index = m_unused.back(); + m_unused.pop_back(); + return index; + } + } + + void release( std::size_t index ) { + m_streams[index]->copyfmt( m_referenceStream ); // Restore initial flags and other state + m_unused.push_back(index); + } + }; + + ReusableStringStream::ReusableStringStream() + : m_index( Singleton::getMutable().add() ), + m_oss( Singleton::getMutable().m_streams[m_index].get() ) + {} + + ReusableStringStream::~ReusableStringStream() { + static_cast( m_oss )->str(""); + m_oss->clear(); + Singleton::getMutable().release( m_index ); + } + + std::string ReusableStringStream::str() const { + return static_cast( m_oss )->str(); + } + + void ReusableStringStream::str( std::string const& str ) { + static_cast( m_oss )->str( str ); + } + + +} + + + + +#include +#include + +namespace Catch { + + namespace Generators { + struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker { + GeneratorBasePtr m_generator; + + GeneratorTracker( TestCaseTracking::NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : TrackerBase( nameAndLocation, ctx, parent ) + {} + ~GeneratorTracker() override; + + static GeneratorTracker& acquire( TrackerContext& ctx, TestCaseTracking::NameAndLocation const& nameAndLocation ) { + GeneratorTracker* tracker; + + ITracker& currentTracker = ctx.currentTracker(); + // Under specific circumstances, the generator we want + // to acquire is also the current tracker. If this is + // the case, we have to avoid looking through current + // tracker's children, and instead return the current + // tracker. + // A case where this check is important is e.g. + // for (int i = 0; i < 5; ++i) { + // int n = GENERATE(1, 2); + // } + // + // without it, the code above creates 5 nested generators. + if ( currentTracker.nameAndLocation() == nameAndLocation ) { + auto thisTracker = + currentTracker.parent()->findChild( nameAndLocation ); + assert( thisTracker ); + assert( thisTracker->isGeneratorTracker() ); + tracker = static_cast( thisTracker ); + } else if ( ITracker* childTracker = + currentTracker.findChild( nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isGeneratorTracker() ); + tracker = static_cast( childTracker ); + } else { + auto newTracker = + Catch::Detail::make_unique( + nameAndLocation, ctx, ¤tTracker ); + tracker = newTracker.get(); + currentTracker.addChild( CATCH_MOVE(newTracker) ); + } + + if( !tracker->isComplete() ) { + tracker->open(); + } + + return *tracker; + } + + // TrackerBase interface + bool isGeneratorTracker() const override { return true; } + auto hasGenerator() const -> bool override { + return !!m_generator; + } + void close() override { + TrackerBase::close(); + // If a generator has a child (it is followed by a section) + // and none of its children have started, then we must wait + // until later to start consuming its values. + // This catches cases where `GENERATE` is placed between two + // `SECTION`s. + // **The check for m_children.empty cannot be removed**. + // doing so would break `GENERATE` _not_ followed by `SECTION`s. + const bool should_wait_for_child = [&]() { + // No children -> nobody to wait for + if ( m_children.empty() ) { + return false; + } + // If at least one child started executing, don't wait + if ( std::find_if( + m_children.begin(), + m_children.end(), + []( TestCaseTracking::ITrackerPtr const& tracker ) { + return tracker->hasStarted(); + } ) != m_children.end() ) { + return false; + } + + // No children have started. We need to check if they _can_ + // start, and thus we should wait for them, or they cannot + // start (due to filters), and we shouldn't wait for them + ITracker* parent = m_parent; + // This is safe: there is always at least one section + // tracker in a test case tracking tree + while ( !parent->isSectionTracker() ) { + parent = parent->parent(); + } + assert( parent && + "Missing root (test case) level section" ); + + auto const& parentSection = + static_cast( *parent ); + auto const& filters = parentSection.getFilters(); + // No filters -> no restrictions on running sections + if ( filters.empty() ) { + return true; + } + + for ( auto const& child : m_children ) { + if ( child->isSectionTracker() && + std::find( + filters.begin(), + filters.end(), + static_cast( *child ) + .trimmedName() ) != filters.end() ) { + return true; + } + } + return false; + }(); + + // This check is a bit tricky, because m_generator->next() + // has a side-effect, where it consumes generator's current + // value, but we do not want to invoke the side-effect if + // this generator is still waiting for any child to start. + if ( should_wait_for_child || + ( m_runState == CompletedSuccessfully && + m_generator->countedNext() ) ) { + m_children.clear(); + m_runState = Executing; + } + } + + // IGeneratorTracker interface + auto getGenerator() const -> GeneratorBasePtr const& override { + return m_generator; + } + void setGenerator( GeneratorBasePtr&& generator ) override { + m_generator = CATCH_MOVE( generator ); + } + }; + GeneratorTracker::~GeneratorTracker() = default; + } + + RunContext::RunContext(IConfig const* _config, IEventListenerPtr&& reporter) + : m_runInfo(_config->name()), + m_context(getCurrentMutableContext()), + m_config(_config), + m_reporter(CATCH_MOVE(reporter)), + m_lastAssertionInfo{ StringRef(), SourceLineInfo("",0), StringRef(), ResultDisposition::Normal }, + m_includeSuccessfulResults( m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions ) + { + m_context.setResultCapture(this); + m_reporter->testRunStarting(m_runInfo); + } + + RunContext::~RunContext() { + m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, aborting())); + } + + Totals RunContext::runTest(TestCaseHandle const& testCase) { + const Totals prevTotals = m_totals; + + std::string redirectedCout; + std::string redirectedCerr; + + auto const& testInfo = testCase.getTestCaseInfo(); + + m_reporter->testCaseStarting(testInfo); + + m_activeTestCase = &testCase; + + + ITracker& rootTracker = m_trackerContext.startRun(); + assert(rootTracker.isSectionTracker()); + static_cast(rootTracker).addInitialFilters(m_config->getSectionsToRun()); + + // We intentionally only seed the internal RNG once per test case, + // before it is first invoked. The reason for that is a complex + // interplay of generator/section implementation details and the + // Random*Generator types. + // + // The issue boils down to us needing to seed the Random*Generators + // with different seed each, so that they return different sequences + // of random numbers. We do this by giving them a number from the + // shared RNG instance as their seed. + // + // However, this runs into an issue if the reseeding happens each + // time the test case is entered (as opposed to first time only), + // because multiple generators could get the same seed, e.g. in + // ```cpp + // TEST_CASE() { + // auto i = GENERATE(take(10, random(0, 100)); + // SECTION("A") { + // auto j = GENERATE(take(10, random(0, 100)); + // } + // SECTION("B") { + // auto k = GENERATE(take(10, random(0, 100)); + // } + // } + // ``` + // `i` and `j` would properly return values from different sequences, + // but `i` and `k` would return the same sequence, because their seed + // would be the same. + // (The reason their seeds would be the same is that the generator + // for k would be initialized when the test case is entered the second + // time, after the shared RNG instance was reset to the same value + // it had when the generator for i was initialized.) + seedRng( *m_config ); + + uint64_t testRuns = 0; + do { + m_trackerContext.startCycle(); + m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(testInfo.name, testInfo.lineInfo)); + + m_reporter->testCasePartialStarting(testInfo, testRuns); + + const auto beforeRunTotals = m_totals; + std::string oneRunCout, oneRunCerr; + runCurrentTest(oneRunCout, oneRunCerr); + redirectedCout += oneRunCout; + redirectedCerr += oneRunCerr; + + const auto singleRunTotals = m_totals.delta(beforeRunTotals); + auto statsForOneRun = TestCaseStats(testInfo, singleRunTotals, oneRunCout, oneRunCerr, aborting()); + + m_reporter->testCasePartialEnded(statsForOneRun, testRuns); + ++testRuns; + } while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting()); + + Totals deltaTotals = m_totals.delta(prevTotals); + if (testInfo.expectedToFail() && deltaTotals.testCases.passed > 0) { + deltaTotals.assertions.failed++; + deltaTotals.testCases.passed--; + deltaTotals.testCases.failed++; + } + m_totals.testCases += deltaTotals.testCases; + m_reporter->testCaseEnded(TestCaseStats(testInfo, + deltaTotals, + redirectedCout, + redirectedCerr, + aborting())); + + m_activeTestCase = nullptr; + m_testCaseTracker = nullptr; + + return deltaTotals; + } + + + void RunContext::assertionEnded(AssertionResult const & result) { + if (result.getResultType() == ResultWas::Ok) { + m_totals.assertions.passed++; + m_lastAssertionPassed = true; + } else if (!result.succeeded()) { + m_lastAssertionPassed = false; + if (result.isOk()) { + } + else if( m_activeTestCase->getTestCaseInfo().okToFail() ) + m_totals.assertions.failedButOk++; + else + m_totals.assertions.failed++; + } + else { + m_lastAssertionPassed = true; + } + + m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals)); + + if (result.getResultType() != ResultWas::Warning) + m_messageScopes.clear(); + + // Reset working state + resetAssertionInfo(); + m_lastResult = result; + } + void RunContext::resetAssertionInfo() { + m_lastAssertionInfo.macroName = StringRef(); + m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr; + } + + bool RunContext::sectionStarted(SectionInfo const & sectionInfo, Counts & assertions) { + ITracker& sectionTracker = SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(sectionInfo.name, sectionInfo.lineInfo)); + if (!sectionTracker.isOpen()) + return false; + m_activeSections.push_back(§ionTracker); + + m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; + + m_reporter->sectionStarting(sectionInfo); + + assertions = m_totals.assertions; + + return true; + } + auto RunContext::acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { + using namespace Generators; + GeneratorTracker& tracker = GeneratorTracker::acquire(m_trackerContext, + TestCaseTracking::NameAndLocation( static_cast(generatorName), lineInfo ) ); + m_lastAssertionInfo.lineInfo = lineInfo; + return tracker; + } + + bool RunContext::testForMissingAssertions(Counts& assertions) { + if (assertions.total() != 0) + return false; + if (!m_config->warnAboutMissingAssertions()) + return false; + if (m_trackerContext.currentTracker().hasChildren()) + return false; + m_totals.assertions.failed++; + assertions.failed++; + return true; + } + + void RunContext::sectionEnded(SectionEndInfo const & endInfo) { + Counts assertions = m_totals.assertions - endInfo.prevAssertions; + bool missingAssertions = testForMissingAssertions(assertions); + + if (!m_activeSections.empty()) { + m_activeSections.back()->close(); + m_activeSections.pop_back(); + } + + m_reporter->sectionEnded(SectionStats(endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions)); + m_messages.clear(); + m_messageScopes.clear(); + } + + void RunContext::sectionEndedEarly(SectionEndInfo const & endInfo) { + if (m_unfinishedSections.empty()) + m_activeSections.back()->fail(); + else + m_activeSections.back()->close(); + m_activeSections.pop_back(); + + m_unfinishedSections.push_back(endInfo); + } + + void RunContext::benchmarkPreparing( StringRef name ) { + m_reporter->benchmarkPreparing(name); + } + void RunContext::benchmarkStarting( BenchmarkInfo const& info ) { + m_reporter->benchmarkStarting( info ); + } + void RunContext::benchmarkEnded( BenchmarkStats<> const& stats ) { + m_reporter->benchmarkEnded( stats ); + } + void RunContext::benchmarkFailed( StringRef error ) { + m_reporter->benchmarkFailed( error ); + } + + void RunContext::pushScopedMessage(MessageInfo const & message) { + m_messages.push_back(message); + } + + void RunContext::popScopedMessage(MessageInfo const & message) { + m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), message), m_messages.end()); + } + + void RunContext::emplaceUnscopedMessage( MessageBuilder const& builder ) { + m_messageScopes.emplace_back( builder ); + } + + std::string RunContext::getCurrentTestName() const { + return m_activeTestCase + ? m_activeTestCase->getTestCaseInfo().name + : std::string(); + } + + const AssertionResult * RunContext::getLastResult() const { + return &(*m_lastResult); + } + + void RunContext::exceptionEarlyReported() { + m_shouldReportUnexpected = false; + } + + void RunContext::handleFatalErrorCondition( StringRef message ) { + // First notify reporter that bad things happened + m_reporter->fatalErrorEncountered(message); + + // Don't rebuild the result -- the stringification itself can cause more fatal errors + // Instead, fake a result data. + AssertionResultData tempResult( ResultWas::FatalErrorCondition, { false } ); + tempResult.message = static_cast(message); + AssertionResult result(m_lastAssertionInfo, tempResult); + + assertionEnded(result); + + handleUnfinishedSections(); + + // Recreate section for test case (as we will lose the one that was in scope) + auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name); + + Counts assertions; + assertions.failed = 1; + SectionStats testCaseSectionStats(testCaseSection, assertions, 0, false); + m_reporter->sectionEnded(testCaseSectionStats); + + auto const& testInfo = m_activeTestCase->getTestCaseInfo(); + + Totals deltaTotals; + deltaTotals.testCases.failed = 1; + deltaTotals.assertions.failed = 1; + m_reporter->testCaseEnded(TestCaseStats(testInfo, + deltaTotals, + std::string(), + std::string(), + false)); + m_totals.testCases.failed++; + m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false)); + } + + bool RunContext::lastAssertionPassed() { + return m_lastAssertionPassed; + } + + void RunContext::assertionPassed() { + m_lastAssertionPassed = true; + ++m_totals.assertions.passed; + resetAssertionInfo(); + m_messageScopes.clear(); + } + + bool RunContext::aborting() const { + return m_totals.assertions.failed >= static_cast(m_config->abortAfter()); + } + + void RunContext::runCurrentTest(std::string & redirectedCout, std::string & redirectedCerr) { + auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name); + m_reporter->sectionStarting(testCaseSection); + Counts prevAssertions = m_totals.assertions; + double duration = 0; + m_shouldReportUnexpected = true; + m_lastAssertionInfo = { "TEST_CASE"_sr, testCaseInfo.lineInfo, StringRef(), ResultDisposition::Normal }; + + Timer timer; + CATCH_TRY { + if (m_reporter->getPreferences().shouldRedirectStdOut) { +#if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) + RedirectedStreams redirectedStreams(redirectedCout, redirectedCerr); + + timer.start(); + invokeActiveTestCase(); +#else + OutputRedirect r(redirectedCout, redirectedCerr); + timer.start(); + invokeActiveTestCase(); +#endif + } else { + timer.start(); + invokeActiveTestCase(); + } + duration = timer.getElapsedSeconds(); + } CATCH_CATCH_ANON (TestFailureException&) { + // This just means the test was aborted due to failure + } CATCH_CATCH_ALL { + // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions + // are reported without translation at the point of origin. + if( m_shouldReportUnexpected ) { + AssertionReaction dummyReaction; + handleUnexpectedInflightException( m_lastAssertionInfo, translateActiveException(), dummyReaction ); + } + } + Counts assertions = m_totals.assertions - prevAssertions; + bool missingAssertions = testForMissingAssertions(assertions); + + m_testCaseTracker->close(); + handleUnfinishedSections(); + m_messages.clear(); + m_messageScopes.clear(); + + SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions); + m_reporter->sectionEnded(testCaseSectionStats); + } + + void RunContext::invokeActiveTestCase() { + // We need to engage a handler for signals/structured exceptions + // before running the tests themselves, or the binary can crash + // without failed test being reported. + FatalConditionHandlerGuard _(&m_fatalConditionhandler); + // We keep having issue where some compilers warn about an unused + // variable, even though the type has non-trivial constructor and + // destructor. This is annoying and ugly, but it makes them stfu. + (void)_; + + m_activeTestCase->invoke(); + } + + void RunContext::handleUnfinishedSections() { + // If sections ended prematurely due to an exception we stored their + // infos here so we can tear them down outside the unwind process. + for (auto it = m_unfinishedSections.rbegin(), + itEnd = m_unfinishedSections.rend(); + it != itEnd; + ++it) + sectionEnded(*it); + m_unfinishedSections.clear(); + } + + void RunContext::handleExpr( + AssertionInfo const& info, + ITransientExpression const& expr, + AssertionReaction& reaction + ) { + m_reporter->assertionStarting( info ); + + bool negated = isFalseTest( info.resultDisposition ); + bool result = expr.getResult() != negated; + + if( result ) { + if (!m_includeSuccessfulResults) { + assertionPassed(); + } + else { + reportExpr(info, ResultWas::Ok, &expr, negated); + } + } + else { + reportExpr(info, ResultWas::ExpressionFailed, &expr, negated ); + populateReaction( reaction ); + } + } + void RunContext::reportExpr( + AssertionInfo const &info, + ResultWas::OfType resultType, + ITransientExpression const *expr, + bool negated ) { + + m_lastAssertionInfo = info; + AssertionResultData data( resultType, LazyExpression( negated ) ); + + AssertionResult assertionResult{ info, data }; + assertionResult.m_resultData.lazyExpression.m_transientExpression = expr; + + assertionEnded( assertionResult ); + } + + void RunContext::handleMessage( + AssertionInfo const& info, + ResultWas::OfType resultType, + StringRef message, + AssertionReaction& reaction + ) { + m_reporter->assertionStarting( info ); + + m_lastAssertionInfo = info; + + AssertionResultData data( resultType, LazyExpression( false ) ); + data.message = static_cast(message); + AssertionResult assertionResult{ m_lastAssertionInfo, data }; + assertionEnded( assertionResult ); + if( !assertionResult.isOk() ) + populateReaction( reaction ); + } + void RunContext::handleUnexpectedExceptionNotThrown( + AssertionInfo const& info, + AssertionReaction& reaction + ) { + handleNonExpr(info, Catch::ResultWas::DidntThrowException, reaction); + } + + void RunContext::handleUnexpectedInflightException( + AssertionInfo const& info, + std::string const& message, + AssertionReaction& reaction + ) { + m_lastAssertionInfo = info; + + AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); + data.message = message; + AssertionResult assertionResult{ info, data }; + assertionEnded( assertionResult ); + populateReaction( reaction ); + } + + void RunContext::populateReaction( AssertionReaction& reaction ) { + reaction.shouldDebugBreak = m_config->shouldDebugBreak(); + reaction.shouldThrow = aborting() || (m_lastAssertionInfo.resultDisposition & ResultDisposition::Normal); + } + + void RunContext::handleIncomplete( + AssertionInfo const& info + ) { + m_lastAssertionInfo = info; + + AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); + data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"; + AssertionResult assertionResult{ info, data }; + assertionEnded( assertionResult ); + } + void RunContext::handleNonExpr( + AssertionInfo const &info, + ResultWas::OfType resultType, + AssertionReaction &reaction + ) { + m_lastAssertionInfo = info; + + AssertionResultData data( resultType, LazyExpression( false ) ); + AssertionResult assertionResult{ info, data }; + assertionEnded( assertionResult ); + + if( !assertionResult.isOk() ) + populateReaction( reaction ); + } + + + IResultCapture& getResultCapture() { + if (auto* capture = getCurrentContext().getResultCapture()) + return *capture; + else + CATCH_INTERNAL_ERROR("No result capture instance"); + } + + void seedRng(IConfig const& config) { + sharedRng().seed(config.rngSeed()); + } + + unsigned int rngSeed() { + return getCurrentContext().getConfig()->rngSeed(); + } + +} + + + +namespace Catch { + + Section::Section( SectionInfo&& info ): + m_info( CATCH_MOVE( info ) ), + m_sectionIncluded( + getResultCapture().sectionStarted( m_info, m_assertions ) ) { + // Non-"included" sections will not use the timing information + // anyway, so don't bother with the potential syscall. + if (m_sectionIncluded) { + m_timer.start(); + } + } + + Section::~Section() { + if( m_sectionIncluded ) { + SectionEndInfo endInfo{ m_info, m_assertions, m_timer.getElapsedSeconds() }; + if( uncaught_exceptions() ) + getResultCapture().sectionEndedEarly( endInfo ); + else + getResultCapture().sectionEnded( endInfo ); + } + } + + // This indicates whether the section should be executed or not + Section::operator bool() const { + return m_sectionIncluded; + } + + +} // end namespace Catch + + + +#include + +namespace Catch { + + namespace { + static auto getSingletons() -> std::vector*& { + static std::vector* g_singletons = nullptr; + if( !g_singletons ) + g_singletons = new std::vector(); + return g_singletons; + } + } + + ISingleton::~ISingleton() = default; + + void addSingleton(ISingleton* singleton ) { + getSingletons()->push_back( singleton ); + } + void cleanupSingletons() { + auto& singletons = getSingletons(); + for( auto singleton : *singletons ) + delete singleton; + delete singletons; + singletons = nullptr; + } + +} // namespace Catch + + + +#include +#include + +namespace Catch { + + bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const noexcept { + return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0); + } + bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const noexcept { + // We can assume that the same file will usually have the same pointer. + // Thus, if the pointers are the same, there is no point in calling the strcmp + return line < other.line || ( line == other.line && file != other.file && (std::strcmp(file, other.file) < 0)); + } + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { +#ifndef __GNUG__ + os << info.file << '(' << info.line << ')'; +#else + os << info.file << ':' << info.line; +#endif + return os; + } + +} // end namespace Catch + + + + +namespace Catch { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + void StartupExceptionRegistry::add( std::exception_ptr const& exception ) noexcept { + CATCH_TRY { + m_exceptions.push_back(exception); + } CATCH_CATCH_ALL { + // If we run out of memory during start-up there's really not a lot more we can do about it + std::terminate(); + } + } + + std::vector const& StartupExceptionRegistry::getExceptions() const noexcept { + return m_exceptions; + } +#endif + +} // end namespace Catch + + + + + +#include + +namespace Catch { + +// If you #define this you must implement these functions +#if !defined( CATCH_CONFIG_NOSTDOUT ) + std::ostream& cout() { return std::cout; } + std::ostream& cerr() { return std::cerr; } + std::ostream& clog() { return std::clog; } +#endif + +} // namespace Catch + + + +#include +#include +#include +#include +#include + +namespace Catch { + + bool startsWith( std::string const& s, std::string const& prefix ) { + return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin()); + } + bool startsWith( StringRef s, char prefix ) { + return !s.empty() && s[0] == prefix; + } + bool endsWith( std::string const& s, std::string const& suffix ) { + return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin()); + } + bool endsWith( std::string const& s, char suffix ) { + return !s.empty() && s[s.size()-1] == suffix; + } + bool contains( std::string const& s, std::string const& infix ) { + return s.find( infix ) != std::string::npos; + } + void toLowerInPlace( std::string& s ) { + std::transform( s.begin(), s.end(), s.begin(), []( char c ) { + return toLower( c ); + } ); + } + std::string toLower( std::string const& s ) { + std::string lc = s; + toLowerInPlace( lc ); + return lc; + } + char toLower(char c) { + return static_cast(std::tolower(static_cast(c))); + } + + std::string trim( std::string const& str ) { + static char const* whitespaceChars = "\n\r\t "; + std::string::size_type start = str.find_first_not_of( whitespaceChars ); + std::string::size_type end = str.find_last_not_of( whitespaceChars ); + + return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string(); + } + + StringRef trim(StringRef ref) { + const auto is_ws = [](char c) { + return c == ' ' || c == '\t' || c == '\n' || c == '\r'; + }; + size_t real_begin = 0; + while (real_begin < ref.size() && is_ws(ref[real_begin])) { ++real_begin; } + size_t real_end = ref.size(); + while (real_end > real_begin && is_ws(ref[real_end - 1])) { --real_end; } + + return ref.substr(real_begin, real_end - real_begin); + } + + bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) { + bool replaced = false; + std::size_t i = str.find( replaceThis ); + while( i != std::string::npos ) { + replaced = true; + str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() ); + if( i < str.size()-withThis.size() ) + i = str.find( replaceThis, i+withThis.size() ); + else + i = std::string::npos; + } + return replaced; + } + + std::vector splitStringRef( StringRef str, char delimiter ) { + std::vector subStrings; + std::size_t start = 0; + for(std::size_t pos = 0; pos < str.size(); ++pos ) { + if( str[pos] == delimiter ) { + if( pos - start > 1 ) + subStrings.push_back( str.substr( start, pos-start ) ); + start = pos+1; + } + } + if( start < str.size() ) + subStrings.push_back( str.substr( start, str.size()-start ) ); + return subStrings; + } + + std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) { + os << pluraliser.m_count << ' ' << pluraliser.m_label; + if( pluraliser.m_count != 1 ) + os << 's'; + return os; + } + +} + + + +#include +#include +#include +#include + +namespace Catch { + StringRef::StringRef( char const* rawChars ) noexcept + : StringRef( rawChars, std::strlen(rawChars) ) + {} + + auto StringRef::operator == ( StringRef other ) const noexcept -> bool { + return m_size == other.m_size + && (std::memcmp( m_start, other.m_start, m_size ) == 0); + } + + bool StringRef::operator<(StringRef rhs) const noexcept { + if (m_size < rhs.m_size) { + return strncmp(m_start, rhs.m_start, m_size) <= 0; + } + return strncmp(m_start, rhs.m_start, rhs.m_size) < 0; + } + + int StringRef::compare( StringRef rhs ) const { + auto cmpResult = + strncmp( m_start, rhs.m_start, std::min( m_size, rhs.m_size ) ); + + // This means that strncmp found a difference before the strings + // ended, and we can return it directly + if ( cmpResult != 0 ) { + return cmpResult; + } + + // If strings are equal up to length, then their comparison results on + // their size + if ( m_size < rhs.m_size ) { + return -1; + } else if ( m_size > rhs.m_size ) { + return 1; + } else { + return 0; + } + } + + auto operator << ( std::ostream& os, StringRef str ) -> std::ostream& { + return os.write(str.data(), static_cast(str.size())); + } + + std::string operator+(StringRef lhs, StringRef rhs) { + std::string ret; + ret.reserve(lhs.size() + rhs.size()); + ret += lhs; + ret += rhs; + return ret; + } + + auto operator+=( std::string& lhs, StringRef rhs ) -> std::string& { + lhs.append(rhs.data(), rhs.size()); + return lhs; + } + +} // namespace Catch + + + +namespace Catch { + + TagAliasRegistry::~TagAliasRegistry() {} + + TagAlias const* TagAliasRegistry::find( std::string const& alias ) const { + auto it = m_registry.find( alias ); + if( it != m_registry.end() ) + return &(it->second); + else + return nullptr; + } + + std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const { + std::string expandedTestSpec = unexpandedTestSpec; + for( auto const& registryKvp : m_registry ) { + std::size_t pos = expandedTestSpec.find( registryKvp.first ); + if( pos != std::string::npos ) { + expandedTestSpec = expandedTestSpec.substr( 0, pos ) + + registryKvp.second.tag + + expandedTestSpec.substr( pos + registryKvp.first.size() ); + } + } + return expandedTestSpec; + } + + void TagAliasRegistry::add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) { + CATCH_ENFORCE( startsWith(alias, "[@") && endsWith(alias, ']'), + "error: tag alias, '" << alias << "' is not of the form [@alias name].\n" << lineInfo ); + + CATCH_ENFORCE( m_registry.insert(std::make_pair(alias, TagAlias(tag, lineInfo))).second, + "error: tag alias, '" << alias << "' already registered.\n" + << "\tFirst seen at: " << find(alias)->lineInfo << "\n" + << "\tRedefined at: " << lineInfo ); + } + + ITagAliasRegistry::~ITagAliasRegistry() = default; + + ITagAliasRegistry const& ITagAliasRegistry::get() { + return getRegistryHub().getTagAliasRegistry(); + } + +} // end namespace Catch + + + + +namespace Catch { + TestCaseInfoHasher::TestCaseInfoHasher( hash_t seed ): m_seed( seed ) {} + + uint32_t TestCaseInfoHasher::operator()( TestCaseInfo const& t ) const { + // FNV-1a hash algorithm that is designed for uniqueness: + const hash_t prime = 1099511628211u; + hash_t hash = 14695981039346656037u; + for ( const char c : t.name ) { + hash ^= c; + hash *= prime; + } + for ( const char c : t.className ) { + hash ^= c; + hash *= prime; + } + for ( const Tag& tag : t.tags ) { + for ( const char c : tag.original ) { + hash ^= c; + hash *= prime; + } + } + hash ^= m_seed; + hash *= prime; + const uint32_t low{ static_cast( hash ) }; + const uint32_t high{ static_cast( hash >> 32 ) }; + return low * high; + } +} // namespace Catch + + + + +#include +#include + +namespace Catch { + + std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { + switch (config.runOrder()) { + case TestRunOrder::Declared: + return unsortedTestCases; + + case TestRunOrder::LexicographicallySorted: { + std::vector sorted = unsortedTestCases; + std::sort( + sorted.begin(), + sorted.end(), + []( TestCaseHandle const& lhs, TestCaseHandle const& rhs ) { + return lhs.getTestCaseInfo() < rhs.getTestCaseInfo(); + } + ); + return sorted; + } + case TestRunOrder::Randomized: { + seedRng(config); + using TestWithHash = std::pair; + + TestCaseInfoHasher h{ config.rngSeed() }; + std::vector indexed_tests; + indexed_tests.reserve(unsortedTestCases.size()); + + for (auto const& handle : unsortedTestCases) { + indexed_tests.emplace_back(h(handle.getTestCaseInfo()), handle); + } + + std::sort( indexed_tests.begin(), + indexed_tests.end(), + []( TestWithHash const& lhs, TestWithHash const& rhs ) { + if ( lhs.first == rhs.first ) { + return lhs.second.getTestCaseInfo() < + rhs.second.getTestCaseInfo(); + } + return lhs.first < rhs.first; + } ); + + std::vector randomized; + randomized.reserve(indexed_tests.size()); + + for (auto const& indexed : indexed_tests) { + randomized.push_back(indexed.second); + } + + return randomized; + } + } + + CATCH_INTERNAL_ERROR("Unknown test order value!"); + } + + bool isThrowSafe( TestCaseHandle const& testCase, IConfig const& config ) { + return !testCase.getTestCaseInfo().throws() || config.allowThrows(); + } + + bool matchTest( TestCaseHandle const& testCase, TestSpec const& testSpec, IConfig const& config ) { + return testSpec.matches( testCase.getTestCaseInfo() ) && isThrowSafe( testCase, config ); + } + + void + enforceNoDuplicateTestCases( std::vector const& tests ) { + auto testInfoCmp = []( TestCaseInfo const* lhs, + TestCaseInfo const* rhs ) { + return *lhs < *rhs; + }; + std::set seenTests(testInfoCmp); + for ( auto const& test : tests ) { + const auto infoPtr = &test.getTestCaseInfo(); + const auto prev = seenTests.insert( infoPtr ); + CATCH_ENFORCE( + prev.second, + "error: test case \"" << infoPtr->name << "\", with tags \"" + << infoPtr->tagsAsString() << "\" already defined.\n" + << "\tFirst seen at " << ( *prev.first )->lineInfo << "\n" + << "\tRedefined at " << infoPtr->lineInfo ); + } + } + + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ) { + std::vector filtered; + filtered.reserve( testCases.size() ); + for (auto const& testCase : testCases) { + if ((!testSpec.hasFilters() && !testCase.getTestCaseInfo().isHidden()) || + (testSpec.hasFilters() && matchTest(testCase, testSpec, config))) { + filtered.push_back(testCase); + } + } + return createShard(filtered, config.shardCount(), config.shardIndex()); + } + std::vector const& getAllTestCasesSorted( IConfig const& config ) { + return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config ); + } + + void TestRegistry::registerTest(Detail::unique_ptr testInfo, Detail::unique_ptr testInvoker) { + m_handles.emplace_back(testInfo.get(), testInvoker.get()); + m_viewed_test_infos.push_back(testInfo.get()); + m_owned_test_infos.push_back(CATCH_MOVE(testInfo)); + m_invokers.push_back(CATCH_MOVE(testInvoker)); + } + + std::vector const& TestRegistry::getAllInfos() const { + return m_viewed_test_infos; + } + + std::vector const& TestRegistry::getAllTests() const { + return m_handles; + } + std::vector const& TestRegistry::getAllTestsSorted( IConfig const& config ) const { + if( m_sortedFunctions.empty() ) + enforceNoDuplicateTestCases( m_handles ); + + if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) { + m_sortedFunctions = sortTests( config, m_handles ); + m_currentSortOrder = config.runOrder(); + } + return m_sortedFunctions; + } + + + + /////////////////////////////////////////////////////////////////////////// + void TestInvokerAsFunction::invoke() const { + m_testAsFunction(); + } + +} // end namespace Catch + + + + +#include +#include + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +#endif + +namespace Catch { +namespace TestCaseTracking { + + NameAndLocation::NameAndLocation( std::string const& _name, SourceLineInfo const& _location ) + : name( _name ), + location( _location ) + {} + + + ITracker::~ITracker() = default; + + void ITracker::markAsNeedingAnotherRun() { + m_runState = NeedsAnotherRun; + } + + void ITracker::addChild( ITrackerPtr&& child ) { + m_children.push_back( CATCH_MOVE(child) ); + } + + ITracker* ITracker::findChild( NameAndLocation const& nameAndLocation ) { + auto it = std::find_if( + m_children.begin(), + m_children.end(), + [&nameAndLocation]( ITrackerPtr const& tracker ) { + return tracker->nameAndLocation().location == + nameAndLocation.location && + tracker->nameAndLocation().name == nameAndLocation.name; + } ); + return ( it != m_children.end() ) ? it->get() : nullptr; + } + + bool ITracker::isSectionTracker() const { return false; } + bool ITracker::isGeneratorTracker() const { return false; } + + bool ITracker::isSuccessfullyCompleted() const { + return m_runState == CompletedSuccessfully; + } + + bool ITracker::isOpen() const { + return m_runState != NotStarted && !isComplete(); + } + + bool ITracker::hasStarted() const { return m_runState != NotStarted; } + + void ITracker::openChild() { + if (m_runState != ExecutingChildren) { + m_runState = ExecutingChildren; + if (m_parent) { + m_parent->openChild(); + } + } + } + + ITracker& TrackerContext::startRun() { + using namespace std::string_literals; + m_rootTracker = Catch::Detail::make_unique( + NameAndLocation( "{root}"s, CATCH_INTERNAL_LINEINFO ), + *this, + nullptr ); + m_currentTracker = nullptr; + m_runState = Executing; + return *m_rootTracker; + } + + void TrackerContext::endRun() { + m_rootTracker.reset(); + m_currentTracker = nullptr; + m_runState = NotStarted; + } + + void TrackerContext::startCycle() { + m_currentTracker = m_rootTracker.get(); + m_runState = Executing; + } + void TrackerContext::completeCycle() { + m_runState = CompletedCycle; + } + + bool TrackerContext::completedCycle() const { + return m_runState == CompletedCycle; + } + ITracker& TrackerContext::currentTracker() { + return *m_currentTracker; + } + void TrackerContext::setCurrentTracker( ITracker* tracker ) { + m_currentTracker = tracker; + } + + + TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ): + ITracker(nameAndLocation, parent), + m_ctx( ctx ) + {} + + bool TrackerBase::isComplete() const { + return m_runState == CompletedSuccessfully || m_runState == Failed; + } + + void TrackerBase::open() { + m_runState = Executing; + moveToThis(); + if( m_parent ) + m_parent->openChild(); + } + + void TrackerBase::close() { + + // Close any still open children (e.g. generators) + while( &m_ctx.currentTracker() != this ) + m_ctx.currentTracker().close(); + + switch( m_runState ) { + case NeedsAnotherRun: + break; + + case Executing: + m_runState = CompletedSuccessfully; + break; + case ExecutingChildren: + if( std::all_of(m_children.begin(), m_children.end(), [](ITrackerPtr const& t){ return t->isComplete(); }) ) + m_runState = CompletedSuccessfully; + break; + + case NotStarted: + case CompletedSuccessfully: + case Failed: + CATCH_INTERNAL_ERROR( "Illogical state: " << m_runState ); + + default: + CATCH_INTERNAL_ERROR( "Unknown state: " << m_runState ); + } + moveToParent(); + m_ctx.completeCycle(); + } + void TrackerBase::fail() { + m_runState = Failed; + if( m_parent ) + m_parent->markAsNeedingAnotherRun(); + moveToParent(); + m_ctx.completeCycle(); + } + + void TrackerBase::moveToParent() { + assert( m_parent ); + m_ctx.setCurrentTracker( m_parent ); + } + void TrackerBase::moveToThis() { + m_ctx.setCurrentTracker( this ); + } + + SectionTracker::SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : TrackerBase( nameAndLocation, ctx, parent ), + m_trimmed_name(trim(nameAndLocation.name)) + { + if( parent ) { + while( !parent->isSectionTracker() ) + parent = parent->parent(); + + SectionTracker& parentSection = static_cast( *parent ); + addNextFilters( parentSection.m_filters ); + } + } + + bool SectionTracker::isComplete() const { + bool complete = true; + + if (m_filters.empty() + || m_filters[0].empty() + || std::find(m_filters.begin(), m_filters.end(), m_trimmed_name) != m_filters.end()) { + complete = TrackerBase::isComplete(); + } + return complete; + } + + bool SectionTracker::isSectionTracker() const { return true; } + + SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { + SectionTracker* section; + + ITracker& currentTracker = ctx.currentTracker(); + if ( ITracker* childTracker = + currentTracker.findChild( nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isSectionTracker() ); + section = static_cast( childTracker ); + } else { + auto newSection = Catch::Detail::make_unique( + nameAndLocation, ctx, ¤tTracker ); + section = newSection.get(); + currentTracker.addChild( CATCH_MOVE( newSection ) ); + } + if( !ctx.completedCycle() ) + section->tryOpen(); + return *section; + } + + void SectionTracker::tryOpen() { + if( !isComplete() ) + open(); + } + + void SectionTracker::addInitialFilters( std::vector const& filters ) { + if( !filters.empty() ) { + m_filters.reserve( m_filters.size() + filters.size() + 2 ); + m_filters.emplace_back(StringRef{}); // Root - should never be consulted + m_filters.emplace_back(StringRef{}); // Test Case - not a section filter + m_filters.insert( m_filters.end(), filters.begin(), filters.end() ); + } + } + void SectionTracker::addNextFilters( std::vector const& filters ) { + if( filters.size() > 1 ) + m_filters.insert( m_filters.end(), filters.begin()+1, filters.end() ); + } + + std::vector const& SectionTracker::getFilters() const { + return m_filters; + } + + StringRef SectionTracker::trimmedName() const { + return m_trimmed_name; + } + +} // namespace TestCaseTracking + +} // namespace Catch + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif + + + +#include +#include + +namespace Catch { + + namespace { + StringRef extractClassName( StringRef classOrMethodName ) { + if ( !startsWith( classOrMethodName, '&' ) ) { + return classOrMethodName; + } + + // Remove the leading '&' to avoid having to special case it later + const auto methodName = + classOrMethodName.substr( 1, classOrMethodName.size() ); + + auto reverseStart = std::make_reverse_iterator( methodName.end() ); + auto reverseEnd = std::make_reverse_iterator( methodName.begin() ); + + // We make a simplifying assumption that ":" is only present + // in the input as part of "::" from C++ typenames (this is + // relatively safe assumption because the input is generated + // as stringification of type through preprocessor). + auto lastColons = std::find( reverseStart, reverseEnd, ':' ) + 1; + auto secondLastColons = + std::find( lastColons + 1, reverseEnd, ':' ); + + auto const startIdx = reverseEnd - secondLastColons; + auto const classNameSize = secondLastColons - lastColons - 1; + + return methodName.substr( + static_cast( startIdx ), + static_cast( classNameSize ) ); + } + } // namespace + + Detail::unique_ptr makeTestInvoker( void(*testAsFunction)() ) { + return Detail::make_unique( testAsFunction ); + } + + AutoReg::AutoReg( Detail::unique_ptr invoker, SourceLineInfo const& lineInfo, StringRef classOrMethod, NameAndTags const& nameAndTags ) noexcept { + CATCH_TRY { + getMutableRegistryHub() + .registerTest( + makeTestCaseInfo( + extractClassName( classOrMethod ), + nameAndTags, + lineInfo), + CATCH_MOVE(invoker) + ); + } CATCH_CATCH_ALL { + // Do not throw when constructing global objects, instead register the exception to be processed later + getMutableRegistryHub().registerStartupException(); + } + } +} + + + + + +namespace Catch { + + TestSpecParser::TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {} + + TestSpecParser& TestSpecParser::parse( std::string const& arg ) { + m_mode = None; + m_exclusion = false; + m_arg = m_tagAliases->expandAliases( arg ); + m_escapeChars.clear(); + m_substring.reserve(m_arg.size()); + m_patternName.reserve(m_arg.size()); + m_realPatternPos = 0; + + for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) + //if visitChar fails + if( !visitChar( m_arg[m_pos] ) ){ + m_testSpec.m_invalidSpecs.push_back(arg); + break; + } + endMode(); + return *this; + } + TestSpec TestSpecParser::testSpec() { + addFilter(); + return CATCH_MOVE(m_testSpec); + } + bool TestSpecParser::visitChar( char c ) { + if( (m_mode != EscapedName) && (c == '\\') ) { + escape(); + addCharToPattern(c); + return true; + }else if((m_mode != EscapedName) && (c == ',') ) { + return separate(); + } + + switch( m_mode ) { + case None: + if( processNoneChar( c ) ) + return true; + break; + case Name: + processNameChar( c ); + break; + case EscapedName: + endMode(); + addCharToPattern(c); + return true; + default: + case Tag: + case QuotedName: + if( processOtherChar( c ) ) + return true; + break; + } + + m_substring += c; + if( !isControlChar( c ) ) { + m_patternName += c; + m_realPatternPos++; + } + return true; + } + // Two of the processing methods return true to signal the caller to return + // without adding the given character to the current pattern strings + bool TestSpecParser::processNoneChar( char c ) { + switch( c ) { + case ' ': + return true; + case '~': + m_exclusion = true; + return false; + case '[': + startNewMode( Tag ); + return false; + case '"': + startNewMode( QuotedName ); + return false; + default: + startNewMode( Name ); + return false; + } + } + void TestSpecParser::processNameChar( char c ) { + if( c == '[' ) { + if( m_substring == "exclude:" ) + m_exclusion = true; + else + endMode(); + startNewMode( Tag ); + } + } + bool TestSpecParser::processOtherChar( char c ) { + if( !isControlChar( c ) ) + return false; + m_substring += c; + endMode(); + return true; + } + void TestSpecParser::startNewMode( Mode mode ) { + m_mode = mode; + } + void TestSpecParser::endMode() { + switch( m_mode ) { + case Name: + case QuotedName: + return addNamePattern(); + case Tag: + return addTagPattern(); + case EscapedName: + revertBackToLastMode(); + return; + case None: + default: + return startNewMode( None ); + } + } + void TestSpecParser::escape() { + saveLastMode(); + m_mode = EscapedName; + m_escapeChars.push_back(m_realPatternPos); + } + bool TestSpecParser::isControlChar( char c ) const { + switch( m_mode ) { + default: + return false; + case None: + return c == '~'; + case Name: + return c == '['; + case EscapedName: + return true; + case QuotedName: + return c == '"'; + case Tag: + return c == '[' || c == ']'; + } + } + + void TestSpecParser::addFilter() { + if( !m_currentFilter.m_required.empty() || !m_currentFilter.m_forbidden.empty() ) { + m_testSpec.m_filters.push_back( CATCH_MOVE(m_currentFilter) ); + m_currentFilter = TestSpec::Filter(); + } + } + + void TestSpecParser::saveLastMode() { + lastMode = m_mode; + } + + void TestSpecParser::revertBackToLastMode() { + m_mode = lastMode; + } + + bool TestSpecParser::separate() { + if( (m_mode==QuotedName) || (m_mode==Tag) ){ + //invalid argument, signal failure to previous scope. + m_mode = None; + m_pos = m_arg.size(); + m_substring.clear(); + m_patternName.clear(); + m_realPatternPos = 0; + return false; + } + endMode(); + addFilter(); + return true; //success + } + + std::string TestSpecParser::preprocessPattern() { + std::string token = m_patternName; + for (std::size_t i = 0; i < m_escapeChars.size(); ++i) + token = token.substr(0, m_escapeChars[i] - i) + token.substr(m_escapeChars[i] - i + 1); + m_escapeChars.clear(); + if (startsWith(token, "exclude:")) { + m_exclusion = true; + token = token.substr(8); + } + + m_patternName.clear(); + m_realPatternPos = 0; + + return token; + } + + void TestSpecParser::addNamePattern() { + auto token = preprocessPattern(); + + if (!token.empty()) { + if (m_exclusion) { + m_currentFilter.m_forbidden.emplace_back(Detail::make_unique(token, m_substring)); + } else { + m_currentFilter.m_required.emplace_back(Detail::make_unique(token, m_substring)); + } + } + m_substring.clear(); + m_exclusion = false; + m_mode = None; + } + + void TestSpecParser::addTagPattern() { + auto token = preprocessPattern(); + + if (!token.empty()) { + // If the tag pattern is the "hide and tag" shorthand (e.g. [.foo]) + // we have to create a separate hide tag and shorten the real one + if (token.size() > 1 && token[0] == '.') { + token.erase(token.begin()); + if (m_exclusion) { + m_currentFilter.m_forbidden.emplace_back(Detail::make_unique(".", m_substring)); + m_currentFilter.m_forbidden.emplace_back(Detail::make_unique(token, m_substring)); + } else { + m_currentFilter.m_required.emplace_back(Detail::make_unique(".", m_substring)); + m_currentFilter.m_required.emplace_back(Detail::make_unique(token, m_substring)); + } + } + if (m_exclusion) { + m_currentFilter.m_forbidden.emplace_back(Detail::make_unique(token, m_substring)); + } else { + m_currentFilter.m_required.emplace_back(Detail::make_unique(token, m_substring)); + } + } + m_substring.clear(); + m_exclusion = false; + m_mode = None; + } + + TestSpec parseTestSpec( std::string const& arg ) { + return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec(); + } + +} // namespace Catch + + + +#include +#include +#include + +namespace { + bool isWhitespace( char c ) { + return c == ' ' || c == '\t' || c == '\n' || c == '\r'; + } + + bool isBreakableBefore( char c ) { + static const char chars[] = "[({<|"; + return std::memchr( chars, c, sizeof( chars ) - 1 ) != nullptr; + } + + bool isBreakableAfter( char c ) { + static const char chars[] = "])}>.,:;*+-=&/\\"; + return std::memchr( chars, c, sizeof( chars ) - 1 ) != nullptr; + } + + bool isBoundary( std::string const& line, size_t at ) { + assert( at > 0 ); + assert( at <= line.size() ); + + return at == line.size() || + ( isWhitespace( line[at] ) && !isWhitespace( line[at - 1] ) ) || + isBreakableBefore( line[at] ) || + isBreakableAfter( line[at - 1] ); + } + +} // namespace + +namespace Catch { + namespace TextFlow { + + void Column::const_iterator::calcLength() { + m_addHyphen = false; + m_parsedTo = m_lineStart; + + std::string const& current_line = m_column.m_string; + if ( current_line[m_lineStart] == '\n' ) { + ++m_parsedTo; + } + + const auto maxLineLength = m_column.m_width - indentSize(); + const auto maxParseTo = std::min(current_line.size(), m_lineStart + maxLineLength); + while ( m_parsedTo < maxParseTo && + current_line[m_parsedTo] != '\n' ) { + ++m_parsedTo; + } + + // If we encountered a newline before the column is filled, + // then we linebreak at the newline and consider this line + // finished. + if ( m_parsedTo < m_lineStart + maxLineLength ) { + m_lineLength = m_parsedTo - m_lineStart; + } else { + // Look for a natural linebreak boundary in the column + // (We look from the end, so that the first found boundary is + // the right one) + size_t newLineLength = maxLineLength; + while ( newLineLength > 0 && !isBoundary( current_line, m_lineStart + newLineLength ) ) { + --newLineLength; + } + while ( newLineLength > 0 && + isWhitespace( current_line[m_lineStart + newLineLength - 1] ) ) { + --newLineLength; + } + + // If we found one, then that is where we linebreak + if ( newLineLength > 0 ) { + m_lineLength = newLineLength; + } else { + // Otherwise we have to split text with a hyphen + m_addHyphen = true; + m_lineLength = maxLineLength - 1; + } + } + } + + size_t Column::const_iterator::indentSize() const { + auto initial = + m_lineStart == 0 ? m_column.m_initialIndent : std::string::npos; + return initial == std::string::npos ? m_column.m_indent : initial; + } + + std::string + Column::const_iterator::addIndentAndSuffix( size_t position, + size_t length ) const { + std::string ret; + const auto desired_indent = indentSize(); + ret.reserve( desired_indent + length + m_addHyphen ); + ret.append( desired_indent, ' ' ); + ret.append( m_column.m_string, position, length ); + if ( m_addHyphen ) { + ret.push_back( '-' ); + } + + return ret; + } + + Column::const_iterator::const_iterator( Column const& column ): m_column( column ) { + assert( m_column.m_width > m_column.m_indent ); + assert( m_column.m_initialIndent == std::string::npos || + m_column.m_width > m_column.m_initialIndent ); + calcLength(); + if ( m_lineLength == 0 ) { + m_lineStart = m_column.m_string.size(); + } + } + + std::string Column::const_iterator::operator*() const { + assert( m_lineStart <= m_parsedTo ); + return addIndentAndSuffix( m_lineStart, m_lineLength ); + } + + Column::const_iterator& Column::const_iterator::operator++() { + m_lineStart += m_lineLength; + std::string const& current_line = m_column.m_string; + if ( m_lineStart < current_line.size() && current_line[m_lineStart] == '\n' ) { + m_lineStart += 1; + } else { + while ( m_lineStart < current_line.size() && + isWhitespace( current_line[m_lineStart] ) ) { + ++m_lineStart; + } + } + + if ( m_lineStart != current_line.size() ) { + calcLength(); + } + return *this; + } + + Column::const_iterator Column::const_iterator::operator++( int ) { + const_iterator prev( *this ); + operator++(); + return prev; + } + + std::ostream& operator<<( std::ostream& os, Column const& col ) { + bool first = true; + for ( auto line : col ) { + if ( first ) { + first = false; + } else { + os << '\n'; + } + os << line; + } + return os; + } + + Column Spacer( size_t spaceWidth ) { + Column ret{ "" }; + ret.width( spaceWidth ); + return ret; + } + + Columns::iterator::iterator( Columns const& columns, EndTag ): + m_columns( columns.m_columns ), m_activeIterators( 0 ) { + + m_iterators.reserve( m_columns.size() ); + for ( auto const& col : m_columns ) { + m_iterators.push_back( col.end() ); + } + } + + Columns::iterator::iterator( Columns const& columns ): + m_columns( columns.m_columns ), + m_activeIterators( m_columns.size() ) { + + m_iterators.reserve( m_columns.size() ); + for ( auto const& col : m_columns ) { + m_iterators.push_back( col.begin() ); + } + } + + std::string Columns::iterator::operator*() const { + std::string row, padding; + + for ( size_t i = 0; i < m_columns.size(); ++i ) { + const auto width = m_columns[i].width(); + if ( m_iterators[i] != m_columns[i].end() ) { + std::string col = *m_iterators[i]; + row += padding; + row += col; + + padding.clear(); + if ( col.size() < width ) { + padding.append( width - col.size(), ' ' ); + } + } else { + padding.append( width, ' ' ); + } + } + return row; + } + + Columns::iterator& Columns::iterator::operator++() { + for ( size_t i = 0; i < m_columns.size(); ++i ) { + if ( m_iterators[i] != m_columns[i].end() ) { + ++m_iterators[i]; + } + } + return *this; + } + + Columns::iterator Columns::iterator::operator++( int ) { + iterator prev( *this ); + operator++(); + return prev; + } + + std::ostream& operator<<( std::ostream& os, Columns const& cols ) { + bool first = true; + for ( auto line : cols ) { + if ( first ) { + first = false; + } else { + os << '\n'; + } + os << line; + } + return os; + } + + Columns Column::operator+( Column const& other ) { + Columns cols; + cols += *this; + cols += other; + return cols; + } + + Columns& Columns::operator+=( Column const& col ) { + m_columns.push_back( col ); + return *this; + } + + Columns Columns::operator+( Column const& col ) { + Columns combined = *this; + combined += col; + return combined; + } + + } // namespace TextFlow +} // namespace Catch + + + + +#include + +namespace Catch { + bool uncaught_exceptions() { +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + return false; +#elif defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) + return std::uncaught_exceptions() > 0; +#else + return std::uncaught_exception(); +#endif + } +} // end namespace Catch + + + +namespace Catch { + + WildcardPattern::WildcardPattern( std::string const& pattern, + CaseSensitive caseSensitivity ) + : m_caseSensitivity( caseSensitivity ), + m_pattern( normaliseString( pattern ) ) + { + if( startsWith( m_pattern, '*' ) ) { + m_pattern = m_pattern.substr( 1 ); + m_wildcard = WildcardAtStart; + } + if( endsWith( m_pattern, '*' ) ) { + m_pattern = m_pattern.substr( 0, m_pattern.size()-1 ); + m_wildcard = static_cast( m_wildcard | WildcardAtEnd ); + } + } + + bool WildcardPattern::matches( std::string const& str ) const { + switch( m_wildcard ) { + case NoWildcard: + return m_pattern == normaliseString( str ); + case WildcardAtStart: + return endsWith( normaliseString( str ), m_pattern ); + case WildcardAtEnd: + return startsWith( normaliseString( str ), m_pattern ); + case WildcardAtBothEnds: + return contains( normaliseString( str ), m_pattern ); + default: + CATCH_INTERNAL_ERROR( "Unknown enum" ); + } + } + + std::string WildcardPattern::normaliseString( std::string const& str ) const { + return trim( m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str ); + } +} + + +// Note: swapping these two includes around causes MSVC to error out +// while in /permissive- mode. No, I don't know why. +// Tested on VS 2019, 18.{3, 4}.x + +#include +#include + +namespace Catch { + +namespace { + + size_t trailingBytes(unsigned char c) { + if ((c & 0xE0) == 0xC0) { + return 2; + } + if ((c & 0xF0) == 0xE0) { + return 3; + } + if ((c & 0xF8) == 0xF0) { + return 4; + } + CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); + } + + uint32_t headerValue(unsigned char c) { + if ((c & 0xE0) == 0xC0) { + return c & 0x1F; + } + if ((c & 0xF0) == 0xE0) { + return c & 0x0F; + } + if ((c & 0xF8) == 0xF0) { + return c & 0x07; + } + CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); + } + + void hexEscapeChar(std::ostream& os, unsigned char c) { + std::ios_base::fmtflags f(os.flags()); + os << "\\x" + << std::uppercase << std::hex << std::setfill('0') << std::setw(2) + << static_cast(c); + os.flags(f); + } + + bool shouldNewline(XmlFormatting fmt) { + return !!(static_cast>(fmt & XmlFormatting::Newline)); + } + + bool shouldIndent(XmlFormatting fmt) { + return !!(static_cast>(fmt & XmlFormatting::Indent)); + } + +} // anonymous namespace + + XmlFormatting operator | (XmlFormatting lhs, XmlFormatting rhs) { + return static_cast( + static_cast>(lhs) | + static_cast>(rhs) + ); + } + + XmlFormatting operator & (XmlFormatting lhs, XmlFormatting rhs) { + return static_cast( + static_cast>(lhs) & + static_cast>(rhs) + ); + } + + + XmlEncode::XmlEncode( StringRef str, ForWhat forWhat ) + : m_str( str ), + m_forWhat( forWhat ) + {} + + void XmlEncode::encodeTo( std::ostream& os ) const { + // Apostrophe escaping not necessary if we always use " to write attributes + // (see: http://www.w3.org/TR/xml/#syntax) + + for( std::size_t idx = 0; idx < m_str.size(); ++ idx ) { + unsigned char c = static_cast(m_str[idx]); + switch (c) { + case '<': os << "<"; break; + case '&': os << "&"; break; + + case '>': + // See: http://www.w3.org/TR/xml/#syntax + if (idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']') + os << ">"; + else + os << c; + break; + + case '\"': + if (m_forWhat == ForAttributes) + os << """; + else + os << c; + break; + + default: + // Check for control characters and invalid utf-8 + + // Escape control characters in standard ascii + // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 + if (c < 0x09 || (c > 0x0D && c < 0x20) || c == 0x7F) { + hexEscapeChar(os, c); + break; + } + + // Plain ASCII: Write it to stream + if (c < 0x7F) { + os << c; + break; + } + + // UTF-8 territory + // Check if the encoding is valid and if it is not, hex escape bytes. + // Important: We do not check the exact decoded values for validity, only the encoding format + // First check that this bytes is a valid lead byte: + // This means that it is not encoded as 1111 1XXX + // Or as 10XX XXXX + if (c < 0xC0 || + c >= 0xF8) { + hexEscapeChar(os, c); + break; + } + + auto encBytes = trailingBytes(c); + // Are there enough bytes left to avoid accessing out-of-bounds memory? + if (idx + encBytes - 1 >= m_str.size()) { + hexEscapeChar(os, c); + break; + } + // The header is valid, check data + // The next encBytes bytes must together be a valid utf-8 + // This means: bitpattern 10XX XXXX and the extracted value is sane (ish) + bool valid = true; + uint32_t value = headerValue(c); + for (std::size_t n = 1; n < encBytes; ++n) { + unsigned char nc = static_cast(m_str[idx + n]); + valid &= ((nc & 0xC0) == 0x80); + value = (value << 6) | (nc & 0x3F); + } + + if ( + // Wrong bit pattern of following bytes + (!valid) || + // Overlong encodings + (value < 0x80) || + (0x80 <= value && value < 0x800 && encBytes > 2) || + (0x800 < value && value < 0x10000 && encBytes > 3) || + // Encoded value out of range + (value >= 0x110000) + ) { + hexEscapeChar(os, c); + break; + } + + // If we got here, this is in fact a valid(ish) utf-8 sequence + for (std::size_t n = 0; n < encBytes; ++n) { + os << m_str[idx + n]; + } + idx += encBytes - 1; + break; + } + } + } + + std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) { + xmlEncode.encodeTo( os ); + return os; + } + + XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer, XmlFormatting fmt ) + : m_writer( writer ), + m_fmt(fmt) + {} + + XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) noexcept + : m_writer( other.m_writer ), + m_fmt(other.m_fmt) + { + other.m_writer = nullptr; + other.m_fmt = XmlFormatting::None; + } + XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) noexcept { + if ( m_writer ) { + m_writer->endElement(); + } + m_writer = other.m_writer; + other.m_writer = nullptr; + m_fmt = other.m_fmt; + other.m_fmt = XmlFormatting::None; + return *this; + } + + + XmlWriter::ScopedElement::~ScopedElement() { + if (m_writer) { + m_writer->endElement(m_fmt); + } + } + + XmlWriter::ScopedElement& + XmlWriter::ScopedElement::writeText( StringRef text, XmlFormatting fmt ) { + m_writer->writeText( text, fmt ); + return *this; + } + + XmlWriter::ScopedElement& + XmlWriter::ScopedElement::writeAttribute( StringRef name, + StringRef attribute ) { + m_writer->writeAttribute( name, attribute ); + return *this; + } + + + XmlWriter::XmlWriter( std::ostream& os ) : m_os( os ) + { + writeDeclaration(); + } + + XmlWriter::~XmlWriter() { + while (!m_tags.empty()) { + endElement(); + } + newlineIfNecessary(); + } + + XmlWriter& XmlWriter::startElement( std::string const& name, XmlFormatting fmt ) { + ensureTagClosed(); + newlineIfNecessary(); + if (shouldIndent(fmt)) { + m_os << m_indent; + m_indent += " "; + } + m_os << '<' << name; + m_tags.push_back( name ); + m_tagIsOpen = true; + applyFormatting(fmt); + return *this; + } + + XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name, XmlFormatting fmt ) { + ScopedElement scoped( this, fmt ); + startElement( name, fmt ); + return scoped; + } + + XmlWriter& XmlWriter::endElement(XmlFormatting fmt) { + m_indent = m_indent.substr(0, m_indent.size() - 2); + + if( m_tagIsOpen ) { + m_os << "/>"; + m_tagIsOpen = false; + } else { + newlineIfNecessary(); + if (shouldIndent(fmt)) { + m_os << m_indent; + } + m_os << "'; + } + m_os << std::flush; + applyFormatting(fmt); + m_tags.pop_back(); + return *this; + } + + XmlWriter& XmlWriter::writeAttribute( StringRef name, + StringRef attribute ) { + if( !name.empty() && !attribute.empty() ) + m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"'; + return *this; + } + + XmlWriter& XmlWriter::writeAttribute( StringRef name, bool attribute ) { + writeAttribute(name, (attribute ? "true"_sr : "false"_sr)); + return *this; + } + + XmlWriter& XmlWriter::writeAttribute( StringRef name, + char const* attribute ) { + writeAttribute( name, StringRef( attribute ) ); + return *this; + } + + XmlWriter& XmlWriter::writeText( StringRef text, XmlFormatting fmt ) { + CATCH_ENFORCE(!m_tags.empty(), "Cannot write text as top level element"); + if( !text.empty() ){ + bool tagWasOpen = m_tagIsOpen; + ensureTagClosed(); + if (tagWasOpen && shouldIndent(fmt)) { + m_os << m_indent; + } + m_os << XmlEncode( text, XmlEncode::ForTextNodes ); + applyFormatting(fmt); + } + return *this; + } + + XmlWriter& XmlWriter::writeComment( StringRef text, XmlFormatting fmt ) { + ensureTagClosed(); + if (shouldIndent(fmt)) { + m_os << m_indent; + } + m_os << ""; + applyFormatting(fmt); + return *this; + } + + void XmlWriter::writeStylesheetRef( StringRef url ) { + m_os << R"()" << '\n'; + } + + void XmlWriter::ensureTagClosed() { + if( m_tagIsOpen ) { + m_os << '>' << std::flush; + newlineIfNecessary(); + m_tagIsOpen = false; + } + } + + void XmlWriter::applyFormatting(XmlFormatting fmt) { + m_needsNewline = shouldNewline(fmt); + } + + void XmlWriter::writeDeclaration() { + m_os << R"()" << '\n'; + } + + void XmlWriter::newlineIfNecessary() { + if( m_needsNewline ) { + m_os << '\n' << std::flush; + m_needsNewline = false; + } + } +} + + + + + +namespace Catch { +namespace Matchers { + + std::string MatcherUntypedBase::toString() const { + if (m_cachedToString.empty()) { + m_cachedToString = describe(); + } + return m_cachedToString; + } + + MatcherUntypedBase::~MatcherUntypedBase() = default; + +} // namespace Matchers +} // namespace Catch + + + + +namespace Catch { +namespace Matchers { + + std::string IsEmptyMatcher::describe() const { + return "is empty"; + } + + std::string HasSizeMatcher::describe() const { + ReusableStringStream sstr; + sstr << "has size == " << m_target_size; + return sstr.str(); + } + + IsEmptyMatcher IsEmpty() { + return {}; + } + + HasSizeMatcher SizeIs(std::size_t sz) { + return HasSizeMatcher{ sz }; + } + +} // end namespace Matchers +} // end namespace Catch + + + +namespace Catch { +namespace Matchers { + +bool ExceptionMessageMatcher::match(std::exception const& ex) const { + return ex.what() == m_message; +} + +std::string ExceptionMessageMatcher::describe() const { + return "exception message matches \"" + m_message + '"'; +} + +ExceptionMessageMatcher Message(std::string const& message) { + return ExceptionMessageMatcher(message); +} + +} // namespace Matchers +} // namespace Catch + + + +#include +#include +#include +#include +#include +#include +#include + + +namespace Catch { +namespace { + + template + bool almostEqualUlps(FP lhs, FP rhs, uint64_t maxUlpDiff) { + // Comparison with NaN should always be false. + // This way we can rule it out before getting into the ugly details + if (Catch::isnan(lhs) || Catch::isnan(rhs)) { + return false; + } + + // This should also handle positive and negative zeros, infinities + const auto ulpDist = ulpDistance(lhs, rhs); + + return ulpDist <= maxUlpDiff; + } + +#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) + + float nextafter(float x, float y) { + return ::nextafterf(x, y); + } + + double nextafter(double x, double y) { + return ::nextafter(x, y); + } + +#endif // ^^^ CATCH_CONFIG_GLOBAL_NEXTAFTER ^^^ + +template +FP step(FP start, FP direction, uint64_t steps) { + for (uint64_t i = 0; i < steps; ++i) { +#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) + start = Catch::nextafter(start, direction); +#else + start = std::nextafter(start, direction); +#endif + } + return start; +} + +// Performs equivalent check of std::fabs(lhs - rhs) <= margin +// But without the subtraction to allow for INFINITY in comparison +bool marginComparison(double lhs, double rhs, double margin) { + return (lhs + margin >= rhs) && (rhs + margin >= lhs); +} + +template +void write(std::ostream& out, FloatingPoint num) { + out << std::scientific + << std::setprecision(std::numeric_limits::max_digits10 - 1) + << num; +} + +} // end anonymous namespace + +namespace Matchers { +namespace Detail { + + enum class FloatingPointKind : uint8_t { + Float, + Double + }; + +} // end namespace Detail + + + WithinAbsMatcher::WithinAbsMatcher(double target, double margin) + :m_target{ target }, m_margin{ margin } { + CATCH_ENFORCE(margin >= 0, "Invalid margin: " << margin << '.' + << " Margin has to be non-negative."); + } + + // Performs equivalent check of std::fabs(lhs - rhs) <= margin + // But without the subtraction to allow for INFINITY in comparison + bool WithinAbsMatcher::match(double const& matchee) const { + return (matchee + m_margin >= m_target) && (m_target + m_margin >= matchee); + } + + std::string WithinAbsMatcher::describe() const { + return "is within " + ::Catch::Detail::stringify(m_margin) + " of " + ::Catch::Detail::stringify(m_target); + } + + + WithinUlpsMatcher::WithinUlpsMatcher(double target, uint64_t ulps, Detail::FloatingPointKind baseType) + :m_target{ target }, m_ulps{ ulps }, m_type{ baseType } { + CATCH_ENFORCE(m_type == Detail::FloatingPointKind::Double + || m_ulps < (std::numeric_limits::max)(), + "Provided ULP is impossibly large for a float comparison."); + CATCH_ENFORCE( std::numeric_limits::is_iec559, + "WithinUlp matcher only supports platforms with " + "IEEE-754 compatible floating point representation" ); + } + +#if defined(__clang__) +#pragma clang diagnostic push +// Clang <3.5 reports on the default branch in the switch below +#pragma clang diagnostic ignored "-Wunreachable-code" +#endif + + bool WithinUlpsMatcher::match(double const& matchee) const { + switch (m_type) { + case Detail::FloatingPointKind::Float: + return almostEqualUlps(static_cast(matchee), static_cast(m_target), m_ulps); + case Detail::FloatingPointKind::Double: + return almostEqualUlps(matchee, m_target, m_ulps); + default: + CATCH_INTERNAL_ERROR( "Unknown Detail::FloatingPointKind value" ); + } + } + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + + std::string WithinUlpsMatcher::describe() const { + std::stringstream ret; + + ret << "is within " << m_ulps << " ULPs of "; + + if (m_type == Detail::FloatingPointKind::Float) { + write(ret, static_cast(m_target)); + ret << 'f'; + } else { + write(ret, m_target); + } + + ret << " (["; + if (m_type == Detail::FloatingPointKind::Double) { + write( ret, + step( m_target, + -std::numeric_limits::infinity(), + m_ulps ) ); + ret << ", "; + write( ret, + step( m_target, + std::numeric_limits::infinity(), + m_ulps ) ); + } else { + // We have to cast INFINITY to float because of MinGW, see #1782 + write( ret, + step( static_cast( m_target ), + -std::numeric_limits::infinity(), + m_ulps ) ); + ret << ", "; + write( ret, + step( static_cast( m_target ), + std::numeric_limits::infinity(), + m_ulps ) ); + } + ret << "])"; + + return ret.str(); + } + + WithinRelMatcher::WithinRelMatcher(double target, double epsilon): + m_target(target), + m_epsilon(epsilon){ + CATCH_ENFORCE(m_epsilon >= 0., "Relative comparison with epsilon < 0 does not make sense."); + CATCH_ENFORCE(m_epsilon < 1., "Relative comparison with epsilon >= 1 does not make sense."); + } + + bool WithinRelMatcher::match(double const& matchee) const { + const auto relMargin = m_epsilon * (std::max)(std::fabs(matchee), std::fabs(m_target)); + return marginComparison(matchee, m_target, + std::isinf(relMargin)? 0 : relMargin); + } + + std::string WithinRelMatcher::describe() const { + Catch::ReusableStringStream sstr; + sstr << "and " << m_target << " are within " << m_epsilon * 100. << "% of each other"; + return sstr.str(); + } + + +WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff) { + return WithinUlpsMatcher(target, maxUlpDiff, Detail::FloatingPointKind::Double); +} + +WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff) { + return WithinUlpsMatcher(target, maxUlpDiff, Detail::FloatingPointKind::Float); +} + +WithinAbsMatcher WithinAbs(double target, double margin) { + return WithinAbsMatcher(target, margin); +} + +WithinRelMatcher WithinRel(double target, double eps) { + return WithinRelMatcher(target, eps); +} + +WithinRelMatcher WithinRel(double target) { + return WithinRelMatcher(target, std::numeric_limits::epsilon() * 100); +} + +WithinRelMatcher WithinRel(float target, float eps) { + return WithinRelMatcher(target, eps); +} + +WithinRelMatcher WithinRel(float target) { + return WithinRelMatcher(target, std::numeric_limits::epsilon() * 100); +} + + +} // namespace Matchers +} // namespace Catch + + + + +std::string Catch::Matchers::Detail::finalizeDescription(const std::string& desc) { + if (desc.empty()) { + return "matches undescribed predicate"; + } else { + return "matches predicate: \"" + desc + '"'; + } +} + + + +namespace Catch { + namespace Matchers { + std::string AllTrueMatcher::describe() const { return "contains only true"; } + + AllTrueMatcher AllTrue() { return AllTrueMatcher{}; } + + std::string NoneTrueMatcher::describe() const { return "contains no true"; } + + NoneTrueMatcher NoneTrue() { return NoneTrueMatcher{}; } + + std::string AnyTrueMatcher::describe() const { return "contains at least one true"; } + + AnyTrueMatcher AnyTrue() { return AnyTrueMatcher{}; } + } // namespace Matchers +} // namespace Catch + + + +#include + +namespace Catch { +namespace Matchers { + + CasedString::CasedString( std::string const& str, CaseSensitive caseSensitivity ) + : m_caseSensitivity( caseSensitivity ), + m_str( adjustString( str ) ) + {} + std::string CasedString::adjustString( std::string const& str ) const { + return m_caseSensitivity == CaseSensitive::No + ? toLower( str ) + : str; + } + StringRef CasedString::caseSensitivitySuffix() const { + return m_caseSensitivity == CaseSensitive::Yes + ? StringRef() + : " (case insensitive)"_sr; + } + + + StringMatcherBase::StringMatcherBase( StringRef operation, CasedString const& comparator ) + : m_comparator( comparator ), + m_operation( operation ) { + } + + std::string StringMatcherBase::describe() const { + std::string description; + description.reserve(5 + m_operation.size() + m_comparator.m_str.size() + + m_comparator.caseSensitivitySuffix().size()); + description += m_operation; + description += ": \""; + description += m_comparator.m_str; + description += '"'; + description += m_comparator.caseSensitivitySuffix(); + return description; + } + + StringEqualsMatcher::StringEqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals"_sr, comparator ) {} + + bool StringEqualsMatcher::match( std::string const& source ) const { + return m_comparator.adjustString( source ) == m_comparator.m_str; + } + + + StringContainsMatcher::StringContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains"_sr, comparator ) {} + + bool StringContainsMatcher::match( std::string const& source ) const { + return contains( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + + StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with"_sr, comparator ) {} + + bool StartsWithMatcher::match( std::string const& source ) const { + return startsWith( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + + EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with"_sr, comparator ) {} + + bool EndsWithMatcher::match( std::string const& source ) const { + return endsWith( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + + + RegexMatcher::RegexMatcher(std::string regex, CaseSensitive caseSensitivity): m_regex(CATCH_MOVE(regex)), m_caseSensitivity(caseSensitivity) {} + + bool RegexMatcher::match(std::string const& matchee) const { + auto flags = std::regex::ECMAScript; // ECMAScript is the default syntax option anyway + if (m_caseSensitivity == CaseSensitive::No) { + flags |= std::regex::icase; + } + auto reg = std::regex(m_regex, flags); + return std::regex_match(matchee, reg); + } + + std::string RegexMatcher::describe() const { + return "matches " + ::Catch::Detail::stringify(m_regex) + ((m_caseSensitivity == CaseSensitive::Yes)? " case sensitively" : " case insensitively"); + } + + + StringEqualsMatcher Equals( std::string const& str, CaseSensitive caseSensitivity ) { + return StringEqualsMatcher( CasedString( str, caseSensitivity) ); + } + StringContainsMatcher ContainsSubstring( std::string const& str, CaseSensitive caseSensitivity ) { + return StringContainsMatcher( CasedString( str, caseSensitivity) ); + } + EndsWithMatcher EndsWith( std::string const& str, CaseSensitive caseSensitivity ) { + return EndsWithMatcher( CasedString( str, caseSensitivity) ); + } + StartsWithMatcher StartsWith( std::string const& str, CaseSensitive caseSensitivity ) { + return StartsWithMatcher( CasedString( str, caseSensitivity) ); + } + + RegexMatcher Matches(std::string const& regex, CaseSensitive caseSensitivity) { + return RegexMatcher(regex, caseSensitivity); + } + +} // namespace Matchers +} // namespace Catch + + + +namespace Catch { +namespace Matchers { + MatcherGenericBase::~MatcherGenericBase() = default; + + namespace Detail { + + std::string describe_multi_matcher(StringRef combine, std::string const* descriptions_begin, std::string const* descriptions_end) { + std::string description; + std::size_t combined_size = 4; + for ( auto desc = descriptions_begin; desc != descriptions_end; ++desc ) { + combined_size += desc->size(); + } + combined_size += static_cast(descriptions_end - descriptions_begin - 1) * combine.size(); + + description.reserve(combined_size); + + description += "( "; + bool first = true; + for( auto desc = descriptions_begin; desc != descriptions_end; ++desc ) { + if( first ) + first = false; + else + description += combine; + description += *desc; + } + description += " )"; + return description; + } + + } // namespace Detail +} // namespace Matchers +} // namespace Catch + + + + +namespace Catch { + + // This is the general overload that takes a any string matcher + // There is another overload, in catch_assertionhandler.h/.cpp, that only takes a string and infers + // the Equals matcher (so the header does not mention matchers) + void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef matcherString ) { + std::string exceptionMessage = Catch::translateActiveException(); + MatchExpr expr( CATCH_MOVE(exceptionMessage), matcher, matcherString ); + handler.handleExpr( expr ); + } + +} // namespace Catch + + + +#include + +namespace Catch { + + AutomakeReporter::~AutomakeReporter() {} + + void AutomakeReporter::testCaseEnded(TestCaseStats const& _testCaseStats) { + // Possible values to emit are PASS, XFAIL, SKIP, FAIL, XPASS and ERROR. + m_stream << ":test-result: "; + if (_testCaseStats.totals.assertions.allPassed()) { + m_stream << "PASS"; + } else if (_testCaseStats.totals.assertions.allOk()) { + m_stream << "XFAIL"; + } else { + m_stream << "FAIL"; + } + m_stream << ' ' << _testCaseStats.testInfo->name << '\n'; + StreamingReporterBase::testCaseEnded(_testCaseStats); + } + + void AutomakeReporter::skipTest(TestCaseInfo const& testInfo) { + m_stream << ":test-result: SKIP " << testInfo.name << '\n'; + } + +} // end namespace Catch + + + + + + +namespace Catch { + ReporterBase::ReporterBase( ReporterConfig&& config ): + IEventListener( config.fullConfig() ), + m_wrapped_stream( CATCH_MOVE(config).takeStream() ), + m_stream( m_wrapped_stream->stream() ), + m_colour( makeColourImpl( config.colourMode(), m_wrapped_stream.get() ) ), + m_customOptions( config.customOptions() ) + {} + + ReporterBase::~ReporterBase() = default; + + void ReporterBase::listReporters( + std::vector const& descriptions ) { + defaultListReporters(m_stream, descriptions, m_config->verbosity()); + } + + void ReporterBase::listListeners( + std::vector const& descriptions ) { + defaultListListeners( m_stream, descriptions ); + } + + void ReporterBase::listTests(std::vector const& tests) { + defaultListTests(m_stream, + m_colour.get(), + tests, + m_config->hasTestFilters(), + m_config->verbosity()); + } + + void ReporterBase::listTags(std::vector const& tags) { + defaultListTags( m_stream, tags, m_config->hasTestFilters() ); + } + +} // namespace Catch + + + + +#include + +namespace { + + constexpr Catch::StringRef bothOrAll( std::uint64_t count ) { + switch (count) { + case 1: + return Catch::StringRef{}; + case 2: + return "both "_catch_sr; + default: + return "all "_catch_sr; + } + } + +} // anon namespace + + +namespace Catch { +namespace { + + // Colour::LightGrey + static constexpr Colour::Code compactDimColour = Colour::FileName; + +#ifdef CATCH_PLATFORM_MAC + static constexpr Catch::StringRef compactFailedString = "FAILED"_sr; + static constexpr Catch::StringRef compactPassedString = "PASSED"_sr; +#else + static constexpr Catch::StringRef compactFailedString = "failed"_sr; + static constexpr Catch::StringRef compactPassedString = "passed"_sr; +#endif + +// Colour, message variants: +// - white: No tests ran. +// - red: Failed [both/all] N test cases, failed [both/all] M assertions. +// - white: Passed [both/all] N test cases (no assertions). +// - red: Failed N tests cases, failed M assertions. +// - green: Passed [both/all] N tests cases with M assertions. +void printTotals(std::ostream& out, const Totals& totals, ColourImpl* colourImpl) { + if (totals.testCases.total() == 0) { + out << "No tests ran."; + } else if (totals.testCases.failed == totals.testCases.total()) { + auto guard = colourImpl->guardColour( Colour::ResultError ).engage( out ); + const StringRef qualify_assertions_failed = + totals.assertions.failed == totals.assertions.total() ? + bothOrAll(totals.assertions.failed) : StringRef{}; + out << + "Failed " << bothOrAll(totals.testCases.failed) + << pluralise(totals.testCases.failed, "test case"_sr) << ", " + "failed " << qualify_assertions_failed << + pluralise(totals.assertions.failed, "assertion"_sr) << '.'; + } else if (totals.assertions.total() == 0) { + out << + "Passed " << bothOrAll(totals.testCases.total()) + << pluralise(totals.testCases.total(), "test case"_sr) + << " (no assertions)."; + } else if (totals.assertions.failed) { + out << colourImpl->guardColour( Colour::ResultError ) << + "Failed " << pluralise(totals.testCases.failed, "test case"_sr) << ", " + "failed " << pluralise(totals.assertions.failed, "assertion"_sr) << '.'; + } else { + out << colourImpl->guardColour( Colour::ResultSuccess ) << + "Passed " << bothOrAll(totals.testCases.passed) + << pluralise(totals.testCases.passed, "test case"_sr) << + " with " << pluralise(totals.assertions.passed, "assertion"_sr) << '.'; + } +} + +// Implementation of CompactReporter formatting +class AssertionPrinter { +public: + AssertionPrinter& operator= (AssertionPrinter const&) = delete; + AssertionPrinter(AssertionPrinter const&) = delete; + AssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages, ColourImpl* colourImpl_) + : stream(_stream) + , result(_stats.assertionResult) + , messages(_stats.infoMessages) + , itMessage(_stats.infoMessages.begin()) + , printInfoMessages(_printInfoMessages) + , colourImpl(colourImpl_) + {} + + void print() { + printSourceInfo(); + + itMessage = messages.begin(); + + switch (result.getResultType()) { + case ResultWas::Ok: + printResultType(Colour::ResultSuccess, compactPassedString); + printOriginalExpression(); + printReconstructedExpression(); + if (!result.hasExpression()) + printRemainingMessages(Colour::None); + else + printRemainingMessages(); + break; + case ResultWas::ExpressionFailed: + if (result.isOk()) + printResultType(Colour::ResultSuccess, compactFailedString + " - but was ok"_sr); + else + printResultType(Colour::Error, compactFailedString); + printOriginalExpression(); + printReconstructedExpression(); + printRemainingMessages(); + break; + case ResultWas::ThrewException: + printResultType(Colour::Error, compactFailedString); + printIssue("unexpected exception with message:"); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::FatalErrorCondition: + printResultType(Colour::Error, compactFailedString); + printIssue("fatal error condition with message:"); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::DidntThrowException: + printResultType(Colour::Error, compactFailedString); + printIssue("expected exception, got none"); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::Info: + printResultType(Colour::None, "info"_sr); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::Warning: + printResultType(Colour::None, "warning"_sr); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::ExplicitFailure: + printResultType(Colour::Error, compactFailedString); + printIssue("explicitly"); + printRemainingMessages(Colour::None); + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + printResultType(Colour::Error, "** internal error **"); + break; + } + } + +private: + void printSourceInfo() const { + stream << colourImpl->guardColour( Colour::FileName ) + << result.getSourceInfo() << ':'; + } + + void printResultType(Colour::Code colour, StringRef passOrFail) const { + if (!passOrFail.empty()) { + stream << colourImpl->guardColour(colour) << ' ' << passOrFail; + stream << ':'; + } + } + + void printIssue(char const* issue) const { + stream << ' ' << issue; + } + + void printExpressionWas() { + if (result.hasExpression()) { + stream << ';'; + { + stream << colourImpl->guardColour(compactDimColour) << " expression was:"; + } + printOriginalExpression(); + } + } + + void printOriginalExpression() const { + if (result.hasExpression()) { + stream << ' ' << result.getExpression(); + } + } + + void printReconstructedExpression() const { + if (result.hasExpandedExpression()) { + stream << colourImpl->guardColour(compactDimColour) << " for: "; + stream << result.getExpandedExpression(); + } + } + + void printMessage() { + if (itMessage != messages.end()) { + stream << " '" << itMessage->message << '\''; + ++itMessage; + } + } + + void printRemainingMessages(Colour::Code colour = compactDimColour) { + if (itMessage == messages.end()) + return; + + const auto itEnd = messages.cend(); + const auto N = static_cast(std::distance(itMessage, itEnd)); + + stream << colourImpl->guardColour( colour ) << " with " + << pluralise( N, "message"_sr ) << ':'; + + while (itMessage != itEnd) { + // If this assertion is a warning ignore any INFO messages + if (printInfoMessages || itMessage->type != ResultWas::Info) { + printMessage(); + if (itMessage != itEnd) { + stream << colourImpl->guardColour(compactDimColour) << " and"; + } + continue; + } + ++itMessage; + } + } + +private: + std::ostream& stream; + AssertionResult const& result; + std::vector messages; + std::vector::const_iterator itMessage; + bool printInfoMessages; + ColourImpl* colourImpl; +}; + +} // anon namespace + + std::string CompactReporter::getDescription() { + return "Reports test results on a single line, suitable for IDEs"; + } + + void CompactReporter::noMatchingTestCases( StringRef unmatchedSpec ) { + m_stream << "No test cases matched '" << unmatchedSpec << "'\n"; + } + + void CompactReporter::testRunStarting( TestRunInfo const& ) { + if ( m_config->testSpec().hasFilters() ) { + m_stream << m_colour->guardColour( Colour::BrightYellow ) + << "Filters: " + << serializeFilters( m_config->getTestsOrTags() ) + << '\n'; + } + m_stream << "RNG seed: " << getSeed() << '\n'; + } + + void CompactReporter::assertionEnded( AssertionStats const& _assertionStats ) { + AssertionResult const& result = _assertionStats.assertionResult; + + bool printInfoMessages = true; + + // Drop out if result was successful and we're not printing those + if( !m_config->includeSuccessfulResults() && result.isOk() ) { + if( result.getResultType() != ResultWas::Warning ) + return; + printInfoMessages = false; + } + + AssertionPrinter printer( m_stream, _assertionStats, printInfoMessages, m_colour.get() ); + printer.print(); + + m_stream << '\n' << std::flush; + } + + void CompactReporter::sectionEnded(SectionStats const& _sectionStats) { + double dur = _sectionStats.durationInSeconds; + if ( shouldShowDuration( *m_config, dur ) ) { + m_stream << getFormattedDuration( dur ) << " s: " << _sectionStats.sectionInfo.name << '\n' << std::flush; + } + } + + void CompactReporter::testRunEnded( TestRunStats const& _testRunStats ) { + printTotals( m_stream, _testRunStats.totals, m_colour.get() ); + m_stream << "\n\n" << std::flush; + StreamingReporterBase::testRunEnded( _testRunStats ); + } + + CompactReporter::~CompactReporter() {} + +} // end namespace Catch + + + + +#include + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch + // Note that 4062 (not all labels are handled and default is missing) is enabled +#endif + +#if defined(__clang__) +# pragma clang diagnostic push +// For simplicity, benchmarking-only helpers are always enabled +# pragma clang diagnostic ignored "-Wunused-function" +#endif + + + +namespace Catch { + +namespace { + +// Formatter impl for ConsoleReporter +class ConsoleAssertionPrinter { +public: + ConsoleAssertionPrinter& operator= (ConsoleAssertionPrinter const&) = delete; + ConsoleAssertionPrinter(ConsoleAssertionPrinter const&) = delete; + ConsoleAssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, ColourImpl* colourImpl_, bool _printInfoMessages) + : stream(_stream), + stats(_stats), + result(_stats.assertionResult), + colour(Colour::None), + message(result.getMessage()), + messages(_stats.infoMessages), + colourImpl(colourImpl_), + printInfoMessages(_printInfoMessages) { + switch (result.getResultType()) { + case ResultWas::Ok: + colour = Colour::Success; + passOrFail = "PASSED"_sr; + //if( result.hasMessage() ) + if (_stats.infoMessages.size() == 1) + messageLabel = "with message"; + if (_stats.infoMessages.size() > 1) + messageLabel = "with messages"; + break; + case ResultWas::ExpressionFailed: + if (result.isOk()) { + colour = Colour::Success; + passOrFail = "FAILED - but was ok"_sr; + } else { + colour = Colour::Error; + passOrFail = "FAILED"_sr; + } + if (_stats.infoMessages.size() == 1) + messageLabel = "with message"; + if (_stats.infoMessages.size() > 1) + messageLabel = "with messages"; + break; + case ResultWas::ThrewException: + colour = Colour::Error; + passOrFail = "FAILED"_sr; + messageLabel = "due to unexpected exception with "; + if (_stats.infoMessages.size() == 1) + messageLabel += "message"; + if (_stats.infoMessages.size() > 1) + messageLabel += "messages"; + break; + case ResultWas::FatalErrorCondition: + colour = Colour::Error; + passOrFail = "FAILED"_sr; + messageLabel = "due to a fatal error condition"; + break; + case ResultWas::DidntThrowException: + colour = Colour::Error; + passOrFail = "FAILED"_sr; + messageLabel = "because no exception was thrown where one was expected"; + break; + case ResultWas::Info: + messageLabel = "info"; + break; + case ResultWas::Warning: + messageLabel = "warning"; + break; + case ResultWas::ExplicitFailure: + passOrFail = "FAILED"_sr; + colour = Colour::Error; + if (_stats.infoMessages.size() == 1) + messageLabel = "explicitly with message"; + if (_stats.infoMessages.size() > 1) + messageLabel = "explicitly with messages"; + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + passOrFail = "** internal error **"_sr; + colour = Colour::Error; + break; + } + } + + void print() const { + printSourceInfo(); + if (stats.totals.assertions.total() > 0) { + printResultType(); + printOriginalExpression(); + printReconstructedExpression(); + } else { + stream << '\n'; + } + printMessage(); + } + +private: + void printResultType() const { + if (!passOrFail.empty()) { + stream << colourImpl->guardColour(colour) << passOrFail << ":\n"; + } + } + void printOriginalExpression() const { + if (result.hasExpression()) { + stream << colourImpl->guardColour( Colour::OriginalExpression ) + << " " << result.getExpressionInMacro() << '\n'; + } + } + void printReconstructedExpression() const { + if (result.hasExpandedExpression()) { + stream << "with expansion:\n"; + stream << colourImpl->guardColour( Colour::ReconstructedExpression ) + << TextFlow::Column( result.getExpandedExpression() ) + .indent( 2 ) + << '\n'; + } + } + void printMessage() const { + if (!messageLabel.empty()) + stream << messageLabel << ':' << '\n'; + for (auto const& msg : messages) { + // If this assertion is a warning ignore any INFO messages + if (printInfoMessages || msg.type != ResultWas::Info) + stream << TextFlow::Column(msg.message).indent(2) << '\n'; + } + } + void printSourceInfo() const { + stream << colourImpl->guardColour( Colour::FileName ) + << result.getSourceInfo() << ": "; + } + + std::ostream& stream; + AssertionStats const& stats; + AssertionResult const& result; + Colour::Code colour; + StringRef passOrFail; + std::string messageLabel; + std::string message; + std::vector messages; + ColourImpl* colourImpl; + bool printInfoMessages; +}; + +std::size_t makeRatio( std::uint64_t number, std::uint64_t total ) { + const auto ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number / total : 0; + return (ratio == 0 && number > 0) ? 1 : static_cast(ratio); +} + +std::size_t& findMax( std::size_t& i, std::size_t& j, std::size_t& k ) { + if (i > j && i > k) + return i; + else if (j > k) + return j; + else + return k; +} + +enum class Justification { Left, Right }; + +struct ColumnInfo { + std::string name; + std::size_t width; + Justification justification; +}; +struct ColumnBreak {}; +struct RowBreak {}; + +class Duration { + enum class Unit { + Auto, + Nanoseconds, + Microseconds, + Milliseconds, + Seconds, + Minutes + }; + static const uint64_t s_nanosecondsInAMicrosecond = 1000; + static const uint64_t s_nanosecondsInAMillisecond = 1000 * s_nanosecondsInAMicrosecond; + static const uint64_t s_nanosecondsInASecond = 1000 * s_nanosecondsInAMillisecond; + static const uint64_t s_nanosecondsInAMinute = 60 * s_nanosecondsInASecond; + + double m_inNanoseconds; + Unit m_units; + +public: + explicit Duration(double inNanoseconds, Unit units = Unit::Auto) + : m_inNanoseconds(inNanoseconds), + m_units(units) { + if (m_units == Unit::Auto) { + if (m_inNanoseconds < s_nanosecondsInAMicrosecond) + m_units = Unit::Nanoseconds; + else if (m_inNanoseconds < s_nanosecondsInAMillisecond) + m_units = Unit::Microseconds; + else if (m_inNanoseconds < s_nanosecondsInASecond) + m_units = Unit::Milliseconds; + else if (m_inNanoseconds < s_nanosecondsInAMinute) + m_units = Unit::Seconds; + else + m_units = Unit::Minutes; + } + + } + + auto value() const -> double { + switch (m_units) { + case Unit::Microseconds: + return m_inNanoseconds / static_cast(s_nanosecondsInAMicrosecond); + case Unit::Milliseconds: + return m_inNanoseconds / static_cast(s_nanosecondsInAMillisecond); + case Unit::Seconds: + return m_inNanoseconds / static_cast(s_nanosecondsInASecond); + case Unit::Minutes: + return m_inNanoseconds / static_cast(s_nanosecondsInAMinute); + default: + return m_inNanoseconds; + } + } + StringRef unitsAsString() const { + switch (m_units) { + case Unit::Nanoseconds: + return "ns"_sr; + case Unit::Microseconds: + return "us"_sr; + case Unit::Milliseconds: + return "ms"_sr; + case Unit::Seconds: + return "s"_sr; + case Unit::Minutes: + return "m"_sr; + default: + return "** internal error **"_sr; + } + + } + friend auto operator << (std::ostream& os, Duration const& duration) -> std::ostream& { + return os << duration.value() << ' ' << duration.unitsAsString(); + } +}; +} // end anon namespace + +class TablePrinter { + std::ostream& m_os; + std::vector m_columnInfos; + ReusableStringStream m_oss; + int m_currentColumn = -1; + bool m_isOpen = false; + +public: + TablePrinter( std::ostream& os, std::vector columnInfos ) + : m_os( os ), + m_columnInfos( CATCH_MOVE( columnInfos ) ) {} + + auto columnInfos() const -> std::vector const& { + return m_columnInfos; + } + + void open() { + if (!m_isOpen) { + m_isOpen = true; + *this << RowBreak(); + + TextFlow::Columns headerCols; + auto spacer = TextFlow::Spacer(2); + for (auto const& info : m_columnInfos) { + assert(info.width > 2); + headerCols += TextFlow::Column(info.name).width(info.width - 2); + headerCols += spacer; + } + m_os << headerCols << '\n'; + + m_os << lineOfChars('-') << '\n'; + } + } + void close() { + if (m_isOpen) { + *this << RowBreak(); + m_os << '\n' << std::flush; + m_isOpen = false; + } + } + + template + friend TablePrinter& operator << (TablePrinter& tp, T const& value) { + tp.m_oss << value; + return tp; + } + + friend TablePrinter& operator << (TablePrinter& tp, ColumnBreak) { + auto colStr = tp.m_oss.str(); + const auto strSize = colStr.size(); + tp.m_oss.str(""); + tp.open(); + if (tp.m_currentColumn == static_cast(tp.m_columnInfos.size() - 1)) { + tp.m_currentColumn = -1; + tp.m_os << '\n'; + } + tp.m_currentColumn++; + + auto colInfo = tp.m_columnInfos[tp.m_currentColumn]; + auto padding = (strSize + 1 < colInfo.width) + ? std::string(colInfo.width - (strSize + 1), ' ') + : std::string(); + if (colInfo.justification == Justification::Left) + tp.m_os << colStr << padding << ' '; + else + tp.m_os << padding << colStr << ' '; + return tp; + } + + friend TablePrinter& operator << (TablePrinter& tp, RowBreak) { + if (tp.m_currentColumn > 0) { + tp.m_os << '\n'; + tp.m_currentColumn = -1; + } + return tp; + } +}; + +ConsoleReporter::ConsoleReporter(ReporterConfig&& config): + StreamingReporterBase( CATCH_MOVE( config ) ), + m_tablePrinter(Detail::make_unique(m_stream, + [&config]() -> std::vector { + if (config.fullConfig()->benchmarkNoAnalysis()) + { + return{ + { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, Justification::Left }, + { " samples", 14, Justification::Right }, + { " iterations", 14, Justification::Right }, + { " mean", 14, Justification::Right } + }; + } + else + { + return{ + { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, Justification::Left }, + { "samples mean std dev", 14, Justification::Right }, + { "iterations low mean low std dev", 14, Justification::Right }, + { "estimated high mean high std dev", 14, Justification::Right } + }; + } + }())) {} +ConsoleReporter::~ConsoleReporter() = default; + +std::string ConsoleReporter::getDescription() { + return "Reports test results as plain lines of text"; +} + +void ConsoleReporter::noMatchingTestCases( StringRef unmatchedSpec ) { + m_stream << "No test cases matched '" << unmatchedSpec << "'\n"; +} + +void ConsoleReporter::reportInvalidTestSpec( StringRef arg ) { + m_stream << "Invalid Filter: " << arg << '\n'; +} + +void ConsoleReporter::assertionStarting(AssertionInfo const&) {} + +void ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) { + AssertionResult const& result = _assertionStats.assertionResult; + + bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); + + // Drop out if result was successful but we're not printing them. + if (!includeResults && result.getResultType() != ResultWas::Warning) + return; + + lazyPrint(); + + ConsoleAssertionPrinter printer(m_stream, _assertionStats, m_colour.get(), includeResults); + printer.print(); + m_stream << '\n' << std::flush; +} + +void ConsoleReporter::sectionStarting(SectionInfo const& _sectionInfo) { + m_tablePrinter->close(); + m_headerPrinted = false; + StreamingReporterBase::sectionStarting(_sectionInfo); +} +void ConsoleReporter::sectionEnded(SectionStats const& _sectionStats) { + m_tablePrinter->close(); + if (_sectionStats.missingAssertions) { + lazyPrint(); + auto guard = + m_colour->guardColour( Colour::ResultError ).engage( m_stream ); + if (m_sectionStack.size() > 1) + m_stream << "\nNo assertions in section"; + else + m_stream << "\nNo assertions in test case"; + m_stream << " '" << _sectionStats.sectionInfo.name << "'\n\n" << std::flush; + } + double dur = _sectionStats.durationInSeconds; + if (shouldShowDuration(*m_config, dur)) { + m_stream << getFormattedDuration(dur) << " s: " << _sectionStats.sectionInfo.name << '\n' << std::flush; + } + if (m_headerPrinted) { + m_headerPrinted = false; + } + StreamingReporterBase::sectionEnded(_sectionStats); +} + +void ConsoleReporter::benchmarkPreparing( StringRef name ) { + lazyPrintWithoutClosingBenchmarkTable(); + + auto nameCol = TextFlow::Column( static_cast( name ) ) + .width( m_tablePrinter->columnInfos()[0].width - 2 ); + + bool firstLine = true; + for (auto line : nameCol) { + if (!firstLine) + (*m_tablePrinter) << ColumnBreak() << ColumnBreak() << ColumnBreak(); + else + firstLine = false; + + (*m_tablePrinter) << line << ColumnBreak(); + } +} + +void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) { + (*m_tablePrinter) << info.samples << ColumnBreak() + << info.iterations << ColumnBreak(); + if (!m_config->benchmarkNoAnalysis()) + (*m_tablePrinter) << Duration(info.estimatedDuration) << ColumnBreak(); +} +void ConsoleReporter::benchmarkEnded(BenchmarkStats<> const& stats) { + if (m_config->benchmarkNoAnalysis()) + { + (*m_tablePrinter) << Duration(stats.mean.point.count()) << ColumnBreak(); + } + else + { + (*m_tablePrinter) << ColumnBreak() + << Duration(stats.mean.point.count()) << ColumnBreak() + << Duration(stats.mean.lower_bound.count()) << ColumnBreak() + << Duration(stats.mean.upper_bound.count()) << ColumnBreak() << ColumnBreak() + << Duration(stats.standardDeviation.point.count()) << ColumnBreak() + << Duration(stats.standardDeviation.lower_bound.count()) << ColumnBreak() + << Duration(stats.standardDeviation.upper_bound.count()) << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak(); + } +} + +void ConsoleReporter::benchmarkFailed( StringRef error ) { + auto guard = m_colour->guardColour( Colour::Red ).engage( m_stream ); + (*m_tablePrinter) + << "Benchmark failed (" << error << ')' + << ColumnBreak() << RowBreak(); +} + +void ConsoleReporter::testCaseEnded(TestCaseStats const& _testCaseStats) { + m_tablePrinter->close(); + StreamingReporterBase::testCaseEnded(_testCaseStats); + m_headerPrinted = false; +} +void ConsoleReporter::testRunEnded(TestRunStats const& _testRunStats) { + printTotalsDivider(_testRunStats.totals); + printTotals(_testRunStats.totals); + m_stream << '\n' << std::flush; + StreamingReporterBase::testRunEnded(_testRunStats); +} +void ConsoleReporter::testRunStarting(TestRunInfo const& _testInfo) { + StreamingReporterBase::testRunStarting(_testInfo); + if ( m_config->testSpec().hasFilters() ) { + m_stream << m_colour->guardColour( Colour::BrightYellow ) << "Filters: " + << serializeFilters( m_config->getTestsOrTags() ) << '\n'; + } + m_stream << "Randomness seeded to: " << getSeed() << '\n'; +} + +void ConsoleReporter::lazyPrint() { + + m_tablePrinter->close(); + lazyPrintWithoutClosingBenchmarkTable(); +} + +void ConsoleReporter::lazyPrintWithoutClosingBenchmarkTable() { + + if ( !m_testRunInfoPrinted ) { + lazyPrintRunInfo(); + } + if (!m_headerPrinted) { + printTestCaseAndSectionHeader(); + m_headerPrinted = true; + } +} +void ConsoleReporter::lazyPrintRunInfo() { + m_stream << '\n' + << lineOfChars( '~' ) << '\n' + << m_colour->guardColour( Colour::SecondaryText ) + << currentTestRunInfo.name << " is a Catch2 v" << libraryVersion() + << " host application.\n" + << "Run with -? for options\n\n"; + + m_testRunInfoPrinted = true; +} +void ConsoleReporter::printTestCaseAndSectionHeader() { + assert(!m_sectionStack.empty()); + printOpenHeader(currentTestCaseInfo->name); + + if (m_sectionStack.size() > 1) { + auto guard = m_colour->guardColour( Colour::Headers ).engage( m_stream ); + + auto + it = m_sectionStack.begin() + 1, // Skip first section (test case) + itEnd = m_sectionStack.end(); + for (; it != itEnd; ++it) + printHeaderString(it->name, 2); + } + + SourceLineInfo lineInfo = m_sectionStack.back().lineInfo; + + + m_stream << lineOfChars( '-' ) << '\n' + << m_colour->guardColour( Colour::FileName ) << lineInfo << '\n' + << lineOfChars( '.' ) << "\n\n" + << std::flush; +} + +void ConsoleReporter::printClosedHeader(std::string const& _name) { + printOpenHeader(_name); + m_stream << lineOfChars('.') << '\n'; +} +void ConsoleReporter::printOpenHeader(std::string const& _name) { + m_stream << lineOfChars('-') << '\n'; + { + auto guard = m_colour->guardColour( Colour::Headers ).engage( m_stream ); + printHeaderString(_name); + } +} + +void ConsoleReporter::printHeaderString(std::string const& _string, std::size_t indent) { + // We want to get a bit fancy with line breaking here, so that subsequent + // lines start after ":" if one is present, e.g. + // ``` + // blablabla: Fancy + // linebreaking + // ``` + // but we also want to avoid problems with overly long indentation causing + // the text to take up too many lines, e.g. + // ``` + // blablabla: F + // a + // n + // c + // y + // . + // . + // . + // ``` + // So we limit the prefix indentation check to first quarter of the possible + // width + std::size_t idx = _string.find( ": " ); + if ( idx != std::string::npos && idx < CATCH_CONFIG_CONSOLE_WIDTH / 4 ) { + idx += 2; + } else { + idx = 0; + } + m_stream << TextFlow::Column( _string ) + .indent( indent + idx ) + .initialIndent( indent ) + << '\n'; +} + +struct SummaryColumn { + + SummaryColumn( std::string _label, Colour::Code _colour ) + : label( CATCH_MOVE( _label ) ), + colour( _colour ) {} + SummaryColumn addRow( std::uint64_t count ) { + ReusableStringStream rss; + rss << count; + std::string row = rss.str(); + for (auto& oldRow : rows) { + while (oldRow.size() < row.size()) + oldRow = ' ' + oldRow; + while (oldRow.size() > row.size()) + row = ' ' + row; + } + rows.push_back(row); + return *this; + } + + std::string label; + Colour::Code colour; + std::vector rows; + +}; + +void ConsoleReporter::printTotals( Totals const& totals ) { + if (totals.testCases.total() == 0) { + m_stream << m_colour->guardColour( Colour::Warning ) + << "No tests ran\n"; + } else if (totals.assertions.total() > 0 && totals.testCases.allPassed()) { + m_stream << m_colour->guardColour( Colour::ResultSuccess ) + << "All tests passed"; + m_stream << " (" + << pluralise(totals.assertions.passed, "assertion"_sr) << " in " + << pluralise(totals.testCases.passed, "test case"_sr) << ')' + << '\n'; + } else { + + std::vector columns; + columns.push_back(SummaryColumn("", Colour::None) + .addRow(totals.testCases.total()) + .addRow(totals.assertions.total())); + columns.push_back(SummaryColumn("passed", Colour::Success) + .addRow(totals.testCases.passed) + .addRow(totals.assertions.passed)); + columns.push_back(SummaryColumn("failed", Colour::ResultError) + .addRow(totals.testCases.failed) + .addRow(totals.assertions.failed)); + columns.push_back(SummaryColumn("failed as expected", Colour::ResultExpectedFailure) + .addRow(totals.testCases.failedButOk) + .addRow(totals.assertions.failedButOk)); + + printSummaryRow("test cases"_sr, columns, 0); + printSummaryRow("assertions"_sr, columns, 1); + } +} +void ConsoleReporter::printSummaryRow(StringRef label, std::vector const& cols, std::size_t row) { + for (auto col : cols) { + std::string const& value = col.rows[row]; + if (col.label.empty()) { + m_stream << label << ": "; + if ( value != "0" ) { + m_stream << value; + } else { + m_stream << m_colour->guardColour( Colour::Warning ) + << "- none -"; + } + } else if (value != "0") { + m_stream << m_colour->guardColour( Colour::LightGrey ) << " | " + << m_colour->guardColour( col.colour ) << value << ' ' + << col.label; + } + } + m_stream << '\n'; +} + +void ConsoleReporter::printTotalsDivider(Totals const& totals) { + if (totals.testCases.total() > 0) { + std::size_t failedRatio = makeRatio(totals.testCases.failed, totals.testCases.total()); + std::size_t failedButOkRatio = makeRatio(totals.testCases.failedButOk, totals.testCases.total()); + std::size_t passedRatio = makeRatio(totals.testCases.passed, totals.testCases.total()); + while (failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH - 1) + findMax(failedRatio, failedButOkRatio, passedRatio)++; + while (failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH - 1) + findMax(failedRatio, failedButOkRatio, passedRatio)--; + + m_stream << m_colour->guardColour( Colour::Error ) + << std::string( failedRatio, '=' ) + << m_colour->guardColour( Colour::ResultExpectedFailure ) + << std::string( failedButOkRatio, '=' ); + if ( totals.testCases.allPassed() ) { + m_stream << m_colour->guardColour( Colour::ResultSuccess ) + << std::string( passedRatio, '=' ); + } else { + m_stream << m_colour->guardColour( Colour::Success ) + << std::string( passedRatio, '=' ); + } + } else { + m_stream << m_colour->guardColour( Colour::Warning ) + << std::string( CATCH_CONFIG_CONSOLE_WIDTH - 1, '=' ); + } + m_stream << '\n'; +} +void ConsoleReporter::printSummaryDivider() { + m_stream << lineOfChars('-') << '\n'; +} + +} // end namespace Catch + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif + + + + +#include +#include + +namespace Catch { + namespace { + struct BySectionInfo { + BySectionInfo( SectionInfo const& other ): m_other( other ) {} + BySectionInfo( BySectionInfo const& other ): + m_other( other.m_other ) {} + bool operator()( + Detail::unique_ptr const& + node ) const { + return ( + ( node->stats.sectionInfo.name == m_other.name ) && + ( node->stats.sectionInfo.lineInfo == m_other.lineInfo ) ); + } + void operator=( BySectionInfo const& ) = delete; + + private: + SectionInfo const& m_other; + }; + + } // namespace + + namespace Detail { + AssertionOrBenchmarkResult::AssertionOrBenchmarkResult( + AssertionStats const& assertion ): + m_assertion( assertion ) {} + + AssertionOrBenchmarkResult::AssertionOrBenchmarkResult( + BenchmarkStats<> const& benchmark ): + m_benchmark( benchmark ) {} + + bool AssertionOrBenchmarkResult::isAssertion() const { + return m_assertion.some(); + } + bool AssertionOrBenchmarkResult::isBenchmark() const { + return m_benchmark.some(); + } + + AssertionStats const& AssertionOrBenchmarkResult::asAssertion() const { + assert(m_assertion.some()); + + return *m_assertion; + } + BenchmarkStats<> const& AssertionOrBenchmarkResult::asBenchmark() const { + assert(m_benchmark.some()); + + return *m_benchmark; + } + + } + + CumulativeReporterBase::~CumulativeReporterBase() = default; + + void CumulativeReporterBase::benchmarkEnded(BenchmarkStats<> const& benchmarkStats) { + m_sectionStack.back()->assertionsAndBenchmarks.emplace_back(benchmarkStats); + } + + void + CumulativeReporterBase::sectionStarting( SectionInfo const& sectionInfo ) { + SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); + SectionNode* node; + if ( m_sectionStack.empty() ) { + if ( !m_rootSection ) { + m_rootSection = + Detail::make_unique( incompleteStats ); + } + node = m_rootSection.get(); + } else { + SectionNode& parentNode = *m_sectionStack.back(); + auto it = std::find_if( parentNode.childSections.begin(), + parentNode.childSections.end(), + BySectionInfo( sectionInfo ) ); + if ( it == parentNode.childSections.end() ) { + auto newNode = + Detail::make_unique( incompleteStats ); + node = newNode.get(); + parentNode.childSections.push_back( CATCH_MOVE( newNode ) ); + } else { + node = it->get(); + } + } + + m_deepestSection = node; + m_sectionStack.push_back( node ); + } + + void CumulativeReporterBase::assertionEnded( + AssertionStats const& assertionStats ) { + assert( !m_sectionStack.empty() ); + // AssertionResult holds a pointer to a temporary DecomposedExpression, + // which getExpandedExpression() calls to build the expression string. + // Our section stack copy of the assertionResult will likely outlive the + // temporary, so it must be expanded or discarded now to avoid calling + // a destroyed object later. + if ( m_shouldStoreFailedAssertions && + !assertionStats.assertionResult.isOk() ) { + static_cast( + assertionStats.assertionResult.getExpandedExpression() ); + } + if ( m_shouldStoreSuccesfulAssertions && + assertionStats.assertionResult.isOk() ) { + static_cast( + assertionStats.assertionResult.getExpandedExpression() ); + } + SectionNode& sectionNode = *m_sectionStack.back(); + sectionNode.assertionsAndBenchmarks.emplace_back( assertionStats ); + } + + void CumulativeReporterBase::sectionEnded( SectionStats const& sectionStats ) { + assert( !m_sectionStack.empty() ); + SectionNode& node = *m_sectionStack.back(); + node.stats = sectionStats; + m_sectionStack.pop_back(); + } + + void CumulativeReporterBase::testCaseEnded( + TestCaseStats const& testCaseStats ) { + auto node = Detail::make_unique( testCaseStats ); + assert( m_sectionStack.size() == 0 ); + node->children.push_back( CATCH_MOVE(m_rootSection) ); + m_testCases.push_back( CATCH_MOVE(node) ); + + assert( m_deepestSection ); + m_deepestSection->stdOut = testCaseStats.stdOut; + m_deepestSection->stdErr = testCaseStats.stdErr; + } + + + void CumulativeReporterBase::testRunEnded( TestRunStats const& testRunStats ) { + assert(!m_testRun && "CumulativeReporterBase assumes there can only be one test run"); + m_testRun = Detail::make_unique( testRunStats ); + m_testRun->children.swap( m_testCases ); + testRunEndedCumulative(); + } + + bool CumulativeReporterBase::SectionNode::hasAnyAssertions() const { + return std::any_of( + assertionsAndBenchmarks.begin(), + assertionsAndBenchmarks.end(), + []( Detail::AssertionOrBenchmarkResult const& res ) { + return res.isAssertion(); + } ); + } + +} // end namespace Catch + + + + +namespace Catch { + + void EventListenerBase::fatalErrorEncountered( StringRef ) {} + + void EventListenerBase::benchmarkPreparing( StringRef ) {} + void EventListenerBase::benchmarkStarting( BenchmarkInfo const& ) {} + void EventListenerBase::benchmarkEnded( BenchmarkStats<> const& ) {} + void EventListenerBase::benchmarkFailed( StringRef ) {} + + void EventListenerBase::assertionStarting( AssertionInfo const& ) {} + + void EventListenerBase::assertionEnded( AssertionStats const& ) {} + void EventListenerBase::listReporters( + std::vector const& ) {} + void EventListenerBase::listListeners( + std::vector const& ) {} + void EventListenerBase::listTests( std::vector const& ) {} + void EventListenerBase::listTags( std::vector const& ) {} + void EventListenerBase::noMatchingTestCases( StringRef ) {} + void EventListenerBase::reportInvalidTestSpec( StringRef ) {} + void EventListenerBase::testRunStarting( TestRunInfo const& ) {} + void EventListenerBase::testCaseStarting( TestCaseInfo const& ) {} + void EventListenerBase::testCasePartialStarting(TestCaseInfo const&, uint64_t) {} + void EventListenerBase::sectionStarting( SectionInfo const& ) {} + void EventListenerBase::sectionEnded( SectionStats const& ) {} + void EventListenerBase::testCasePartialEnded(TestCaseStats const&, uint64_t) {} + void EventListenerBase::testCaseEnded( TestCaseStats const& ) {} + void EventListenerBase::testRunEnded( TestRunStats const& ) {} + void EventListenerBase::skipTest( TestCaseInfo const& ) {} +} // namespace Catch + + + + +#include +#include +#include +#include +#include + +namespace Catch { + + namespace { + void listTestNamesOnly(std::ostream& out, + std::vector const& tests) { + for (auto const& test : tests) { + auto const& testCaseInfo = test.getTestCaseInfo(); + + if (startsWith(testCaseInfo.name, '#')) { + out << '"' << testCaseInfo.name << '"'; + } else { + out << testCaseInfo.name; + } + + out << '\n'; + } + out << std::flush; + } + } // end unnamed namespace + + + // Because formatting using c++ streams is stateful, drop down to C is + // required Alternatively we could use stringstream, but its performance + // is... not good. + std::string getFormattedDuration( double duration ) { + // Max exponent + 1 is required to represent the whole part + // + 1 for decimal point + // + 3 for the 3 decimal places + // + 1 for null terminator + const std::size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1; + char buffer[maxDoubleSize]; + + // Save previous errno, to prevent sprintf from overwriting it + ErrnoGuard guard; +#ifdef _MSC_VER + size_t printedLength = static_cast( + sprintf_s( buffer, "%.3f", duration ) ); +#else + size_t printedLength = static_cast( + std::snprintf( buffer, maxDoubleSize, "%.3f", duration ) ); +#endif + return std::string( buffer, printedLength ); + } + + bool shouldShowDuration( IConfig const& config, double duration ) { + if ( config.showDurations() == ShowDurations::Always ) { + return true; + } + if ( config.showDurations() == ShowDurations::Never ) { + return false; + } + const double min = config.minDuration(); + return min >= 0 && duration >= min; + } + + std::string serializeFilters( std::vector const& filters ) { + // We add a ' ' separator between each filter + size_t serialized_size = filters.size() - 1; + for (auto const& filter : filters) { + serialized_size += filter.size(); + } + + std::string serialized; + serialized.reserve(serialized_size); + bool first = true; + + for (auto const& filter : filters) { + if (!first) { + serialized.push_back(' '); + } + first = false; + serialized.append(filter); + } + + return serialized; + } + + std::ostream& operator<<( std::ostream& out, lineOfChars value ) { + for ( size_t idx = 0; idx < CATCH_CONFIG_CONSOLE_WIDTH - 1; ++idx ) { + out.put( value.c ); + } + return out; + } + + void + defaultListReporters( std::ostream& out, + std::vector const& descriptions, + Verbosity verbosity ) { + out << "Available reporters:\n"; + const auto maxNameLen = + std::max_element( descriptions.begin(), + descriptions.end(), + []( ReporterDescription const& lhs, + ReporterDescription const& rhs ) { + return lhs.name.size() < rhs.name.size(); + } ) + ->name.size(); + + for ( auto const& desc : descriptions ) { + if ( verbosity == Verbosity::Quiet ) { + out << TextFlow::Column( desc.name ) + .indent( 2 ) + .width( 5 + maxNameLen ) + << '\n'; + } else { + out << TextFlow::Column( desc.name + ':' ) + .indent( 2 ) + .width( 5 + maxNameLen ) + + TextFlow::Column( desc.description ) + .initialIndent( 0 ) + .indent( 2 ) + .width( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen - 8 ) + << '\n'; + } + } + out << '\n' << std::flush; + } + + void defaultListListeners( std::ostream& out, + std::vector const& descriptions ) { + out << "Registered listeners:\n"; + + if(descriptions.empty()) { + return; + } + + const auto maxNameLen = + std::max_element( descriptions.begin(), + descriptions.end(), + []( ListenerDescription const& lhs, + ListenerDescription const& rhs ) { + return lhs.name.size() < rhs.name.size(); + } ) + ->name.size(); + + for ( auto const& desc : descriptions ) { + out << TextFlow::Column( static_cast( desc.name ) + + ':' ) + .indent( 2 ) + .width( maxNameLen + 5 ) + + TextFlow::Column( desc.description ) + .initialIndent( 0 ) + .indent( 2 ) + .width( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen - 8 ) + << '\n'; + } + + out << '\n' << std::flush; + } + + void defaultListTags( std::ostream& out, + std::vector const& tags, + bool isFiltered ) { + if ( isFiltered ) { + out << "Tags for matching test cases:\n"; + } else { + out << "All available tags:\n"; + } + + for ( auto const& tagCount : tags ) { + ReusableStringStream rss; + rss << " " << std::setw( 2 ) << tagCount.count << " "; + auto str = rss.str(); + auto wrapper = TextFlow::Column( tagCount.all() ) + .initialIndent( 0 ) + .indent( str.size() ) + .width( CATCH_CONFIG_CONSOLE_WIDTH - 10 ); + out << str << wrapper << '\n'; + } + out << pluralise(tags.size(), "tag"_sr) << "\n\n" << std::flush; + } + + void defaultListTests(std::ostream& out, ColourImpl* streamColour, std::vector const& tests, bool isFiltered, Verbosity verbosity) { + // We special case this to provide the equivalent of old + // `--list-test-names-only`, which could then be used by the + // `--input-file` option. + if (verbosity == Verbosity::Quiet) { + listTestNamesOnly(out, tests); + return; + } + + if (isFiltered) { + out << "Matching test cases:\n"; + } else { + out << "All available test cases:\n"; + } + + for (auto const& test : tests) { + auto const& testCaseInfo = test.getTestCaseInfo(); + Colour::Code colour = testCaseInfo.isHidden() + ? Colour::SecondaryText + : Colour::None; + auto colourGuard = streamColour->guardColour( colour ).engage( out ); + + out << TextFlow::Column(testCaseInfo.name).indent(2) << '\n'; + if (verbosity >= Verbosity::High) { + out << TextFlow::Column(Catch::Detail::stringify(testCaseInfo.lineInfo)).indent(4) << '\n'; + } + if (!testCaseInfo.tags.empty() && + verbosity > Verbosity::Quiet) { + out << TextFlow::Column(testCaseInfo.tagsAsString()).indent(6) << '\n'; + } + } + + if (isFiltered) { + out << pluralise(tests.size(), "matching test case"_sr); + } else { + out << pluralise(tests.size(), "test case"_sr); + } + out << "\n\n" << std::flush; + } + +} // namespace Catch + + + + +#include +#include +#include +#include + +namespace Catch { + + namespace { + std::string getCurrentTimestamp() { + time_t rawtime; + std::time(&rawtime); + + std::tm timeInfo = {}; +#if defined (_MSC_VER) || defined (__MINGW32__) + gmtime_s(&timeInfo, &rawtime); +#else + gmtime_r(&rawtime, &timeInfo); +#endif + + auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); + char timeStamp[timeStampSize]; + const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; + + std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); + + return std::string(timeStamp, timeStampSize - 1); + } + + std::string fileNameTag(std::vector const& tags) { + auto it = std::find_if(begin(tags), + end(tags), + [] (Tag const& tag) { + return tag.original.size() > 0 + && tag.original[0] == '#'; }); + if (it != tags.end()) { + return static_cast( + it->original.substr(1, it->original.size() - 1) + ); + } + return std::string(); + } + + // Formats the duration in seconds to 3 decimal places. + // This is done because some genius defined Maven Surefire schema + // in a way that only accepts 3 decimal places, and tools like + // Jenkins use that schema for validation JUnit reporter output. + std::string formatDuration( double seconds ) { + ReusableStringStream rss; + rss << std::fixed << std::setprecision( 3 ) << seconds; + return rss.str(); + } + + static void normalizeNamespaceMarkers(std::string& str) { + std::size_t pos = str.find( "::" ); + while ( pos != str.npos ) { + str.replace( pos, 2, "." ); + pos += 1; + pos = str.find( "::", pos ); + } + } + + } // anonymous namespace + + JunitReporter::JunitReporter( ReporterConfig&& _config ) + : CumulativeReporterBase( CATCH_MOVE(_config) ), + xml( m_stream ) + { + m_preferences.shouldRedirectStdOut = true; + m_preferences.shouldReportAllAssertions = true; + m_shouldStoreSuccesfulAssertions = false; + } + + std::string JunitReporter::getDescription() { + return "Reports test results in an XML format that looks like Ant's junitreport target"; + } + + void JunitReporter::testRunStarting( TestRunInfo const& runInfo ) { + CumulativeReporterBase::testRunStarting( runInfo ); + xml.startElement( "testsuites" ); + suiteTimer.start(); + stdOutForSuite.clear(); + stdErrForSuite.clear(); + unexpectedExceptions = 0; + } + + void JunitReporter::testCaseStarting( TestCaseInfo const& testCaseInfo ) { + m_okToFail = testCaseInfo.okToFail(); + } + + void JunitReporter::assertionEnded( AssertionStats const& assertionStats ) { + if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail ) + unexpectedExceptions++; + CumulativeReporterBase::assertionEnded( assertionStats ); + } + + void JunitReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { + stdOutForSuite += testCaseStats.stdOut; + stdErrForSuite += testCaseStats.stdErr; + CumulativeReporterBase::testCaseEnded( testCaseStats ); + } + + void JunitReporter::testRunEndedCumulative() { + const auto suiteTime = suiteTimer.getElapsedSeconds(); + writeRun( *m_testRun, suiteTime ); + xml.endElement(); + } + + void JunitReporter::writeRun( TestRunNode const& testRunNode, double suiteTime ) { + XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" ); + + TestRunStats const& stats = testRunNode.value; + xml.writeAttribute( "name"_sr, stats.runInfo.name ); + xml.writeAttribute( "errors"_sr, unexpectedExceptions ); + xml.writeAttribute( "failures"_sr, stats.totals.assertions.failed-unexpectedExceptions ); + xml.writeAttribute( "tests"_sr, stats.totals.assertions.total() ); + xml.writeAttribute( "hostname"_sr, "tbd"_sr ); // !TBD + if( m_config->showDurations() == ShowDurations::Never ) + xml.writeAttribute( "time"_sr, ""_sr ); + else + xml.writeAttribute( "time"_sr, formatDuration( suiteTime ) ); + xml.writeAttribute( "timestamp"_sr, getCurrentTimestamp() ); + + // Write properties + { + auto properties = xml.scopedElement("properties"); + xml.scopedElement("property") + .writeAttribute("name"_sr, "random-seed"_sr) + .writeAttribute("value"_sr, m_config->rngSeed()); + if (m_config->hasTestFilters()) { + xml.scopedElement("property") + .writeAttribute("name"_sr, "filters"_sr) + .writeAttribute("value"_sr, serializeFilters(m_config->getTestsOrTags())); + } + } + + // Write test cases + for( auto const& child : testRunNode.children ) + writeTestCase( *child ); + + xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite ), XmlFormatting::Newline ); + xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite ), XmlFormatting::Newline ); + } + + void JunitReporter::writeTestCase( TestCaseNode const& testCaseNode ) { + TestCaseStats const& stats = testCaseNode.value; + + // All test cases have exactly one section - which represents the + // test case itself. That section may have 0-n nested sections + assert( testCaseNode.children.size() == 1 ); + SectionNode const& rootSection = *testCaseNode.children.front(); + + std::string className = + static_cast( stats.testInfo->className ); + + if( className.empty() ) { + className = fileNameTag(stats.testInfo->tags); + if ( className.empty() ) { + className = "global"; + } + } + + if ( !m_config->name().empty() ) + className = static_cast(m_config->name()) + '.' + className; + + normalizeNamespaceMarkers(className); + + writeSection( className, "", rootSection, stats.testInfo->okToFail() ); + } + + void JunitReporter::writeSection( std::string const& className, + std::string const& rootName, + SectionNode const& sectionNode, + bool testOkToFail) { + std::string name = trim( sectionNode.stats.sectionInfo.name ); + if( !rootName.empty() ) + name = rootName + '/' + name; + + if( sectionNode.hasAnyAssertions() + || !sectionNode.stdOut.empty() + || !sectionNode.stdErr.empty() ) { + XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); + if( className.empty() ) { + xml.writeAttribute( "classname"_sr, name ); + xml.writeAttribute( "name"_sr, "root"_sr ); + } + else { + xml.writeAttribute( "classname"_sr, className ); + xml.writeAttribute( "name"_sr, name ); + } + xml.writeAttribute( "time"_sr, formatDuration( sectionNode.stats.durationInSeconds ) ); + // This is not ideal, but it should be enough to mimic gtest's + // junit output. + // Ideally the JUnit reporter would also handle `skipTest` + // events and write those out appropriately. + xml.writeAttribute( "status"_sr, "run"_sr ); + + if (sectionNode.stats.assertions.failedButOk) { + xml.scopedElement("skipped") + .writeAttribute("message", "TEST_CASE tagged with !mayfail"); + } + + writeAssertions( sectionNode ); + + + if( !sectionNode.stdOut.empty() ) + xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), XmlFormatting::Newline ); + if( !sectionNode.stdErr.empty() ) + xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), XmlFormatting::Newline ); + } + for( auto const& childNode : sectionNode.childSections ) + if( className.empty() ) + writeSection( name, "", *childNode, testOkToFail ); + else + writeSection( className, name, *childNode, testOkToFail ); + } + + void JunitReporter::writeAssertions( SectionNode const& sectionNode ) { + for (auto const& assertionOrBenchmark : sectionNode.assertionsAndBenchmarks) { + if (assertionOrBenchmark.isAssertion()) { + writeAssertion(assertionOrBenchmark.asAssertion()); + } + } + } + + void JunitReporter::writeAssertion( AssertionStats const& stats ) { + AssertionResult const& result = stats.assertionResult; + if( !result.isOk() ) { + std::string elementName; + switch( result.getResultType() ) { + case ResultWas::ThrewException: + case ResultWas::FatalErrorCondition: + elementName = "error"; + break; + case ResultWas::ExplicitFailure: + case ResultWas::ExpressionFailed: + case ResultWas::DidntThrowException: + elementName = "failure"; + break; + + // We should never see these here: + case ResultWas::Info: + case ResultWas::Warning: + case ResultWas::Ok: + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + elementName = "internalError"; + break; + } + + XmlWriter::ScopedElement e = xml.scopedElement( elementName ); + + xml.writeAttribute( "message"_sr, result.getExpression() ); + xml.writeAttribute( "type"_sr, result.getTestMacroName() ); + + ReusableStringStream rss; + if (stats.totals.assertions.total() > 0) { + rss << "FAILED" << ":\n"; + if (result.hasExpression()) { + rss << " "; + rss << result.getExpressionInMacro(); + rss << '\n'; + } + if (result.hasExpandedExpression()) { + rss << "with expansion:\n"; + rss << TextFlow::Column(result.getExpandedExpression()).indent(2) << '\n'; + } + } else { + rss << '\n'; + } + + if( !result.getMessage().empty() ) + rss << result.getMessage() << '\n'; + for( auto const& msg : stats.infoMessages ) + if( msg.type == ResultWas::Info ) + rss << msg.message << '\n'; + + rss << "at " << result.getSourceInfo(); + xml.writeText( rss.str(), XmlFormatting::Newline ); + } + } + +} // end namespace Catch + + + + +#include + +namespace Catch { + void MultiReporter::updatePreferences(IEventListener const& reporterish) { + m_preferences.shouldRedirectStdOut |= + reporterish.getPreferences().shouldRedirectStdOut; + m_preferences.shouldReportAllAssertions |= + reporterish.getPreferences().shouldReportAllAssertions; + } + + void MultiReporter::addListener( IEventListenerPtr&& listener ) { + updatePreferences(*listener); + m_reporterLikes.insert(m_reporterLikes.begin() + m_insertedListeners, CATCH_MOVE(listener) ); + ++m_insertedListeners; + } + + void MultiReporter::addReporter( IEventListenerPtr&& reporter ) { + updatePreferences(*reporter); + + // We will need to output the captured stdout if there are reporters + // that do not want it captured. + // We do not consider listeners, because it is generally assumed that + // listeners are output-transparent, even though they can ask for stdout + // capture to do something with it. + m_haveNoncapturingReporters |= !reporter->getPreferences().shouldRedirectStdOut; + + // Reporters can always be placed to the back without breaking the + // reporting order + m_reporterLikes.push_back( CATCH_MOVE( reporter ) ); + } + + void MultiReporter::noMatchingTestCases( StringRef unmatchedSpec ) { + for ( auto& reporterish : m_reporterLikes ) { + reporterish->noMatchingTestCases( unmatchedSpec ); + } + } + + void MultiReporter::fatalErrorEncountered( StringRef error ) { + for ( auto& reporterish : m_reporterLikes ) { + reporterish->fatalErrorEncountered( error ); + } + } + + void MultiReporter::reportInvalidTestSpec( StringRef arg ) { + for ( auto& reporterish : m_reporterLikes ) { + reporterish->reportInvalidTestSpec( arg ); + } + } + + void MultiReporter::benchmarkPreparing( StringRef name ) { + for (auto& reporterish : m_reporterLikes) { + reporterish->benchmarkPreparing(name); + } + } + void MultiReporter::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) { + for ( auto& reporterish : m_reporterLikes ) { + reporterish->benchmarkStarting( benchmarkInfo ); + } + } + void MultiReporter::benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) { + for ( auto& reporterish : m_reporterLikes ) { + reporterish->benchmarkEnded( benchmarkStats ); + } + } + + void MultiReporter::benchmarkFailed( StringRef error ) { + for (auto& reporterish : m_reporterLikes) { + reporterish->benchmarkFailed(error); + } + } + + void MultiReporter::testRunStarting( TestRunInfo const& testRunInfo ) { + for ( auto& reporterish : m_reporterLikes ) { + reporterish->testRunStarting( testRunInfo ); + } + } + + void MultiReporter::testCaseStarting( TestCaseInfo const& testInfo ) { + for ( auto& reporterish : m_reporterLikes ) { + reporterish->testCaseStarting( testInfo ); + } + } + + void + MultiReporter::testCasePartialStarting( TestCaseInfo const& testInfo, + uint64_t partNumber ) { + for ( auto& reporterish : m_reporterLikes ) { + reporterish->testCasePartialStarting( testInfo, partNumber ); + } + } + + void MultiReporter::sectionStarting( SectionInfo const& sectionInfo ) { + for ( auto& reporterish : m_reporterLikes ) { + reporterish->sectionStarting( sectionInfo ); + } + } + + void MultiReporter::assertionStarting( AssertionInfo const& assertionInfo ) { + for ( auto& reporterish : m_reporterLikes ) { + reporterish->assertionStarting( assertionInfo ); + } + } + + // The return value indicates if the messages buffer should be cleared: + void MultiReporter::assertionEnded( AssertionStats const& assertionStats ) { + const bool reportByDefault = + assertionStats.assertionResult.getResultType() != ResultWas::Ok || + m_config->includeSuccessfulResults(); + + for ( auto & reporterish : m_reporterLikes ) { + if ( reportByDefault || + reporterish->getPreferences().shouldReportAllAssertions ) { + reporterish->assertionEnded( assertionStats ); + } + } + } + + void MultiReporter::sectionEnded( SectionStats const& sectionStats ) { + for ( auto& reporterish : m_reporterLikes ) { + reporterish->sectionEnded( sectionStats ); + } + } + + void MultiReporter::testCasePartialEnded( TestCaseStats const& testStats, + uint64_t partNumber ) { + if ( m_preferences.shouldRedirectStdOut && + m_haveNoncapturingReporters ) { + if ( !testStats.stdOut.empty() ) { + Catch::cout() << testStats.stdOut << std::flush; + } + if ( !testStats.stdErr.empty() ) { + Catch::cerr() << testStats.stdErr << std::flush; + } + } + + for ( auto& reporterish : m_reporterLikes ) { + reporterish->testCasePartialEnded( testStats, partNumber ); + } + } + + void MultiReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { + for ( auto& reporterish : m_reporterLikes ) { + reporterish->testCaseEnded( testCaseStats ); + } + } + + void MultiReporter::testRunEnded( TestRunStats const& testRunStats ) { + for ( auto& reporterish : m_reporterLikes ) { + reporterish->testRunEnded( testRunStats ); + } + } + + + void MultiReporter::skipTest( TestCaseInfo const& testInfo ) { + for ( auto& reporterish : m_reporterLikes ) { + reporterish->skipTest( testInfo ); + } + } + + void MultiReporter::listReporters(std::vector const& descriptions) { + for (auto& reporterish : m_reporterLikes) { + reporterish->listReporters(descriptions); + } + } + + void MultiReporter::listListeners( + std::vector const& descriptions ) { + for ( auto& reporterish : m_reporterLikes ) { + reporterish->listListeners( descriptions ); + } + } + + void MultiReporter::listTests(std::vector const& tests) { + for (auto& reporterish : m_reporterLikes) { + reporterish->listTests(tests); + } + } + + void MultiReporter::listTags(std::vector const& tags) { + for (auto& reporterish : m_reporterLikes) { + reporterish->listTags(tags); + } + } + +} // end namespace Catch + + + + + +namespace Catch { + namespace Detail { + + void registerReporterImpl( std::string const& name, + IReporterFactoryPtr reporterPtr ) { + CATCH_TRY { + getMutableRegistryHub().registerReporter( + name, CATCH_MOVE( reporterPtr ) ); + } + CATCH_CATCH_ALL { + // Do not throw when constructing global objects, instead + // register the exception to be processed later + getMutableRegistryHub().registerStartupException(); + } + } + + } // namespace Detail +} // namespace Catch + + + + +#include + +namespace Catch { + + namespace { + std::string createRngSeedString(uint32_t seed) { + ReusableStringStream sstr; + sstr << "rng-seed=" << seed; + return sstr.str(); + } + } + + void SonarQubeReporter::testRunStarting(TestRunInfo const& testRunInfo) { + CumulativeReporterBase::testRunStarting(testRunInfo); + + xml.writeComment( createRngSeedString( m_config->rngSeed() ) ); + xml.startElement("testExecutions"); + xml.writeAttribute("version"_sr, '1'); + } + + void SonarQubeReporter::writeRun( TestRunNode const& runNode ) { + std::map> testsPerFile; + + for ( auto const& child : runNode.children ) { + testsPerFile[child->value.testInfo->lineInfo.file].push_back( + child.get() ); + } + + for ( auto const& kv : testsPerFile ) { + writeTestFile( kv.first, kv.second ); + } + } + + void SonarQubeReporter::writeTestFile(std::string const& filename, std::vector const& testCaseNodes) { + XmlWriter::ScopedElement e = xml.scopedElement("file"); + xml.writeAttribute("path"_sr, filename); + + for (auto const& child : testCaseNodes) + writeTestCase(*child); + } + + void SonarQubeReporter::writeTestCase(TestCaseNode const& testCaseNode) { + // All test cases have exactly one section - which represents the + // test case itself. That section may have 0-n nested sections + assert(testCaseNode.children.size() == 1); + SectionNode const& rootSection = *testCaseNode.children.front(); + writeSection("", rootSection, testCaseNode.value.testInfo->okToFail()); + } + + void SonarQubeReporter::writeSection(std::string const& rootName, SectionNode const& sectionNode, bool okToFail) { + std::string name = trim(sectionNode.stats.sectionInfo.name); + if (!rootName.empty()) + name = rootName + '/' + name; + + if ( sectionNode.hasAnyAssertions() + || !sectionNode.stdOut.empty() + || !sectionNode.stdErr.empty() ) { + XmlWriter::ScopedElement e = xml.scopedElement("testCase"); + xml.writeAttribute("name"_sr, name); + xml.writeAttribute("duration"_sr, static_cast(sectionNode.stats.durationInSeconds * 1000)); + + writeAssertions(sectionNode, okToFail); + } + + for (auto const& childNode : sectionNode.childSections) + writeSection(name, *childNode, okToFail); + } + + void SonarQubeReporter::writeAssertions(SectionNode const& sectionNode, bool okToFail) { + for (auto const& assertionOrBenchmark : sectionNode.assertionsAndBenchmarks) { + if (assertionOrBenchmark.isAssertion()) { + writeAssertion(assertionOrBenchmark.asAssertion(), okToFail); + } + } + } + + void SonarQubeReporter::writeAssertion(AssertionStats const& stats, bool okToFail) { + AssertionResult const& result = stats.assertionResult; + if (!result.isOk()) { + std::string elementName; + if (okToFail) { + elementName = "skipped"; + } else { + switch (result.getResultType()) { + case ResultWas::ThrewException: + case ResultWas::FatalErrorCondition: + elementName = "error"; + break; + case ResultWas::ExplicitFailure: + elementName = "failure"; + break; + case ResultWas::ExpressionFailed: + elementName = "failure"; + break; + case ResultWas::DidntThrowException: + elementName = "failure"; + break; + + // We should never see these here: + case ResultWas::Info: + case ResultWas::Warning: + case ResultWas::Ok: + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + elementName = "internalError"; + break; + } + } + + XmlWriter::ScopedElement e = xml.scopedElement(elementName); + + ReusableStringStream messageRss; + messageRss << result.getTestMacroName() << '(' << result.getExpression() << ')'; + xml.writeAttribute("message"_sr, messageRss.str()); + + ReusableStringStream textRss; + if (stats.totals.assertions.total() > 0) { + textRss << "FAILED:\n"; + if (result.hasExpression()) { + textRss << '\t' << result.getExpressionInMacro() << '\n'; + } + if (result.hasExpandedExpression()) { + textRss << "with expansion:\n\t" << result.getExpandedExpression() << '\n'; + } + } + + if (!result.getMessage().empty()) + textRss << result.getMessage() << '\n'; + + for (auto const& msg : stats.infoMessages) + if (msg.type == ResultWas::Info) + textRss << msg.message << '\n'; + + textRss << "at " << result.getSourceInfo(); + xml.writeText(textRss.str(), XmlFormatting::Newline); + } + } + +} // end namespace Catch + + + +namespace Catch { + + StreamingReporterBase::~StreamingReporterBase() = default; + + void + StreamingReporterBase::testRunStarting( TestRunInfo const& _testRunInfo ) { + currentTestRunInfo = _testRunInfo; + } + + void StreamingReporterBase::testRunEnded( TestRunStats const& ) { + currentTestCaseInfo = nullptr; + } + +} // end namespace Catch + + + +#include +#include +#include + +namespace Catch { + + namespace { + // Yes, this has to be outside the class and namespaced by naming. + // Making older compiler happy is hard. + static constexpr StringRef tapFailedString = "not ok"_sr; + static constexpr StringRef tapPassedString = "ok"_sr; + static constexpr Colour::Code tapDimColour = Colour::FileName; + + class TapAssertionPrinter { + public: + TapAssertionPrinter& operator= (TapAssertionPrinter const&) = delete; + TapAssertionPrinter(TapAssertionPrinter const&) = delete; + TapAssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, std::size_t _counter, ColourImpl* colour_) + : stream(_stream) + , result(_stats.assertionResult) + , messages(_stats.infoMessages) + , itMessage(_stats.infoMessages.begin()) + , printInfoMessages(true) + , counter(_counter) + , colourImpl( colour_ ) {} + + void print() { + itMessage = messages.begin(); + + switch (result.getResultType()) { + case ResultWas::Ok: + printResultType(tapPassedString); + printOriginalExpression(); + printReconstructedExpression(); + if (!result.hasExpression()) + printRemainingMessages(Colour::None); + else + printRemainingMessages(); + break; + case ResultWas::ExpressionFailed: + if (result.isOk()) { + printResultType(tapPassedString); + } else { + printResultType(tapFailedString); + } + printOriginalExpression(); + printReconstructedExpression(); + if (result.isOk()) { + printIssue(" # TODO"); + } + printRemainingMessages(); + break; + case ResultWas::ThrewException: + printResultType(tapFailedString); + printIssue("unexpected exception with message:"_sr); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::FatalErrorCondition: + printResultType(tapFailedString); + printIssue("fatal error condition with message:"_sr); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::DidntThrowException: + printResultType(tapFailedString); + printIssue("expected exception, got none"_sr); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::Info: + printResultType("info"_sr); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::Warning: + printResultType("warning"_sr); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::ExplicitFailure: + printResultType(tapFailedString); + printIssue("explicitly"_sr); + printRemainingMessages(Colour::None); + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + printResultType("** internal error **"_sr); + break; + } + } + + private: + void printResultType(StringRef passOrFail) const { + if (!passOrFail.empty()) { + stream << passOrFail << ' ' << counter << " -"; + } + } + + void printIssue(StringRef issue) const { + stream << ' ' << issue; + } + + void printExpressionWas() { + if (result.hasExpression()) { + stream << ';'; + stream << colourImpl->guardColour( tapDimColour ) + << " expression was:"; + printOriginalExpression(); + } + } + + void printOriginalExpression() const { + if (result.hasExpression()) { + stream << ' ' << result.getExpression(); + } + } + + void printReconstructedExpression() const { + if (result.hasExpandedExpression()) { + stream << colourImpl->guardColour( tapDimColour ) << " for: "; + + std::string expr = result.getExpandedExpression(); + std::replace(expr.begin(), expr.end(), '\n', ' '); + stream << expr; + } + } + + void printMessage() { + if (itMessage != messages.end()) { + stream << " '" << itMessage->message << '\''; + ++itMessage; + } + } + + void printRemainingMessages(Colour::Code colour = tapDimColour) { + if (itMessage == messages.end()) { + return; + } + + // using messages.end() directly (or auto) yields compilation error: + std::vector::const_iterator itEnd = messages.end(); + const std::size_t N = static_cast(std::distance(itMessage, itEnd)); + + stream << colourImpl->guardColour( colour ) << " with " + << pluralise( N, "message"_sr ) << ':'; + + for (; itMessage != itEnd; ) { + // If this assertion is a warning ignore any INFO messages + if (printInfoMessages || itMessage->type != ResultWas::Info) { + stream << " '" << itMessage->message << '\''; + if (++itMessage != itEnd) { + stream << colourImpl->guardColour(tapDimColour) << " and"; + } + } + } + } + + private: + std::ostream& stream; + AssertionResult const& result; + std::vector messages; + std::vector::const_iterator itMessage; + bool printInfoMessages; + std::size_t counter; + ColourImpl* colourImpl; + }; + + } // End anonymous namespace + + void TAPReporter::testRunStarting( TestRunInfo const& ) { + m_stream << "# rng-seed: " << m_config->rngSeed() << '\n'; + } + + void TAPReporter::noMatchingTestCases( StringRef unmatchedSpec ) { + m_stream << "# No test cases matched '" << unmatchedSpec << "'\n"; + } + + void TAPReporter::assertionEnded(AssertionStats const& _assertionStats) { + ++counter; + + m_stream << "# " << currentTestCaseInfo->name << '\n'; + TapAssertionPrinter printer(m_stream, _assertionStats, counter, m_colour.get()); + printer.print(); + + m_stream << '\n' << std::flush; + } + + void TAPReporter::testRunEnded(TestRunStats const& _testRunStats) { + m_stream << "1.." << _testRunStats.totals.assertions.total(); + if (_testRunStats.totals.testCases.total() == 0) { + m_stream << " # Skipped: No tests ran."; + } + m_stream << "\n\n" << std::flush; + StreamingReporterBase::testRunEnded(_testRunStats); + } + + + + +} // end namespace Catch + + + + +#include +#include + +namespace Catch { + + namespace { + // if string has a : in first line will set indent to follow it on + // subsequent lines + void printHeaderString(std::ostream& os, std::string const& _string, std::size_t indent = 0) { + std::size_t i = _string.find(": "); + if (i != std::string::npos) + i += 2; + else + i = 0; + os << TextFlow::Column(_string) + .indent(indent + i) + .initialIndent(indent) << '\n'; + } + + std::string escape(StringRef str) { + std::string escaped = static_cast(str); + replaceInPlace(escaped, "|", "||"); + replaceInPlace(escaped, "'", "|'"); + replaceInPlace(escaped, "\n", "|n"); + replaceInPlace(escaped, "\r", "|r"); + replaceInPlace(escaped, "[", "|["); + replaceInPlace(escaped, "]", "|]"); + return escaped; + } + } // end anonymous namespace + + + TeamCityReporter::~TeamCityReporter() {} + + void TeamCityReporter::testRunStarting( TestRunInfo const& runInfo ) { + m_stream << "##teamcity[testSuiteStarted name='" << escape( runInfo.name ) + << "']\n"; + } + + void TeamCityReporter::testRunEnded( TestRunStats const& runStats ) { + m_stream << "##teamcity[testSuiteFinished name='" + << escape( runStats.runInfo.name ) << "']\n"; + } + + void TeamCityReporter::assertionEnded(AssertionStats const& assertionStats) { + AssertionResult const& result = assertionStats.assertionResult; + if (!result.isOk()) { + + ReusableStringStream msg; + if (!m_headerPrintedForThisSection) + printSectionHeader(msg.get()); + m_headerPrintedForThisSection = true; + + msg << result.getSourceInfo() << '\n'; + + switch (result.getResultType()) { + case ResultWas::ExpressionFailed: + msg << "expression failed"; + break; + case ResultWas::ThrewException: + msg << "unexpected exception"; + break; + case ResultWas::FatalErrorCondition: + msg << "fatal error condition"; + break; + case ResultWas::DidntThrowException: + msg << "no exception was thrown where one was expected"; + break; + case ResultWas::ExplicitFailure: + msg << "explicit failure"; + break; + + // We shouldn't get here because of the isOk() test + case ResultWas::Ok: + case ResultWas::Info: + case ResultWas::Warning: + CATCH_ERROR("Internal error in TeamCity reporter"); + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + CATCH_ERROR("Not implemented"); + } + if (assertionStats.infoMessages.size() == 1) + msg << " with message:"; + if (assertionStats.infoMessages.size() > 1) + msg << " with messages:"; + for (auto const& messageInfo : assertionStats.infoMessages) + msg << "\n \"" << messageInfo.message << '"'; + + + if (result.hasExpression()) { + msg << + "\n " << result.getExpressionInMacro() << "\n" + "with expansion:\n" + " " << result.getExpandedExpression() << '\n'; + } + + if (currentTestCaseInfo->okToFail()) { + msg << "- failure ignore as test marked as 'ok to fail'\n"; + m_stream << "##teamcity[testIgnored" + << " name='" << escape(currentTestCaseInfo->name) << '\'' + << " message='" << escape(msg.str()) << '\'' + << "]\n"; + } else { + m_stream << "##teamcity[testFailed" + << " name='" << escape(currentTestCaseInfo->name) << '\'' + << " message='" << escape(msg.str()) << '\'' + << "]\n"; + } + } + m_stream.flush(); + } + + void TeamCityReporter::testCaseStarting(TestCaseInfo const& testInfo) { + m_testTimer.start(); + StreamingReporterBase::testCaseStarting(testInfo); + m_stream << "##teamcity[testStarted name='" + << escape(testInfo.name) << "']\n"; + m_stream.flush(); + } + + void TeamCityReporter::testCaseEnded(TestCaseStats const& testCaseStats) { + StreamingReporterBase::testCaseEnded(testCaseStats); + auto const& testCaseInfo = *testCaseStats.testInfo; + if (!testCaseStats.stdOut.empty()) + m_stream << "##teamcity[testStdOut name='" + << escape(testCaseInfo.name) + << "' out='" << escape(testCaseStats.stdOut) << "']\n"; + if (!testCaseStats.stdErr.empty()) + m_stream << "##teamcity[testStdErr name='" + << escape(testCaseInfo.name) + << "' out='" << escape(testCaseStats.stdErr) << "']\n"; + m_stream << "##teamcity[testFinished name='" + << escape(testCaseInfo.name) << "' duration='" + << m_testTimer.getElapsedMilliseconds() << "']\n"; + m_stream.flush(); + } + + void TeamCityReporter::printSectionHeader(std::ostream& os) { + assert(!m_sectionStack.empty()); + + if (m_sectionStack.size() > 1) { + os << lineOfChars('-') << '\n'; + + std::vector::const_iterator + it = m_sectionStack.begin() + 1, // Skip first section (test case) + itEnd = m_sectionStack.end(); + for (; it != itEnd; ++it) + printHeaderString(os, it->name); + os << lineOfChars('-') << '\n'; + } + + SourceLineInfo lineInfo = m_sectionStack.front().lineInfo; + + os << lineInfo << '\n'; + os << lineOfChars('.') << "\n\n"; + } + +} // end namespace Catch + + + + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch + // Note that 4062 (not all labels are handled + // and default is missing) is enabled +#endif + +namespace Catch { + XmlReporter::XmlReporter( ReporterConfig&& _config ) + : StreamingReporterBase( CATCH_MOVE(_config) ), + m_xml(m_stream) + { + m_preferences.shouldRedirectStdOut = true; + m_preferences.shouldReportAllAssertions = true; + } + + XmlReporter::~XmlReporter() = default; + + std::string XmlReporter::getDescription() { + return "Reports test results as an XML document"; + } + + std::string XmlReporter::getStylesheetRef() const { + return std::string(); + } + + void XmlReporter::writeSourceInfo( SourceLineInfo const& sourceInfo ) { + m_xml + .writeAttribute( "filename"_sr, sourceInfo.file ) + .writeAttribute( "line"_sr, sourceInfo.line ); + } + + void XmlReporter::testRunStarting( TestRunInfo const& testInfo ) { + StreamingReporterBase::testRunStarting( testInfo ); + std::string stylesheetRef = getStylesheetRef(); + if( !stylesheetRef.empty() ) + m_xml.writeStylesheetRef( stylesheetRef ); + m_xml.startElement("Catch2TestRun") + .writeAttribute("name"_sr, m_config->name()) + .writeAttribute("rng-seed"_sr, m_config->rngSeed()) + .writeAttribute("catch2-version"_sr, libraryVersion()); + if (m_config->testSpec().hasFilters()) + m_xml.writeAttribute( "filters"_sr, serializeFilters( m_config->getTestsOrTags() ) ); + } + + void XmlReporter::testCaseStarting( TestCaseInfo const& testInfo ) { + StreamingReporterBase::testCaseStarting(testInfo); + m_xml.startElement( "TestCase" ) + .writeAttribute( "name"_sr, trim( testInfo.name ) ) + .writeAttribute( "tags"_sr, testInfo.tagsAsString() ); + + writeSourceInfo( testInfo.lineInfo ); + + if ( m_config->showDurations() == ShowDurations::Always ) + m_testCaseTimer.start(); + m_xml.ensureTagClosed(); + } + + void XmlReporter::sectionStarting( SectionInfo const& sectionInfo ) { + StreamingReporterBase::sectionStarting( sectionInfo ); + if( m_sectionDepth++ > 0 ) { + m_xml.startElement( "Section" ) + .writeAttribute( "name"_sr, trim( sectionInfo.name ) ); + writeSourceInfo( sectionInfo.lineInfo ); + m_xml.ensureTagClosed(); + } + } + + void XmlReporter::assertionStarting( AssertionInfo const& ) { } + + void XmlReporter::assertionEnded( AssertionStats const& assertionStats ) { + + AssertionResult const& result = assertionStats.assertionResult; + + bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); + + if( includeResults || result.getResultType() == ResultWas::Warning ) { + // Print any info messages in tags. + for( auto const& msg : assertionStats.infoMessages ) { + if( msg.type == ResultWas::Info && includeResults ) { + m_xml.scopedElement( "Info" ) + .writeText( msg.message ); + } else if ( msg.type == ResultWas::Warning ) { + m_xml.scopedElement( "Warning" ) + .writeText( msg.message ); + } + } + } + + // Drop out if result was successful but we're not printing them. + if( !includeResults && result.getResultType() != ResultWas::Warning ) + return; + + + // Print the expression if there is one. + if( result.hasExpression() ) { + m_xml.startElement( "Expression" ) + .writeAttribute( "success"_sr, result.succeeded() ) + .writeAttribute( "type"_sr, result.getTestMacroName() ); + + writeSourceInfo( result.getSourceInfo() ); + + m_xml.scopedElement( "Original" ) + .writeText( result.getExpression() ); + m_xml.scopedElement( "Expanded" ) + .writeText( result.getExpandedExpression() ); + } + + // And... Print a result applicable to each result type. + switch( result.getResultType() ) { + case ResultWas::ThrewException: + m_xml.startElement( "Exception" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); + break; + case ResultWas::FatalErrorCondition: + m_xml.startElement( "FatalErrorCondition" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); + break; + case ResultWas::Info: + m_xml.scopedElement( "Info" ) + .writeText( result.getMessage() ); + break; + case ResultWas::Warning: + // Warning will already have been written + break; + case ResultWas::ExplicitFailure: + m_xml.startElement( "Failure" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); + break; + default: + break; + } + + if( result.hasExpression() ) + m_xml.endElement(); + } + + void XmlReporter::sectionEnded( SectionStats const& sectionStats ) { + StreamingReporterBase::sectionEnded( sectionStats ); + if( --m_sectionDepth > 0 ) { + XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" ); + e.writeAttribute( "successes"_sr, sectionStats.assertions.passed ); + e.writeAttribute( "failures"_sr, sectionStats.assertions.failed ); + e.writeAttribute( "expectedFailures"_sr, sectionStats.assertions.failedButOk ); + + if ( m_config->showDurations() == ShowDurations::Always ) + e.writeAttribute( "durationInSeconds"_sr, sectionStats.durationInSeconds ); + + m_xml.endElement(); + } + } + + void XmlReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { + StreamingReporterBase::testCaseEnded( testCaseStats ); + XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" ); + e.writeAttribute( "success"_sr, testCaseStats.totals.assertions.allOk() ); + + if ( m_config->showDurations() == ShowDurations::Always ) + e.writeAttribute( "durationInSeconds"_sr, m_testCaseTimer.getElapsedSeconds() ); + + if( !testCaseStats.stdOut.empty() ) + m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), XmlFormatting::Newline ); + if( !testCaseStats.stdErr.empty() ) + m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), XmlFormatting::Newline ); + + m_xml.endElement(); + } + + void XmlReporter::testRunEnded( TestRunStats const& testRunStats ) { + StreamingReporterBase::testRunEnded( testRunStats ); + m_xml.scopedElement( "OverallResults" ) + .writeAttribute( "successes"_sr, testRunStats.totals.assertions.passed ) + .writeAttribute( "failures"_sr, testRunStats.totals.assertions.failed ) + .writeAttribute( "expectedFailures"_sr, testRunStats.totals.assertions.failedButOk ); + m_xml.scopedElement( "OverallResultsCases") + .writeAttribute( "successes"_sr, testRunStats.totals.testCases.passed ) + .writeAttribute( "failures"_sr, testRunStats.totals.testCases.failed ) + .writeAttribute( "expectedFailures"_sr, testRunStats.totals.testCases.failedButOk ); + m_xml.endElement(); + } + + void XmlReporter::benchmarkPreparing( StringRef name ) { + m_xml.startElement("BenchmarkResults") + .writeAttribute("name"_sr, name); + } + + void XmlReporter::benchmarkStarting(BenchmarkInfo const &info) { + m_xml.writeAttribute("samples"_sr, info.samples) + .writeAttribute("resamples"_sr, info.resamples) + .writeAttribute("iterations"_sr, info.iterations) + .writeAttribute("clockResolution"_sr, info.clockResolution) + .writeAttribute("estimatedDuration"_sr, info.estimatedDuration) + .writeComment("All values in nano seconds"_sr); + } + + void XmlReporter::benchmarkEnded(BenchmarkStats<> const& benchmarkStats) { + m_xml.startElement("mean") + .writeAttribute("value"_sr, benchmarkStats.mean.point.count()) + .writeAttribute("lowerBound"_sr, benchmarkStats.mean.lower_bound.count()) + .writeAttribute("upperBound"_sr, benchmarkStats.mean.upper_bound.count()) + .writeAttribute("ci"_sr, benchmarkStats.mean.confidence_interval); + m_xml.endElement(); + m_xml.startElement("standardDeviation") + .writeAttribute("value"_sr, benchmarkStats.standardDeviation.point.count()) + .writeAttribute("lowerBound"_sr, benchmarkStats.standardDeviation.lower_bound.count()) + .writeAttribute("upperBound"_sr, benchmarkStats.standardDeviation.upper_bound.count()) + .writeAttribute("ci"_sr, benchmarkStats.standardDeviation.confidence_interval); + m_xml.endElement(); + m_xml.startElement("outliers") + .writeAttribute("variance"_sr, benchmarkStats.outlierVariance) + .writeAttribute("lowMild"_sr, benchmarkStats.outliers.low_mild) + .writeAttribute("lowSevere"_sr, benchmarkStats.outliers.low_severe) + .writeAttribute("highMild"_sr, benchmarkStats.outliers.high_mild) + .writeAttribute("highSevere"_sr, benchmarkStats.outliers.high_severe); + m_xml.endElement(); + m_xml.endElement(); + } + + void XmlReporter::benchmarkFailed(StringRef error) { + m_xml.scopedElement("failed"). + writeAttribute("message"_sr, error); + m_xml.endElement(); + } + + void XmlReporter::listReporters(std::vector const& descriptions) { + auto outerTag = m_xml.scopedElement("AvailableReporters"); + for (auto const& reporter : descriptions) { + auto inner = m_xml.scopedElement("Reporter"); + m_xml.startElement("Name", XmlFormatting::Indent) + .writeText(reporter.name, XmlFormatting::None) + .endElement(XmlFormatting::Newline); + m_xml.startElement("Description", XmlFormatting::Indent) + .writeText(reporter.description, XmlFormatting::None) + .endElement(XmlFormatting::Newline); + } + } + + void XmlReporter::listListeners(std::vector const& descriptions) { + auto outerTag = m_xml.scopedElement( "RegisteredListeners" ); + for ( auto const& listener : descriptions ) { + auto inner = m_xml.scopedElement( "Listener" ); + m_xml.startElement( "Name", XmlFormatting::Indent ) + .writeText( listener.name, XmlFormatting::None ) + .endElement( XmlFormatting::Newline ); + m_xml.startElement( "Description", XmlFormatting::Indent ) + .writeText( listener.description, XmlFormatting::None ) + .endElement( XmlFormatting::Newline ); + } + } + + void XmlReporter::listTests(std::vector const& tests) { + auto outerTag = m_xml.scopedElement("MatchingTests"); + for (auto const& test : tests) { + auto innerTag = m_xml.scopedElement("TestCase"); + auto const& testInfo = test.getTestCaseInfo(); + m_xml.startElement("Name", XmlFormatting::Indent) + .writeText(testInfo.name, XmlFormatting::None) + .endElement(XmlFormatting::Newline); + m_xml.startElement("ClassName", XmlFormatting::Indent) + .writeText(testInfo.className, XmlFormatting::None) + .endElement(XmlFormatting::Newline); + m_xml.startElement("Tags", XmlFormatting::Indent) + .writeText(testInfo.tagsAsString(), XmlFormatting::None) + .endElement(XmlFormatting::Newline); + + auto sourceTag = m_xml.scopedElement("SourceInfo"); + m_xml.startElement("File", XmlFormatting::Indent) + .writeText(testInfo.lineInfo.file, XmlFormatting::None) + .endElement(XmlFormatting::Newline); + m_xml.startElement("Line", XmlFormatting::Indent) + .writeText(std::to_string(testInfo.lineInfo.line), XmlFormatting::None) + .endElement(XmlFormatting::Newline); + } + } + + void XmlReporter::listTags(std::vector const& tags) { + auto outerTag = m_xml.scopedElement("TagsFromMatchingTests"); + for (auto const& tag : tags) { + auto innerTag = m_xml.scopedElement("Tag"); + m_xml.startElement("Count", XmlFormatting::Indent) + .writeText(std::to_string(tag.count), XmlFormatting::None) + .endElement(XmlFormatting::Newline); + auto aliasTag = m_xml.scopedElement("Aliases"); + for (auto const& alias : tag.spellings) { + m_xml.startElement("Alias", XmlFormatting::Indent) + .writeText(alias, XmlFormatting::None) + .endElement(XmlFormatting::Newline); + } + } + } + +} // end namespace Catch + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif diff --git a/Tests/Contrib/Catch/catch_amalgamated.hpp b/Tests/Contrib/Catch/catch_amalgamated.hpp new file mode 100644 index 00000000..cff2cedf --- /dev/null +++ b/Tests/Contrib/Catch/catch_amalgamated.hpp @@ -0,0 +1,12260 @@ +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + +// Catch v3.1.1 +// Generated: 2022-10-17 18:47:20.510385 +// ---------------------------------------------------------- +// This file is an amalgamation of multiple different files. +// You probably shouldn't edit it directly. +// ---------------------------------------------------------- +#ifndef CATCH_AMALGAMATED_HPP_INCLUDED +#define CATCH_AMALGAMATED_HPP_INCLUDED + + +/** \file + * This is a convenience header for Catch2. It includes **all** of Catch2 headers. + * + * Generally the Catch2 users should use specific includes they need, + * but this header can be used instead for ease-of-experimentation, or + * just plain convenience, at the cost of (significantly) increased + * compilation times. + * + * When a new header is added to either the top level folder, or to the + * corresponding internal subfolder, it should be added here. Headers + * added to the various subparts (e.g. matchers, generators, etc...), + * should go their respective catch-all headers. + */ + +#ifndef CATCH_ALL_HPP_INCLUDED +#define CATCH_ALL_HPP_INCLUDED + + + +/** \file + * This is a convenience header for Catch2's benchmarking. It includes + * **all** of Catch2 headers related to benchmarking. + * + * Generally the Catch2 users should use specific includes they need, + * but this header can be used instead for ease-of-experimentation, or + * just plain convenience, at the cost of (significantly) increased + * compilation times. + * + * When a new header is added to either the `benchmark` folder, or to + * the corresponding internal (detail) subfolder, it should be added here. + */ + +#ifndef CATCH_BENCHMARK_ALL_HPP_INCLUDED +#define CATCH_BENCHMARK_ALL_HPP_INCLUDED + + + +// Adapted from donated nonius code. + +#ifndef CATCH_BENCHMARK_HPP_INCLUDED +#define CATCH_BENCHMARK_HPP_INCLUDED + + + +#ifndef CATCH_INTERFACES_CONFIG_HPP_INCLUDED +#define CATCH_INTERFACES_CONFIG_HPP_INCLUDED + + + +#ifndef CATCH_NONCOPYABLE_HPP_INCLUDED +#define CATCH_NONCOPYABLE_HPP_INCLUDED + +namespace Catch { + namespace Detail { + + //! Deriving classes become noncopyable and nonmovable + class NonCopyable { + NonCopyable( NonCopyable const& ) = delete; + NonCopyable( NonCopyable&& ) = delete; + NonCopyable& operator=( NonCopyable const& ) = delete; + NonCopyable& operator=( NonCopyable&& ) = delete; + + protected: + NonCopyable() noexcept = default; + }; + + } // namespace Detail +} // namespace Catch + +#endif // CATCH_NONCOPYABLE_HPP_INCLUDED + + +#ifndef CATCH_STRINGREF_HPP_INCLUDED +#define CATCH_STRINGREF_HPP_INCLUDED + +#include +#include +#include +#include + +namespace Catch { + + /// A non-owning string class (similar to the forthcoming std::string_view) + /// Note that, because a StringRef may be a substring of another string, + /// it may not be null terminated. + class StringRef { + public: + using size_type = std::size_t; + using const_iterator = const char*; + + private: + static constexpr char const* const s_empty = ""; + + char const* m_start = s_empty; + size_type m_size = 0; + + public: // construction + constexpr StringRef() noexcept = default; + + StringRef( char const* rawChars ) noexcept; + + constexpr StringRef( char const* rawChars, size_type size ) noexcept + : m_start( rawChars ), + m_size( size ) + {} + + StringRef( std::string const& stdString ) noexcept + : m_start( stdString.c_str() ), + m_size( stdString.size() ) + {} + + explicit operator std::string() const { + return std::string(m_start, m_size); + } + + public: // operators + auto operator == ( StringRef other ) const noexcept -> bool; + auto operator != (StringRef other) const noexcept -> bool { + return !(*this == other); + } + + constexpr auto operator[] ( size_type index ) const noexcept -> char { + assert(index < m_size); + return m_start[index]; + } + + bool operator<(StringRef rhs) const noexcept; + + public: // named queries + constexpr auto empty() const noexcept -> bool { + return m_size == 0; + } + constexpr auto size() const noexcept -> size_type { + return m_size; + } + + // Returns a substring of [start, start + length). + // If start + length > size(), then the substring is [start, start + size()). + // If start > size(), then the substring is empty. + constexpr StringRef substr(size_type start, size_type length) const noexcept { + if (start < m_size) { + const auto shortened_size = m_size - start; + return StringRef(m_start + start, (shortened_size < length) ? shortened_size : length); + } else { + return StringRef(); + } + } + + // Returns the current start pointer. May not be null-terminated. + constexpr char const* data() const noexcept { + return m_start; + } + + constexpr const_iterator begin() const { return m_start; } + constexpr const_iterator end() const { return m_start + m_size; } + + + friend std::string& operator += (std::string& lhs, StringRef sr); + friend std::ostream& operator << (std::ostream& os, StringRef sr); + friend std::string operator+(StringRef lhs, StringRef rhs); + + /** + * Provides a three-way comparison with rhs + * + * Returns negative number if lhs < rhs, 0 if lhs == rhs, and a positive + * number if lhs > rhs + */ + int compare( StringRef rhs ) const; + }; + + + constexpr auto operator ""_sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { + return StringRef( rawChars, size ); + } +} // namespace Catch + +constexpr auto operator ""_catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { + return Catch::StringRef( rawChars, size ); +} + +#endif // CATCH_STRINGREF_HPP_INCLUDED + +#include +#include +#include +#include + +namespace Catch { + + enum class Verbosity { + Quiet = 0, + Normal, + High + }; + + struct WarnAbout { enum What { + Nothing = 0x00, + //! A test case or leaf section did not run any assertions + NoAssertions = 0x01, + //! A command line test spec matched no test cases + UnmatchedTestSpec = 0x02, + }; }; + + enum class ShowDurations { + DefaultForReporter, + Always, + Never + }; + enum class TestRunOrder { + Declared, + LexicographicallySorted, + Randomized + }; + enum class ColourMode : std::uint8_t { + //! Let Catch2 pick implementation based on platform detection + PlatformDefault, + //! Use ANSI colour code escapes + ANSI, + //! Use Win32 console colour API + Win32, + //! Don't use any colour + None + }; + struct WaitForKeypress { enum When { + Never, + BeforeStart = 1, + BeforeExit = 2, + BeforeStartAndExit = BeforeStart | BeforeExit + }; }; + + class TestSpec; + class IStream; + + class IConfig : public Detail::NonCopyable { + public: + virtual ~IConfig(); + + virtual bool allowThrows() const = 0; + virtual StringRef name() const = 0; + virtual bool includeSuccessfulResults() const = 0; + virtual bool shouldDebugBreak() const = 0; + virtual bool warnAboutMissingAssertions() const = 0; + virtual bool warnAboutUnmatchedTestSpecs() const = 0; + virtual bool zeroTestsCountAsSuccess() const = 0; + virtual int abortAfter() const = 0; + virtual bool showInvisibles() const = 0; + virtual ShowDurations showDurations() const = 0; + virtual double minDuration() const = 0; + virtual TestSpec const& testSpec() const = 0; + virtual bool hasTestFilters() const = 0; + virtual std::vector const& getTestsOrTags() const = 0; + virtual TestRunOrder runOrder() const = 0; + virtual uint32_t rngSeed() const = 0; + virtual unsigned int shardCount() const = 0; + virtual unsigned int shardIndex() const = 0; + virtual ColourMode defaultColourMode() const = 0; + virtual std::vector const& getSectionsToRun() const = 0; + virtual Verbosity verbosity() const = 0; + + virtual bool skipBenchmarks() const = 0; + virtual bool benchmarkNoAnalysis() const = 0; + virtual unsigned int benchmarkSamples() const = 0; + virtual double benchmarkConfidenceInterval() const = 0; + virtual unsigned int benchmarkResamples() const = 0; + virtual std::chrono::milliseconds benchmarkWarmupTime() const = 0; + }; +} + +#endif // CATCH_INTERFACES_CONFIG_HPP_INCLUDED + + +#ifndef CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED +#define CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED + +// Detect a number of compiler features - by compiler +// The following features are defined: +// +// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? +// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? +// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled? +// **************** +// Note to maintainers: if new toggles are added please document them +// in configuration.md, too +// **************** + +// In general each macro has a _NO_ form +// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. +// Many features, at point of detection, define an _INTERNAL_ macro, so they +// can be combined, en-mass, with the _NO_ forms later. + + + +#ifndef CATCH_PLATFORM_HPP_INCLUDED +#define CATCH_PLATFORM_HPP_INCLUDED + +// See e.g.: +// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html +#ifdef __APPLE__ +# include +# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \ + (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) +# define CATCH_PLATFORM_MAC +# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) +# define CATCH_PLATFORM_IPHONE +# endif + +#elif defined(linux) || defined(__linux) || defined(__linux__) +# define CATCH_PLATFORM_LINUX + +#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) +# define CATCH_PLATFORM_WINDOWS + +# if defined( WINAPI_FAMILY ) && ( WINAPI_FAMILY == WINAPI_FAMILY_APP ) +# define CATCH_PLATFORM_WINDOWS_UWP +# endif +#endif + +#endif // CATCH_PLATFORM_HPP_INCLUDED + +#ifdef __cplusplus + +# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) +# define CATCH_CPP14_OR_GREATER +# endif + +# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +# define CATCH_CPP17_OR_GREATER +# endif + +#endif + +// Only GCC compiler should be used in this block, so other compilers trying to +// mask themselves as GCC should be ignored. +#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) + +// This only works on GCC 9+. so we have to also add a global suppression of Wparentheses +// for older versions of GCC. +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "GCC diagnostic ignored \"-Wparentheses\"" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + _Pragma( "GCC diagnostic ignored \"-Wunused-variable\"" ) + +# define CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ + _Pragma( "GCC diagnostic ignored \"-Wuseless-cast\"" ) + +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) + +#endif + +#if defined(__CUDACC__) && !defined(__clang__) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "nv_diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "nv_diagnostic pop" ) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS _Pragma( "nv_diag_suppress 177" ) +#endif + +// clang-cl defines _MSC_VER as well as __clang__, which could cause the +// start/stop internal suppression macros to be double defined. +#if defined(__clang__) && !defined(_MSC_VER) + +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" ) + +#endif // __clang__ && !_MSC_VER + +#if defined(__clang__) + +// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug +// which results in calls to destructors being emitted for each temporary, +// without a matching initialization. In practice, this can result in something +// like `std::string::~string` being called on an uninitialized value. +// +// For example, this code will likely segfault under IBM XL: +// ``` +// REQUIRE(std::string("12") + "34" == "1234") +// ``` +// +// Similarly, NVHPC's implementation of `__builtin_constant_p` has a bug which +// results in calls to the immediately evaluated lambda expressions to be +// reported as unevaluated lambdas. +// https://developer.nvidia.com/nvidia_bug/3321845. +// +// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. +# if !defined(__ibmxl__) && !defined(__CUDACC__) && !defined( __NVCOMPILER ) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */ +# endif + + +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ + _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") + +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) + +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) + +#endif // __clang__ + + +//////////////////////////////////////////////////////////////////////////////// +// Assume that non-Windows platforms support posix signals by default +#if !defined(CATCH_PLATFORM_WINDOWS) + #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS +#endif + +//////////////////////////////////////////////////////////////////////////////// +// We know some environments not to support full POSIX signals +#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) + #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +#endif + +#ifdef __OS400__ +# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Android somehow still does not support std::to_string +#if defined(__ANDROID__) +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Not all Windows environments support SEH properly +#if defined(__MINGW32__) +# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH +#endif + +//////////////////////////////////////////////////////////////////////////////// +// PS4 +#if defined(__ORBIS__) +# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Cygwin +#ifdef __CYGWIN__ + +// Required for some versions of Cygwin to declare gettimeofday +// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin +# define _BSD_SOURCE +// some versions of cygwin (most) do not support std::to_string. Use the libstd check. +// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 +# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ + && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) + +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING + +# endif +#endif // __CYGWIN__ + +//////////////////////////////////////////////////////////////////////////////// +// Visual C++ +#if defined(_MSC_VER) + +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) ) + +// Universal Windows platform does not support SEH +// Or console colours (or console at all...) +# if defined(CATCH_PLATFORM_WINDOWS_UWP) +# define CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32 +# else +# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH +# endif + +// MSVC traditional preprocessor needs some workaround for __VA_ARGS__ +// _MSVC_TRADITIONAL == 0 means new conformant preprocessor +// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor +# if !defined(__clang__) // Handle Clang masquerading for msvc +# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) +# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +# endif // MSVC_TRADITIONAL +# endif // __clang__ + +#endif // _MSC_VER + +#if defined(_REENTRANT) || defined(_MSC_VER) +// Enable async processing, as -pthread is specified or no additional linking is required +# define CATCH_INTERNAL_CONFIG_USE_ASYNC +#endif // _MSC_VER + +//////////////////////////////////////////////////////////////////////////////// +// Check if we are compiled with -fno-exceptions or equivalent +#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) +# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED +#endif + + +//////////////////////////////////////////////////////////////////////////////// +// Embarcadero C++Build +#if defined(__BORLANDC__) + #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// RTX is a special version of Windows that is real time. +// This means that it is detected as Windows, but does not provide +// the same set of capabilities as real Windows does. +#if defined(UNDER_RTSS) || defined(RTX64_BUILD) + #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH + #define CATCH_INTERNAL_CONFIG_NO_ASYNC + #define CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32 +#endif + +#if !defined(_GLIBCXX_USE_C99_MATH_TR1) +#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER +#endif + +// Various stdlib support checks that require __has_include +#if defined(__has_include) + // Check if string_view is available and usable + #if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW + #endif + + // Check if optional is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + + // Check if byte is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # include + # if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0) + # define CATCH_INTERNAL_CONFIG_CPP17_BYTE + # endif + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + + // Check if variant is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # if defined(__clang__) && (__clang_major__ < 8) + // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 + // fix should be in clang 8, workaround in libstdc++ 8.2 + # include + # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) + # define CATCH_CONFIG_NO_CPP17_VARIANT + # else + # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT + # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) + # else + # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT + # endif // defined(__clang__) && (__clang_major__ < 8) + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +#endif // defined(__has_include) + + +#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) +# define CATCH_CONFIG_WINDOWS_SEH +#endif +// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. +#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) +# define CATCH_CONFIG_POSIX_SIGNALS +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) +# define CATCH_CONFIG_CPP11_TO_STRING +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) +# define CATCH_CONFIG_CPP17_OPTIONAL +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) +# define CATCH_CONFIG_CPP17_STRING_VIEW +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) +# define CATCH_CONFIG_CPP17_VARIANT +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE) +# define CATCH_CONFIG_CPP17_BYTE +#endif + + +#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) +# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) +# define CATCH_CONFIG_NEW_CAPTURE +#endif + +#if !defined( CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED ) && \ + !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS ) && \ + !defined( CATCH_CONFIG_NO_DISABLE_EXCEPTIONS ) +# define CATCH_CONFIG_DISABLE_EXCEPTIONS +#endif + +#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) +# define CATCH_CONFIG_POLYFILL_ISNAN +#endif + +#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) +# define CATCH_CONFIG_USE_ASYNC +#endif + +#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) +# define CATCH_CONFIG_GLOBAL_NEXTAFTER +#endif + + +// Even if we do not think the compiler has that warning, we still have +// to provide a macro that can be used by the code. +#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION +#endif +#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS +#endif + +// The goal of this macro is to avoid evaluation of the arguments, but +// still have the compiler warn on problems inside... +#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) +#endif + +#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#elif defined(__clang__) && (__clang_major__ < 5) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif + +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif + +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +#define CATCH_TRY if ((true)) +#define CATCH_CATCH_ALL if ((false)) +#define CATCH_CATCH_ANON(type) if ((false)) +#else +#define CATCH_TRY try +#define CATCH_CATCH_ALL catch (...) +#define CATCH_CATCH_ANON(type) catch (type) +#endif + +#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) +#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#endif + +#if defined( CATCH_PLATFORM_WINDOWS ) && \ + !defined( CATCH_CONFIG_COLOUR_WIN32 ) && \ + !defined( CATCH_CONFIG_NO_COLOUR_WIN32 ) && \ + !defined( CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32 ) +# define CATCH_CONFIG_COLOUR_WIN32 +#endif + +#if defined( CATCH_CONFIG_SHARED_LIBRARY ) && defined( _MSC_VER ) && \ + !defined( CATCH_CONFIG_STATIC ) +# ifdef Catch2_EXPORTS +# define CATCH_EXPORT //__declspec( dllexport ) // not needed +# else +# define CATCH_EXPORT __declspec( dllimport ) +# endif +#else +# define CATCH_EXPORT +#endif + +#endif // CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED + + +#ifndef CATCH_CONTEXT_HPP_INCLUDED +#define CATCH_CONTEXT_HPP_INCLUDED + + +namespace Catch { + + class IResultCapture; + class IConfig; + + class IContext { + public: + virtual ~IContext(); // = default + + virtual IResultCapture* getResultCapture() = 0; + virtual IConfig const* getConfig() const = 0; + }; + + class IMutableContext : public IContext { + public: + ~IMutableContext() override; // = default + virtual void setResultCapture( IResultCapture* resultCapture ) = 0; + virtual void setConfig( IConfig const* config ) = 0; + + private: + CATCH_EXPORT static IMutableContext* currentContext; + friend IMutableContext& getCurrentMutableContext(); + friend void cleanUpContext(); + static void createContext(); + }; + + inline IMutableContext& getCurrentMutableContext() + { + if( !IMutableContext::currentContext ) + IMutableContext::createContext(); + // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn) + return *IMutableContext::currentContext; + } + + inline IContext& getCurrentContext() + { + return getCurrentMutableContext(); + } + + void cleanUpContext(); + + class SimplePcg32; + SimplePcg32& sharedRng(); +} + +#endif // CATCH_CONTEXT_HPP_INCLUDED + + +#ifndef CATCH_INTERFACES_REPORTER_HPP_INCLUDED +#define CATCH_INTERFACES_REPORTER_HPP_INCLUDED + + + +#ifndef CATCH_SECTION_INFO_HPP_INCLUDED +#define CATCH_SECTION_INFO_HPP_INCLUDED + + + +#ifndef CATCH_MOVE_AND_FORWARD_HPP_INCLUDED +#define CATCH_MOVE_AND_FORWARD_HPP_INCLUDED + +#include + +//! Replacement for std::move with better compile time performance +#define CATCH_MOVE(...) static_cast&&>(__VA_ARGS__) + +//! Replacement for std::forward with better compile time performance +#define CATCH_FORWARD(...) static_cast(__VA_ARGS__) + +#endif // CATCH_MOVE_AND_FORWARD_HPP_INCLUDED + + +#ifndef CATCH_SOURCE_LINE_INFO_HPP_INCLUDED +#define CATCH_SOURCE_LINE_INFO_HPP_INCLUDED + +#include +#include + +namespace Catch { + + struct SourceLineInfo { + + SourceLineInfo() = delete; + constexpr SourceLineInfo( char const* _file, std::size_t _line ) noexcept: + file( _file ), + line( _line ) + {} + + bool operator == ( SourceLineInfo const& other ) const noexcept; + bool operator < ( SourceLineInfo const& other ) const noexcept; + + char const* file; + std::size_t line; + + friend std::ostream& operator << (std::ostream& os, SourceLineInfo const& info); + }; +} + +#define CATCH_INTERNAL_LINEINFO \ + ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) + +#endif // CATCH_SOURCE_LINE_INFO_HPP_INCLUDED + + +#ifndef CATCH_TOTALS_HPP_INCLUDED +#define CATCH_TOTALS_HPP_INCLUDED + +#include + +namespace Catch { + + struct Counts { + Counts operator - ( Counts const& other ) const; + Counts& operator += ( Counts const& other ); + + std::uint64_t total() const; + bool allPassed() const; + bool allOk() const; + + std::uint64_t passed = 0; + std::uint64_t failed = 0; + std::uint64_t failedButOk = 0; + }; + + struct Totals { + + Totals operator - ( Totals const& other ) const; + Totals& operator += ( Totals const& other ); + + Totals delta( Totals const& prevTotals ) const; + + Counts assertions; + Counts testCases; + }; +} + +#endif // CATCH_TOTALS_HPP_INCLUDED + +#include + +namespace Catch { + + struct SectionInfo { + // The last argument is ignored, so that people can write + // SECTION("ShortName", "Proper description that is long") and + // still use the `-c` flag comfortably. + SectionInfo( SourceLineInfo const& _lineInfo, std::string _name, + const char* const = nullptr ): + name(CATCH_MOVE(_name)), + lineInfo(_lineInfo) + {} + + std::string name; + SourceLineInfo lineInfo; + }; + + struct SectionEndInfo { + SectionInfo sectionInfo; + Counts prevAssertions; + double durationInSeconds; + }; + +} // end namespace Catch + +#endif // CATCH_SECTION_INFO_HPP_INCLUDED + + +#ifndef CATCH_ASSERTION_RESULT_HPP_INCLUDED +#define CATCH_ASSERTION_RESULT_HPP_INCLUDED + + + +#ifndef CATCH_ASSERTION_INFO_HPP_INCLUDED +#define CATCH_ASSERTION_INFO_HPP_INCLUDED + + + +#ifndef CATCH_RESULT_TYPE_HPP_INCLUDED +#define CATCH_RESULT_TYPE_HPP_INCLUDED + +namespace Catch { + + // ResultWas::OfType enum + struct ResultWas { enum OfType { + Unknown = -1, + Ok = 0, + Info = 1, + Warning = 2, + + FailureBit = 0x10, + + ExpressionFailed = FailureBit | 1, + ExplicitFailure = FailureBit | 2, + + Exception = 0x100 | FailureBit, + + ThrewException = Exception | 1, + DidntThrowException = Exception | 2, + + FatalErrorCondition = 0x200 | FailureBit + + }; }; + + bool isOk( ResultWas::OfType resultType ); + bool isJustInfo( int flags ); + + + // ResultDisposition::Flags enum + struct ResultDisposition { enum Flags { + Normal = 0x01, + + ContinueOnFailure = 0x02, // Failures fail test, but execution continues + FalseTest = 0x04, // Prefix expression with ! + SuppressFail = 0x08 // Failures are reported but do not fail the test + }; }; + + ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ); + + bool shouldContinueOnFailure( int flags ); + inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; } + bool shouldSuppressFailure( int flags ); + +} // end namespace Catch + +#endif // CATCH_RESULT_TYPE_HPP_INCLUDED + +namespace Catch { + + struct AssertionInfo { + // AssertionInfo() = delete; + + StringRef macroName; + SourceLineInfo lineInfo; + StringRef capturedExpression; + ResultDisposition::Flags resultDisposition; + }; + +} // end namespace Catch + +#endif // CATCH_ASSERTION_INFO_HPP_INCLUDED + + +#ifndef CATCH_LAZY_EXPR_HPP_INCLUDED +#define CATCH_LAZY_EXPR_HPP_INCLUDED + +#include + +namespace Catch { + + class ITransientExpression; + + class LazyExpression { + friend class AssertionHandler; + friend struct AssertionStats; + friend class RunContext; + + ITransientExpression const* m_transientExpression = nullptr; + bool m_isNegated; + public: + LazyExpression( bool isNegated ): + m_isNegated(isNegated) + {} + LazyExpression(LazyExpression const& other) = default; + LazyExpression& operator = ( LazyExpression const& ) = delete; + + explicit operator bool() const { + return m_transientExpression != nullptr; + } + + friend auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream&; + }; + +} // namespace Catch + +#endif // CATCH_LAZY_EXPR_HPP_INCLUDED + +#include + +namespace Catch { + + struct AssertionResultData + { + AssertionResultData() = delete; + + AssertionResultData( ResultWas::OfType _resultType, LazyExpression const& _lazyExpression ); + + std::string message; + mutable std::string reconstructedExpression; + LazyExpression lazyExpression; + ResultWas::OfType resultType; + + std::string reconstructExpression() const; + }; + + class AssertionResult { + public: + AssertionResult() = delete; + AssertionResult( AssertionInfo const& info, AssertionResultData const& data ); + + bool isOk() const; + bool succeeded() const; + ResultWas::OfType getResultType() const; + bool hasExpression() const; + bool hasMessage() const; + std::string getExpression() const; + std::string getExpressionInMacro() const; + bool hasExpandedExpression() const; + std::string getExpandedExpression() const; + StringRef getMessage() const; + SourceLineInfo getSourceInfo() const; + StringRef getTestMacroName() const; + + //protected: + AssertionInfo m_info; + AssertionResultData m_resultData; + }; + +} // end namespace Catch + +#endif // CATCH_ASSERTION_RESULT_HPP_INCLUDED + + +#ifndef CATCH_MESSAGE_INFO_HPP_INCLUDED +#define CATCH_MESSAGE_INFO_HPP_INCLUDED + + + +#ifndef CATCH_INTERFACES_CAPTURE_HPP_INCLUDED +#define CATCH_INTERFACES_CAPTURE_HPP_INCLUDED + +#include +#include + + +namespace Catch { + + class AssertionResult; + struct AssertionInfo; + struct SectionInfo; + struct SectionEndInfo; + struct MessageInfo; + struct MessageBuilder; + struct Counts; + struct AssertionReaction; + struct SourceLineInfo; + + class ITransientExpression; + class IGeneratorTracker; + + struct BenchmarkInfo; + template > + struct BenchmarkStats; + + class IResultCapture { + public: + virtual ~IResultCapture(); + + virtual bool sectionStarted( SectionInfo const& sectionInfo, + Counts& assertions ) = 0; + virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0; + virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0; + + virtual auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& = 0; + + virtual void benchmarkPreparing( StringRef name ) = 0; + virtual void benchmarkStarting( BenchmarkInfo const& info ) = 0; + virtual void benchmarkEnded( BenchmarkStats<> const& stats ) = 0; + virtual void benchmarkFailed( StringRef error ) = 0; + + virtual void pushScopedMessage( MessageInfo const& message ) = 0; + virtual void popScopedMessage( MessageInfo const& message ) = 0; + + virtual void emplaceUnscopedMessage( MessageBuilder const& builder ) = 0; + + virtual void handleFatalErrorCondition( StringRef message ) = 0; + + virtual void handleExpr + ( AssertionInfo const& info, + ITransientExpression const& expr, + AssertionReaction& reaction ) = 0; + virtual void handleMessage + ( AssertionInfo const& info, + ResultWas::OfType resultType, + StringRef message, + AssertionReaction& reaction ) = 0; + virtual void handleUnexpectedExceptionNotThrown + ( AssertionInfo const& info, + AssertionReaction& reaction ) = 0; + virtual void handleUnexpectedInflightException + ( AssertionInfo const& info, + std::string const& message, + AssertionReaction& reaction ) = 0; + virtual void handleIncomplete + ( AssertionInfo const& info ) = 0; + virtual void handleNonExpr + ( AssertionInfo const &info, + ResultWas::OfType resultType, + AssertionReaction &reaction ) = 0; + + + + virtual bool lastAssertionPassed() = 0; + virtual void assertionPassed() = 0; + + // Deprecated, do not use: + virtual std::string getCurrentTestName() const = 0; + virtual const AssertionResult* getLastResult() const = 0; + virtual void exceptionEarlyReported() = 0; + }; + + IResultCapture& getResultCapture(); +} + +#endif // CATCH_INTERFACES_CAPTURE_HPP_INCLUDED + +#include + +namespace Catch { + + struct MessageInfo { + MessageInfo( StringRef _macroName, + SourceLineInfo const& _lineInfo, + ResultWas::OfType _type ); + + StringRef macroName; + std::string message; + SourceLineInfo lineInfo; + ResultWas::OfType type; + unsigned int sequence; + + bool operator == (MessageInfo const& other) const { + return sequence == other.sequence; + } + bool operator < (MessageInfo const& other) const { + return sequence < other.sequence; + } + private: + static unsigned int globalCount; + }; + +} // end namespace Catch + +#endif // CATCH_MESSAGE_INFO_HPP_INCLUDED + + +#ifndef CATCH_UNIQUE_PTR_HPP_INCLUDED +#define CATCH_UNIQUE_PTR_HPP_INCLUDED + +#include +#include + + +namespace Catch { +namespace Detail { + /** + * A reimplementation of `std::unique_ptr` for improved compilation performance + * + * Does not support arrays nor custom deleters. + */ + template + class unique_ptr { + T* m_ptr; + public: + constexpr unique_ptr(std::nullptr_t = nullptr): + m_ptr{} + {} + explicit constexpr unique_ptr(T* ptr): + m_ptr(ptr) + {} + + template ::value>> + unique_ptr(unique_ptr&& from): + m_ptr(from.release()) + {} + + template ::value>> + unique_ptr& operator=(unique_ptr&& from) { + reset(from.release()); + + return *this; + } + + unique_ptr(unique_ptr const&) = delete; + unique_ptr& operator=(unique_ptr const&) = delete; + + unique_ptr(unique_ptr&& rhs) noexcept: + m_ptr(rhs.m_ptr) { + rhs.m_ptr = nullptr; + } + unique_ptr& operator=(unique_ptr&& rhs) noexcept { + reset(rhs.release()); + + return *this; + } + + ~unique_ptr() { + delete m_ptr; + } + + T& operator*() { + assert(m_ptr); + return *m_ptr; + } + T const& operator*() const { + assert(m_ptr); + return *m_ptr; + } + T* operator->() noexcept { + assert(m_ptr); + return m_ptr; + } + T const* operator->() const noexcept { + assert(m_ptr); + return m_ptr; + } + + T* get() { return m_ptr; } + T const* get() const { return m_ptr; } + + void reset(T* ptr = nullptr) { + delete m_ptr; + m_ptr = ptr; + } + + T* release() { + auto temp = m_ptr; + m_ptr = nullptr; + return temp; + } + + explicit operator bool() const { + return m_ptr; + } + + friend void swap(unique_ptr& lhs, unique_ptr& rhs) { + auto temp = lhs.m_ptr; + lhs.m_ptr = rhs.m_ptr; + rhs.m_ptr = temp; + } + }; + + //! Specialization to cause compile-time error for arrays + template + class unique_ptr; + + template + unique_ptr make_unique(Args&&... args) { + return unique_ptr(new T(CATCH_FORWARD(args)...)); + } + + +} // end namespace Detail +} // end namespace Catch + +#endif // CATCH_UNIQUE_PTR_HPP_INCLUDED + + +// Adapted from donated nonius code. + +#ifndef CATCH_ESTIMATE_HPP_INCLUDED +#define CATCH_ESTIMATE_HPP_INCLUDED + +namespace Catch { + namespace Benchmark { + template + struct Estimate { + Duration point; + Duration lower_bound; + Duration upper_bound; + double confidence_interval; + + template + operator Estimate() const { + return { point, lower_bound, upper_bound, confidence_interval }; + } + }; + } // namespace Benchmark +} // namespace Catch + +#endif // CATCH_ESTIMATE_HPP_INCLUDED + + +// Adapted from donated nonius code. + +#ifndef CATCH_OUTLIER_CLASSIFICATION_HPP_INCLUDED +#define CATCH_OUTLIER_CLASSIFICATION_HPP_INCLUDED + +namespace Catch { + namespace Benchmark { + struct OutlierClassification { + int samples_seen = 0; + int low_severe = 0; // more than 3 times IQR below Q1 + int low_mild = 0; // 1.5 to 3 times IQR below Q1 + int high_mild = 0; // 1.5 to 3 times IQR above Q3 + int high_severe = 0; // more than 3 times IQR above Q3 + + int total() const { + return low_severe + low_mild + high_mild + high_severe; + } + }; + } // namespace Benchmark +} // namespace Catch + +#endif // CATCH_OUTLIERS_CLASSIFICATION_HPP_INCLUDED + + +#include +#include +#include +#include + +namespace Catch { + + struct ReporterDescription; + struct ListenerDescription; + struct TagInfo; + struct TestCaseInfo; + class TestCaseHandle; + class IConfig; + class IStream; + enum class ColourMode : std::uint8_t; + + struct ReporterConfig { + ReporterConfig( IConfig const* _fullConfig, + Detail::unique_ptr _stream, + ColourMode colourMode, + std::map customOptions ); + + ReporterConfig( ReporterConfig&& ) = default; + ReporterConfig& operator=( ReporterConfig&& ) = default; + ~ReporterConfig(); // = default + + Detail::unique_ptr takeStream() &&; + IConfig const* fullConfig() const; + ColourMode colourMode() const; + std::map const& customOptions() const; + + private: + Detail::unique_ptr m_stream; + IConfig const* m_fullConfig; + ColourMode m_colourMode; + std::map m_customOptions; + }; + + struct TestRunInfo { + constexpr TestRunInfo(StringRef _name) : name(_name) {} + StringRef name; + }; + + struct AssertionStats { + AssertionStats( AssertionResult const& _assertionResult, + std::vector const& _infoMessages, + Totals const& _totals ); + + AssertionStats( AssertionStats const& ) = default; + AssertionStats( AssertionStats && ) = default; + AssertionStats& operator = ( AssertionStats const& ) = delete; + AssertionStats& operator = ( AssertionStats && ) = delete; + + AssertionResult assertionResult; + std::vector infoMessages; + Totals totals; + }; + + struct SectionStats { + SectionStats( SectionInfo const& _sectionInfo, + Counts const& _assertions, + double _durationInSeconds, + bool _missingAssertions ); + + SectionInfo sectionInfo; + Counts assertions; + double durationInSeconds; + bool missingAssertions; + }; + + struct TestCaseStats { + TestCaseStats( TestCaseInfo const& _testInfo, + Totals const& _totals, + std::string const& _stdOut, + std::string const& _stdErr, + bool _aborting ); + + TestCaseInfo const * testInfo; + Totals totals; + std::string stdOut; + std::string stdErr; + bool aborting; + }; + + struct TestRunStats { + TestRunStats( TestRunInfo const& _runInfo, + Totals const& _totals, + bool _aborting ); + + TestRunInfo runInfo; + Totals totals; + bool aborting; + }; + + + struct BenchmarkInfo { + std::string name; + double estimatedDuration; + int iterations; + unsigned int samples; + unsigned int resamples; + double clockResolution; + double clockCost; + }; + + template + struct BenchmarkStats { + BenchmarkInfo info; + + std::vector samples; + Benchmark::Estimate mean; + Benchmark::Estimate standardDeviation; + Benchmark::OutlierClassification outliers; + double outlierVariance; + + template + operator BenchmarkStats() const { + std::vector samples2; + samples2.reserve(samples.size()); + for (auto const& sample : samples) { + samples2.push_back(Duration2(sample)); + } + return { + info, + CATCH_MOVE(samples2), + mean, + standardDeviation, + outliers, + outlierVariance, + }; + } + }; + + //! By setting up its preferences, a reporter can modify Catch2's behaviour + //! in some regards, e.g. it can request Catch2 to capture writes to + //! stdout/stderr during test execution, and pass them to the reporter. + struct ReporterPreferences { + //! Catch2 should redirect writes to stdout and pass them to the + //! reporter + bool shouldRedirectStdOut = false; + //! Catch2 should call `Reporter::assertionEnded` even for passing + //! assertions + bool shouldReportAllAssertions = false; + }; + + /** + * The common base for all reporters and event listeners + * + * Implementing classes must also implement: + * + * //! User-friendly description of the reporter/listener type + * static std::string getDescription() + * + * Generally shouldn't be derived from by users of Catch2 directly, + * instead they should derive from one of the utility bases that + * derive from this class. + */ + class IEventListener { + protected: + //! Derived classes can set up their preferences here + ReporterPreferences m_preferences; + //! The test run's config as filled in from CLI and defaults + IConfig const* m_config; + + public: + IEventListener( IConfig const* config ): m_config( config ) {} + + virtual ~IEventListener(); // = default; + + // Implementing class must also provide the following static methods: + // static std::string getDescription(); + + ReporterPreferences const& getPreferences() const { + return m_preferences; + } + + //! Called when no test cases match provided test spec + virtual void noMatchingTestCases( StringRef unmatchedSpec ) = 0; + //! Called for all invalid test specs from the cli + virtual void reportInvalidTestSpec( StringRef invalidArgument ) = 0; + + /** + * Called once in a testing run before tests are started + * + * Not called if tests won't be run (e.g. only listing will happen) + */ + virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; + + //! Called _once_ for each TEST_CASE, no matter how many times it is entered + virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; + //! Called _every time_ a TEST_CASE is entered, including repeats (due to sections) + virtual void testCasePartialStarting( TestCaseInfo const& testInfo, uint64_t partNumber ) = 0; + //! Called when a `SECTION` is being entered. Not called for skipped sections + virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; + + //! Called when user-code is being probed before the actual benchmark runs + virtual void benchmarkPreparing( StringRef benchmarkName ) = 0; + //! Called after probe but before the user-code is being benchmarked + virtual void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) = 0; + //! Called with the benchmark results if benchmark successfully finishes + virtual void benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) = 0; + //! Called if running the benchmarks fails for any reason + virtual void benchmarkFailed( StringRef benchmarkName ) = 0; + + //! Called before assertion success/failure is evaluated + virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; + + //! Called after assertion was fully evaluated + virtual void assertionEnded( AssertionStats const& assertionStats ) = 0; + + //! Called after a `SECTION` has finished running + virtual void sectionEnded( SectionStats const& sectionStats ) = 0; + //! Called _every time_ a TEST_CASE is entered, including repeats (due to sections) + virtual void testCasePartialEnded(TestCaseStats const& testCaseStats, uint64_t partNumber ) = 0; + //! Called _once_ for each TEST_CASE, no matter how many times it is entered + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; + /** + * Called once after all tests in a testing run are finished + * + * Not called if tests weren't run (e.g. only listings happened) + */ + virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; + + //! Called with test cases that are skipped due to the test run aborting + virtual void skipTest( TestCaseInfo const& testInfo ) = 0; + + //! Called if a fatal error (signal/structured exception) occured + virtual void fatalErrorEncountered( StringRef error ) = 0; + + //! Writes out information about provided reporters using reporter-specific format + virtual void listReporters(std::vector const& descriptions) = 0; + //! Writes out the provided listeners descriptions using reporter-specific format + virtual void listListeners(std::vector const& descriptions) = 0; + //! Writes out information about provided tests using reporter-specific format + virtual void listTests(std::vector const& tests) = 0; + //! Writes out information about the provided tags using reporter-specific format + virtual void listTags(std::vector const& tags) = 0; + }; + using IEventListenerPtr = Detail::unique_ptr; + +} // end namespace Catch + +#endif // CATCH_INTERFACES_REPORTER_HPP_INCLUDED + + +#ifndef CATCH_UNIQUE_NAME_HPP_INCLUDED +#define CATCH_UNIQUE_NAME_HPP_INCLUDED + + + + +/** \file + * Wrapper for the CONFIG configuration option + * + * When generating internal unique names, there are two options. Either + * we mix in the current line number, or mix in an incrementing number. + * We prefer the latter, using `__COUNTER__`, but users might want to + * use the former. + */ + +#ifndef CATCH_CONFIG_COUNTER_HPP_INCLUDED +#define CATCH_CONFIG_COUNTER_HPP_INCLUDED + +#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) + #define CATCH_INTERNAL_CONFIG_COUNTER +#endif + +#if defined( CATCH_INTERNAL_CONFIG_COUNTER ) && \ + !defined( CATCH_CONFIG_NO_COUNTER ) && \ + !defined( CATCH_CONFIG_COUNTER ) +# define CATCH_CONFIG_COUNTER +#endif + + +#endif // CATCH_CONFIG_COUNTER_HPP_INCLUDED +#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line +#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) +#ifdef CATCH_CONFIG_COUNTER +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) +#else +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) +#endif + +#endif // CATCH_UNIQUE_NAME_HPP_INCLUDED + + +// Adapted from donated nonius code. + +#ifndef CATCH_CHRONOMETER_HPP_INCLUDED +#define CATCH_CHRONOMETER_HPP_INCLUDED + + + +// Adapted from donated nonius code. + +#ifndef CATCH_CLOCK_HPP_INCLUDED +#define CATCH_CLOCK_HPP_INCLUDED + +#include +#include + +namespace Catch { + namespace Benchmark { + template + using ClockDuration = typename Clock::duration; + template + using FloatDuration = std::chrono::duration; + + template + using TimePoint = typename Clock::time_point; + + using default_clock = std::chrono::steady_clock; + + template + struct now { + TimePoint operator()() const { + return Clock::now(); + } + }; + + using fp_seconds = std::chrono::duration>; + } // namespace Benchmark +} // namespace Catch + +#endif // CATCH_CLOCK_HPP_INCLUDED + + +// Adapted from donated nonius code. + +#ifndef CATCH_OPTIMIZER_HPP_INCLUDED +#define CATCH_OPTIMIZER_HPP_INCLUDED + +#if defined(_MSC_VER) +# include // atomic_thread_fence +#endif + + +#include + +namespace Catch { + namespace Benchmark { +#if defined(__GNUC__) || defined(__clang__) + template + inline void keep_memory(T* p) { + asm volatile("" : : "g"(p) : "memory"); + } + inline void keep_memory() { + asm volatile("" : : : "memory"); + } + + namespace Detail { + inline void optimizer_barrier() { keep_memory(); } + } // namespace Detail +#elif defined(_MSC_VER) + +#pragma optimize("", off) + template + inline void keep_memory(T* p) { + // thanks @milleniumbug + *reinterpret_cast(p) = *reinterpret_cast(p); + } + // TODO equivalent keep_memory() +#pragma optimize("", on) + + namespace Detail { + inline void optimizer_barrier() { + std::atomic_thread_fence(std::memory_order_seq_cst); + } + } // namespace Detail + +#endif + + template + inline void deoptimize_value(T&& x) { + keep_memory(&x); + } + + template + inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> std::enable_if_t::value> { + deoptimize_value(CATCH_FORWARD(fn) (CATCH_FORWARD(args)...)); + } + + template + inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> std::enable_if_t::value> { + CATCH_FORWARD(fn) (CATCH_FORWARD(args)...); + } + } // namespace Benchmark +} // namespace Catch + +#endif // CATCH_OPTIMIZER_HPP_INCLUDED + + +// Adapted from donated nonius code. + +#ifndef CATCH_COMPLETE_INVOKE_HPP_INCLUDED +#define CATCH_COMPLETE_INVOKE_HPP_INCLUDED + + + +#ifndef CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED +#define CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED + +namespace Catch { + + //! Used to signal that an assertion macro failed + struct TestFailureException{}; + +} // namespace Catch + +#endif // CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED + + +#ifndef CATCH_META_HPP_INCLUDED +#define CATCH_META_HPP_INCLUDED + +#include + +namespace Catch { + template + struct always_false : std::false_type {}; + + template struct true_given : std::true_type {}; + struct is_callable_tester { + template + static true_given()(std::declval()...))> test(int); + template + static std::false_type test(...); + }; + + template + struct is_callable; + + template + struct is_callable : decltype(is_callable_tester::test(0)) {}; + + +#if defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703 + // std::result_of is deprecated in C++17 and removed in C++20. Hence, it is + // replaced with std::invoke_result here. + template + using FunctionReturnType = std::remove_reference_t>>; +#else + template + using FunctionReturnType = std::remove_reference_t>>; +#endif + +} // namespace Catch + +namespace mpl_{ + struct na; +} + +#endif // CATCH_META_HPP_INCLUDED + + +#ifndef CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED +#define CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED + + +#include + +namespace Catch { + + class TestCaseHandle; + struct TestCaseInfo; + class ITestCaseRegistry; + class IExceptionTranslatorRegistry; + class IExceptionTranslator; + class IReporterRegistry; + class IReporterFactory; + class ITagAliasRegistry; + class ITestInvoker; + class IMutableEnumValuesRegistry; + struct SourceLineInfo; + + class StartupExceptionRegistry; + class EventListenerFactory; + + using IReporterFactoryPtr = Detail::unique_ptr; + + class IRegistryHub { + public: + virtual ~IRegistryHub(); // = default + + virtual IReporterRegistry const& getReporterRegistry() const = 0; + virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; + virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0; + virtual IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const = 0; + + + virtual StartupExceptionRegistry const& getStartupExceptionRegistry() const = 0; + }; + + class IMutableRegistryHub { + public: + virtual ~IMutableRegistryHub(); // = default + virtual void registerReporter( std::string const& name, IReporterFactoryPtr factory ) = 0; + virtual void registerListener( Detail::unique_ptr factory ) = 0; + virtual void registerTest(Detail::unique_ptr&& testInfo, Detail::unique_ptr&& invoker) = 0; + virtual void registerTranslator( Detail::unique_ptr&& translator ) = 0; + virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0; + virtual void registerStartupException() noexcept = 0; + virtual IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() = 0; + }; + + IRegistryHub const& getRegistryHub(); + IMutableRegistryHub& getMutableRegistryHub(); + void cleanUp(); + std::string translateActiveException(); + +} + +#endif // CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED + +#include + +namespace Catch { + namespace Benchmark { + namespace Detail { + template + struct CompleteType { using type = T; }; + template <> + struct CompleteType { struct type {}; }; + + template + using CompleteType_t = typename CompleteType::type; + + template + struct CompleteInvoker { + template + static Result invoke(Fun&& fun, Args&&... args) { + return CATCH_FORWARD(fun)(CATCH_FORWARD(args)...); + } + }; + template <> + struct CompleteInvoker { + template + static CompleteType_t invoke(Fun&& fun, Args&&... args) { + CATCH_FORWARD(fun)(CATCH_FORWARD(args)...); + return {}; + } + }; + + // invoke and not return void :( + template + CompleteType_t> complete_invoke(Fun&& fun, Args&&... args) { + return CompleteInvoker>::invoke(CATCH_FORWARD(fun), CATCH_FORWARD(args)...); + } + + } // namespace Detail + + template + Detail::CompleteType_t> user_code(Fun&& fun) { + return Detail::complete_invoke(CATCH_FORWARD(fun)); + } + } // namespace Benchmark +} // namespace Catch + +#endif // CATCH_COMPLETE_INVOKE_HPP_INCLUDED + +namespace Catch { + namespace Benchmark { + namespace Detail { + struct ChronometerConcept { + virtual void start() = 0; + virtual void finish() = 0; + virtual ~ChronometerConcept(); // = default; + + ChronometerConcept() = default; + ChronometerConcept(ChronometerConcept const&) = default; + ChronometerConcept& operator=(ChronometerConcept const&) = default; + }; + template + struct ChronometerModel final : public ChronometerConcept { + void start() override { started = Clock::now(); } + void finish() override { finished = Clock::now(); } + + ClockDuration elapsed() const { return finished - started; } + + TimePoint started; + TimePoint finished; + }; + } // namespace Detail + + struct Chronometer { + public: + template + void measure(Fun&& fun) { measure(CATCH_FORWARD(fun), is_callable()); } + + int runs() const { return repeats; } + + Chronometer(Detail::ChronometerConcept& meter, int repeats_) + : impl(&meter) + , repeats(repeats_) {} + + private: + template + void measure(Fun&& fun, std::false_type) { + measure([&fun](int) { return fun(); }, std::true_type()); + } + + template + void measure(Fun&& fun, std::true_type) { + Detail::optimizer_barrier(); + impl->start(); + for (int i = 0; i < repeats; ++i) invoke_deoptimized(fun, i); + impl->finish(); + Detail::optimizer_barrier(); + } + + Detail::ChronometerConcept* impl; + int repeats; + }; + } // namespace Benchmark +} // namespace Catch + +#endif // CATCH_CHRONOMETER_HPP_INCLUDED + + +// Adapted from donated nonius code. + +#ifndef CATCH_ENVIRONMENT_HPP_INCLUDED +#define CATCH_ENVIRONMENT_HPP_INCLUDED + + +namespace Catch { + namespace Benchmark { + template + struct EnvironmentEstimate { + Duration mean; + OutlierClassification outliers; + + template + operator EnvironmentEstimate() const { + return { mean, outliers }; + } + }; + template + struct Environment { + using clock_type = Clock; + EnvironmentEstimate> clock_resolution; + EnvironmentEstimate> clock_cost; + }; + } // namespace Benchmark +} // namespace Catch + +#endif // CATCH_ENVIRONMENT_HPP_INCLUDED + + +// Adapted from donated nonius code. + +#ifndef CATCH_EXECUTION_PLAN_HPP_INCLUDED +#define CATCH_EXECUTION_PLAN_HPP_INCLUDED + + + +// Adapted from donated nonius code. + +#ifndef CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED +#define CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED + + +#include + +namespace Catch { + namespace Benchmark { + namespace Detail { + template + struct is_related + : std::is_same, std::decay_t> {}; + + /// We need to reinvent std::function because every piece of code that might add overhead + /// in a measurement context needs to have consistent performance characteristics so that we + /// can account for it in the measurement. + /// Implementations of std::function with optimizations that aren't always applicable, like + /// small buffer optimizations, are not uncommon. + /// This is effectively an implementation of std::function without any such optimizations; + /// it may be slow, but it is consistently slow. + struct BenchmarkFunction { + private: + struct callable { + virtual void call(Chronometer meter) const = 0; + virtual Catch::Detail::unique_ptr clone() const = 0; + virtual ~callable(); // = default; + + callable() = default; + callable(callable const&) = default; + callable& operator=(callable const&) = default; + }; + template + struct model : public callable { + model(Fun&& fun_) : fun(CATCH_MOVE(fun_)) {} + model(Fun const& fun_) : fun(fun_) {} + + Catch::Detail::unique_ptr clone() const override { + return Catch::Detail::make_unique>( *this ); + } + + void call(Chronometer meter) const override { + call(meter, is_callable()); + } + void call(Chronometer meter, std::true_type) const { + fun(meter); + } + void call(Chronometer meter, std::false_type) const { + meter.measure(fun); + } + + Fun fun; + }; + + struct do_nothing { void operator()() const {} }; + + template + BenchmarkFunction(model* c) : f(c) {} + + public: + BenchmarkFunction() + : f(new model{ {} }) {} + + template ::value, int> = 0> + BenchmarkFunction(Fun&& fun) + : f(new model>(CATCH_FORWARD(fun))) {} + + BenchmarkFunction( BenchmarkFunction&& that ) noexcept: + f( CATCH_MOVE( that.f ) ) {} + + BenchmarkFunction(BenchmarkFunction const& that) + : f(that.f->clone()) {} + + BenchmarkFunction& + operator=( BenchmarkFunction&& that ) noexcept { + f = CATCH_MOVE( that.f ); + return *this; + } + + BenchmarkFunction& operator=(BenchmarkFunction const& that) { + f = that.f->clone(); + return *this; + } + + void operator()(Chronometer meter) const { f->call(meter); } + + private: + Catch::Detail::unique_ptr f; + }; + } // namespace Detail + } // namespace Benchmark +} // namespace Catch + +#endif // CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED + + +// Adapted from donated nonius code. + +#ifndef CATCH_REPEAT_HPP_INCLUDED +#define CATCH_REPEAT_HPP_INCLUDED + +#include + +namespace Catch { + namespace Benchmark { + namespace Detail { + template + struct repeater { + void operator()(int k) const { + for (int i = 0; i < k; ++i) { + fun(); + } + } + Fun fun; + }; + template + repeater> repeat(Fun&& fun) { + return { CATCH_FORWARD(fun) }; + } + } // namespace Detail + } // namespace Benchmark +} // namespace Catch + +#endif // CATCH_REPEAT_HPP_INCLUDED + + +// Adapted from donated nonius code. + +#ifndef CATCH_RUN_FOR_AT_LEAST_HPP_INCLUDED +#define CATCH_RUN_FOR_AT_LEAST_HPP_INCLUDED + + + +// Adapted from donated nonius code. + +#ifndef CATCH_MEASURE_HPP_INCLUDED +#define CATCH_MEASURE_HPP_INCLUDED + + + +// Adapted from donated nonius code. + +#ifndef CATCH_TIMING_HPP_INCLUDED +#define CATCH_TIMING_HPP_INCLUDED + + +#include + +namespace Catch { + namespace Benchmark { + template + struct Timing { + Duration elapsed; + Result result; + int iterations; + }; + template + using TimingOf = Timing, Detail::CompleteType_t>>; + } // namespace Benchmark +} // namespace Catch + +#endif // CATCH_TIMING_HPP_INCLUDED + +namespace Catch { + namespace Benchmark { + namespace Detail { + template + TimingOf measure(Fun&& fun, Args&&... args) { + auto start = Clock::now(); + auto&& r = Detail::complete_invoke(fun, CATCH_FORWARD(args)...); + auto end = Clock::now(); + auto delta = end - start; + return { delta, CATCH_FORWARD(r), 1 }; + } + } // namespace Detail + } // namespace Benchmark +} // namespace Catch + +#endif // CATCH_MEASURE_HPP_INCLUDED + +#include + +namespace Catch { + namespace Benchmark { + namespace Detail { + template + TimingOf measure_one(Fun&& fun, int iters, std::false_type) { + return Detail::measure(fun, iters); + } + template + TimingOf measure_one(Fun&& fun, int iters, std::true_type) { + Detail::ChronometerModel meter; + auto&& result = Detail::complete_invoke(fun, Chronometer(meter, iters)); + + return { meter.elapsed(), CATCH_MOVE(result), iters }; + } + + template + using run_for_at_least_argument_t = std::conditional_t::value, Chronometer, int>; + + + [[noreturn]] + void throw_optimized_away_error(); + + template + TimingOf> + run_for_at_least(ClockDuration how_long, + const int initial_iterations, + Fun&& fun) { + auto iters = initial_iterations; + while (iters < (1 << 30)) { + auto&& Timing = measure_one(fun, iters, is_callable()); + + if (Timing.elapsed >= how_long) { + return { Timing.elapsed, CATCH_MOVE(Timing.result), iters }; + } + iters *= 2; + } + throw_optimized_away_error(); + } + } // namespace Detail + } // namespace Benchmark +} // namespace Catch + +#endif // CATCH_RUN_FOR_AT_LEAST_HPP_INCLUDED + +#include +#include + +namespace Catch { + namespace Benchmark { + template + struct ExecutionPlan { + int iterations_per_sample; + Duration estimated_duration; + Detail::BenchmarkFunction benchmark; + Duration warmup_time; + int warmup_iterations; + + template + operator ExecutionPlan() const { + return { iterations_per_sample, estimated_duration, benchmark, warmup_time, warmup_iterations }; + } + + template + std::vector> run(const IConfig &cfg, Environment> env) const { + // warmup a bit + Detail::run_for_at_least(std::chrono::duration_cast>(warmup_time), warmup_iterations, Detail::repeat(now{})); + + std::vector> times; + times.reserve(cfg.benchmarkSamples()); + std::generate_n(std::back_inserter(times), cfg.benchmarkSamples(), [this, env] { + Detail::ChronometerModel model; + this->benchmark(Chronometer(model, iterations_per_sample)); + auto sample_time = model.elapsed() - env.clock_cost.mean; + if (sample_time < FloatDuration::zero()) sample_time = FloatDuration::zero(); + return sample_time / iterations_per_sample; + }); + return times; + } + }; + } // namespace Benchmark +} // namespace Catch + +#endif // CATCH_EXECUTION_PLAN_HPP_INCLUDED + + +// Adapted from donated nonius code. + +#ifndef CATCH_ESTIMATE_CLOCK_HPP_INCLUDED +#define CATCH_ESTIMATE_CLOCK_HPP_INCLUDED + + + +// Adapted from donated nonius code. + +#ifndef CATCH_STATS_HPP_INCLUDED +#define CATCH_STATS_HPP_INCLUDED + + +#include +#include +#include +#include +#include + +namespace Catch { + namespace Benchmark { + namespace Detail { + using sample = std::vector; + + // Used when we know we want == comparison of two doubles + // to centralize warning suppression + bool directCompare( double lhs, double rhs ); + + double weighted_average_quantile(int k, int q, std::vector::iterator first, std::vector::iterator last); + + template + OutlierClassification classify_outliers(Iterator first, Iterator last) { + std::vector copy(first, last); + + auto q1 = weighted_average_quantile(1, 4, copy.begin(), copy.end()); + auto q3 = weighted_average_quantile(3, 4, copy.begin(), copy.end()); + auto iqr = q3 - q1; + auto los = q1 - (iqr * 3.); + auto lom = q1 - (iqr * 1.5); + auto him = q3 + (iqr * 1.5); + auto his = q3 + (iqr * 3.); + + OutlierClassification o; + for (; first != last; ++first) { + auto&& t = *first; + if (t < los) ++o.low_severe; + else if (t < lom) ++o.low_mild; + else if (t > his) ++o.high_severe; + else if (t > him) ++o.high_mild; + ++o.samples_seen; + } + return o; + } + + template + double mean(Iterator first, Iterator last) { + auto count = last - first; + double sum = std::accumulate(first, last, 0.); + return sum / static_cast(count); + } + + template + sample jackknife(Estimator&& estimator, Iterator first, Iterator last) { + auto n = static_cast(last - first); + auto second = first; + ++second; + sample results; + results.reserve(n); + + for (auto it = first; it != last; ++it) { + std::iter_swap(it, first); + results.push_back(estimator(second, last)); + } + + return results; + } + + inline double normal_cdf(double x) { + return std::erfc(-x / std::sqrt(2.0)) / 2.0; + } + + double erfc_inv(double x); + + double normal_quantile(double p); + + template + Estimate bootstrap(double confidence_level, Iterator first, Iterator last, sample const& resample, Estimator&& estimator) { + auto n_samples = last - first; + + double point = estimator(first, last); + // Degenerate case with a single sample + if (n_samples == 1) return { point, point, point, confidence_level }; + + sample jack = jackknife(estimator, first, last); + double jack_mean = mean(jack.begin(), jack.end()); + double sum_squares, sum_cubes; + std::tie(sum_squares, sum_cubes) = std::accumulate(jack.begin(), jack.end(), std::make_pair(0., 0.), [jack_mean](std::pair sqcb, double x) -> std::pair { + auto d = jack_mean - x; + auto d2 = d * d; + auto d3 = d2 * d; + return { sqcb.first + d2, sqcb.second + d3 }; + }); + + double accel = sum_cubes / (6 * std::pow(sum_squares, 1.5)); + long n = static_cast(resample.size()); + double prob_n = std::count_if(resample.begin(), resample.end(), [point](double x) { return x < point; }) / static_cast(n); + // degenerate case with uniform samples + if ( directCompare( prob_n, 0. ) ) { + return { point, point, point, confidence_level }; + } + + double bias = normal_quantile(prob_n); + double z1 = normal_quantile((1. - confidence_level) / 2.); + + auto cumn = [n]( double x ) -> long { + return std::lround( normal_cdf( x ) * static_cast(n) ); + }; + auto a = [bias, accel](double b) { return bias + b / (1. - accel * b); }; + double b1 = bias + z1; + double b2 = bias - z1; + double a1 = a(b1); + double a2 = a(b2); + auto lo = static_cast((std::max)(cumn(a1), 0l)); + auto hi = static_cast((std::min)(cumn(a2), n - 1)); + + return { point, resample[lo], resample[hi], confidence_level }; + } + + double outlier_variance(Estimate mean, Estimate stddev, int n); + + struct bootstrap_analysis { + Estimate mean; + Estimate standard_deviation; + double outlier_variance; + }; + + bootstrap_analysis analyse_samples(double confidence_level, unsigned int n_resamples, std::vector::iterator first, std::vector::iterator last); + } // namespace Detail + } // namespace Benchmark +} // namespace Catch + +#endif // CATCH_STATS_HPP_INCLUDED + +#include +#include +#include +#include + +namespace Catch { + namespace Benchmark { + namespace Detail { + template + std::vector resolution(int k) { + std::vector> times; + times.reserve(static_cast(k + 1)); + std::generate_n(std::back_inserter(times), k + 1, now{}); + + std::vector deltas; + deltas.reserve(static_cast(k)); + std::transform(std::next(times.begin()), times.end(), times.begin(), + std::back_inserter(deltas), + [](TimePoint a, TimePoint b) { return static_cast((a - b).count()); }); + + return deltas; + } + + const auto warmup_iterations = 10000; + const auto warmup_time = std::chrono::milliseconds(100); + const auto minimum_ticks = 1000; + const auto warmup_seed = 10000; + const auto clock_resolution_estimation_time = std::chrono::milliseconds(500); + const auto clock_cost_estimation_time_limit = std::chrono::seconds(1); + const auto clock_cost_estimation_tick_limit = 100000; + const auto clock_cost_estimation_time = std::chrono::milliseconds(10); + const auto clock_cost_estimation_iterations = 10000; + + template + int warmup() { + return run_for_at_least(std::chrono::duration_cast>(warmup_time), warmup_seed, &resolution) + .iterations; + } + template + EnvironmentEstimate> estimate_clock_resolution(int iterations) { + auto r = run_for_at_least(std::chrono::duration_cast>(clock_resolution_estimation_time), iterations, &resolution) + .result; + return { + FloatDuration(mean(r.begin(), r.end())), + classify_outliers(r.begin(), r.end()), + }; + } + template + EnvironmentEstimate> estimate_clock_cost(FloatDuration resolution) { + auto time_limit = (std::min)( + resolution * clock_cost_estimation_tick_limit, + FloatDuration(clock_cost_estimation_time_limit)); + auto time_clock = [](int k) { + return Detail::measure([k] { + for (int i = 0; i < k; ++i) { + volatile auto ignored = Clock::now(); + (void)ignored; + } + }).elapsed; + }; + time_clock(1); + int iters = clock_cost_estimation_iterations; + auto&& r = run_for_at_least(std::chrono::duration_cast>(clock_cost_estimation_time), iters, time_clock); + std::vector times; + int nsamples = static_cast(std::ceil(time_limit / r.elapsed)); + times.reserve(static_cast(nsamples)); + std::generate_n(std::back_inserter(times), nsamples, [time_clock, &r] { + return static_cast((time_clock(r.iterations) / r.iterations).count()); + }); + return { + FloatDuration(mean(times.begin(), times.end())), + classify_outliers(times.begin(), times.end()), + }; + } + + template + Environment> measure_environment() { +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +#endif + static Catch::Detail::unique_ptr>> env; +#if defined(__clang__) +# pragma clang diagnostic pop +#endif + if (env) { + return *env; + } + + auto iters = Detail::warmup(); + auto resolution = Detail::estimate_clock_resolution(iters); + auto cost = Detail::estimate_clock_cost(resolution.mean); + + env = Catch::Detail::make_unique>>( Environment>{resolution, cost} ); + return *env; + } + } // namespace Detail + } // namespace Benchmark +} // namespace Catch + +#endif // CATCH_ESTIMATE_CLOCK_HPP_INCLUDED + + +// Adapted from donated nonius code. + +#ifndef CATCH_ANALYSE_HPP_INCLUDED +#define CATCH_ANALYSE_HPP_INCLUDED + + + +// Adapted from donated nonius code. + +#ifndef CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED +#define CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED + + +#include +#include +#include + +namespace Catch { + namespace Benchmark { + template + struct SampleAnalysis { + std::vector samples; + Estimate mean; + Estimate standard_deviation; + OutlierClassification outliers; + double outlier_variance; + + template + operator SampleAnalysis() const { + std::vector samples2; + samples2.reserve(samples.size()); + std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](Duration d) { return Duration2(d); }); + return { + CATCH_MOVE(samples2), + mean, + standard_deviation, + outliers, + outlier_variance, + }; + } + }; + } // namespace Benchmark +} // namespace Catch + +#endif // CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED + +#include +#include +#include + +namespace Catch { + namespace Benchmark { + namespace Detail { + template + SampleAnalysis analyse(const IConfig &cfg, Environment, Iterator first, Iterator last) { + if (!cfg.benchmarkNoAnalysis()) { + std::vector samples; + samples.reserve(static_cast(last - first)); + std::transform(first, last, std::back_inserter(samples), [](Duration d) { return d.count(); }); + + auto analysis = Catch::Benchmark::Detail::analyse_samples(cfg.benchmarkConfidenceInterval(), cfg.benchmarkResamples(), samples.begin(), samples.end()); + auto outliers = Catch::Benchmark::Detail::classify_outliers(samples.begin(), samples.end()); + + auto wrap_estimate = [](Estimate e) { + return Estimate { + Duration(e.point), + Duration(e.lower_bound), + Duration(e.upper_bound), + e.confidence_interval, + }; + }; + std::vector samples2; + samples2.reserve(samples.size()); + std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](double d) { return Duration(d); }); + return { + CATCH_MOVE(samples2), + wrap_estimate(analysis.mean), + wrap_estimate(analysis.standard_deviation), + outliers, + analysis.outlier_variance, + }; + } else { + std::vector samples; + samples.reserve(static_cast(last - first)); + + Duration mean = Duration(0); + int i = 0; + for (auto it = first; it < last; ++it, ++i) { + samples.push_back(Duration(*it)); + mean += Duration(*it); + } + mean /= i; + + return { + CATCH_MOVE(samples), + Estimate{mean, mean, mean, 0.0}, + Estimate{Duration(0), Duration(0), Duration(0), 0.0}, + OutlierClassification{}, + 0.0 + }; + } + } + } // namespace Detail + } // namespace Benchmark +} // namespace Catch + +#endif // CATCH_ANALYSE_HPP_INCLUDED + +#include +#include +#include +#include +#include + +namespace Catch { + namespace Benchmark { + struct Benchmark { + Benchmark(std::string&& benchmarkName) + : name(CATCH_MOVE(benchmarkName)) {} + + template + Benchmark(std::string&& benchmarkName , FUN &&func) + : fun(CATCH_MOVE(func)), name(CATCH_MOVE(benchmarkName)) {} + + template + ExecutionPlan> prepare(const IConfig &cfg, Environment> env) const { + auto min_time = env.clock_resolution.mean * Detail::minimum_ticks; + auto run_time = std::max(min_time, std::chrono::duration_cast(cfg.benchmarkWarmupTime())); + auto&& test = Detail::run_for_at_least(std::chrono::duration_cast>(run_time), 1, fun); + int new_iters = static_cast(std::ceil(min_time * test.iterations / test.elapsed)); + return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), fun, std::chrono::duration_cast>(cfg.benchmarkWarmupTime()), Detail::warmup_iterations }; + } + + template + void run() { + auto const* cfg = getCurrentContext().getConfig(); + + auto env = Detail::measure_environment(); + + getResultCapture().benchmarkPreparing(name); + CATCH_TRY{ + auto plan = user_code([&] { + return prepare(*cfg, env); + }); + + BenchmarkInfo info { + name, + plan.estimated_duration.count(), + plan.iterations_per_sample, + cfg->benchmarkSamples(), + cfg->benchmarkResamples(), + env.clock_resolution.mean.count(), + env.clock_cost.mean.count() + }; + + getResultCapture().benchmarkStarting(info); + + auto samples = user_code([&] { + return plan.template run(*cfg, env); + }); + + auto analysis = Detail::analyse(*cfg, env, samples.begin(), samples.end()); + BenchmarkStats> stats{ info, analysis.samples, analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance }; + getResultCapture().benchmarkEnded(stats); + } CATCH_CATCH_ANON (TestFailureException) { + getResultCapture().benchmarkFailed("Benchmark failed due to failed assertion"_sr); + } CATCH_CATCH_ALL{ + getResultCapture().benchmarkFailed(translateActiveException()); + // We let the exception go further up so that the + // test case is marked as failed. + std::rethrow_exception(std::current_exception()); + } + } + + // sets lambda to be used in fun *and* executes benchmark! + template ::value, int> = 0> + Benchmark & operator=(Fun func) { + auto const* cfg = getCurrentContext().getConfig(); + if (!cfg->skipBenchmarks()) { + fun = Detail::BenchmarkFunction(func); + run(); + } + return *this; + } + + explicit operator bool() { + return true; + } + + private: + Detail::BenchmarkFunction fun; + std::string name; + }; + } +} // namespace Catch + +#define INTERNAL_CATCH_GET_1_ARG(arg1, arg2, ...) arg1 +#define INTERNAL_CATCH_GET_2_ARG(arg1, arg2, ...) arg2 + +#define INTERNAL_CATCH_BENCHMARK(BenchmarkName, name, benchmarkIndex)\ + if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \ + BenchmarkName = [&](int benchmarkIndex) + +#define INTERNAL_CATCH_BENCHMARK_ADVANCED(BenchmarkName, name)\ + if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \ + BenchmarkName = [&] + +#if defined(CATCH_CONFIG_PREFIX_ALL) + +#define CATCH_BENCHMARK(...) \ + INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,)) +#define CATCH_BENCHMARK_ADVANCED(name) \ + INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), name) + +#else + +#define BENCHMARK(...) \ + INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,)) +#define BENCHMARK_ADVANCED(name) \ + INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), name) + +#endif + +#endif // CATCH_BENCHMARK_HPP_INCLUDED + + +// Adapted from donated nonius code. + +#ifndef CATCH_CONSTRUCTOR_HPP_INCLUDED +#define CATCH_CONSTRUCTOR_HPP_INCLUDED + + +#include + +namespace Catch { + namespace Benchmark { + namespace Detail { + template + struct ObjectStorage + { + ObjectStorage() = default; + + ObjectStorage(const ObjectStorage& other) + { + new(&data) T(other.stored_object()); + } + + ObjectStorage(ObjectStorage&& other) + { + new(data) T(CATCH_MOVE(other.stored_object())); + } + + ~ObjectStorage() { destruct_on_exit(); } + + template + void construct(Args&&... args) + { + new (data) T(CATCH_FORWARD(args)...); + } + + template + std::enable_if_t destruct() + { + stored_object().~T(); + } + + private: + // If this is a constructor benchmark, destruct the underlying object + template + void destruct_on_exit(std::enable_if_t* = nullptr) { destruct(); } + // Otherwise, don't + template + void destruct_on_exit(std::enable_if_t* = nullptr) { } + +#if defined( __GNUC__ ) && __GNUC__ <= 6 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wstrict-aliasing" +#endif + T& stored_object() { return *reinterpret_cast( data ); } + + T const& stored_object() const { + return *reinterpret_cast( data ); + } +#if defined( __GNUC__ ) && __GNUC__ <= 6 +# pragma GCC diagnostic pop +#endif + + alignas( T ) unsigned char data[sizeof( T )]{}; + }; + } // namespace Detail + + template + using storage_for = Detail::ObjectStorage; + + template + using destructable_object = Detail::ObjectStorage; + } // namespace Benchmark +} // namespace Catch + +#endif // CATCH_CONSTRUCTOR_HPP_INCLUDED + +#endif // CATCH_BENCHMARK_ALL_HPP_INCLUDED + + +#ifndef CATCH_APPROX_HPP_INCLUDED +#define CATCH_APPROX_HPP_INCLUDED + + + +#ifndef CATCH_TOSTRING_HPP_INCLUDED +#define CATCH_TOSTRING_HPP_INCLUDED + + +#include +#include +#include +#include + + + + +/** \file + * Wrapper for the WCHAR configuration option + * + * We want to support platforms that do not provide `wchar_t`, so we + * sometimes have to disable providing wchar_t overloads through Catch2, + * e.g. the StringMaker specialization for `std::wstring`. + */ + +#ifndef CATCH_CONFIG_WCHAR_HPP_INCLUDED +#define CATCH_CONFIG_WCHAR_HPP_INCLUDED + +// We assume that WCHAR should be enabled by default, and only disabled +// for a shortlist (so far only DJGPP) of compilers. + +#if defined(__DJGPP__) +# define CATCH_INTERNAL_CONFIG_NO_WCHAR +#endif // __DJGPP__ + +#if !defined( CATCH_INTERNAL_CONFIG_NO_WCHAR ) && \ + !defined( CATCH_CONFIG_NO_WCHAR ) && \ + !defined( CATCH_CONFIG_WCHAR ) +# define CATCH_CONFIG_WCHAR +#endif + +#endif // CATCH_CONFIG_WCHAR_HPP_INCLUDED + + +#ifndef CATCH_REUSABLE_STRING_STREAM_HPP_INCLUDED +#define CATCH_REUSABLE_STRING_STREAM_HPP_INCLUDED + + +#include +#include +#include +#include + +namespace Catch { + + class ReusableStringStream : Detail::NonCopyable { + std::size_t m_index; + std::ostream* m_oss; + public: + ReusableStringStream(); + ~ReusableStringStream(); + + //! Returns the serialized state + std::string str() const; + //! Sets internal state to `str` + void str(std::string const& str); + +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +// Old versions of GCC do not understand -Wnonnull-compare +#pragma GCC diagnostic ignored "-Wpragmas" +// Streaming a function pointer triggers Waddress and Wnonnull-compare +// on GCC, because it implicitly converts it to bool and then decides +// that the check it uses (a? true : false) is tautological and cannot +// be null... +#pragma GCC diagnostic ignored "-Waddress" +#pragma GCC diagnostic ignored "-Wnonnull-compare" +#endif + + template + auto operator << ( T const& value ) -> ReusableStringStream& { + *m_oss << value; + return *this; + } + +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + auto get() -> std::ostream& { return *m_oss; } + }; +} + +#endif // CATCH_REUSABLE_STRING_STREAM_HPP_INCLUDED + + +#ifndef CATCH_VOID_TYPE_HPP_INCLUDED +#define CATCH_VOID_TYPE_HPP_INCLUDED + + +namespace Catch { + namespace Detail { + + template + struct make_void { using type = void; }; + + template + using void_t = typename make_void::type; + + } // namespace Detail +} // namespace Catch + + +#endif // CATCH_VOID_TYPE_HPP_INCLUDED + + +#ifndef CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED +#define CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED + + +#include + +namespace Catch { + + namespace Detail { + struct EnumInfo { + StringRef m_name; + std::vector> m_values; + + ~EnumInfo(); + + StringRef lookup( int value ) const; + }; + } // namespace Detail + + class IMutableEnumValuesRegistry { + public: + virtual ~IMutableEnumValuesRegistry(); // = default; + + virtual Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector const& values ) = 0; + + template + Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::initializer_list values ) { + static_assert(sizeof(int) >= sizeof(E), "Cannot serialize enum to int"); + std::vector intValues; + intValues.reserve( values.size() ); + for( auto enumValue : values ) + intValues.push_back( static_cast( enumValue ) ); + return registerEnum( enumName, allEnums, intValues ); + } + }; + +} // Catch + +#endif // CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED + +#ifdef CATCH_CONFIG_CPP17_STRING_VIEW +#include +#endif + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4180) // We attempt to stream a function (address) by const&, which MSVC complains about but is harmless +#endif + +// We need a dummy global operator<< so we can bring it into Catch namespace later +struct Catch_global_namespace_dummy{}; +std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); + +namespace Catch { + // Bring in global namespace operator<< for ADL lookup in + // `IsStreamInsertable` below. + using ::operator<<; + + namespace Detail { + + inline std::size_t catch_strnlen(const char *str, std::size_t n) { + auto ret = std::char_traits::find(str, n, '\0'); + if (ret != nullptr) { + return static_cast(ret - str); + } + return n; + } + + constexpr StringRef unprintableString = "{?}"_sr; + + //! Encases `string in quotes, and optionally escapes invisibles + std::string convertIntoString( StringRef string, bool escapeInvisibles ); + + //! Encases `string` in quotes, and escapes invisibles if user requested + //! it via CLI + std::string convertIntoString( StringRef string ); + + std::string rawMemoryToString( const void *object, std::size_t size ); + + template + std::string rawMemoryToString( const T& object ) { + return rawMemoryToString( &object, sizeof(object) ); + } + + template + class IsStreamInsertable { + template + static auto test(int) + -> decltype(std::declval() << std::declval(), std::true_type()); + + template + static auto test(...)->std::false_type; + + public: + static const bool value = decltype(test(0))::value; + }; + + template + std::string convertUnknownEnumToString( E e ); + + template + std::enable_if_t< + !std::is_enum::value && !std::is_base_of::value, + std::string> convertUnstreamable( T const& ) { + return std::string(Detail::unprintableString); + } + template + std::enable_if_t< + !std::is_enum::value && std::is_base_of::value, + std::string> convertUnstreamable(T const& ex) { + return ex.what(); + } + + + template + std::enable_if_t< + std::is_enum::value, + std::string> convertUnstreamable( T const& value ) { + return convertUnknownEnumToString( value ); + } + +#if defined(_MANAGED) + //! Convert a CLR string to a utf8 std::string + template + std::string clrReferenceToString( T^ ref ) { + if (ref == nullptr) + return std::string("null"); + auto bytes = System::Text::Encoding::UTF8->GetBytes(ref->ToString()); + cli::pin_ptr p = &bytes[0]; + return std::string(reinterpret_cast(p), bytes->Length); + } +#endif + + } // namespace Detail + + + // If we decide for C++14, change these to enable_if_ts + template + struct StringMaker { + template + static + std::enable_if_t<::Catch::Detail::IsStreamInsertable::value, std::string> + convert(const Fake& value) { + ReusableStringStream rss; + // NB: call using the function-like syntax to avoid ambiguity with + // user-defined templated operator<< under clang. + rss.operator<<(value); + return rss.str(); + } + + template + static + std::enable_if_t::value, std::string> + convert( const Fake& value ) { +#if !defined(CATCH_CONFIG_FALLBACK_STRINGIFIER) + return Detail::convertUnstreamable(value); +#else + return CATCH_CONFIG_FALLBACK_STRINGIFIER(value); +#endif + } + }; + + namespace Detail { + + // This function dispatches all stringification requests inside of Catch. + // Should be preferably called fully qualified, like ::Catch::Detail::stringify + template + std::string stringify(const T& e) { + return ::Catch::StringMaker>>::convert(e); + } + + template + std::string convertUnknownEnumToString( E e ) { + return ::Catch::Detail::stringify(static_cast>(e)); + } + +#if defined(_MANAGED) + template + std::string stringify( T^ e ) { + return ::Catch::StringMaker::convert(e); + } +#endif + + } // namespace Detail + + // Some predefined specializations + + template<> + struct StringMaker { + static std::string convert(const std::string& str); + }; + +#ifdef CATCH_CONFIG_CPP17_STRING_VIEW + template<> + struct StringMaker { + static std::string convert(std::string_view str); + }; +#endif + + template<> + struct StringMaker { + static std::string convert(char const * str); + }; + template<> + struct StringMaker { + static std::string convert(char * str); + }; + +#if defined(CATCH_CONFIG_WCHAR) + template<> + struct StringMaker { + static std::string convert(const std::wstring& wstr); + }; + +# ifdef CATCH_CONFIG_CPP17_STRING_VIEW + template<> + struct StringMaker { + static std::string convert(std::wstring_view str); + }; +# endif + + template<> + struct StringMaker { + static std::string convert(wchar_t const * str); + }; + template<> + struct StringMaker { + static std::string convert(wchar_t * str); + }; +#endif // CATCH_CONFIG_WCHAR + + template + struct StringMaker { + static std::string convert(char const* str) { + return Detail::convertIntoString( + StringRef( str, Detail::catch_strnlen( str, SZ ) ) ); + } + }; + template + struct StringMaker { + static std::string convert(signed char const* str) { + auto reinterpreted = reinterpret_cast(str); + return Detail::convertIntoString( + StringRef(reinterpreted, Detail::catch_strnlen(reinterpreted, SZ))); + } + }; + template + struct StringMaker { + static std::string convert(unsigned char const* str) { + auto reinterpreted = reinterpret_cast(str); + return Detail::convertIntoString( + StringRef(reinterpreted, Detail::catch_strnlen(reinterpreted, SZ))); + } + }; + +#if defined(CATCH_CONFIG_CPP17_BYTE) + template<> + struct StringMaker { + static std::string convert(std::byte value); + }; +#endif // defined(CATCH_CONFIG_CPP17_BYTE) + template<> + struct StringMaker { + static std::string convert(int value); + }; + template<> + struct StringMaker { + static std::string convert(long value); + }; + template<> + struct StringMaker { + static std::string convert(long long value); + }; + template<> + struct StringMaker { + static std::string convert(unsigned int value); + }; + template<> + struct StringMaker { + static std::string convert(unsigned long value); + }; + template<> + struct StringMaker { + static std::string convert(unsigned long long value); + }; + + template<> + struct StringMaker { + static std::string convert(bool b) { + using namespace std::string_literals; + return b ? "true"s : "false"s; + } + }; + + template<> + struct StringMaker { + static std::string convert(char c); + }; + template<> + struct StringMaker { + static std::string convert(signed char c); + }; + template<> + struct StringMaker { + static std::string convert(unsigned char c); + }; + + template<> + struct StringMaker { + static std::string convert(std::nullptr_t) { + using namespace std::string_literals; + return "nullptr"s; + } + }; + + template<> + struct StringMaker { + static std::string convert(float value); + CATCH_EXPORT static int precision; + }; + + template<> + struct StringMaker { + static std::string convert(double value); + CATCH_EXPORT static int precision; + }; + + template + struct StringMaker { + template + static std::string convert(U* p) { + if (p) { + return ::Catch::Detail::rawMemoryToString(p); + } else { + return "nullptr"; + } + } + }; + + template + struct StringMaker { + static std::string convert(R C::* p) { + if (p) { + return ::Catch::Detail::rawMemoryToString(p); + } else { + return "nullptr"; + } + } + }; + +#if defined(_MANAGED) + template + struct StringMaker { + static std::string convert( T^ ref ) { + return ::Catch::Detail::clrReferenceToString(ref); + } + }; +#endif + + namespace Detail { + template + std::string rangeToString(InputIterator first, Sentinel last) { + ReusableStringStream rss; + rss << "{ "; + if (first != last) { + rss << ::Catch::Detail::stringify(*first); + for (++first; first != last; ++first) + rss << ", " << ::Catch::Detail::stringify(*first); + } + rss << " }"; + return rss.str(); + } + } + +} // namespace Catch + +////////////////////////////////////////////////////// +// Separate std-lib types stringification, so it can be selectively enabled +// This means that we do not bring in their headers + +#if defined(CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS) +# define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER +# define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER +# define CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER +# define CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER +#endif + +// Separate std::pair specialization +#if defined(CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER) +#include +namespace Catch { + template + struct StringMaker > { + static std::string convert(const std::pair& pair) { + ReusableStringStream rss; + rss << "{ " + << ::Catch::Detail::stringify(pair.first) + << ", " + << ::Catch::Detail::stringify(pair.second) + << " }"; + return rss.str(); + } + }; +} +#endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER + +#if defined(CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_OPTIONAL) +#include +namespace Catch { + template + struct StringMaker > { + static std::string convert(const std::optional& optional) { + if (optional.has_value()) { + return ::Catch::Detail::stringify(*optional); + } else { + return "{ }"; + } + } + }; +} +#endif // CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER + +// Separate std::tuple specialization +#if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER) +#include +namespace Catch { + namespace Detail { + template< + typename Tuple, + std::size_t N = 0, + bool = (N < std::tuple_size::value) + > + struct TupleElementPrinter { + static void print(const Tuple& tuple, std::ostream& os) { + os << (N ? ", " : " ") + << ::Catch::Detail::stringify(std::get(tuple)); + TupleElementPrinter::print(tuple, os); + } + }; + + template< + typename Tuple, + std::size_t N + > + struct TupleElementPrinter { + static void print(const Tuple&, std::ostream&) {} + }; + + } + + + template + struct StringMaker> { + static std::string convert(const std::tuple& tuple) { + ReusableStringStream rss; + rss << '{'; + Detail::TupleElementPrinter>::print(tuple, rss.get()); + rss << " }"; + return rss.str(); + } + }; +} +#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER + +#if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT) +#include +namespace Catch { + template<> + struct StringMaker { + static std::string convert(const std::monostate&) { + return "{ }"; + } + }; + + template + struct StringMaker> { + static std::string convert(const std::variant& variant) { + if (variant.valueless_by_exception()) { + return "{valueless variant}"; + } else { + return std::visit( + [](const auto& value) { + return ::Catch::Detail::stringify(value); + }, + variant + ); + } + } + }; +} +#endif // CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER + +namespace Catch { + // Import begin/ end from std here + using std::begin; + using std::end; + + namespace Detail { + template + struct is_range_impl : std::false_type {}; + + template + struct is_range_impl()))>> : std::true_type {}; + } // namespace Detail + + template + struct is_range : Detail::is_range_impl {}; + +#if defined(_MANAGED) // Managed types are never ranges + template + struct is_range { + static const bool value = false; + }; +#endif + + template + std::string rangeToString( Range const& range ) { + return ::Catch::Detail::rangeToString( begin( range ), end( range ) ); + } + + // Handle vector specially + template + std::string rangeToString( std::vector const& v ) { + ReusableStringStream rss; + rss << "{ "; + bool first = true; + for( bool b : v ) { + if( first ) + first = false; + else + rss << ", "; + rss << ::Catch::Detail::stringify( b ); + } + rss << " }"; + return rss.str(); + } + + template + struct StringMaker::value && !::Catch::Detail::IsStreamInsertable::value>> { + static std::string convert( R const& range ) { + return rangeToString( range ); + } + }; + + template + struct StringMaker { + static std::string convert(T const(&arr)[SZ]) { + return rangeToString(arr); + } + }; + + +} // namespace Catch + +// Separate std::chrono::duration specialization +#include +#include +#include + + +namespace Catch { + +template +struct ratio_string { + static std::string symbol() { + Catch::ReusableStringStream rss; + rss << '[' << Ratio::num << '/' + << Ratio::den << ']'; + return rss.str(); + } +}; + +template <> +struct ratio_string { + static char symbol() { return 'a'; } +}; +template <> +struct ratio_string { + static char symbol() { return 'f'; } +}; +template <> +struct ratio_string { + static char symbol() { return 'p'; } +}; +template <> +struct ratio_string { + static char symbol() { return 'n'; } +}; +template <> +struct ratio_string { + static char symbol() { return 'u'; } +}; +template <> +struct ratio_string { + static char symbol() { return 'm'; } +}; + + //////////// + // std::chrono::duration specializations + template + struct StringMaker> { + static std::string convert(std::chrono::duration const& duration) { + ReusableStringStream rss; + rss << duration.count() << ' ' << ratio_string::symbol() << 's'; + return rss.str(); + } + }; + template + struct StringMaker>> { + static std::string convert(std::chrono::duration> const& duration) { + ReusableStringStream rss; + rss << duration.count() << " s"; + return rss.str(); + } + }; + template + struct StringMaker>> { + static std::string convert(std::chrono::duration> const& duration) { + ReusableStringStream rss; + rss << duration.count() << " m"; + return rss.str(); + } + }; + template + struct StringMaker>> { + static std::string convert(std::chrono::duration> const& duration) { + ReusableStringStream rss; + rss << duration.count() << " h"; + return rss.str(); + } + }; + + //////////// + // std::chrono::time_point specialization + // Generic time_point cannot be specialized, only std::chrono::time_point + template + struct StringMaker> { + static std::string convert(std::chrono::time_point const& time_point) { + return ::Catch::Detail::stringify(time_point.time_since_epoch()) + " since epoch"; + } + }; + // std::chrono::time_point specialization + template + struct StringMaker> { + static std::string convert(std::chrono::time_point const& time_point) { + auto converted = std::chrono::system_clock::to_time_t(time_point); + +#ifdef _MSC_VER + std::tm timeInfo = {}; + gmtime_s(&timeInfo, &converted); +#else + std::tm* timeInfo = std::gmtime(&converted); +#endif + + auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); + char timeStamp[timeStampSize]; + const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; + +#ifdef _MSC_VER + std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); +#else + std::strftime(timeStamp, timeStampSize, fmt, timeInfo); +#endif + return std::string(timeStamp, timeStampSize - 1); + } + }; +} + + +#define INTERNAL_CATCH_REGISTER_ENUM( enumName, ... ) \ +namespace Catch { \ + template<> struct StringMaker { \ + static std::string convert( enumName value ) { \ + static const auto& enumInfo = ::Catch::getMutableRegistryHub().getMutableEnumValuesRegistry().registerEnum( #enumName, #__VA_ARGS__, { __VA_ARGS__ } ); \ + return static_cast(enumInfo.lookup( static_cast( value ) )); \ + } \ + }; \ +} + +#define CATCH_REGISTER_ENUM( enumName, ... ) INTERNAL_CATCH_REGISTER_ENUM( enumName, __VA_ARGS__ ) + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif // CATCH_TOSTRING_HPP_INCLUDED + +#include + +namespace Catch { + + class Approx { + private: + bool equalityComparisonImpl(double other) const; + // Sets and validates the new margin (margin >= 0) + void setMargin(double margin); + // Sets and validates the new epsilon (0 < epsilon < 1) + void setEpsilon(double epsilon); + + public: + explicit Approx ( double value ); + + static Approx custom(); + + Approx operator-() const; + + template ::value>> + Approx operator()( T const& value ) const { + Approx approx( static_cast(value) ); + approx.m_epsilon = m_epsilon; + approx.m_margin = m_margin; + approx.m_scale = m_scale; + return approx; + } + + template ::value>> + explicit Approx( T const& value ): Approx(static_cast(value)) + {} + + + template ::value>> + friend bool operator == ( const T& lhs, Approx const& rhs ) { + auto lhs_v = static_cast(lhs); + return rhs.equalityComparisonImpl(lhs_v); + } + + template ::value>> + friend bool operator == ( Approx const& lhs, const T& rhs ) { + return operator==( rhs, lhs ); + } + + template ::value>> + friend bool operator != ( T const& lhs, Approx const& rhs ) { + return !operator==( lhs, rhs ); + } + + template ::value>> + friend bool operator != ( Approx const& lhs, T const& rhs ) { + return !operator==( rhs, lhs ); + } + + template ::value>> + friend bool operator <= ( T const& lhs, Approx const& rhs ) { + return static_cast(lhs) < rhs.m_value || lhs == rhs; + } + + template ::value>> + friend bool operator <= ( Approx const& lhs, T const& rhs ) { + return lhs.m_value < static_cast(rhs) || lhs == rhs; + } + + template ::value>> + friend bool operator >= ( T const& lhs, Approx const& rhs ) { + return static_cast(lhs) > rhs.m_value || lhs == rhs; + } + + template ::value>> + friend bool operator >= ( Approx const& lhs, T const& rhs ) { + return lhs.m_value > static_cast(rhs) || lhs == rhs; + } + + template ::value>> + Approx& epsilon( T const& newEpsilon ) { + const auto epsilonAsDouble = static_cast(newEpsilon); + setEpsilon(epsilonAsDouble); + return *this; + } + + template ::value>> + Approx& margin( T const& newMargin ) { + const auto marginAsDouble = static_cast(newMargin); + setMargin(marginAsDouble); + return *this; + } + + template ::value>> + Approx& scale( T const& newScale ) { + m_scale = static_cast(newScale); + return *this; + } + + std::string toString() const; + + private: + double m_epsilon; + double m_margin; + double m_scale; + double m_value; + }; + +namespace literals { + Approx operator ""_a(long double val); + Approx operator ""_a(unsigned long long val); +} // end namespace literals + +template<> +struct StringMaker { + static std::string convert(Catch::Approx const& value); +}; + +} // end namespace Catch + +#endif // CATCH_APPROX_HPP_INCLUDED + + +#ifndef CATCH_CONFIG_HPP_INCLUDED +#define CATCH_CONFIG_HPP_INCLUDED + + + +#ifndef CATCH_TEST_SPEC_HPP_INCLUDED +#define CATCH_TEST_SPEC_HPP_INCLUDED + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + + + +#ifndef CATCH_WILDCARD_PATTERN_HPP_INCLUDED +#define CATCH_WILDCARD_PATTERN_HPP_INCLUDED + + + +#ifndef CATCH_CASE_SENSITIVE_HPP_INCLUDED +#define CATCH_CASE_SENSITIVE_HPP_INCLUDED + +namespace Catch { + + enum class CaseSensitive { Yes, No }; + +} // namespace Catch + +#endif // CATCH_CASE_SENSITIVE_HPP_INCLUDED + +#include + +namespace Catch +{ + class WildcardPattern { + enum WildcardPosition { + NoWildcard = 0, + WildcardAtStart = 1, + WildcardAtEnd = 2, + WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd + }; + + public: + + WildcardPattern( std::string const& pattern, CaseSensitive caseSensitivity ); + bool matches( std::string const& str ) const; + + private: + std::string normaliseString( std::string const& str ) const; + CaseSensitive m_caseSensitivity; + WildcardPosition m_wildcard = NoWildcard; + std::string m_pattern; + }; +} + +#endif // CATCH_WILDCARD_PATTERN_HPP_INCLUDED + +#include +#include + +namespace Catch { + + class IConfig; + struct TestCaseInfo; + class TestCaseHandle; + + class TestSpec { + + class Pattern { + public: + explicit Pattern( std::string const& name ); + virtual ~Pattern(); + virtual bool matches( TestCaseInfo const& testCase ) const = 0; + std::string const& name() const; + private: + std::string const m_name; + }; + + class NamePattern : public Pattern { + public: + explicit NamePattern( std::string const& name, std::string const& filterString ); + bool matches( TestCaseInfo const& testCase ) const override; + private: + WildcardPattern m_wildcardPattern; + }; + + class TagPattern : public Pattern { + public: + explicit TagPattern( std::string const& tag, std::string const& filterString ); + bool matches( TestCaseInfo const& testCase ) const override; + private: + std::string m_tag; + }; + + struct Filter { + std::vector> m_required; + std::vector> m_forbidden; + + bool matches( TestCaseInfo const& testCase ) const; + std::string name() const; + }; + + public: + struct FilterMatch { + std::string name; + std::vector tests; + }; + using Matches = std::vector; + using vectorStrings = std::vector; + + bool hasFilters() const; + bool matches( TestCaseInfo const& testCase ) const; + Matches matchesByFilter( std::vector const& testCases, IConfig const& config ) const; + const vectorStrings & getInvalidSpecs() const; + + private: + std::vector m_filters; + std::vector m_invalidSpecs; + friend class TestSpecParser; + }; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#endif // CATCH_TEST_SPEC_HPP_INCLUDED + + +#ifndef CATCH_OPTIONAL_HPP_INCLUDED +#define CATCH_OPTIONAL_HPP_INCLUDED + +#include + +namespace Catch { + + // An optional type + template + class Optional { + public: + Optional() : nullableValue( nullptr ) {} + Optional( T const& _value ) + : nullableValue( new( storage ) T( _value ) ) + {} + Optional( Optional const& _other ) + : nullableValue( _other ? new( storage ) T( *_other ) : nullptr ) + {} + + ~Optional() { + reset(); + } + + Optional& operator= ( Optional const& _other ) { + if( &_other != this ) { + reset(); + if( _other ) + nullableValue = new( storage ) T( *_other ); + } + return *this; + } + Optional& operator = ( T const& _value ) { + reset(); + nullableValue = new( storage ) T( _value ); + return *this; + } + + void reset() { + if( nullableValue ) + nullableValue->~T(); + nullableValue = nullptr; + } + + T& operator*() { + assert(nullableValue); + return *nullableValue; + } + T const& operator*() const { + assert(nullableValue); + return *nullableValue; + } + T* operator->() { + assert(nullableValue); + return nullableValue; + } + const T* operator->() const { + assert(nullableValue); + return nullableValue; + } + + T valueOr( T const& defaultValue ) const { + return nullableValue ? *nullableValue : defaultValue; + } + + bool some() const { return nullableValue != nullptr; } + bool none() const { return nullableValue == nullptr; } + + bool operator !() const { return nullableValue == nullptr; } + explicit operator bool() const { + return some(); + } + + friend bool operator==(Optional const& a, Optional const& b) { + if (a.none() && b.none()) { + return true; + } else if (a.some() && b.some()) { + return *a == *b; + } else { + return false; + } + } + friend bool operator!=(Optional const& a, Optional const& b) { + return !( a == b ); + } + + private: + T *nullableValue; + alignas(alignof(T)) char storage[sizeof(T)]; + }; + +} // end namespace Catch + +#endif // CATCH_OPTIONAL_HPP_INCLUDED + + +#ifndef CATCH_RANDOM_SEED_GENERATION_HPP_INCLUDED +#define CATCH_RANDOM_SEED_GENERATION_HPP_INCLUDED + +#include + +namespace Catch { + + enum class GenerateFrom { + Time, + RandomDevice, + //! Currently equivalent to RandomDevice, but can change at any point + Default + }; + + std::uint32_t generateRandomSeed(GenerateFrom from); + +} // end namespace Catch + +#endif // CATCH_RANDOM_SEED_GENERATION_HPP_INCLUDED + + +#ifndef CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED +#define CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED + + + +#ifndef CATCH_CONSOLE_COLOUR_HPP_INCLUDED +#define CATCH_CONSOLE_COLOUR_HPP_INCLUDED + + +#include +#include + +namespace Catch { + + enum class ColourMode : std::uint8_t; + class IStream; + + struct Colour { + enum Code { + None = 0, + + White, + Red, + Green, + Blue, + Cyan, + Yellow, + Grey, + + Bright = 0x10, + + BrightRed = Bright | Red, + BrightGreen = Bright | Green, + LightGrey = Bright | Grey, + BrightWhite = Bright | White, + BrightYellow = Bright | Yellow, + + // By intention + FileName = LightGrey, + Warning = BrightYellow, + ResultError = BrightRed, + ResultSuccess = BrightGreen, + ResultExpectedFailure = Warning, + + Error = BrightRed, + Success = Green, + + OriginalExpression = Cyan, + ReconstructedExpression = BrightYellow, + + SecondaryText = LightGrey, + Headers = White + }; + }; + + class ColourImpl { + protected: + //! The associated stream of this ColourImpl instance + IStream* m_stream; + public: + ColourImpl( IStream* stream ): m_stream( stream ) {} + + //! RAII wrapper around writing specific colour of text using specific + //! colour impl into a stream. + class ColourGuard { + ColourImpl const* m_colourImpl; + Colour::Code m_code; + bool m_engaged = false; + + public: + //! Does **not** engage the guard/start the colour + ColourGuard( Colour::Code code, + ColourImpl const* colour ); + + ColourGuard( ColourGuard const& rhs ) = delete; + ColourGuard& operator=( ColourGuard const& rhs ) = delete; + + ColourGuard( ColourGuard&& rhs ) noexcept; + ColourGuard& operator=( ColourGuard&& rhs ) noexcept; + + //! Removes colour _if_ the guard was engaged + ~ColourGuard(); + + /** + * Explicitly engages colour for given stream. + * + * The API based on operator<< should be preferred. + */ + ColourGuard& engage( std::ostream& stream ) &; + /** + * Explicitly engages colour for given stream. + * + * The API based on operator<< should be preferred. + */ + ColourGuard&& engage( std::ostream& stream ) &&; + + private: + //! Engages the guard and starts using colour + friend std::ostream& operator<<( std::ostream& lhs, + ColourGuard& guard ) { + guard.engageImpl( lhs ); + return lhs; + } + //! Engages the guard and starts using colour + friend std::ostream& operator<<( std::ostream& lhs, + ColourGuard&& guard) { + guard.engageImpl( lhs ); + return lhs; + } + + void engageImpl( std::ostream& stream ); + + }; + + virtual ~ColourImpl(); // = default + /** + * Creates a guard object for given colour and this colour impl + * + * **Important:** + * the guard starts disengaged, and has to be engaged explicitly. + */ + ColourGuard guardColour( Colour::Code colourCode ); + + private: + virtual void use( Colour::Code colourCode ) const = 0; + }; + + //! Provides ColourImpl based on global config and target compilation platform + Detail::unique_ptr makeColourImpl( ColourMode colourSelection, + IStream* stream ); + + //! Checks if specific colour impl has been compiled into the binary + bool isColourImplAvailable( ColourMode colourSelection ); + +} // end namespace Catch + +#endif // CATCH_CONSOLE_COLOUR_HPP_INCLUDED + +#include +#include +#include + +namespace Catch { + + enum class ColourMode : std::uint8_t; + + namespace Detail { + //! Splits the reporter spec into reporter name and kv-pair options + std::vector splitReporterSpec( StringRef reporterSpec ); + + Optional stringToColourMode( StringRef colourMode ); + } + + /** + * Structured reporter spec that a reporter can be created from + * + * Parsing has been validated, but semantics have not. This means e.g. + * that the colour mode is known to Catch2, but it might not be + * compiled into the binary, and the output filename might not be + * openable. + */ + class ReporterSpec { + std::string m_name; + Optional m_outputFileName; + Optional m_colourMode; + std::map m_customOptions; + + friend bool operator==( ReporterSpec const& lhs, + ReporterSpec const& rhs ); + friend bool operator!=( ReporterSpec const& lhs, + ReporterSpec const& rhs ) { + return !( lhs == rhs ); + } + + public: + ReporterSpec( + std::string name, + Optional outputFileName, + Optional colourMode, + std::map customOptions ); + + std::string const& name() const { return m_name; } + + Optional const& outputFile() const { + return m_outputFileName; + } + + Optional const& colourMode() const { return m_colourMode; } + + std::map const& customOptions() const { + return m_customOptions; + } + }; + + /** + * Parses provided reporter spec string into + * + * Returns empty optional on errors, e.g. + * * field that is not first and not a key+value pair + * * duplicated keys in kv pair + * * unknown catch reporter option + * * empty key/value in an custom kv pair + * * ... + */ + Optional parseReporterSpec( StringRef reporterSpec ); + +} + +#endif // CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED + +#include +#include +#include +#include + +namespace Catch { + + class IStream; + + /** + * `ReporterSpec` but with the defaults filled in. + * + * Like `ReporterSpec`, the semantics are unchecked. + */ + struct ProcessedReporterSpec { + std::string name; + std::string outputFilename; + ColourMode colourMode; + std::map customOptions; + friend bool operator==( ProcessedReporterSpec const& lhs, + ProcessedReporterSpec const& rhs ); + friend bool operator!=( ProcessedReporterSpec const& lhs, + ProcessedReporterSpec const& rhs ) { + return !( lhs == rhs ); + } + }; + + struct ConfigData { + + bool listTests = false; + bool listTags = false; + bool listReporters = false; + bool listListeners = false; + + bool showSuccessfulTests = false; + bool shouldDebugBreak = false; + bool noThrow = false; + bool showHelp = false; + bool showInvisibles = false; + bool filenamesAsTags = false; + bool libIdentify = false; + bool allowZeroTests = false; + + int abortAfter = -1; + uint32_t rngSeed = generateRandomSeed(GenerateFrom::Default); + + unsigned int shardCount = 1; + unsigned int shardIndex = 0; + + bool skipBenchmarks = false; + bool benchmarkNoAnalysis = false; + unsigned int benchmarkSamples = 100; + double benchmarkConfidenceInterval = 0.95; + unsigned int benchmarkResamples = 100000; + std::chrono::milliseconds::rep benchmarkWarmupTime = 100; + + Verbosity verbosity = Verbosity::Normal; + WarnAbout::What warnings = WarnAbout::Nothing; + ShowDurations showDurations = ShowDurations::DefaultForReporter; + double minDuration = -1; + TestRunOrder runOrder = TestRunOrder::Declared; + ColourMode defaultColourMode = ColourMode::PlatformDefault; + WaitForKeypress::When waitForKeypress = WaitForKeypress::Never; + + std::string defaultOutputFilename; + std::string name; + std::string processName; + std::vector reporterSpecifications; + + std::vector testsOrTags; + std::vector sectionsToRun; + }; + + + class Config : public IConfig { + public: + + Config() = default; + Config( ConfigData const& data ); + ~Config() override; // = default in the cpp file + + bool listTests() const; + bool listTags() const; + bool listReporters() const; + bool listListeners() const; + + std::vector const& getReporterSpecs() const; + std::vector const& + getProcessedReporterSpecs() const; + + std::vector const& getTestsOrTags() const override; + std::vector const& getSectionsToRun() const override; + + TestSpec const& testSpec() const override; + bool hasTestFilters() const override; + + bool showHelp() const; + + // IConfig interface + bool allowThrows() const override; + StringRef name() const override; + bool includeSuccessfulResults() const override; + bool warnAboutMissingAssertions() const override; + bool warnAboutUnmatchedTestSpecs() const override; + bool zeroTestsCountAsSuccess() const override; + ShowDurations showDurations() const override; + double minDuration() const override; + TestRunOrder runOrder() const override; + uint32_t rngSeed() const override; + unsigned int shardCount() const override; + unsigned int shardIndex() const override; + ColourMode defaultColourMode() const override; + bool shouldDebugBreak() const override; + int abortAfter() const override; + bool showInvisibles() const override; + Verbosity verbosity() const override; + bool skipBenchmarks() const override; + bool benchmarkNoAnalysis() const override; + unsigned int benchmarkSamples() const override; + double benchmarkConfidenceInterval() const override; + unsigned int benchmarkResamples() const override; + std::chrono::milliseconds benchmarkWarmupTime() const override; + + private: + ConfigData m_data; + std::vector m_processedReporterSpecs; + TestSpec m_testSpec; + bool m_hasTestFilters = false; + }; +} // end namespace Catch + +#endif // CATCH_CONFIG_HPP_INCLUDED + + +#ifndef CATCH_GET_RANDOM_SEED_HPP_INCLUDED +#define CATCH_GET_RANDOM_SEED_HPP_INCLUDED + +#include + +namespace Catch { + //! Returns Catch2's current RNG seed. + std::uint32_t getSeed(); +} + +#endif // CATCH_GET_RANDOM_SEED_HPP_INCLUDED + + +#ifndef CATCH_MESSAGE_HPP_INCLUDED +#define CATCH_MESSAGE_HPP_INCLUDED + + + +#ifndef CATCH_STREAM_END_STOP_HPP_INCLUDED +#define CATCH_STREAM_END_STOP_HPP_INCLUDED + + +namespace Catch { + + // Use this in variadic streaming macros to allow + // << +StreamEndStop + // as well as + // << stuff +StreamEndStop + struct StreamEndStop { + StringRef operator+() const { return StringRef(); } + + template + friend T const& operator+( T const& value, StreamEndStop ) { + return value; + } + }; + +} // namespace Catch + +#endif // CATCH_STREAM_END_STOP_HPP_INCLUDED + +#include +#include + +namespace Catch { + + struct SourceLineInfo; + + struct MessageStream { + + template + MessageStream& operator << ( T const& value ) { + m_stream << value; + return *this; + } + + ReusableStringStream m_stream; + }; + + struct MessageBuilder : MessageStream { + MessageBuilder( StringRef macroName, + SourceLineInfo const& lineInfo, + ResultWas::OfType type ): + m_info(macroName, lineInfo, type) {} + + + template + MessageBuilder& operator << ( T const& value ) { + m_stream << value; + return *this; + } + + MessageInfo m_info; + }; + + class ScopedMessage { + public: + explicit ScopedMessage( MessageBuilder const& builder ); + ScopedMessage( ScopedMessage& duplicate ) = delete; + ScopedMessage( ScopedMessage&& old ) noexcept; + ~ScopedMessage(); + + MessageInfo m_info; + bool m_moved = false; + }; + + class Capturer { + std::vector m_messages; + IResultCapture& m_resultCapture = getResultCapture(); + size_t m_captured = 0; + public: + Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ); + + Capturer(Capturer const&) = delete; + Capturer& operator=(Capturer const&) = delete; + + ~Capturer(); + + void captureValue( size_t index, std::string const& value ); + + template + void captureValues( size_t index, T const& value ) { + captureValue( index, Catch::Detail::stringify( value ) ); + } + + template + void captureValues( size_t index, T const& value, Ts const&... values ) { + captureValue( index, Catch::Detail::stringify(value) ); + captureValues( index+1, values... ); + } + }; + +} // end namespace Catch + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::StringRef(), resultDisposition ); \ + catchAssertionHandler.handleMessage( messageType, ( Catch::MessageStream() << __VA_ARGS__ + ::Catch::StreamEndStop() ).m_stream.str() ); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_CAPTURE( varName, macroName, ... ) \ + Catch::Capturer varName( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info, #__VA_ARGS__ ); \ + varName.captureValues( 0, __VA_ARGS__ ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_INFO( macroName, log ) \ + Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage )( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_UNSCOPED_INFO( macroName, log ) \ + Catch::getResultCapture().emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log ) + + +#if defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE) + + #define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg ) + #define CATCH_UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "CATCH_UNSCOPED_INFO", msg ) + #define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) + #define CATCH_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_CAPTURE", __VA_ARGS__ ) + +#elif defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE) + + #define CATCH_INFO( msg ) (void)(0) + #define CATCH_UNSCOPED_INFO( msg ) (void)(0) + #define CATCH_WARN( msg ) (void)(0) + #define CATCH_CAPTURE( ... ) (void)(0) + +#elif !defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE) + + #define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) + #define UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "UNSCOPED_INFO", msg ) + #define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) + #define CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CAPTURE", __VA_ARGS__ ) + +#elif !defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE) + + #define INFO( msg ) (void)(0) + #define UNSCOPED_INFO( msg ) (void)(0) + #define WARN( msg ) (void)(0) + #define CAPTURE( ... ) (void)(0) + +#endif // end of user facing macro declarations + + + + +#endif // CATCH_MESSAGE_HPP_INCLUDED + + +#ifndef CATCH_SESSION_HPP_INCLUDED +#define CATCH_SESSION_HPP_INCLUDED + + + +#ifndef CATCH_COMMANDLINE_HPP_INCLUDED +#define CATCH_COMMANDLINE_HPP_INCLUDED + + + +#ifndef CATCH_CLARA_HPP_INCLUDED +#define CATCH_CLARA_HPP_INCLUDED + +#if defined( __clang__ ) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wweak-vtables" +# pragma clang diagnostic ignored "-Wshadow" +# pragma clang diagnostic ignored "-Wdeprecated" +#endif + +#if defined( __GNUC__ ) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wsign-conversion" +#endif + +#ifndef CLARA_CONFIG_OPTIONAL_TYPE +# ifdef __has_include +# if __has_include( ) && __cplusplus >= 201703L +# include +# define CLARA_CONFIG_OPTIONAL_TYPE std::optional +# endif +# endif +#endif + + +#include +#include +#include +#include +#include +#include +#include + +namespace Catch { + namespace Clara { + + class Args; + class Parser; + + // enum of result types from a parse + enum class ParseResultType { + Matched, + NoMatch, + ShortCircuitAll, + ShortCircuitSame + }; + + struct accept_many_t {}; + constexpr accept_many_t accept_many {}; + + namespace Detail { + struct fake_arg { + template + operator T(); + }; + + template + struct is_unary_function : std::false_type {}; + + template + struct is_unary_function< + F, + Catch::Detail::void_t()( fake_arg() ) ) + > + > : std::true_type {}; + + // Traits for extracting arg and return type of lambdas (for single + // argument lambdas) + template + struct UnaryLambdaTraits + : UnaryLambdaTraits {}; + + template + struct UnaryLambdaTraits { + static const bool isValid = false; + }; + + template + struct UnaryLambdaTraits { + static const bool isValid = true; + using ArgType = std::remove_const_t>; + using ReturnType = ReturnT; + }; + + class TokenStream; + + // Wraps a token coming from a token stream. These may not directly + // correspond to strings as a single string may encode an option + + // its argument if the : or = form is used + enum class TokenType { Option, Argument }; + struct Token { + TokenType type; + std::string token; + }; + + // Abstracts iterators into args as a stream of tokens, with option + // arguments uniformly handled + class TokenStream { + using Iterator = std::vector::const_iterator; + Iterator it; + Iterator itEnd; + std::vector m_tokenBuffer; + + void loadBuffer(); + + public: + explicit TokenStream( Args const& args ); + TokenStream( Iterator it, Iterator itEnd ); + + explicit operator bool() const { + return !m_tokenBuffer.empty() || it != itEnd; + } + + size_t count() const { + return m_tokenBuffer.size() + ( itEnd - it ); + } + + Token operator*() const { + assert( !m_tokenBuffer.empty() ); + return m_tokenBuffer.front(); + } + + Token const* operator->() const { + assert( !m_tokenBuffer.empty() ); + return &m_tokenBuffer.front(); + } + + TokenStream& operator++(); + }; + + //! Denotes type of a parsing result + enum class ResultType { + Ok, ///< No errors + LogicError, ///< Error in user-specified arguments for + ///< construction + RuntimeError ///< Error in parsing inputs + }; + + class ResultBase { + protected: + ResultBase( ResultType type ): m_type( type ) {} + virtual ~ResultBase(); // = default; + + + ResultBase(ResultBase const&) = default; + ResultBase& operator=(ResultBase const&) = default; + ResultBase(ResultBase&&) = default; + ResultBase& operator=(ResultBase&&) = default; + + virtual void enforceOk() const = 0; + + ResultType m_type; + }; + + template class ResultValueBase : public ResultBase { + public: + auto value() const -> T const& { + enforceOk(); + return m_value; + } + + protected: + ResultValueBase( ResultType type ): ResultBase( type ) {} + + ResultValueBase( ResultValueBase const& other ): + ResultBase( other ) { + if ( m_type == ResultType::Ok ) + new ( &m_value ) T( other.m_value ); + } + + ResultValueBase( ResultType, T const& value ): ResultBase( ResultType::Ok ) { + new ( &m_value ) T( value ); + } + + auto operator=( ResultValueBase const& other ) + -> ResultValueBase& { + if ( m_type == ResultType::Ok ) + m_value.~T(); + ResultBase::operator=( other ); + if ( m_type == ResultType::Ok ) + new ( &m_value ) T( other.m_value ); + return *this; + } + + ~ResultValueBase() override { + if ( m_type == ResultType::Ok ) + m_value.~T(); + } + + union { + T m_value; + }; + }; + + template <> class ResultValueBase : public ResultBase { + protected: + using ResultBase::ResultBase; + }; + + template + class BasicResult : public ResultValueBase { + public: + template + explicit BasicResult( BasicResult const& other ): + ResultValueBase( other.type() ), + m_errorMessage( other.errorMessage() ) { + assert( type() != ResultType::Ok ); + } + + template + static auto ok( U const& value ) -> BasicResult { + return { ResultType::Ok, value }; + } + static auto ok() -> BasicResult { return { ResultType::Ok }; } + static auto logicError( std::string&& message ) + -> BasicResult { + return { ResultType::LogicError, CATCH_MOVE(message) }; + } + static auto runtimeError( std::string&& message ) + -> BasicResult { + return { ResultType::RuntimeError, CATCH_MOVE(message) }; + } + + explicit operator bool() const { + return m_type == ResultType::Ok; + } + auto type() const -> ResultType { return m_type; } + auto errorMessage() const -> std::string const& { + return m_errorMessage; + } + + protected: + void enforceOk() const override { + + // Errors shouldn't reach this point, but if they do + // the actual error message will be in m_errorMessage + assert( m_type != ResultType::LogicError ); + assert( m_type != ResultType::RuntimeError ); + if ( m_type != ResultType::Ok ) + std::abort(); + } + + std::string + m_errorMessage; // Only populated if resultType is an error + + BasicResult( ResultType type, + std::string&& message ): + ResultValueBase( type ), m_errorMessage( CATCH_MOVE(message) ) { + assert( m_type != ResultType::Ok ); + } + + using ResultValueBase::ResultValueBase; + using ResultBase::m_type; + }; + + class ParseState { + public: + ParseState( ParseResultType type, + TokenStream const& remainingTokens ); + + ParseResultType type() const { return m_type; } + TokenStream const& remainingTokens() const { + return m_remainingTokens; + } + + private: + ParseResultType m_type; + TokenStream m_remainingTokens; + }; + + using Result = BasicResult; + using ParserResult = BasicResult; + using InternalParseResult = BasicResult; + + struct HelpColumns { + std::string left; + std::string right; + }; + + template + ParserResult convertInto( std::string const& source, T& target ) { + std::stringstream ss( source ); + ss >> target; + if ( ss.fail() ) { + return ParserResult::runtimeError( + "Unable to convert '" + source + + "' to destination type" ); + } else { + return ParserResult::ok( ParseResultType::Matched ); + } + } + ParserResult convertInto( std::string const& source, + std::string& target ); + ParserResult convertInto( std::string const& source, bool& target ); + +#ifdef CLARA_CONFIG_OPTIONAL_TYPE + template + auto convertInto( std::string const& source, + CLARA_CONFIG_OPTIONAL_TYPE& target ) + -> ParserResult { + T temp; + auto result = convertInto( source, temp ); + if ( result ) + target = CATCH_MOVE( temp ); + return result; + } +#endif // CLARA_CONFIG_OPTIONAL_TYPE + + struct BoundRef : Catch::Detail::NonCopyable { + virtual ~BoundRef() = default; + virtual bool isContainer() const; + virtual bool isFlag() const; + }; + struct BoundValueRefBase : BoundRef { + virtual auto setValue( std::string const& arg ) + -> ParserResult = 0; + }; + struct BoundFlagRefBase : BoundRef { + virtual auto setFlag( bool flag ) -> ParserResult = 0; + bool isFlag() const override; + }; + + template struct BoundValueRef : BoundValueRefBase { + T& m_ref; + + explicit BoundValueRef( T& ref ): m_ref( ref ) {} + + ParserResult setValue( std::string const& arg ) override { + return convertInto( arg, m_ref ); + } + }; + + template + struct BoundValueRef> : BoundValueRefBase { + std::vector& m_ref; + + explicit BoundValueRef( std::vector& ref ): m_ref( ref ) {} + + auto isContainer() const -> bool override { return true; } + + auto setValue( std::string const& arg ) + -> ParserResult override { + T temp; + auto result = convertInto( arg, temp ); + if ( result ) + m_ref.push_back( temp ); + return result; + } + }; + + struct BoundFlagRef : BoundFlagRefBase { + bool& m_ref; + + explicit BoundFlagRef( bool& ref ): m_ref( ref ) {} + + ParserResult setFlag( bool flag ) override; + }; + + template struct LambdaInvoker { + static_assert( + std::is_same::value, + "Lambda must return void or clara::ParserResult" ); + + template + static auto invoke( L const& lambda, ArgType const& arg ) + -> ParserResult { + return lambda( arg ); + } + }; + + template <> struct LambdaInvoker { + template + static auto invoke( L const& lambda, ArgType const& arg ) + -> ParserResult { + lambda( arg ); + return ParserResult::ok( ParseResultType::Matched ); + } + }; + + template + auto invokeLambda( L const& lambda, std::string const& arg ) + -> ParserResult { + ArgType temp{}; + auto result = convertInto( arg, temp ); + return !result ? result + : LambdaInvoker::ReturnType>::invoke( lambda, temp ); + } + + template struct BoundLambda : BoundValueRefBase { + L m_lambda; + + static_assert( + UnaryLambdaTraits::isValid, + "Supplied lambda must take exactly one argument" ); + explicit BoundLambda( L const& lambda ): m_lambda( lambda ) {} + + auto setValue( std::string const& arg ) + -> ParserResult override { + return invokeLambda::ArgType>( + m_lambda, arg ); + } + }; + + template struct BoundManyLambda : BoundLambda { + explicit BoundManyLambda( L const& lambda ): BoundLambda( lambda ) {} + bool isContainer() const override { return true; } + }; + + template struct BoundFlagLambda : BoundFlagRefBase { + L m_lambda; + + static_assert( + UnaryLambdaTraits::isValid, + "Supplied lambda must take exactly one argument" ); + static_assert( + std::is_same::ArgType, + bool>::value, + "flags must be boolean" ); + + explicit BoundFlagLambda( L const& lambda ): + m_lambda( lambda ) {} + + auto setFlag( bool flag ) -> ParserResult override { + return LambdaInvoker::ReturnType>::invoke( m_lambda, flag ); + } + }; + + enum class Optionality { Optional, Required }; + + class ParserBase { + public: + virtual ~ParserBase() = default; + virtual auto validate() const -> Result { return Result::ok(); } + virtual auto parse( std::string const& exeName, + TokenStream const& tokens ) const + -> InternalParseResult = 0; + virtual size_t cardinality() const; + + InternalParseResult parse( Args const& args ) const; + }; + + template + class ComposableParserImpl : public ParserBase { + public: + template + auto operator|( T const& other ) const -> Parser; + }; + + // Common code and state for Args and Opts + template + class ParserRefImpl : public ComposableParserImpl { + protected: + Optionality m_optionality = Optionality::Optional; + std::shared_ptr m_ref; + std::string m_hint; + std::string m_description; + + explicit ParserRefImpl( std::shared_ptr const& ref ): + m_ref( ref ) {} + + public: + template + ParserRefImpl( accept_many_t, + LambdaT const& ref, + std::string const& hint ): + m_ref( std::make_shared>( ref ) ), + m_hint( hint ) {} + + template ::value>> + ParserRefImpl( T& ref, std::string const& hint ): + m_ref( std::make_shared>( ref ) ), + m_hint( hint ) {} + + template ::value>> + ParserRefImpl( LambdaT const& ref, std::string const& hint ): + m_ref( std::make_shared>( ref ) ), + m_hint( hint ) {} + + auto operator()( std::string const& description ) -> DerivedT& { + m_description = description; + return static_cast( *this ); + } + + auto optional() -> DerivedT& { + m_optionality = Optionality::Optional; + return static_cast( *this ); + } + + auto required() -> DerivedT& { + m_optionality = Optionality::Required; + return static_cast( *this ); + } + + auto isOptional() const -> bool { + return m_optionality == Optionality::Optional; + } + + auto cardinality() const -> size_t override { + if ( m_ref->isContainer() ) + return 0; + else + return 1; + } + + std::string const& hint() const { return m_hint; } + }; + + } // namespace detail + + + // A parser for arguments + class Arg : public Detail::ParserRefImpl { + public: + using ParserRefImpl::ParserRefImpl; + using ParserBase::parse; + + Detail::InternalParseResult + parse(std::string const&, + Detail::TokenStream const& tokens) const override; + }; + + // A parser for options + class Opt : public Detail::ParserRefImpl { + protected: + std::vector m_optNames; + + public: + template + explicit Opt(LambdaT const& ref) : + ParserRefImpl( + std::make_shared>(ref)) {} + + explicit Opt(bool& ref); + + template ::value>> + Opt( LambdaT const& ref, std::string const& hint ): + ParserRefImpl( ref, hint ) {} + + template + Opt( accept_many_t, LambdaT const& ref, std::string const& hint ): + ParserRefImpl( accept_many, ref, hint ) {} + + template ::value>> + Opt( T& ref, std::string const& hint ): + ParserRefImpl( ref, hint ) {} + + auto operator[](std::string const& optName) -> Opt& { + m_optNames.push_back(optName); + return *this; + } + + std::vector getHelpColumns() const; + + bool isMatch(std::string const& optToken) const; + + using ParserBase::parse; + + Detail::InternalParseResult + parse(std::string const&, + Detail::TokenStream const& tokens) const override; + + Detail::Result validate() const override; + }; + + // Specifies the name of the executable + class ExeName : public Detail::ComposableParserImpl { + std::shared_ptr m_name; + std::shared_ptr m_ref; + + public: + ExeName(); + explicit ExeName(std::string& ref); + + template + explicit ExeName(LambdaT const& lambda) : ExeName() { + m_ref = std::make_shared>(lambda); + } + + // The exe name is not parsed out of the normal tokens, but is + // handled specially + Detail::InternalParseResult + parse(std::string const&, + Detail::TokenStream const& tokens) const override; + + std::string const& name() const { return *m_name; } + Detail::ParserResult set(std::string const& newName); + }; + + + // A Combined parser + class Parser : Detail::ParserBase { + mutable ExeName m_exeName; + std::vector m_options; + std::vector m_args; + + public: + + auto operator|=(ExeName const& exeName) -> Parser& { + m_exeName = exeName; + return *this; + } + + auto operator|=(Arg const& arg) -> Parser& { + m_args.push_back(arg); + return *this; + } + + auto operator|=(Opt const& opt) -> Parser& { + m_options.push_back(opt); + return *this; + } + + Parser& operator|=(Parser const& other); + + template + auto operator|(T const& other) const -> Parser { + return Parser(*this) |= other; + } + + std::vector getHelpColumns() const; + + void writeToStream(std::ostream& os) const; + + friend auto operator<<(std::ostream& os, Parser const& parser) + -> std::ostream& { + parser.writeToStream(os); + return os; + } + + Detail::Result validate() const override; + + using ParserBase::parse; + Detail::InternalParseResult + parse(std::string const& exeName, + Detail::TokenStream const& tokens) const override; + }; + + // Transport for raw args (copied from main args, or supplied via + // init list for testing) + class Args { + friend Detail::TokenStream; + std::string m_exeName; + std::vector m_args; + + public: + Args(int argc, char const* const* argv); + Args(std::initializer_list args); + + std::string const& exeName() const { return m_exeName; } + }; + + + // Convenience wrapper for option parser that specifies the help option + struct Help : Opt { + Help(bool& showHelpFlag); + }; + + // Result type for parser operation + using Detail::ParserResult; + + namespace Detail { + template + template + Parser + ComposableParserImpl::operator|(T const& other) const { + return Parser() | static_cast(*this) | other; + } + } + + } // namespace Clara +} // namespace Catch + +#if defined( __clang__ ) +# pragma clang diagnostic pop +#endif + +#if defined( __GNUC__ ) +# pragma GCC diagnostic pop +#endif + +#endif // CATCH_CLARA_HPP_INCLUDED + +namespace Catch { + + struct ConfigData; + + Clara::Parser makeCommandLineParser( ConfigData& config ); + +} // end namespace Catch + +#endif // CATCH_COMMANDLINE_HPP_INCLUDED + +namespace Catch { + + class Session : Detail::NonCopyable { + public: + + Session(); + ~Session(); + + void showHelp() const; + void libIdentify(); + + int applyCommandLine( int argc, char const * const * argv ); + #if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE) + int applyCommandLine( int argc, wchar_t const * const * argv ); + #endif + + void useConfigData( ConfigData const& configData ); + + template + int run(int argc, CharT const * const argv[]) { + if (m_startupExceptions) + return 1; + int returnCode = applyCommandLine(argc, argv); + if (returnCode == 0) + returnCode = run(); + return returnCode; + } + + int run(); + + Clara::Parser const& cli() const; + void cli( Clara::Parser const& newParser ); + ConfigData& configData(); + Config& config(); + private: + int runInternal(); + + Clara::Parser m_cli; + ConfigData m_configData; + Detail::unique_ptr m_config; + bool m_startupExceptions = false; + }; + +} // end namespace Catch + +#endif // CATCH_SESSION_HPP_INCLUDED + + +#ifndef CATCH_TAG_ALIAS_HPP_INCLUDED +#define CATCH_TAG_ALIAS_HPP_INCLUDED + + +#include + +namespace Catch { + + struct TagAlias { + TagAlias(std::string const& _tag, SourceLineInfo _lineInfo): + tag(_tag), + lineInfo(_lineInfo) + {} + + std::string tag; + SourceLineInfo lineInfo; + }; + +} // end namespace Catch + +#endif // CATCH_TAG_ALIAS_HPP_INCLUDED + + +#ifndef CATCH_TAG_ALIAS_AUTOREGISTRAR_HPP_INCLUDED +#define CATCH_TAG_ALIAS_AUTOREGISTRAR_HPP_INCLUDED + + +namespace Catch { + + struct RegistrarForTagAliases { + RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); + }; + +} // end namespace Catch + +#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +#endif // CATCH_TAG_ALIAS_AUTOREGISTRAR_HPP_INCLUDED + + +#ifndef CATCH_TEMPLATE_TEST_MACROS_HPP_INCLUDED +#define CATCH_TEMPLATE_TEST_MACROS_HPP_INCLUDED + +// We need this suppression to leak, because it took until GCC 10 +// for the front end to handle local suppression via _Pragma properly +// inside templates (so `TEMPLATE_TEST_CASE` and co). +// **THIS IS DIFFERENT FOR STANDARD TESTS, WHERE GCC 9 IS SUFFICIENT** +#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && __GNUC__ < 10 +#pragma GCC diagnostic ignored "-Wparentheses" +#endif + + + + +#ifndef CATCH_TEST_MACROS_HPP_INCLUDED +#define CATCH_TEST_MACROS_HPP_INCLUDED + + + +#ifndef CATCH_TEST_MACRO_IMPL_HPP_INCLUDED +#define CATCH_TEST_MACRO_IMPL_HPP_INCLUDED + + + +#ifndef CATCH_ASSERTION_HANDLER_HPP_INCLUDED +#define CATCH_ASSERTION_HANDLER_HPP_INCLUDED + + + +#ifndef CATCH_DECOMPOSER_HPP_INCLUDED +#define CATCH_DECOMPOSER_HPP_INCLUDED + + +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4389) // '==' : signed/unsigned mismatch +#pragma warning(disable:4018) // more "signed/unsigned mismatch" +#pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform) +#pragma warning(disable:4180) // qualifier applied to function type has no meaning +#pragma warning(disable:4800) // Forcing result to true or false +#endif + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wsign-compare" +#elif defined __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wsign-compare" +#endif + +namespace Catch { + + class ITransientExpression { + bool m_isBinaryExpression; + bool m_result; + + public: + auto isBinaryExpression() const -> bool { return m_isBinaryExpression; } + auto getResult() const -> bool { return m_result; } + virtual void streamReconstructedExpression( std::ostream &os ) const = 0; + + ITransientExpression( bool isBinaryExpression, bool result ) + : m_isBinaryExpression( isBinaryExpression ), + m_result( result ) + {} + + ITransientExpression() = default; + ITransientExpression(ITransientExpression const&) = default; + ITransientExpression& operator=(ITransientExpression const&) = default; + + // We don't actually need a virtual destructor, but many static analysers + // complain if it's not here :-( + virtual ~ITransientExpression(); // = default; + + friend std::ostream& operator<<(std::ostream& out, ITransientExpression const& expr) { + expr.streamReconstructedExpression(out); + return out; + } + }; + + void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ); + + template + class BinaryExpr : public ITransientExpression { + LhsT m_lhs; + StringRef m_op; + RhsT m_rhs; + + void streamReconstructedExpression( std::ostream &os ) const override { + formatReconstructedExpression + ( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) ); + } + + public: + BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs ) + : ITransientExpression{ true, comparisonResult }, + m_lhs( lhs ), + m_op( op ), + m_rhs( rhs ) + {} + + template + auto operator && ( T ) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator || ( T ) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator == ( T ) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator != ( T ) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator > ( T ) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator < ( T ) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator >= ( T ) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator <= ( T ) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + }; + + template + class UnaryExpr : public ITransientExpression { + LhsT m_lhs; + + void streamReconstructedExpression( std::ostream &os ) const override { + os << Catch::Detail::stringify( m_lhs ); + } + + public: + explicit UnaryExpr( LhsT lhs ) + : ITransientExpression{ false, static_cast(lhs) }, + m_lhs( lhs ) + {} + }; + + + // Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int) + template + auto compareEqual( LhsT const& lhs, RhsT const& rhs ) -> bool { return static_cast(lhs == rhs); } + template + auto compareEqual( T* const& lhs, int rhs ) -> bool { return lhs == reinterpret_cast( rhs ); } + template + auto compareEqual( T* const& lhs, long rhs ) -> bool { return lhs == reinterpret_cast( rhs ); } + template + auto compareEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) == rhs; } + template + auto compareEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) == rhs; } + + template + auto compareNotEqual( LhsT const& lhs, RhsT&& rhs ) -> bool { return static_cast(lhs != rhs); } + template + auto compareNotEqual( T* const& lhs, int rhs ) -> bool { return lhs != reinterpret_cast( rhs ); } + template + auto compareNotEqual( T* const& lhs, long rhs ) -> bool { return lhs != reinterpret_cast( rhs ); } + template + auto compareNotEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) != rhs; } + template + auto compareNotEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) != rhs; } + + + template + class ExprLhs { + LhsT m_lhs; + public: + explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {} + + template>::value, int> = 0> + friend auto operator == ( ExprLhs && lhs, RhsT && rhs ) -> BinaryExpr { + return { compareEqual( lhs.m_lhs, rhs ), lhs.m_lhs, "=="_sr, rhs }; + } + template::value, int> = 0> + friend auto operator == ( ExprLhs && lhs, RhsT rhs ) -> BinaryExpr { + return { compareEqual( lhs.m_lhs, rhs ), lhs.m_lhs, "=="_sr, rhs }; + } + + template>::value, int> = 0> + friend auto operator != ( ExprLhs && lhs, RhsT && rhs ) -> BinaryExpr { + return { compareNotEqual( lhs.m_lhs, rhs ), lhs.m_lhs, "!="_sr, rhs }; + } + template::value, int> = 0> + friend auto operator != ( ExprLhs && lhs, RhsT rhs ) -> BinaryExpr { + return { compareNotEqual( lhs.m_lhs, rhs ), lhs.m_lhs, "!="_sr, rhs }; + } + + #define CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(op) \ + template>::value, int> = 0> \ + friend auto operator op ( ExprLhs && lhs, RhsT && rhs ) -> BinaryExpr { \ + return { static_cast(lhs.m_lhs op rhs), lhs.m_lhs, #op##_sr, rhs }; \ + } \ + template::value, int> = 0> \ + friend auto operator op ( ExprLhs && lhs, RhsT rhs ) -> BinaryExpr { \ + return { static_cast(lhs.m_lhs op rhs), lhs.m_lhs, #op##_sr, rhs }; \ + } + + CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(<) + CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(>) + CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(<=) + CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(>=) + CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(|) + CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(&) + CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(^) + + #undef CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR + + template + friend auto operator && ( ExprLhs &&, RhsT && ) -> BinaryExpr { + static_assert(always_false::value, + "operator&& is not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + friend auto operator || ( ExprLhs &&, RhsT && ) -> BinaryExpr { + static_assert(always_false::value, + "operator|| is not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + auto makeUnaryExpr() const -> UnaryExpr { + return UnaryExpr{ m_lhs }; + } + }; + + struct Decomposer { + template>::value, int> = 0> + friend auto operator <= ( Decomposer &&, T && lhs ) -> ExprLhs { + return ExprLhs{ lhs }; + } + + template::value, int> = 0> + friend auto operator <= ( Decomposer &&, T value ) -> ExprLhs { + return ExprLhs{ value }; + } + }; + +} // end namespace Catch + +#ifdef _MSC_VER +#pragma warning(pop) +#endif +#ifdef __clang__ +# pragma clang diagnostic pop +#elif defined __GNUC__ +# pragma GCC diagnostic pop +#endif + +#endif // CATCH_DECOMPOSER_HPP_INCLUDED + +#include + +namespace Catch { + + class IResultCapture; + + struct AssertionReaction { + bool shouldDebugBreak = false; + bool shouldThrow = false; + }; + + class AssertionHandler { + AssertionInfo m_assertionInfo; + AssertionReaction m_reaction; + bool m_completed = false; + IResultCapture& m_resultCapture; + + public: + AssertionHandler + ( StringRef macroName, + SourceLineInfo const& lineInfo, + StringRef capturedExpression, + ResultDisposition::Flags resultDisposition ); + ~AssertionHandler() { + if ( !m_completed ) { + m_resultCapture.handleIncomplete( m_assertionInfo ); + } + } + + + template + void handleExpr( ExprLhs const& expr ) { + handleExpr( expr.makeUnaryExpr() ); + } + void handleExpr( ITransientExpression const& expr ); + + void handleMessage(ResultWas::OfType resultType, StringRef message); + + void handleExceptionThrownAsExpected(); + void handleUnexpectedExceptionNotThrown(); + void handleExceptionNotThrownAsExpected(); + void handleThrowingCallSkipped(); + void handleUnexpectedInflightException(); + + void complete(); + void setCompleted(); + + // query + auto allowThrows() const -> bool; + }; + + void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef matcherString ); + +} // namespace Catch + +#endif // CATCH_ASSERTION_HANDLER_HPP_INCLUDED + +// We need this suppression to leak, because it took until GCC 10 +// for the front end to handle local suppression via _Pragma properly +#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && __GNUC__ <= 9 + #pragma GCC diagnostic ignored "-Wparentheses" +#endif + +#if !defined(CATCH_CONFIG_DISABLE) + +#if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION) + #define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__ +#else + #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION" +#endif + +#if defined(CATCH_CONFIG_FAST_COMPILE) || defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + +/////////////////////////////////////////////////////////////////////////////// +// Another way to speed-up compilation is to omit local try-catch for REQUIRE* +// macros. +#define INTERNAL_CATCH_TRY +#define INTERNAL_CATCH_CATCH( capturer ) + +#else // CATCH_CONFIG_FAST_COMPILE + +#define INTERNAL_CATCH_TRY try +#define INTERNAL_CATCH_CATCH( handler ) catch(...) { handler.handleUnexpectedInflightException(); } + +#endif + +#define INTERNAL_CATCH_REACT( handler ) handler.complete(); + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \ + do { /* NOLINT(bugprone-infinite-loop) */ \ + /* The expression should not be evaluated, but warnings should hopefully be checked */ \ + CATCH_INTERNAL_IGNORE_BUT_WARN(__VA_ARGS__); \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ + INTERNAL_CATCH_TRY { \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( (void)0, (false) && static_cast( !!(__VA_ARGS__) ) ) // the expression here is never evaluated at runtime but it forces the compiler to give it a look + // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_IF( macroName, resultDisposition, ... ) \ + INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ + if( Catch::getResultCapture().lastAssertionPassed() ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_ELSE( macroName, resultDisposition, ... ) \ + INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ + if( !Catch::getResultCapture().lastAssertionPassed() ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ + try { \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ + static_cast(__VA_ARGS__); \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + catchAssertionHandler.handleExceptionNotThrownAsExpected(); \ + } \ + catch( ... ) { \ + catchAssertionHandler.handleUnexpectedInflightException(); \ + } \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS( macroName, resultDisposition, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition); \ + if( catchAssertionHandler.allowThrows() ) \ + try { \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ + static_cast(__VA_ARGS__); \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } \ + catch( ... ) { \ + catchAssertionHandler.handleExceptionThrownAsExpected(); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr) ", " CATCH_INTERNAL_STRINGIFY(exceptionType), resultDisposition ); \ + if( catchAssertionHandler.allowThrows() ) \ + try { \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ + static_cast(expr); \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } \ + catch( exceptionType const& ) { \ + catchAssertionHandler.handleExceptionThrownAsExpected(); \ + } \ + catch( ... ) { \ + catchAssertionHandler.handleUnexpectedInflightException(); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + + + +/////////////////////////////////////////////////////////////////////////////// +// Although this is matcher-based, it can be used with just a string +#define INTERNAL_CATCH_THROWS_STR_MATCHES( macroName, resultDisposition, matcher, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ + if( catchAssertionHandler.allowThrows() ) \ + try { \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ + static_cast(__VA_ARGS__); \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } \ + catch( ... ) { \ + Catch::handleExceptionMatchExpr( catchAssertionHandler, matcher, #matcher##_catch_sr ); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +#endif // CATCH_CONFIG_DISABLE + +#endif // CATCH_TEST_MACRO_IMPL_HPP_INCLUDED + + +#ifndef CATCH_SECTION_HPP_INCLUDED +#define CATCH_SECTION_HPP_INCLUDED + + + +#ifndef CATCH_TIMER_HPP_INCLUDED +#define CATCH_TIMER_HPP_INCLUDED + +#include + +namespace Catch { + + class Timer { + uint64_t m_nanoseconds = 0; + public: + void start(); + auto getElapsedNanoseconds() const -> uint64_t; + auto getElapsedMicroseconds() const -> uint64_t; + auto getElapsedMilliseconds() const -> unsigned int; + auto getElapsedSeconds() const -> double; + }; + +} // namespace Catch + +#endif // CATCH_TIMER_HPP_INCLUDED + +namespace Catch { + + class Section : Detail::NonCopyable { + public: + Section( SectionInfo&& info ); + ~Section(); + + // This indicates whether the section should be executed or not + explicit operator bool() const; + + private: + SectionInfo m_info; + + Counts m_assertions; + bool m_sectionIncluded; + Timer m_timer; + }; + +} // end namespace Catch + +#define INTERNAL_CATCH_SECTION( ... ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +#define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, (Catch::ReusableStringStream() << __VA_ARGS__).str() ) ) \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +#endif // CATCH_SECTION_HPP_INCLUDED + + +#ifndef CATCH_TEST_REGISTRY_HPP_INCLUDED +#define CATCH_TEST_REGISTRY_HPP_INCLUDED + + + +#ifndef CATCH_INTERFACES_TESTCASE_HPP_INCLUDED +#define CATCH_INTERFACES_TESTCASE_HPP_INCLUDED + +#include + +namespace Catch { + + class TestSpec; + struct TestCaseInfo; + + class ITestInvoker { + public: + virtual void invoke () const = 0; + virtual ~ITestInvoker(); // = default + }; + + class TestCaseHandle; + class IConfig; + + class ITestCaseRegistry { + public: + virtual ~ITestCaseRegistry(); // = default + // TODO: this exists only for adding filenames to test cases -- let's expose this in a saner way later + virtual std::vector const& getAllInfos() const = 0; + virtual std::vector const& getAllTests() const = 0; + virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; + }; + + bool isThrowSafe( TestCaseHandle const& testCase, IConfig const& config ); + bool matchTest( TestCaseHandle const& testCase, TestSpec const& testSpec, IConfig const& config ); + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector const& getAllTestCasesSorted( IConfig const& config ); + +} + +#endif // CATCH_INTERFACES_TESTCASE_HPP_INCLUDED + + +#ifndef CATCH_PREPROCESSOR_REMOVE_PARENS_HPP_INCLUDED +#define CATCH_PREPROCESSOR_REMOVE_PARENS_HPP_INCLUDED + +#define INTERNAL_CATCH_EXPAND1( param ) INTERNAL_CATCH_EXPAND2( param ) +#define INTERNAL_CATCH_EXPAND2( ... ) INTERNAL_CATCH_NO##__VA_ARGS__ +#define INTERNAL_CATCH_DEF( ... ) INTERNAL_CATCH_DEF __VA_ARGS__ +#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF + +#define INTERNAL_CATCH_REMOVE_PARENS( ... ) \ + INTERNAL_CATCH_EXPAND1( INTERNAL_CATCH_DEF __VA_ARGS__ ) + +#endif // CATCH_PREPROCESSOR_REMOVE_PARENS_HPP_INCLUDED + +// GCC 5 and older do not properly handle disabling unused-variable warning +// with a _Pragma. This means that we have to leak the suppression to the +// user code as well :-( +#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 5 +#pragma GCC diagnostic ignored "-Wunused-variable" +#endif + + + +namespace Catch { + +template +class TestInvokerAsMethod : public ITestInvoker { + void (C::*m_testAsMethod)(); +public: + TestInvokerAsMethod( void (C::*testAsMethod)() ) noexcept : m_testAsMethod( testAsMethod ) {} + + void invoke() const override { + C obj; + (obj.*m_testAsMethod)(); + } +}; + +Detail::unique_ptr makeTestInvoker( void(*testAsFunction)() ); + +template +Detail::unique_ptr makeTestInvoker( void (C::*testAsMethod)() ) { + return Detail::make_unique>( testAsMethod ); +} + +struct NameAndTags { + constexpr NameAndTags( StringRef name_ = StringRef(), + StringRef tags_ = StringRef() ) noexcept: + name( name_ ), tags( tags_ ) {} + StringRef name; + StringRef tags; +}; + +struct AutoReg : Detail::NonCopyable { + AutoReg( Detail::unique_ptr invoker, SourceLineInfo const& lineInfo, StringRef classOrMethod, NameAndTags const& nameAndTags ) noexcept; +}; + +} // end namespace Catch + +#if defined(CATCH_CONFIG_DISABLE) + #define INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( TestName, ... ) \ + static inline void TestName() + #define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \ + namespace{ \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ + void test(); \ + }; \ + } \ + void TestName::test() +#endif + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ + static void TestName(); \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + static void TestName() + #define INTERNAL_CATCH_TESTCASE( ... ) \ + INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__ ) + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + namespace{ \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ + void test(); \ + }; \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ + } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + void TestName::test() + #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \ + INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), ClassName, __VA_ARGS__ ) + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ + do { \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + } while(false) + + +#endif // CATCH_TEST_REGISTRY_HPP_INCLUDED + + +// All of our user-facing macros support configuration toggle, that +// forces them to be defined prefixed with CATCH_. We also like to +// support another toggle that can minimize (disable) their implementation. +// Given this, we have 4 different configuration options below + +#if defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE) + + #define CATCH_REQUIRE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ ) + #define CATCH_REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) + + #define CATCH_REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ ) + #define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) + #define CATCH_REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ ) + + #define CATCH_CHECK( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) + #define CATCH_CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) + #define CATCH_CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) + #define CATCH_CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) + #define CATCH_CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) + + #define CATCH_CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) + #define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) + #define CATCH_CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) + + #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) + #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) + #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) + #define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) + #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) + #define CATCH_DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ ) + #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) + #define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) + #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) + + + #if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE) + #define CATCH_STATIC_REQUIRE( ... ) static_assert( __VA_ARGS__ , #__VA_ARGS__ ); CATCH_SUCCEED( #__VA_ARGS__ ) + #define CATCH_STATIC_REQUIRE_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); CATCH_SUCCEED( #__VA_ARGS__ ) + #define CATCH_STATIC_CHECK( ... ) static_assert( __VA_ARGS__ , #__VA_ARGS__ ); CATCH_SUCCEED( #__VA_ARGS__ ) + #define CATCH_STATIC_CHECK_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); CATCH_SUCCEED( #__VA_ARGS__ ) + #else + #define CATCH_STATIC_REQUIRE( ... ) CATCH_REQUIRE( __VA_ARGS__ ) + #define CATCH_STATIC_REQUIRE_FALSE( ... ) CATCH_REQUIRE_FALSE( __VA_ARGS__ ) + #define CATCH_STATIC_CHECK( ... ) CATCH_CHECK( __VA_ARGS__ ) + #define CATCH_STATIC_CHECK_FALSE( ... ) CATCH_CHECK_FALSE( __VA_ARGS__ ) + #endif + + + // "BDD-style" convenience wrappers + #define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ ) + #define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) + #define CATCH_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Given: " << desc ) + #define CATCH_AND_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( "And given: " << desc ) + #define CATCH_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " When: " << desc ) + #define CATCH_AND_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And when: " << desc ) + #define CATCH_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Then: " << desc ) + #define CATCH_AND_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And: " << desc ) + +#elif defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE) // ^^ prefixed, implemented | vv prefixed, disabled + + #define CATCH_REQUIRE( ... ) (void)(0) + #define CATCH_REQUIRE_FALSE( ... ) (void)(0) + + #define CATCH_REQUIRE_THROWS( ... ) (void)(0) + #define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0) + #define CATCH_REQUIRE_NOTHROW( ... ) (void)(0) + + #define CATCH_CHECK( ... ) (void)(0) + #define CATCH_CHECK_FALSE( ... ) (void)(0) + #define CATCH_CHECKED_IF( ... ) if (__VA_ARGS__) + #define CATCH_CHECKED_ELSE( ... ) if (!(__VA_ARGS__)) + #define CATCH_CHECK_NOFAIL( ... ) (void)(0) + + #define CATCH_CHECK_THROWS( ... ) (void)(0) + #define CATCH_CHECK_THROWS_AS( expr, exceptionType ) (void)(0) + #define CATCH_CHECK_NOTHROW( ... ) (void)(0) + + #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) + #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) + #define CATCH_METHOD_AS_TEST_CASE( method, ... ) + #define CATCH_REGISTER_TEST_CASE( Function, ... ) (void)(0) + #define CATCH_SECTION( ... ) + #define CATCH_DYNAMIC_SECTION( ... ) + #define CATCH_FAIL( ... ) (void)(0) + #define CATCH_FAIL_CHECK( ... ) (void)(0) + #define CATCH_SUCCEED( ... ) (void)(0) + + #define CATCH_STATIC_REQUIRE( ... ) (void)(0) + #define CATCH_STATIC_REQUIRE_FALSE( ... ) (void)(0) + #define CATCH_STATIC_CHECK( ... ) (void)(0) + #define CATCH_STATIC_CHECK_FALSE( ... ) (void)(0) + + // "BDD-style" convenience wrappers + #define CATCH_SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) + #define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), className ) + #define CATCH_GIVEN( desc ) + #define CATCH_AND_GIVEN( desc ) + #define CATCH_WHEN( desc ) + #define CATCH_AND_WHEN( desc ) + #define CATCH_THEN( desc ) + #define CATCH_AND_THEN( desc ) + +#elif !defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE) // ^^ prefixed, disabled | vv unprefixed, implemented + + #define REQUIRE( ... ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ ) + #define REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) + + #define REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ ) + #define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) + #define REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ ) + + #define CHECK( ... ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) + #define CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) + #define CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) + #define CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) + #define CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) + + #define CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) + #define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) + #define CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) + + #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) + #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) + #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) + #define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) + #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) + #define DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ ) + #define FAIL( ... ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) + #define FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) + #define SUCCEED( ... ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) + + + #if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE) + #define STATIC_REQUIRE( ... ) static_assert( __VA_ARGS__, #__VA_ARGS__ ); SUCCEED( #__VA_ARGS__ ) + #define STATIC_REQUIRE_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); SUCCEED( "!(" #__VA_ARGS__ ")" ) + #define STATIC_CHECK( ... ) static_assert( __VA_ARGS__, #__VA_ARGS__ ); SUCCEED( #__VA_ARGS__ ) + #define STATIC_CHECK_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); SUCCEED( "!(" #__VA_ARGS__ ")" ) + #else + #define STATIC_REQUIRE( ... ) REQUIRE( __VA_ARGS__ ) + #define STATIC_REQUIRE_FALSE( ... ) REQUIRE_FALSE( __VA_ARGS__ ) + #define STATIC_CHECK( ... ) CHECK( __VA_ARGS__ ) + #define STATIC_CHECK_FALSE( ... ) CHECK_FALSE( __VA_ARGS__ ) + #endif + + // "BDD-style" convenience wrappers + #define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ ) + #define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) + #define GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Given: " << desc ) + #define AND_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( "And given: " << desc ) + #define WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " When: " << desc ) + #define AND_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And when: " << desc ) + #define THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Then: " << desc ) + #define AND_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And: " << desc ) + +#elif !defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE) // ^^ unprefixed, implemented | vv unprefixed, disabled + + #define REQUIRE( ... ) (void)(0) + #define REQUIRE_FALSE( ... ) (void)(0) + + #define REQUIRE_THROWS( ... ) (void)(0) + #define REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0) + #define REQUIRE_NOTHROW( ... ) (void)(0) + + #define CHECK( ... ) (void)(0) + #define CHECK_FALSE( ... ) (void)(0) + #define CHECKED_IF( ... ) if (__VA_ARGS__) + #define CHECKED_ELSE( ... ) if (!(__VA_ARGS__)) + #define CHECK_NOFAIL( ... ) (void)(0) + + #define CHECK_THROWS( ... ) (void)(0) + #define CHECK_THROWS_AS( expr, exceptionType ) (void)(0) + #define CHECK_NOTHROW( ... ) (void)(0) + + #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__) + #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) + #define METHOD_AS_TEST_CASE( method, ... ) + #define REGISTER_TEST_CASE( Function, ... ) (void)(0) + #define SECTION( ... ) + #define DYNAMIC_SECTION( ... ) + #define FAIL( ... ) (void)(0) + #define FAIL_CHECK( ... ) (void)(0) + #define SUCCEED( ... ) (void)(0) + + #define STATIC_REQUIRE( ... ) (void)(0) + #define STATIC_REQUIRE_FALSE( ... ) (void)(0) + #define STATIC_CHECK( ... ) (void)(0) + #define STATIC_CHECK_FALSE( ... ) (void)(0) + + // "BDD-style" convenience wrappers + #define SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ) ) + #define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), className ) + + #define GIVEN( desc ) + #define AND_GIVEN( desc ) + #define WHEN( desc ) + #define AND_WHEN( desc ) + #define THEN( desc ) + #define AND_THEN( desc ) + +#endif // ^^ unprefixed, disabled + +// end of user facing macros + +#endif // CATCH_TEST_MACROS_HPP_INCLUDED + + +#ifndef CATCH_TEMPLATE_TEST_REGISTRY_HPP_INCLUDED +#define CATCH_TEMPLATE_TEST_REGISTRY_HPP_INCLUDED + + + +#ifndef CATCH_PREPROCESSOR_HPP_INCLUDED +#define CATCH_PREPROCESSOR_HPP_INCLUDED + + +#if defined(__GNUC__) +// We need to silence "empty __VA_ARGS__ warning", and using just _Pragma does not work +#pragma GCC system_header +#endif + + +#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ +#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) + +#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ +// MSVC needs more evaluations +#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) +#else +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) +#endif + +#define CATCH_REC_END(...) +#define CATCH_REC_OUT + +#define CATCH_EMPTY() +#define CATCH_DEFER(id) id CATCH_EMPTY() + +#define CATCH_REC_GET_END2() 0, CATCH_REC_END +#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 +#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 +#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT +#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) +#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) + +#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) + +#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) + +// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, +// and passes userdata as the first parameter to each invocation, +// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) +#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) +#else +// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) +#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) +#endif + +#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ +#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper()) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) +#else +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper())) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) +#endif + +#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\ + CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__) + +#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) +#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) +#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) +#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) +#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) +#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) +#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6) +#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) +#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) +#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) +#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) + +#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N + +#define INTERNAL_CATCH_TYPE_GEN\ + template struct TypeList {};\ + template\ + constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\ + template class...> struct TemplateTypeList{};\ + template class...Cs>\ + constexpr auto get_wrapper() noexcept -> TemplateTypeList { return {}; }\ + template\ + struct append;\ + template\ + struct rewrap;\ + template class, typename...>\ + struct create;\ + template class, typename>\ + struct convert;\ + \ + template \ + struct append { using type = T; };\ + template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ + struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\ + template< template class L1, typename...E1, typename...Rest>\ + struct append, TypeList, Rest...> { using type = L1; };\ + \ + template< template class Container, template class List, typename...elems>\ + struct rewrap, List> { using type = TypeList>; };\ + template< template class Container, template class List, class...Elems, typename...Elements>\ + struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\ + \ + template