From 80ee81d1a81f139d39bd2fc18e872671a648257d Mon Sep 17 00:00:00 2001 From: Nicolas Pope <nicolas.pope@utu.fi> Date: Sun, 9 Jun 2019 20:34:23 +0300 Subject: [PATCH] Resolves #36 and most of #59 --- applications/gui/src/main.cpp | 2 +- applications/gui/src/src_window.cpp | 16 +- applications/gui/src/src_window.hpp | 4 +- .../include/ftl/depth_camera_params.hpp | 2 +- .../reconstruct/include/ftl/registration.hpp | 16 +- .../include/ftl/scene_rep_hash_sdf.hpp | 2 +- .../include/ftl/virtual_source.hpp | 14 +- applications/reconstruct/src/main.cpp | 80 +- applications/reconstruct/src/registration.cpp | 38 +- .../reconstruct/src/virtual_source.cpp | 23 +- applications/vision/src/main.cpp | 19 +- components/common/cpp/include/ftl/config.h.in | 2 + .../common/cpp/include/ftl/configurable.hpp | 2 + .../common/cpp/include/ftl/configuration.hpp | 76 +- components/common/cpp/include/ftl/uri.hpp | 3 +- components/common/cpp/src/configuration.cpp | 20 +- components/common/cpp/src/loguru.cpp | 3 + components/common/cpp/src/uri.cpp | 4 +- .../renderers/cpp/include/ftl/display.hpp | 4 +- .../cpp/include/ftl/rgbd_display.hpp | 8 +- components/renderers/cpp/src/display.cpp | 4 +- components/renderers/cpp/src/rgbd_display.cpp | 8 +- components/rgbd-sources/CMakeLists.txt | 28 +- components/rgbd-sources/include/ftl/rgbd.hpp | 10 +- .../{camera_params.hpp => rgbd/camera.hpp} | 2 +- .../include/ftl/rgbd/detail/source.hpp | 39 + .../{rgbdimage_source.hpp => rgbd/group.hpp} | 0 .../include/ftl/{ => rgbd}/snapshot.hpp | 8 +- .../rgbd-sources/include/ftl/rgbd/source.hpp | 167 + .../{rgbd_streamer.hpp => rgbd/streamer.hpp} | 10 +- .../include/ftl/rgbdvideo_source.hpp | 0 .../include/ftl/snapshot_source.hpp | 23 - .../include/ftl/stereoimage_source.hpp | 0 .../src/algorithms/fixstars_sgm.hpp | 2 +- .../rgbd-sources/src/algorithms/rtcensus.hpp | 2 +- .../src/algorithms/rtcensus_sgm.cpp | 2 +- .../src/algorithms/rtcensus_sgm.hpp | 2 +- components/rgbd-sources/src/calibrate.cpp | 5 +- components/rgbd-sources/src/calibrate.hpp | 11 +- components/rgbd-sources/src/disparity.cpp | 6 +- components/rgbd-sources/src/disparity.hpp | 7 +- components/rgbd-sources/src/image.hpp | 25 + components/rgbd-sources/src/local.cpp | 2 +- components/rgbd-sources/src/local.hpp | 8 +- .../src/{net_source.cpp => net.cpp} | 56 +- .../ftl/net_source.hpp => src/net.hpp} | 21 +- components/rgbd-sources/src/rgbd_source.cpp | 80 - components/rgbd-sources/src/snapshot.cpp | 12 +- .../rgbd-sources/src/snapshot_source.cpp | 5 +- .../rgbd-sources/src/snapshot_source.hpp | 31 + components/rgbd-sources/src/source.cpp | 166 + ...stereovideo_source.cpp => stereovideo.cpp} | 39 +- .../stereovideo.hpp} | 28 +- .../src/{rgbd_streamer.cpp => streamer.cpp} | 22 +- components/rgbd-sources/test/CMakeLists.txt | 10 + components/rgbd-sources/test/catch.hpp | 14362 ++++++++++++++++ components/rgbd-sources/test/data/image.png | Bin 0 -> 509 bytes components/rgbd-sources/test/data/video.mp4 | Bin 0 -> 179698 bytes components/rgbd-sources/test/source_unit.cpp | 171 + components/rgbd-sources/test/tests.cpp | 3 + 60 files changed, 15334 insertions(+), 381 deletions(-) rename components/rgbd-sources/include/ftl/{camera_params.hpp => rgbd/camera.hpp} (91%) create mode 100644 components/rgbd-sources/include/ftl/rgbd/detail/source.hpp rename components/rgbd-sources/include/ftl/{rgbdimage_source.hpp => rgbd/group.hpp} (100%) rename components/rgbd-sources/include/ftl/{ => rgbd}/snapshot.hpp (90%) create mode 100644 components/rgbd-sources/include/ftl/rgbd/source.hpp rename components/rgbd-sources/include/ftl/{rgbd_streamer.hpp => rgbd/streamer.hpp} (94%) delete mode 100644 components/rgbd-sources/include/ftl/rgbdvideo_source.hpp delete mode 100644 components/rgbd-sources/include/ftl/snapshot_source.hpp delete mode 100644 components/rgbd-sources/include/ftl/stereoimage_source.hpp create mode 100644 components/rgbd-sources/src/image.hpp rename components/rgbd-sources/src/{net_source.cpp => net.cpp} (64%) rename components/rgbd-sources/{include/ftl/net_source.hpp => src/net.hpp} (68%) delete mode 100644 components/rgbd-sources/src/rgbd_source.cpp create mode 100644 components/rgbd-sources/src/snapshot_source.hpp create mode 100644 components/rgbd-sources/src/source.cpp rename components/rgbd-sources/src/{stereovideo_source.cpp => stereovideo.cpp} (75%) rename components/rgbd-sources/{include/ftl/stereovideo_source.hpp => src/stereovideo.hpp} (56%) rename components/rgbd-sources/src/{rgbd_streamer.cpp => streamer.cpp} (93%) create mode 100644 components/rgbd-sources/test/CMakeLists.txt create mode 100644 components/rgbd-sources/test/catch.hpp create mode 100644 components/rgbd-sources/test/data/image.png create mode 100644 components/rgbd-sources/test/data/video.mp4 create mode 100644 components/rgbd-sources/test/source_unit.cpp create mode 100644 components/rgbd-sources/test/tests.cpp diff --git a/applications/gui/src/main.cpp b/applications/gui/src/main.cpp index 184e38c88..f090bc019 100644 --- a/applications/gui/src/main.cpp +++ b/applications/gui/src/main.cpp @@ -20,7 +20,7 @@ #include "src_window.hpp" using std::string; -using ftl::rgbd::RGBDSource; +using ftl::rgbd::Source; /*struct SourceViews { ftl::rgbd::RGBDSource *source; diff --git a/applications/gui/src/src_window.cpp b/applications/gui/src/src_window.cpp index a4f30d997..15b8c5439 100644 --- a/applications/gui/src/src_window.cpp +++ b/applications/gui/src/src_window.cpp @@ -9,11 +9,11 @@ #include <nanogui/layout.h> #ifdef HAVE_LIBARCHIVE -#include "ftl/snapshot.hpp" +#include "ftl/rgbd/snapshot.hpp" #endif using ftl::gui::SourceWindow; -using ftl::rgbd::RGBDSource; +using ftl::rgbd::Source; using std::string; class GLTexture { @@ -84,7 +84,7 @@ class VirtualCameraView : public nanogui::ImageView { depth_ = false; } - void setSource(RGBDSource *src) { src_ = src; } + void setSource(Source *src) { src_ = src; } bool mouseButtonEvent(const nanogui::Vector2i &p, int button, bool down, int modifiers) { //LOG(INFO) << "Mouse move: " << p[0]; @@ -118,7 +118,7 @@ class VirtualCameraView : public nanogui::ImageView { src_->setPose(viewPose); src_->grab(); - src_->getRGBD(rgb, depth); + src_->getFrames(rgb, depth); if (depth_) { if (depth.rows > 0) { @@ -143,7 +143,7 @@ class VirtualCameraView : public nanogui::ImageView { void setDepth(bool d) { depth_ = d; } private: - RGBDSource *src_; + Source *src_; GLTexture texture_; Eigen::Vector3f eye_; Eigen::Vector3f centre_; @@ -159,7 +159,7 @@ SourceWindow::SourceWindow(nanogui::Widget *parent, ftl::ctrl::Master *ctrl) using namespace nanogui; - src_ = ftl::create<ftl::rgbd::NetSource>(ctrl->getRoot(), "source", ctrl->getNet()); + src_ = ftl::create<Source>(ctrl->getRoot(), "source", ctrl->getNet()); Widget *tools = new Widget(this); tools->setLayout(new BoxLayout(Orientation::Horizontal, @@ -193,13 +193,13 @@ SourceWindow::SourceWindow(nanogui::Widget *parent, ftl::ctrl::Master *ctrl) std::strftime(timestamp, sizeof(timestamp), "%F-%H%M%S", std::localtime(&t)); auto writer = ftl::rgbd::SnapshotWriter(std::string(timestamp) + ".tar.gz"); cv::Mat rgb, depth; - this->src_->getRGBD(rgb, depth); + this->src_->getFrames(rgb, depth); if (!writer.addCameraRGBD( "0", // TODO rgb, depth, this->src_->getPose(), - this->src_->getParameters() + this->src_->parameters() )) { LOG(ERROR) << "Snapshot failed"; } diff --git a/applications/gui/src/src_window.hpp b/applications/gui/src/src_window.hpp index bba8f9bfa..4b92f2e1f 100644 --- a/applications/gui/src/src_window.hpp +++ b/applications/gui/src/src_window.hpp @@ -4,7 +4,7 @@ #include <nanogui/window.h> #include <ftl/master.hpp> #include <ftl/uuid.hpp> -#include <ftl/net_source.hpp> +#include <ftl/rgbd/source.hpp> #include <vector> #include <string> @@ -23,7 +23,7 @@ class SourceWindow : public nanogui::Window { private: ftl::ctrl::Master *ctrl_; - ftl::rgbd::NetSource *src_; + ftl::rgbd::Source *src_; VirtualCameraView *image_; std::vector<std::string> available_; diff --git a/applications/reconstruct/include/ftl/depth_camera_params.hpp b/applications/reconstruct/include/ftl/depth_camera_params.hpp index f425ccf13..bf4263b9b 100644 --- a/applications/reconstruct/include/ftl/depth_camera_params.hpp +++ b/applications/reconstruct/include/ftl/depth_camera_params.hpp @@ -6,7 +6,7 @@ #include <cuda_runtime.h> #include <ftl/cuda_matrix_util.hpp> -#include <ftl/camera_params.hpp> +#include <ftl/rgbd/camera.hpp> struct __align__(16) DepthCameraParams { float fx; diff --git a/applications/reconstruct/include/ftl/registration.hpp b/applications/reconstruct/include/ftl/registration.hpp index e5375dfe0..be5154f30 100644 --- a/applications/reconstruct/include/ftl/registration.hpp +++ b/applications/reconstruct/include/ftl/registration.hpp @@ -28,12 +28,12 @@ Eigen::Matrix4f findTransformation( std::vector<pcl::PointCloud<pcl::PointXYZ>:: /** @brief Convert chessboard corners found with OpenCV's findChessboardCorners to PCL point cloud. */ -pcl::PointCloud<pcl::PointXYZ>::Ptr cornersToPointCloud(const std::vector<cv::Point2f> &corners, const cv::Mat &disp, const ftl::rgbd::CameraParameters &p); +pcl::PointCloud<pcl::PointXYZ>::Ptr cornersToPointCloud(const std::vector<cv::Point2f> &corners, const cv::Mat &disp, const ftl::rgbd::Camera &p); /** @brief Find chessboard corners from image and store them in PCL PointCloud. * @note Corners will be drawn in rgb. */ -bool findChessboardCorners(cv::Mat &rgb, const cv::Mat &depth, const ftl::rgbd::CameraParameters &p, const cv::Size pattern_size, pcl::PointCloud<pcl::PointXYZ>::Ptr &out, float error_threshold); +bool findChessboardCorners(cv::Mat &rgb, const cv::Mat &depth, const ftl::rgbd::Camera &p, const cv::Size pattern_size, pcl::PointCloud<pcl::PointXYZ>::Ptr &out, float error_threshold); /** * @brief Abstract class for registration @@ -45,7 +45,7 @@ bool findChessboardCorners(cv::Mat &rgb, const cv::Mat &depth, const ftl::rgbd:: class Registration : public ftl::Configurable { public: explicit Registration(nlohmann::json &config); - void addSource(ftl::rgbd::RGBDSource* source); + void addSource(ftl::rgbd::Source* source); size_t getSourcesCount() { return sources_.size(); } /** @@ -75,11 +75,11 @@ public: virtual bool findTransformations(std::map<std::string, Eigen::Matrix4f> &data); protected: - ftl::rgbd::RGBDSource* getSource(size_t idx); + ftl::rgbd::Source* getSource(size_t idx); bool isTargetSourceSet(); bool isTargetSourceFound(); - bool isTargetSource(ftl::rgbd::RGBDSource* source); + bool isTargetSource(ftl::rgbd::Source* source); bool isTargetSource(size_t idx); /** @@ -124,13 +124,13 @@ protected: * always have same idx. Implementations may choose to ignore it. * @return True/false if feature was found. Used to build adjacency matrix. */ - virtual bool findFeatures(ftl::rgbd::RGBDSource* source, size_t idx)=0; + virtual bool findFeatures(ftl::rgbd::Source* source, size_t idx)=0; std::vector<std::vector<bool>> visibility_; /*< Adjacency matrix for sources (feature visibility). */ private: std::optional<std::string> target_source_; /*< Reference coordinate system for transformations. */ - std::vector<ftl::rgbd::RGBDSource*> sources_; + std::vector<ftl::rgbd::Source*> sources_; }; /** @@ -173,7 +173,7 @@ public: bool findTransformations(std::vector<Eigen::Matrix4f> &data) override; protected: - bool findFeatures(ftl::rgbd::RGBDSource* source, size_t idx) override; + bool findFeatures(ftl::rgbd::Source* source, size_t idx) override; bool processData() override; cv::Size pattern_size_; std::vector<std::vector<std::optional<pcl::PointCloud<pcl::PointXYZ>::Ptr>>> data_; diff --git a/applications/reconstruct/include/ftl/scene_rep_hash_sdf.hpp b/applications/reconstruct/include/ftl/scene_rep_hash_sdf.hpp index 5da37d479..09fd3d1a7 100644 --- a/applications/reconstruct/include/ftl/scene_rep_hash_sdf.hpp +++ b/applications/reconstruct/include/ftl/scene_rep_hash_sdf.hpp @@ -337,7 +337,7 @@ private: //t.startEvent("compactifyAllInOne"); m_hashParams.m_numOccupiedBlocks = compactifyHashAllInOneCUDA(m_hashData, m_hashParams); //this version uses atomics over prefix sums, which has a much better performance - //std::cout << "Occ blocks = " << m_hashParams.m_numOccupiedBlocks << std::endl; + std::cout << "Occ blocks = " << m_hashParams.m_numOccupiedBlocks << std::endl; m_hashData.updateParams(m_hashParams); //make sure numOccupiedBlocks is updated on the GPU //t.endEvent(); //t.evaluate(); diff --git a/applications/reconstruct/include/ftl/virtual_source.hpp b/applications/reconstruct/include/ftl/virtual_source.hpp index aed3dee50..ff1a89d78 100644 --- a/applications/reconstruct/include/ftl/virtual_source.hpp +++ b/applications/reconstruct/include/ftl/virtual_source.hpp @@ -2,7 +2,7 @@ #ifndef _FTL_RGBD_VIRTUAL_HPP_ #define _FTL_RGBD_VIRTUAL_HPP_ -#include <ftl/rgbd_source.hpp> +#include <ftl/rgbd/source.hpp> class CUDARayCastSDF; @@ -19,21 +19,17 @@ namespace rgbd { * calculating disparity, before converting to depth. Calibration of the images * is also performed. */ -class VirtualSource : public RGBDSource { +class VirtualSource : public ftl::rgbd::detail::Source { public: - VirtualSource(nlohmann::json &config, ftl::net::Universe *net); + VirtualSource(ftl::rgbd::Source*); ~VirtualSource(); void setScene(ftl::voxhash::SceneRep *); - void grab(); - void getRGBD(cv::Mat &rgb, cv::Mat &depth); + bool grab(); + //void getRGBD(cv::Mat &rgb, cv::Mat &depth); bool isReady(); - static inline RGBDSource *create(nlohmann::json &config, ftl::net::Universe *net) { - return new VirtualSource(config, net); - } - private: ftl::voxhash::SceneRep *scene_; CUDARayCastSDF *rays_; diff --git a/applications/reconstruct/src/main.cpp b/applications/reconstruct/src/main.cpp index f30080957..5ff41ebe4 100644 --- a/applications/reconstruct/src/main.cpp +++ b/applications/reconstruct/src/main.cpp @@ -12,7 +12,7 @@ #include <ftl/scene_rep_hash_sdf.hpp> #include <ftl/rgbd.hpp> #include <ftl/virtual_source.hpp> -#include <ftl/rgbd_streamer.hpp> +#include <ftl/rgbd/streamer.hpp> // #include <zlib.h> // #include <lz4.h> @@ -50,7 +50,7 @@ using ftl::net::Universe; using ftl::rgbd::Display; using std::string; using std::vector; -using ftl::rgbd::RGBDSource; +using ftl::rgbd::Source; using ftl::config::json_t; using json = nlohmann::json; @@ -67,7 +67,7 @@ using ftl::registration::loadTransformations; using ftl::registration::saveTransformations; struct Cameras { - RGBDSource *source; + Source *source; DepthCameraData gpu; DepthCameraParams params; }; @@ -76,36 +76,21 @@ static void run(ftl::Configurable *root) { Universe *net = ftl::create<Universe>(root, "net"); net->start(); - net->waitConnections(); + //net->waitConnections(); std::vector<Cameras> inputs; - auto sources = root->get<vector<json_t>>("sources"); + auto sources = ftl::createArray<Source>(root, "sources", net); //root->get<vector<json_t>>("sources"); - if (!sources) { + if (sources.size() == 0) { LOG(ERROR) << "No sources configured!"; return; } - // TODO Allow for non-net source types - for (auto &src : *sources) { - RGBDSource *in = ftl::rgbd::RGBDSource::create(src, net); - if (!in) { - LOG(ERROR) << "Unrecognized source: " << src; - } else { - auto &cam = inputs.emplace_back(); - cam.source = in; - cam.params.fx = in->getParameters().fx; - cam.params.fy = in->getParameters().fy; - cam.params.mx = -in->getParameters().cx; - cam.params.my = -in->getParameters().cy; - cam.params.m_imageWidth = in->getParameters().width; - cam.params.m_imageHeight = in->getParameters().height; - cam.params.m_sensorDepthWorldMax = in->getParameters().maxDepth; - cam.params.m_sensorDepthWorldMin = in->getParameters().minDepth; - cam.gpu.alloc(cam.params); - - LOG(INFO) << (string) src["uri"] << " loaded " << cam.params.fx; - } + for (int i=0; i<sources.size(); i++) { + Source *in = sources[i]; + auto &cam = inputs.emplace_back(); + cam.source = in; + cam.params.m_imageWidth = 0; } // TODO move this block in its own method and add automated tests @@ -115,6 +100,7 @@ static void run(ftl::Configurable *root) { if (!merge) { LOG(WARNING) << "Input merging not configured, using only first input in configuration"; inputs = { inputs[0] }; + inputs[0].source->setPose(Eigen::Matrix4f::Identity()); } if (inputs.size() > 1) { @@ -171,9 +157,13 @@ static void run(ftl::Configurable *root) { for (auto &input : inputs) { LOG(INFO) << " " + (string) input.source->getURI(); } ftl::rgbd::Display *display = ftl::create<ftl::rgbd::Display>(root, "display"); - ftl::rgbd::VirtualSource *virt = ftl::create<ftl::rgbd::VirtualSource>(root, "virtual", net); + ftl::rgbd::Source *virt = ftl::create<ftl::rgbd::Source>(root, "virtual", net); + + auto virtimpl = new ftl::rgbd::VirtualSource(virt); + virt->customImplementation(virtimpl); + ftl::voxhash::SceneRep *scene = ftl::create<ftl::voxhash::SceneRep>(root, "voxelhash"); - virt->setScene(scene); + virtimpl->setScene(scene); display->setSource(virt); ftl::rgbd::Streamer *stream = ftl::create<ftl::rgbd::Streamer>(root, "stream", net); @@ -192,7 +182,12 @@ static void run(ftl::Configurable *root) { }); int active = inputs.size(); - while (active > 0 && display->active()) { + while (ftl::running && display->active()) { + if (active == 0) { + LOG(INFO) << "Waiting for sources..."; + sleep_for(milliseconds(1000)); + } + active = 0; if (!paused) { @@ -200,11 +195,34 @@ static void run(ftl::Configurable *root) { scene->nextFrame(); for (size_t i = 0; i < inputs.size(); i++) { + if (!inputs[i].source->isReady()) { + inputs[i].params.m_imageWidth = 0; + // TODO(Nick) : Free gpu allocs if was ready before + continue; + } else { + auto &cam = inputs[i]; + auto in = inputs[i].source; + if (cam.params.m_imageWidth == 0) { + LOG(INFO) << "SETTING UP CAM PARAMS: " << in->parameters().fx; + cam.params.fx = in->parameters().fx; + cam.params.fy = in->parameters().fy; + cam.params.mx = -in->parameters().cx; + cam.params.my = -in->parameters().cy; + cam.params.m_imageWidth = in->parameters().width; + cam.params.m_imageHeight = in->parameters().height; + cam.params.m_sensorDepthWorldMax = in->parameters().maxDepth; + cam.params.m_sensorDepthWorldMin = in->parameters().minDepth; + cam.gpu.alloc(cam.params); + } + + //LOG(INFO) << in->getURI() << " loaded " << cam.params.fx; + } + // Get the RGB-Depth frame from input - RGBDSource *input = inputs[i].source; + Source *input = inputs[i].source; Mat rgb, depth; input->grab(); - input->getRGBD(rgb,depth); + input->getFrames(rgb,depth); active += 1; diff --git a/applications/reconstruct/src/registration.cpp b/applications/reconstruct/src/registration.cpp index 370fcf59d..1d61e4c06 100644 --- a/applications/reconstruct/src/registration.cpp +++ b/applications/reconstruct/src/registration.cpp @@ -25,8 +25,8 @@ namespace ftl { namespace registration { -using ftl::rgbd::CameraParameters; -using ftl::rgbd::RGBDSource; +using ftl::rgbd::Camera; +using ftl::rgbd::Source; using std::string; using std::vector; @@ -132,7 +132,7 @@ float fitPlaneError(PointCloud<PointXYZ>::Ptr cloud_in, float distance_threshold } //template<typename T = PointXYZ> typename -PointCloud<PointXYZ>::Ptr cornersToPointCloud(const vector<cv::Point2f> &corners, const Mat &depth, const CameraParameters &p) { +PointCloud<PointXYZ>::Ptr cornersToPointCloud(const vector<cv::Point2f> &corners, const Mat &depth, const Camera &p) { int corners_len = corners.size(); vector<cv::Vec3f> points(corners_len); @@ -170,7 +170,7 @@ PointCloud<PointXYZ>::Ptr cornersToPointCloud(const vector<cv::Point2f> &corners return cloud; } -bool findChessboardCorners(Mat &rgb, const Mat &depth, const CameraParameters &p, const cv::Size pattern_size, PointCloud<PointXYZ>::Ptr &out, float error_threshold) { +bool findChessboardCorners(Mat &rgb, const Mat &depth, const Camera &p, const cv::Size pattern_size, PointCloud<PointXYZ>::Ptr &out, float error_threshold) { vector<cv::Point2f> corners(pattern_size.width * pattern_size.height); #if CV_VERSION_MAJOR >= 4 @@ -257,7 +257,7 @@ Registration::Registration(nlohmann::json &config) : } } -RGBDSource* Registration::getSource(size_t idx) { +Source* Registration::getSource(size_t idx) { return sources_[idx]; } @@ -266,14 +266,14 @@ bool Registration::isTargetSourceSet() { } bool Registration::isTargetSourceFound() { - for (RGBDSource* source : sources_ ) { + for (Source* source : sources_ ) { if (isTargetSource(source)) return true; } return false; } -bool Registration::isTargetSource(RGBDSource *source) { - if (target_source_) { return source->getURI() == *target_source_; } +bool Registration::isTargetSource(Source *source) { + if (target_source_) { return source->getID() == *target_source_; } return false; } @@ -291,7 +291,7 @@ size_t Registration::getTargetSourceIdx() { return 0; } -void Registration::addSource(RGBDSource *source) { +void Registration::addSource(Source *source) { // TODO: check that source is not already included sources_.push_back(source); } @@ -390,7 +390,7 @@ bool Registration::findTransformations(map<string, Matrix4f> &data) { if (!findTransformations(T)) return false; for (size_t i = 0; i < sources_.size(); ++i) { - data[sources_[i]->getURI()] = T[i]; + data[sources_[i]->getID()] = T[i]; } return true; } @@ -438,31 +438,31 @@ void ChessboardRegistration::run() { // TODO: Move GUI elsewhere. Also applies to processData() and findFeatures() for (size_t i = 0; i < getSourcesCount(); ++i) { - cv::namedWindow("Registration: " + getSource(i)->getURI(), + cv::namedWindow("Registration: " + getSource(i)->getID(), cv::WINDOW_KEEPRATIO|cv::WINDOW_NORMAL); } Registration::run(); for (size_t i = 0; i < getSourcesCount(); ++i) { - cv::destroyWindow("Registration: " + getSource(i)->getURI()); + cv::destroyWindow("Registration: " + getSource(i)->getID()); } } -bool ChessboardRegistration::findFeatures(RGBDSource *source, size_t idx) { +bool ChessboardRegistration::findFeatures(Source *source, size_t idx) { optional<PointCloud<PointXYZ>::Ptr> result; PointCloud<PointXYZ>::Ptr cloud(new PointCloud<PointXYZ>); Mat rgb, depth; - source->getRGBD(rgb, depth); + source->getFrames(rgb, depth); - bool retval = findChessboardCorners(rgb, depth, source->getParameters(), pattern_size_, cloud, error_threshold_); + bool retval = findChessboardCorners(rgb, depth, source->parameters(), pattern_size_, cloud, error_threshold_); if (retval) { result.emplace(cloud); } data_[idx].push_back(result); - cv::imshow("Registration: " + source->getURI(), rgb); + cv::imshow("Registration: " + source->getID(), rgb); return retval; } @@ -546,11 +546,11 @@ bool ChessboardRegistrationChain::findTransformations(vector<Matrix4f> &data) { for (vector<pair<size_t, size_t>> level : edges_) { for (pair<size_t, size_t> edge : level) { LOG(INFO) << "Registering source " - << getSource(edge.second)->getURI() << " to source" - << getSource(edge.first)->getURI(); + << getSource(edge.second)->getID() << " to source" + << getSource(edge.first)->getID(); nlohmann::json conf(config_); - conf["targetsource"] = getSource(edge.first)->getURI(); + conf["targetsource"] = getSource(edge.first)->getID(); conf["chain"] = false; vector<Matrix4f> result; diff --git a/applications/reconstruct/src/virtual_source.cpp b/applications/reconstruct/src/virtual_source.cpp index ecaec6d2f..072f71cb5 100644 --- a/applications/reconstruct/src/virtual_source.cpp +++ b/applications/reconstruct/src/virtual_source.cpp @@ -10,19 +10,19 @@ using ftl::rgbd::VirtualSource; using std::mutex; using std::unique_lock; -VirtualSource::VirtualSource(nlohmann::json &config, ftl::net::Universe *net) - : RGBDSource(config, net) { - rays_ = new CUDARayCastSDF(config); +VirtualSource::VirtualSource(ftl::rgbd::Source *host) + : ftl::rgbd::detail::Source(host) { + rays_ = ftl::create<CUDARayCastSDF>(host, "raycaster"); //new CUDARayCastSDF(host->getConfig()); scene_ = nullptr; - params_.fx = config.value("focal", 430.0f); + params_.fx = rays_->value("focal", 430.0f); params_.fy = params_.fx; - params_.width = config.value("width", 640); - params_.height = config.value("height", 480); + params_.width = rays_->value("width", 640); + params_.height = rays_->value("height", 480); params_.cx = params_.width / 2; params_.cy = params_.height / 2; - params_.maxDepth = config.value("max_depth", 10.0f); - params_.minDepth = config.value("min_depth", 0.1f); + params_.maxDepth = rays_->value("max_depth", 10.0f); + params_.minDepth = rays_->value("min_depth", 0.1f); rgb_ = cv::Mat(cv::Size(params_.width,params_.height), CV_8UC3); idepth_ = cv::Mat(cv::Size(params_.width,params_.height), CV_32SC1); @@ -37,7 +37,7 @@ void VirtualSource::setScene(ftl::voxhash::SceneRep *scene) { scene_ = scene; } -void VirtualSource::grab() { +bool VirtualSource::grab() { if (scene_) { DepthCameraParams params; params.fx = params_.fx; @@ -49,12 +49,13 @@ void VirtualSource::grab() { params.m_sensorDepthWorldMin = params_.minDepth; params.m_sensorDepthWorldMax = params_.maxDepth; - rays_->render(scene_->getHashData(), scene_->getHashParams(), params, getPose()); + rays_->render(scene_->getHashData(), scene_->getHashParams(), params, host_->getPose()); - unique_lock<mutex> lk(mutex_); + //unique_lock<mutex> lk(mutex_); rays_->getRayCastData().download((int*)idepth_.data, (uchar3*)rgb_.data, rays_->getRayCastParams()); idepth_.convertTo(depth_, CV_32FC1, 1.0f / 100.0f); } + return true; } bool VirtualSource::isReady() { diff --git a/applications/vision/src/main.cpp b/applications/vision/src/main.cpp index 9a49c9c25..620ebd738 100644 --- a/applications/vision/src/main.cpp +++ b/applications/vision/src/main.cpp @@ -22,7 +22,7 @@ #include <ftl/rgbd.hpp> #include <ftl/middlebury.hpp> #include <ftl/display.hpp> -#include <ftl/rgbd_streamer.hpp> +#include <ftl/rgbd/streamer.hpp> #include <ftl/net/universe.hpp> #include <ftl/slave.hpp> #include <nlohmann/json.hpp> @@ -36,9 +36,8 @@ #pragma comment(lib, "Rpcrt4.lib") #endif -using ftl::rgbd::RGBDSource; -using ftl::rgbd::CameraParameters; -using ftl::rgbd::StereoVideoSource; +using ftl::rgbd::Source; +using ftl::rgbd::Camera; using ftl::Display; using ftl::rgbd::Streamer; using ftl::net::Universe; @@ -87,8 +86,10 @@ static void run(ftl::Configurable *root) { string file = ""; if (paths && (*paths).size() > 0) file = (*paths)[0]; - StereoVideoSource *source = nullptr; - source = ftl::create<StereoVideoSource>(root, "source", file); + Source *source = nullptr; + source = ftl::create<Source>(root, "source"); + + if (file != "") source->set("uri", file); Display *display = ftl::create<Display>(root, "display", "local"); @@ -101,8 +102,8 @@ static void run(ftl::Configurable *root) { while (ftl::running && display->active()) { cv::Mat rgb, depth; - source->getRGBD(rgb, depth); - if (!rgb.empty()) display->render(rgb, depth, source->getParameters()); + source->getFrames(rgb, depth); + if (!rgb.empty()) display->render(rgb, depth, source->parameters()); display->wait(10); } @@ -113,7 +114,7 @@ static void run(ftl::Configurable *root) { delete stream; delete display; - delete source; + //delete source; // TODO(Nick) Add ftl::destroy delete net; } diff --git a/components/common/cpp/include/ftl/config.h.in b/components/common/cpp/include/ftl/config.h.in index 67dc859e6..779918c14 100644 --- a/components/common/cpp/include/ftl/config.h.in +++ b/components/common/cpp/include/ftl/config.h.in @@ -30,6 +30,8 @@ extern const int FTL_VERSION_PATCH; extern const int FTL_VERSION_COMMITS; extern const char *FTL_VERSION_SHA1; +#define FTL_SOURCE_DIRECTORY "@CMAKE_SOURCE_DIR@" + #define FTL_LOCAL_CONFIG_ROOT @FTL_LOCAL_CONFIG_ROOT@ #define FTL_LOCAL_CACHE_ROOT @FTL_LOCAL_CACHE_ROOT@ #define FTL_LOCAL_DATA_ROOT @FTL_LOCAL_DATA_ROOT@ diff --git a/components/common/cpp/include/ftl/configurable.hpp b/components/common/cpp/include/ftl/configurable.hpp index 092fc1493..7999bcfa8 100644 --- a/components/common/cpp/include/ftl/configurable.hpp +++ b/components/common/cpp/include/ftl/configurable.hpp @@ -60,6 +60,8 @@ class Configurable { template <typename T> std::optional<T> get(const std::string &name); + std::string getID() { return *get<std::string>("$id"); } + /** * Get a configuration property, but return a default if not found. */ diff --git a/components/common/cpp/include/ftl/configuration.hpp b/components/common/cpp/include/ftl/configuration.hpp index fd21c63cd..9a01b56b3 100644 --- a/components/common/cpp/include/ftl/configuration.hpp +++ b/components/common/cpp/include/ftl/configuration.hpp @@ -31,6 +31,8 @@ std::optional<std::string> locateFile(const std::string &name); Configurable *configure(int argc, char **argv, const std::string &root); +Configurable *configure(json_t &); + /** * Change a configuration value based upon a URI. Return true if changed, * false if it was not able to change. @@ -84,8 +86,13 @@ T *create(ftl::Configurable *parent, const std::string &name, ARGS ...args); * Create a configurable rooted on a parent but with a specific object * that is not directly a child of the parent. Used by RGB-D Factory. */ +//template <typename T, typename... ARGS> +//T *create(ftl::Configurable *parent, json_t &obj, const std::string &name, ARGS ...args); + template <typename T, typename... ARGS> -T *create(ftl::Configurable *parent, json_t &obj, const std::string &name, ARGS ...args); +std::vector<T*> createArray(ftl::Configurable *parent, const std::string &name, ARGS ...args); + +void destroy(ftl::Configurable *); void set(const std::string &uri, const nlohmann::json &); @@ -93,6 +100,7 @@ void set(const std::string &uri, const nlohmann::json &); // Deprecated using config::create; +using config::createArray; using config::locateFile; using config::configure; @@ -128,8 +136,9 @@ T *ftl::config::create(json_t &link, ARGS ...args) { template <typename T, typename... ARGS> T *ftl::config::create(ftl::Configurable *parent, const std::string &name, ARGS ...args) { - //nlohmann::json &entity = ftl::config::resolve(parent->getConfig()[name]); - nlohmann::json &entity = (!parent->getConfig()[name].is_null()) ? parent->getConfig()[name] : ftl::config::resolve(parent->getConfig())[name]; + nlohmann::json &entity = (!parent->getConfig()[name].is_null()) + ? parent->getConfig()[name] + : ftl::config::resolve(parent->getConfig())[name]; if (entity.is_object()) { if (!entity["$id"].is_string()) { @@ -164,26 +173,51 @@ T *ftl::config::create(ftl::Configurable *parent, const std::string &name, ARGS } template <typename T, typename... ARGS> -T *ftl::config::create(ftl::Configurable *parent, json_t &obj, const std::string &name, ARGS ...args) { - //nlohmann::json &entity = ftl::config::resolve(parent->getConfig()[name]); - nlohmann::json &entity = obj; +std::vector<T*> ftl::config::createArray(ftl::Configurable *parent, const std::string &name, ARGS ...args) { + nlohmann::json &base = (!parent->getConfig()[name].is_null()) + ? parent->getConfig()[name] + : ftl::config::resolve(parent->getConfig())[name]; + + std::vector<T*> result; + + if (base.is_array()) { + for (auto &entity : base) { + if (entity.is_object()) { + if (!entity["$id"].is_string()) { + std::string id_str = *parent->get<std::string>("$id"); + if (id_str.find('#') != std::string::npos) { + entity["$id"] = id_str + std::string("/") + name; + } else { + entity["$id"] = id_str + std::string("#") + name; + } + } + + result.push_back(create<T>(entity, args...)); + } else if (entity.is_null()) { + // Must create the object from scratch... + std::string id_str = *parent->get<std::string>("$id"); + if (id_str.find('#') != std::string::npos) { + id_str = id_str + std::string("/") + name; + } else { + id_str = id_str + std::string("#") + name; + } + parent->getConfig()[name] = { + // cppcheck-suppress constStatement + {"$id", id_str} + }; + + nlohmann::json &entity2 = parent->getConfig()[name]; + result.push_back(create<T>(entity2, args...)); + } + } + } else { + LOG(WARNING) << "Expected an array for '" << name << "' in " << parent->getID(); + } + + return result; +} - if (entity.is_object()) { - if (!entity["$id"].is_string()) { - std::string id_str = *parent->get<std::string>("$id"); - if (id_str.find('#') != std::string::npos) { - entity["$id"] = id_str + std::string("/") + name; - } else { - entity["$id"] = id_str + std::string("#") + name; - } - } - return create<T>(entity, args...); - } else if (entity.is_null()) { - LOG(FATAL) << "Invalid raw object in Configurable construction"; - return nullptr; - } -} #endif // _FTL_COMMON_CONFIGURATION_HPP_ diff --git a/components/common/cpp/include/ftl/uri.hpp b/components/common/cpp/include/ftl/uri.hpp index be6b7fd3e..05325b346 100644 --- a/components/common/cpp/include/ftl/uri.hpp +++ b/components/common/cpp/include/ftl/uri.hpp @@ -30,7 +30,8 @@ namespace ftl { SCHEME_WS, SCHEME_IPC, SCHEME_FILE, - SCHEME_OTHER + SCHEME_OTHER, + SCHEME_DEVICE }; bool isValid() const { return m_valid; }; diff --git a/components/common/cpp/src/configuration.cpp b/components/common/cpp/src/configuration.cpp index 52e8998f8..af8bbd655 100644 --- a/components/common/cpp/src/configuration.cpp +++ b/components/common/cpp/src/configuration.cpp @@ -189,7 +189,7 @@ void ftl::config::registerConfigurable(ftl::Configurable *cfg) { } auto ix = config_instance.find(*uri); if (ix != config_instance.end()) { - LOG(ERROR) << "Attempting to create a duplicate object: " << *uri; + LOG(FATAL) << "Attempting to create a duplicate object: " << *uri; } else { config_instance[*uri] = cfg; LOG(INFO) << "Registering instance: " << *uri; @@ -430,6 +430,24 @@ static void signalIntHandler( int signum ) { ftl::running = false; } +Configurable *ftl::config::configure(ftl::config::json_t &cfg) { + loguru::g_preamble_date = false; + loguru::g_preamble_uptime = false; + loguru::g_preamble_thread = false; + int argc = 1; + const char *argv[] = {"d",0}; + loguru::init(argc, const_cast<char**>(argv), "--verbosity"); + + config_index.clear(); + config_instance.clear(); + + config = cfg; + _indexConfig(config); + Configurable *rootcfg = create<Configurable>(config); + return rootcfg; +} + + Configurable *ftl::config::configure(int argc, char **argv, const std::string &root) { loguru::g_preamble_date = false; loguru::g_preamble_uptime = false; diff --git a/components/common/cpp/src/loguru.cpp b/components/common/cpp/src/loguru.cpp index b481a411e..dd71e012a 100644 --- a/components/common/cpp/src/loguru.cpp +++ b/components/common/cpp/src/loguru.cpp @@ -533,6 +533,9 @@ namespace loguru CHECK_GT_F(argc, 0, "Expected proper argc/argv"); CHECK_EQ_F(argv[argc], nullptr, "Expected proper argc/argv"); + // Nick: Added to allow for multiple calls to init during tests + if (s_argv0_filename.size() > 0) return; + s_argv0_filename = filename(argv[0]); #ifdef _WIN32 diff --git a/components/common/cpp/src/uri.cpp b/components/common/cpp/src/uri.cpp index ab306786b..fe78c9ffe 100644 --- a/components/common/cpp/src/uri.cpp +++ b/components/common/cpp/src/uri.cpp @@ -51,6 +51,8 @@ void URI::_parse(uri_t puri) { else if (prototext == "http") m_proto = SCHEME_HTTP; else if (prototext == "ws") m_proto = SCHEME_WS; else if (prototext == "ipc") m_proto = SCHEME_IPC; + else if (prototext == "device") m_proto = SCHEME_DEVICE; + else if (prototext == "file") m_proto = SCHEME_FILE; else m_proto = SCHEME_OTHER; m_protostr = prototext; @@ -91,7 +93,7 @@ void URI::_parse(uri_t puri) { m_frag = std::string(uri.fragment.first, fraglast - uri.fragment.first); } - m_valid = m_proto != SCHEME_NONE && m_host.size() > 0; + m_valid = m_proto != SCHEME_NONE && (m_host.size() > 0 || m_path.size() > 0); if (m_valid) { if (m_qmap.size() > 0) m_base = std::string(uri.scheme.first, uri.query.first - uri.scheme.first - 1); diff --git a/components/renderers/cpp/include/ftl/display.hpp b/components/renderers/cpp/include/ftl/display.hpp index bf4eb51f5..2e56c459b 100644 --- a/components/renderers/cpp/include/ftl/display.hpp +++ b/components/renderers/cpp/include/ftl/display.hpp @@ -7,7 +7,7 @@ #include <ftl/config.h> #include <ftl/configurable.hpp> -#include "../../../rgbd-sources/include/ftl/camera_params.hpp" +#include "../../../rgbd-sources/include/ftl/rgbd/camera.hpp" #include <nlohmann/json.hpp> #include <opencv2/opencv.hpp> @@ -35,7 +35,7 @@ class Display : public ftl::Configurable { explicit Display(nlohmann::json &config, std::string name); ~Display(); - bool render(const cv::Mat &rgb, const cv::Mat &depth, const ftl::rgbd::CameraParameters &p); + bool render(const cv::Mat &rgb, const cv::Mat &depth, const ftl::rgbd::Camera &p); #if defined HAVE_PCL bool render(pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr); diff --git a/components/renderers/cpp/include/ftl/rgbd_display.hpp b/components/renderers/cpp/include/ftl/rgbd_display.hpp index 0467030b0..2d0c26a36 100644 --- a/components/renderers/cpp/include/ftl/rgbd_display.hpp +++ b/components/renderers/cpp/include/ftl/rgbd_display.hpp @@ -2,7 +2,7 @@ #define _FTL_RGBD_DISPLAY_HPP_ #include <nlohmann/json.hpp> -#include <ftl/rgbd_source.hpp> +#include <ftl/rgbd/source.hpp> using MouseAction = std::function<void(int, int, int, int)>; @@ -12,10 +12,10 @@ namespace rgbd { class Display : public ftl::Configurable { public: explicit Display(nlohmann::json &); - Display(nlohmann::json &, RGBDSource *); + Display(nlohmann::json &, Source *); ~Display(); - void setSource(RGBDSource *src) { source_ = src; } + void setSource(Source *src) { source_ = src; } void update(); bool active() const { return active_; } @@ -25,7 +25,7 @@ class Display : public ftl::Configurable { void wait(int ms); private: - RGBDSource *source_; + Source *source_; std::string name_; std::vector<std::function<void(int)>> key_handlers_; Eigen::Vector3f eye_; diff --git a/components/renderers/cpp/src/display.cpp b/components/renderers/cpp/src/display.cpp index 712590d80..4a7d88e44 100644 --- a/components/renderers/cpp/src/display.cpp +++ b/components/renderers/cpp/src/display.cpp @@ -93,7 +93,7 @@ Display::~Display() { /** * Convert an OpenCV RGB and Depth Mats to a PCL XYZRGB point cloud. */ -static pcl::PointCloud<pcl::PointXYZRGB>::Ptr rgbdToPointXYZ(const cv::Mat &rgb, const cv::Mat &depth, const ftl::rgbd::CameraParameters &p) { +static pcl::PointCloud<pcl::PointXYZRGB>::Ptr rgbdToPointXYZ(const cv::Mat &rgb, const cv::Mat &depth, const ftl::rgbd::Camera &p) { const double CX = p.cx; const double CY = p.cy; const double FX = p.fx; @@ -129,7 +129,7 @@ static pcl::PointCloud<pcl::PointXYZRGB>::Ptr rgbdToPointXYZ(const cv::Mat &rgb, } #endif // HAVE_PCL -bool Display::render(const cv::Mat &rgb, const cv::Mat &depth, const ftl::rgbd::CameraParameters &p) { +bool Display::render(const cv::Mat &rgb, const cv::Mat &depth, const ftl::rgbd::Camera &p) { Mat idepth; if (value("points", false) && rgb.rows != 0) { diff --git a/components/renderers/cpp/src/rgbd_display.cpp b/components/renderers/cpp/src/rgbd_display.cpp index d8f588ad1..4d31132e2 100644 --- a/components/renderers/cpp/src/rgbd_display.cpp +++ b/components/renderers/cpp/src/rgbd_display.cpp @@ -1,7 +1,7 @@ #include <ftl/rgbd_display.hpp> #include <opencv2/opencv.hpp> -using ftl::rgbd::RGBDSource; +using ftl::rgbd::Source; using ftl::rgbd::Display; using std::string; using cv::Mat; @@ -48,7 +48,7 @@ Display::Display(nlohmann::json &config) : ftl::Configurable(config) { init(); } -Display::Display(nlohmann::json &config, RGBDSource *source) +Display::Display(nlohmann::json &config, Source *source) : ftl::Configurable(config) { name_ = config.value("name", string("View [")+std::to_string(viewcount__)+string("]")); viewcount__++; @@ -122,8 +122,8 @@ void Display::update() { source_->setPose(viewPose); Mat rgb, depth; - //source_->grab(); - source_->getRGBD(rgb, depth); + source_->grab(); + source_->getFrames(rgb, depth); if (rgb.rows > 0) cv::imshow(name_, rgb); wait(1); } diff --git a/components/rgbd-sources/CMakeLists.txt b/components/rgbd-sources/CMakeLists.txt index d1326ec16..37b7d0606 100644 --- a/components/rgbd-sources/CMakeLists.txt +++ b/components/rgbd-sources/CMakeLists.txt @@ -2,14 +2,14 @@ set(RGBDSRC src/calibrate.cpp src/local.cpp src/disparity.cpp - src/rgbd_source.cpp - src/stereovideo_source.cpp - src/net_source.cpp - src/rgbd_streamer.cpp + src/source.cpp + src/stereovideo.cpp + src/net.cpp + src/streamer.cpp src/algorithms/rtcensus.cpp - src/algorithms/rtcensus_sgm.cpp - src/algorithms/opencv_sgbm.cpp - src/algorithms/opencv_bm.cpp +# src/algorithms/rtcensus_sgm.cpp +# src/algorithms/opencv_sgbm.cpp +# src/algorithms/opencv_bm.cpp ) if (LibArchive_FOUND) @@ -25,15 +25,16 @@ endif (LIBSGM_FOUND) if (CUDA_FOUND) list(APPEND RGBDSRC - "src/algorithms/opencv_cuda_bm.cpp" - "src/algorithms/opencv_cuda_bp.cpp" +# "src/algorithms/opencv_cuda_bm.cpp" +# "src/algorithms/opencv_cuda_bp.cpp" "src/algorithms/rtcensus.cu" - "src/algorithms/rtcensus_sgm.cu" +# "src/algorithms/rtcensus_sgm.cu" "src/algorithms/consistency.cu" "src/algorithms/sparse_census.cu" "src/algorithms/tex_filter.cu" - "src/algorithms/nick1.cu" - "src/algorithms/nick.cpp") +# "src/algorithms/nick1.cu" +# "src/algorithms/nick.cpp") +) endif (CUDA_FOUND) add_library(ftlrgbd ${RGBDSRC}) @@ -51,6 +52,7 @@ set_property(TARGET ftlrgbd PROPERTY CUDA_SEPARABLE_COMPILATION OFF) endif() #target_include_directories(cv-node PUBLIC ${PROJECT_SOURCE_DIR}/include) -target_link_libraries(ftlrgbd ftlcommon Threads::Threads ${OpenCV_LIBS} ${LIBSGM_LIBRARIES} ${CUDA_LIBRARIES} Eigen3::Eigen glog::glog ftlnet ${LibArchive_LIBRARIES}) +target_link_libraries(ftlrgbd ftlcommon ${OpenCV_LIBS} ${LIBSGM_LIBRARIES} ${CUDA_LIBRARIES} Eigen3::Eigen ftlnet ${LibArchive_LIBRARIES}) +add_subdirectory(test) diff --git a/components/rgbd-sources/include/ftl/rgbd.hpp b/components/rgbd-sources/include/ftl/rgbd.hpp index 774ebfe67..37c7a3b55 100644 --- a/components/rgbd-sources/include/ftl/rgbd.hpp +++ b/components/rgbd-sources/include/ftl/rgbd.hpp @@ -2,14 +2,6 @@ #ifndef _FTL_RGBD_HPP_ #define _FTL_RGBD_HPP_ -#include <ftl/rgbd_source.hpp> -#include <ftl/rgbdimage_source.hpp> -#include <ftl/stereoimage_source.hpp> -#include <ftl/stereovideo_source.hpp> -#include <ftl/net_source.hpp> - -#ifdef HAVE_LIBARCHIVE -#include <ftl/snapshot_source.hpp> -#endif // HAVE_LIBARCHIVE +#include <ftl/rgbd/source.hpp> #endif // _FTL_RGBD_HPP_ diff --git a/components/rgbd-sources/include/ftl/camera_params.hpp b/components/rgbd-sources/include/ftl/rgbd/camera.hpp similarity index 91% rename from components/rgbd-sources/include/ftl/camera_params.hpp rename to components/rgbd-sources/include/ftl/rgbd/camera.hpp index 40f64a1ed..3f4614887 100644 --- a/components/rgbd-sources/include/ftl/camera_params.hpp +++ b/components/rgbd-sources/include/ftl/rgbd/camera.hpp @@ -5,7 +5,7 @@ namespace ftl{ namespace rgbd { -struct CameraParameters { +struct Camera { double fx; double fy; double cx; diff --git a/components/rgbd-sources/include/ftl/rgbd/detail/source.hpp b/components/rgbd-sources/include/ftl/rgbd/detail/source.hpp new file mode 100644 index 000000000..65d4b5d3f --- /dev/null +++ b/components/rgbd-sources/include/ftl/rgbd/detail/source.hpp @@ -0,0 +1,39 @@ +#ifndef _FTL_RGBD_DETAIL_SOURCE_HPP_ +#define _FTL_RGBD_DETAIL_SOURCE_HPP_ + +#include <Eigen/Eigen> +#include <opencv2/opencv.hpp> +#include <ftl/rgbd/camera.hpp> + +namespace ftl{ +namespace rgbd { + +class Source; + +namespace detail { + +class Source { + public: + friend class ftl::rgbd::Source; + + public: + explicit Source(ftl::rgbd::Source *host) : host_(host), params_({0}) { } + virtual ~Source() {} + + virtual bool grab()=0; + virtual bool isReady() { return false; }; + virtual void setPose(const Eigen::Matrix4f &pose) { }; + + protected: + ftl::rgbd::Source *host_; + ftl::rgbd::Camera params_; + cv::Mat rgb_; + cv::Mat depth_; + //Eigen::Matrix4f pose_; +}; + +} +} +} + +#endif // _FTL_RGBD_DETAIL_SOURCE_HPP_ diff --git a/components/rgbd-sources/include/ftl/rgbdimage_source.hpp b/components/rgbd-sources/include/ftl/rgbd/group.hpp similarity index 100% rename from components/rgbd-sources/include/ftl/rgbdimage_source.hpp rename to components/rgbd-sources/include/ftl/rgbd/group.hpp diff --git a/components/rgbd-sources/include/ftl/snapshot.hpp b/components/rgbd-sources/include/ftl/rgbd/snapshot.hpp similarity index 90% rename from components/rgbd-sources/include/ftl/snapshot.hpp rename to components/rgbd-sources/include/ftl/rgbd/snapshot.hpp index b146c49d8..7e055b7cc 100644 --- a/components/rgbd-sources/include/ftl/snapshot.hpp +++ b/components/rgbd-sources/include/ftl/rgbd/snapshot.hpp @@ -9,7 +9,7 @@ #include <Eigen/Eigen> #include <opencv2/core/eigen.hpp> -#include <ftl/camera_params.hpp> +#include <ftl/rgbd/camera.hpp> #include <archive.h> #include <archive_entry.h> @@ -24,7 +24,7 @@ public: explicit SnapshotWriter(const std::string &filename); ~SnapshotWriter(); - bool addCameraRGBD(const std::string &name, const cv::Mat &rgb, const cv::Mat &depth, const Eigen::Matrix4f &pose, const ftl::rgbd::CameraParameters ¶ms); + bool addCameraRGBD(const std::string &name, const cv::Mat &rgb, const cv::Mat &depth, const Eigen::Matrix4f &pose, const ftl::rgbd::Camera ¶ms); bool addMat(const std::string &name, const cv::Mat &mat, const std::string &format="tiff"); bool addEigenMatrix4f(const std::string &name, const Eigen::Matrix4f &m, const std::string &format="pfm"); bool addFile(const std::string &name, const std::vector<uchar> &buf); @@ -39,7 +39,7 @@ struct SnapshotEntry { cv::Mat rgb; cv::Mat depth; Eigen::Matrix4f pose; - ftl::rgbd::CameraParameters params; + ftl::rgbd::Camera params; uint status; SnapshotEntry() : status(1+2+4+8) {}; }; @@ -49,7 +49,7 @@ public: explicit SnapshotReader(const std::string &filename); ~SnapshotReader(); - bool getCameraRGBD(const std::string &id, cv::Mat &rgb, cv::Mat &depth, Eigen::Matrix4f &pose, ftl::rgbd::CameraParameters ¶ms); + bool getCameraRGBD(const std::string &id, cv::Mat &rgb, cv::Mat &depth, Eigen::Matrix4f &pose, ftl::rgbd::Camera ¶ms); std::vector<std::string> getIds(); private: diff --git a/components/rgbd-sources/include/ftl/rgbd/source.hpp b/components/rgbd-sources/include/ftl/rgbd/source.hpp new file mode 100644 index 000000000..885046c3a --- /dev/null +++ b/components/rgbd-sources/include/ftl/rgbd/source.hpp @@ -0,0 +1,167 @@ +#ifndef _FTL_RGBD_SOURCE_HPP_ +#define _FTL_RGBD_SOURCE_HPP_ + +#include <ftl/configuration.hpp> +#include <ftl/rgbd/camera.hpp> +//#include <ftl/net/universe.hpp> +#include <ftl/uri.hpp> +#include <ftl/rgbd/detail/source.hpp> +#include <opencv2/opencv.hpp> +#include <Eigen/Eigen> +#include <shared_mutex> +#include <string> + +namespace ftl { + +namespace net { +class Universe; +} + +namespace rgbd { + +class SnapshotReader; + +enum capability_t { + kCapColour, // Has a colour feed + kCapDepth, // Has a depth feed + kCapRight, // It is possible to get a right image + kCapMovable, // Camera is software movable + kCapVideo, // It is a video feed, not static + kCapDisparity // Raw disparity is available +}; + +/** + * RGBD Generic data source configurable entity. This class hides the + * internal implementation of an RGBD source by providing accessor functions + * and by automatically changing the implementation in response to any URI + * changes. + * + * Cannot be constructed directly, use ftl::create<Source>(...). + * @see ftl::create + */ +class Source : public ftl::Configurable { + public: + template <typename T, typename... ARGS> + friend T *ftl::config::create(ftl::config::json_t &, ARGS ...); + + //template <typename T, typename... ARGS> + //friend T *ftl::config::create(ftl::Configurable *, const std::string &, ARGS ...); + + // This class cannot be constructed directly, use ftl::create + Source()=delete; + + // Also cannot be copied + Source(const Source&)=delete; + Source &operator=(const Source&) =delete; + + private: + explicit Source(ftl::config::json_t &cfg); + Source(ftl::config::json_t &cfg, ftl::rgbd::SnapshotReader *); + Source(ftl::config::json_t &cfg, ftl::net::Universe *net); + ~Source(); + + public: + /** + * Is this source valid and ready to grab?. + */ + bool isReady() { return (impl_) ? impl_->isReady() : false; } + + /** + * Perform the hardware or virtual frame grab operation. + */ + bool grab(); + + /** + * Do any post-grab processing. This function + * may take considerable time to return, especially for sources requiring + * software stereo correspondance. If `process` is not called manually + * after a `grab` and before a `get`, then it will be called automatically + * on first `get`. + */ + //void process(); + + /** + * Get a copy of both colour and depth frames. + */ + void getFrames(cv::Mat &c, cv::Mat &d); + + /** + * Get a copy of the colour frame only. + */ + void getColour(cv::Mat &c); + + /** + * Get a copy of the depth frame only. + */ + void getDepth(cv::Mat &d); + + /** + * Directly upload source RGB and Depth to GPU. + */ + void upload(cv::cuda::GpuMat&, cv::cuda::GpuMat&); + + void uploadColour(cv::cuda::GpuMat&); + void uploadDepth(cv::cuda::GpuMat&); + + /** + * Get the source's camera intrinsics. + */ + const Camera ¶meters() const { + if (impl_) return impl_->params_; + else return params_; + } + + /** + * Change the camera extrinsics by providing a new pose matrix. For virtual + * cameras this will move the camera, for physical cameras it is set by the + * registration process as it attempts to work out a cameras relative pose. + */ + virtual void setPose(const Eigen::Matrix4f &pose); + + /** + * Get the camera position as a pose matrix. + */ + const Eigen::Matrix4f &getPose() const; + + /** + * Check what features this source has available. + */ + virtual bool hasCapability(capability_t); + + /** + * Get a point in camera coordinates at specified pixel location. + */ + Eigen::Vector4f point(uint x, uint y); + + /** + * Force the internal implementation to be reconstructed. + */ + void reset(); + + ftl::net::Universe *getNet() const { return net_; } + + std::string getURI() { return value("uri", std::string("")); } + + void customImplementation(detail::Source *); + + std::shared_mutex &mutex() { return mutex_; } + + private: + detail::Source *impl_; + cv::Mat rgb_; + cv::Mat depth_; + Camera params_; // TODO Find better solution + Eigen::Matrix4f pose_; + ftl::net::Universe *net_; + std::shared_mutex mutex_; + + detail::Source *_createImplementation(); + detail::Source *_createFileImpl(const ftl::URI &uri); + detail::Source *_createNetImpl(const ftl::URI &uri); + detail::Source *_createDeviceImpl(const ftl::URI &uri); +}; + +} +} + +#endif // _FTL_RGBD_SOURCE_HPP_ diff --git a/components/rgbd-sources/include/ftl/rgbd_streamer.hpp b/components/rgbd-sources/include/ftl/rgbd/streamer.hpp similarity index 94% rename from components/rgbd-sources/include/ftl/rgbd_streamer.hpp rename to components/rgbd-sources/include/ftl/rgbd/streamer.hpp index bbe9449ec..02025e222 100644 --- a/components/rgbd-sources/include/ftl/rgbd_streamer.hpp +++ b/components/rgbd-sources/include/ftl/rgbd/streamer.hpp @@ -5,7 +5,7 @@ #include <loguru.hpp> #include <ftl/configuration.hpp> #include <ftl/configurable.hpp> -#include <ftl/rgbd_source.hpp> +#include <ftl/rgbd/source.hpp> #include <ftl/net/universe.hpp> #include <string> #include <list> @@ -28,7 +28,7 @@ static const unsigned int kGrabbed = 0x1; static const unsigned int kTransmitted = 0x2; struct StreamSource { - ftl::rgbd::RGBDSource *src; + ftl::rgbd::Source *src; unsigned int state; // Busy or ready to swap? cv::Mat rgb; // Tx buffer cv::Mat depth; // Tx buffer @@ -69,9 +69,9 @@ class Streamer : public ftl::Configurable { /** * Add an RGB-Depth source to be made available for streaming. */ - void add(RGBDSource *); + void add(Source *); - void remove(RGBDSource *); + void remove(Source *); void remove(const std::string &); /** @@ -91,7 +91,7 @@ class Streamer : public ftl::Configurable { */ void poll(); - RGBDSource *get(const std::string &uri); + Source *get(const std::string &uri); private: std::map<std::string, detail::StreamSource*> sources_; diff --git a/components/rgbd-sources/include/ftl/rgbdvideo_source.hpp b/components/rgbd-sources/include/ftl/rgbdvideo_source.hpp deleted file mode 100644 index e69de29bb..000000000 diff --git a/components/rgbd-sources/include/ftl/snapshot_source.hpp b/components/rgbd-sources/include/ftl/snapshot_source.hpp deleted file mode 100644 index d4900f76a..000000000 --- a/components/rgbd-sources/include/ftl/snapshot_source.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once -#ifndef _FTL_RGBD_SNAPSHOT_SOURCE_HPP_ -#define _FTL_RGBD_SNAPSHOT_SOURCE_HPP_ - -#include <loguru.hpp> - -#include "ftl/rgbd_source.hpp" -#include "ftl/snapshot.hpp" - -namespace ftl { -namespace rgbd { - -class SnapshotSource : public RGBDSource { - public: - SnapshotSource(nlohmann::json &config, ftl::rgbd::SnapshotReader &reader, const std::string &id); - ~SnapshotSource() {}; - void grab() override {}; -}; - -}; -}; - -#endif // _FTL_RGBD_SNAPSHOT_SOURCE_HPP_ diff --git a/components/rgbd-sources/include/ftl/stereoimage_source.hpp b/components/rgbd-sources/include/ftl/stereoimage_source.hpp deleted file mode 100644 index e69de29bb..000000000 diff --git a/components/rgbd-sources/src/algorithms/fixstars_sgm.hpp b/components/rgbd-sources/src/algorithms/fixstars_sgm.hpp index dd92efdb2..18492c6f1 100644 --- a/components/rgbd-sources/src/algorithms/fixstars_sgm.hpp +++ b/components/rgbd-sources/src/algorithms/fixstars_sgm.hpp @@ -22,7 +22,7 @@ namespace algorithms { * NOTE: We are using a modified version that supports disparity of 256. * @see https://github.com/knicos/libSGM */ -class FixstarsSGM : public ftl::Disparity { +class FixstarsSGM : public ftl::rgbd::detail::Disparity { public: explicit FixstarsSGM(nlohmann::json &config); diff --git a/components/rgbd-sources/src/algorithms/rtcensus.hpp b/components/rgbd-sources/src/algorithms/rtcensus.hpp index 0a31a49b1..785af6261 100644 --- a/components/rgbd-sources/src/algorithms/rtcensus.hpp +++ b/components/rgbd-sources/src/algorithms/rtcensus.hpp @@ -23,7 +23,7 @@ namespace algorithms { /** * Real-time Sparse Census disparity algorithm. */ -class RTCensus : public ftl::Disparity { +class RTCensus : public ftl::rgbd::detail::Disparity { public: explicit RTCensus(nlohmann::json &config); diff --git a/components/rgbd-sources/src/algorithms/rtcensus_sgm.cpp b/components/rgbd-sources/src/algorithms/rtcensus_sgm.cpp index be534d290..e84819e60 100644 --- a/components/rgbd-sources/src/algorithms/rtcensus_sgm.cpp +++ b/components/rgbd-sources/src/algorithms/rtcensus_sgm.cpp @@ -30,7 +30,7 @@ using std::get; using std::make_tuple; using std::bitset; -static ftl::Disparity::Register rtcensus("rtcensus_sgm", RTCensusSGM::create); +//static ftl::Disparity::Register rtcensus("rtcensus_sgm", RTCensusSGM::create); RTCensusSGM::RTCensusSGM(nlohmann::json &config) : Disparity(config), diff --git a/components/rgbd-sources/src/algorithms/rtcensus_sgm.hpp b/components/rgbd-sources/src/algorithms/rtcensus_sgm.hpp index 015c67b08..ef4dd7fd9 100644 --- a/components/rgbd-sources/src/algorithms/rtcensus_sgm.hpp +++ b/components/rgbd-sources/src/algorithms/rtcensus_sgm.hpp @@ -21,7 +21,7 @@ namespace algorithms { /** * WORK IN PROGRESS */ -class RTCensusSGM : public ftl::Disparity { +class RTCensusSGM : public ftl::rgbd::detail::Disparity { public: explicit RTCensusSGM(nlohmann::json &config); diff --git a/components/rgbd-sources/src/calibrate.cpp b/components/rgbd-sources/src/calibrate.cpp index 315fc8f35..7fe953c03 100644 --- a/components/rgbd-sources/src/calibrate.cpp +++ b/components/rgbd-sources/src/calibrate.cpp @@ -22,7 +22,8 @@ #include <opencv2/videoio.hpp> #include <opencv2/highgui.hpp> -using ftl::Calibrate; +using ftl::rgbd::detail::Calibrate; +using ftl::rgbd::detail::LocalSource; using cv::FileStorage; using cv::CALIB_FIX_PRINCIPAL_POINT; using cv::CALIB_ZERO_TANGENT_DIST; @@ -181,7 +182,7 @@ bool runCalibration(const Calibrate::Settings& s, Size imageSize, bool release_object); -Calibrate::Calibrate(nlohmann::json &config, ftl::LocalSource *s) : ftl::Configurable(config), local_(s) { +Calibrate::Calibrate(nlohmann::json &config, LocalSource *s) : ftl::Configurable(config), local_(s) { /*FileStorage fs(cal, FileStorage::READ); // Read the settings if (!fs.isOpened()) { diff --git a/components/rgbd-sources/src/calibrate.hpp b/components/rgbd-sources/src/calibrate.hpp index 08c0525ea..c3a163a03 100644 --- a/components/rgbd-sources/src/calibrate.hpp +++ b/components/rgbd-sources/src/calibrate.hpp @@ -17,6 +17,8 @@ class FileNode; }; namespace ftl { +namespace rgbd { +namespace detail { /** * Manage both performing and applying camera calibration. It will attempt to @@ -83,7 +85,7 @@ class Calibrate : public ftl::Configurable { }; public: - Calibrate(nlohmann::json &config, ftl::LocalSource *s); + Calibrate(nlohmann::json &config, LocalSource *s); /** * Perform a new camera calibration. Ignore and replace any existing @@ -122,7 +124,7 @@ class Calibrate : public ftl::Configurable { bool _loadCalibration(); private: - ftl::LocalSource *local_; + LocalSource *local_; Settings settings_; bool calibrated_; std::vector<cv::Mat> map1_; @@ -132,7 +134,10 @@ class Calibrate : public ftl::Configurable { cv::Mat r1_; cv::Mat Q_; }; -}; + +} +} +} #endif // _FTL_CALIBRATION_HPP_ diff --git a/components/rgbd-sources/src/disparity.cpp b/components/rgbd-sources/src/disparity.cpp index e482fa60c..6f413bc2e 100644 --- a/components/rgbd-sources/src/disparity.cpp +++ b/components/rgbd-sources/src/disparity.cpp @@ -7,7 +7,7 @@ #include <ftl/config.h> #include <ftl/configuration.hpp> -using ftl::Disparity; +using ftl::rgbd::detail::Disparity; std::map<std::string, std::function<Disparity*(ftl::Configurable *, const std::string &)>> *Disparity::algorithms__ = nullptr; @@ -40,10 +40,10 @@ void Disparity::_register(const std::string &n, // TODO(Nick) Add remaining algorithms #include "algorithms/rtcensus.hpp" -static ftl::Disparity::Register rtcensus("rtcensus", ftl::algorithms::RTCensus::create); +static ftl::rgbd::detail::Disparity::Register rtcensus("rtcensus", ftl::algorithms::RTCensus::create); #ifdef HAVE_LIBSGM #include "algorithms/fixstars_sgm.hpp" -static ftl::Disparity::Register fixstarssgm("libsgm", ftl::algorithms::FixstarsSGM::create); +static ftl::rgbd::detail::Disparity::Register fixstarssgm("libsgm", ftl::algorithms::FixstarsSGM::create); #endif // HAVE_LIBSGM diff --git a/components/rgbd-sources/src/disparity.hpp b/components/rgbd-sources/src/disparity.hpp index cb25e2895..838a4a164 100644 --- a/components/rgbd-sources/src/disparity.hpp +++ b/components/rgbd-sources/src/disparity.hpp @@ -10,6 +10,8 @@ #include <ftl/configurable.hpp> namespace ftl { +namespace rgbd { +namespace detail { /** * Virtual base class for disparity algorithms. An automatic factory is used @@ -61,7 +63,10 @@ class Disparity : public ftl::Configurable { private: static std::map<std::string,std::function<Disparity*(ftl::Configurable *, const std::string &)>> *algorithms__; }; -}; + +} +} +} #endif // _FTL_DISPARITY_HPP_ diff --git a/components/rgbd-sources/src/image.hpp b/components/rgbd-sources/src/image.hpp new file mode 100644 index 000000000..296a1c876 --- /dev/null +++ b/components/rgbd-sources/src/image.hpp @@ -0,0 +1,25 @@ +#ifndef _FTL_RGBD_IMAGE_HPP_ +#define _FTL_RGBD_IMAGE_HPP_ + +namespace ftl { +namespace rgbd { +namespace detail { + +class ImageSource : public ftl::rgbd::detail::Source { + public: + explicit ImageSource(ftl::rgbd::Source *host) : ftl::rgbd::detail::Source(host) { + + } + ImageSource(ftl::rgbd::Source *host, const std::string &f) : ftl::rgbd::detail::Source(host) { + + } + + bool grab() { return false; }; + bool isReady() { return false; }; +}; + +} +} +} + +#endif // _FTL_RGBD_IMAGE_HPP_ diff --git a/components/rgbd-sources/src/local.cpp b/components/rgbd-sources/src/local.cpp index d3d33d735..63257bad5 100644 --- a/components/rgbd-sources/src/local.cpp +++ b/components/rgbd-sources/src/local.cpp @@ -12,7 +12,7 @@ #include <opencv2/core.hpp> #include <opencv2/opencv.hpp> -using ftl::LocalSource; +using ftl::rgbd::detail::LocalSource; using cv::Mat; using cv::VideoCapture; using cv::Rect; diff --git a/components/rgbd-sources/src/local.hpp b/components/rgbd-sources/src/local.hpp index d3286684b..92437b8c4 100644 --- a/components/rgbd-sources/src/local.hpp +++ b/components/rgbd-sources/src/local.hpp @@ -11,6 +11,9 @@ namespace cv { }; namespace ftl { +namespace rgbd { +namespace detail { + class LocalSource : public Configurable { public: explicit LocalSource(nlohmann::json &config); @@ -44,7 +47,10 @@ class LocalSource : public Configurable { unsigned int width_; unsigned int height_; }; -}; + +} +} +} #endif // _FTL_LOCAL_HPP_ diff --git a/components/rgbd-sources/src/net_source.cpp b/components/rgbd-sources/src/net.cpp similarity index 64% rename from components/rgbd-sources/src/net_source.cpp rename to components/rgbd-sources/src/net.cpp index ae8bd9f9e..96b6d762a 100644 --- a/components/rgbd-sources/src/net_source.cpp +++ b/components/rgbd-sources/src/net.cpp @@ -1,20 +1,21 @@ -#include <ftl/net_source.hpp> +#include "net.hpp" #include <vector> #include <thread> #include <chrono> +#include <shared_mutex> -using ftl::rgbd::NetSource; +using ftl::rgbd::detail::NetSource; using ftl::net::Universe; using ftl::UUID; using std::string; -using ftl::rgbd::CameraParameters; -using std::mutex; +using ftl::rgbd::Camera; +using std::shared_mutex; using std::unique_lock; using std::vector; using std::this_thread::sleep_for; using std::chrono::milliseconds; -bool NetSource::_getCalibration(Universe &net, const UUID &peer, const string &src, ftl::rgbd::CameraParameters &p) { +bool NetSource::_getCalibration(Universe &net, const UUID &peer, const string &src, ftl::rgbd::Camera &p) { try { while(true) { auto buf = net.call<vector<unsigned char>>(peer_, "source_calibration", src); @@ -40,20 +41,13 @@ bool NetSource::_getCalibration(Universe &net, const UUID &peer, const string &s } } -NetSource::NetSource(nlohmann::json &config) : RGBDSource(config) { - -} - -NetSource::NetSource(nlohmann::json &config, ftl::net::Universe *net) - : RGBDSource(config, net), active_(false) { - - on("uri", [this](const config::Event &e) { - _updateURI(); - }); +NetSource::NetSource(ftl::rgbd::Source *host) + : ftl::rgbd::detail::Source(host), active_(false) { _updateURI(); - h_ = net->onConnect([this](ftl::net::Peer *p) { + h_ = host_->getNet()->onConnect([this](ftl::net::Peer *p) { + if (active_) return; LOG(INFO) << "NetSource restart..."; _updateURI(); }); @@ -61,13 +55,15 @@ NetSource::NetSource(nlohmann::json &config, ftl::net::Universe *net) NetSource::~NetSource() { if (uri_.size() > 0) { - net_->unbind(uri_); + host_->getNet()->unbind(uri_); } - net_->removeCallback(h_); + host_->getNet()->removeCallback(h_); } void NetSource::_recv(const vector<unsigned char> &jpg, const vector<unsigned char> &d) { + unique_lock<shared_mutex> lk(host_->mutex()); + cv::imdecode(jpg, cv::IMREAD_COLOR, &rgb_); //Mat(rgb_.size(), CV_16UC1); cv::imdecode(d, cv::IMREAD_UNCHANGED, &depth_); @@ -76,7 +72,7 @@ void NetSource::_recv(const vector<unsigned char> &jpg, const vector<unsigned ch N_--; if (N_ == 0) { N_ += 10; - if (!net_->send(peer_, "get_stream", *get<string>("uri"), 10, 0, net_->id(), *get<string>("uri"))) { + if (!host_->getNet()->send(peer_, "get_stream", *host_->get<string>("uri"), 10, 0, host_->getNet()->id(), *host_->get<string>("uri"))) { active_ = false; } } @@ -87,37 +83,36 @@ void NetSource::setPose(const Eigen::Matrix4f &pose) { vector<unsigned char> vec((unsigned char*)pose.data(), (unsigned char*)(pose.data()+(pose.size()))); try { - if (!net_->send(peer_, "set_pose", *get<string>("uri"), vec)) { + if (!host_->getNet()->send(peer_, "set_pose", *host_->get<string>("uri"), vec)) { active_ = false; } } catch (...) { } - RGBDSource::setPose(pose); + Source::setPose(pose); } void NetSource::_updateURI() { - unique_lock<mutex> lk(mutex_); + //unique_lock<mutex> lk(mutex_); active_ = false; - auto uri = get<string>("uri"); + auto uri = host_->get<string>("uri"); // TODO(Nick) If URI changes then must unbind + rebind. if (uri_.size() > 0) { - net_->unbind(uri_); + host_->getNet()->unbind(uri_); } if (uri) { - auto p = net_->findOne<ftl::UUID>("find_stream", *uri); + auto p = host_->getNet()->findOne<ftl::UUID>("find_stream", *uri); if (!p) { LOG(ERROR) << "Could not find stream: " << *uri; return; } peer_ = *p; - has_calibration_ = _getCalibration(*net_, peer_, *uri, params_); + has_calibration_ = _getCalibration(*host_->getNet(), peer_, *uri, params_); - net_->bind(*uri, [this](const vector<unsigned char> &jpg, const vector<unsigned char> &d) { - unique_lock<mutex> lk(mutex_); + host_->getNet()->bind(*uri, [this](const vector<unsigned char> &jpg, const vector<unsigned char> &d) { _recv(jpg, d); }); @@ -125,7 +120,7 @@ void NetSource::_updateURI() { // Initiate stream with request for first 10 frames try { - net_->send(peer_, "get_stream", *uri, 10, 0, net_->id(), *uri); + host_->getNet()->send(peer_, "get_stream", *uri, 10, 0, host_->getNet()->id(), *uri); } catch(...) { LOG(ERROR) << "Could not connect to stream " << *uri; } @@ -138,8 +133,9 @@ void NetSource::_updateURI() { } } -void NetSource::grab() { +bool NetSource::grab() { // net_.broadcast("grab"); + return true; } bool NetSource::isReady() { diff --git a/components/rgbd-sources/include/ftl/net_source.hpp b/components/rgbd-sources/src/net.hpp similarity index 68% rename from components/rgbd-sources/include/ftl/net_source.hpp rename to components/rgbd-sources/src/net.hpp index 4199d0e80..de4a3b00b 100644 --- a/components/rgbd-sources/include/ftl/net_source.hpp +++ b/components/rgbd-sources/src/net.hpp @@ -3,11 +3,13 @@ #define _FTL_RGBD_NET_HPP_ #include <ftl/net/universe.hpp> -#include <ftl/rgbd_source.hpp> +#include <ftl/rgbd/source.hpp> #include <string> +#include <mutex> namespace ftl { namespace rgbd { +namespace detail { /** * RGBD source from either a stereo video file with left + right images, or @@ -15,21 +17,18 @@ namespace rgbd { * calculating disparity, before converting to depth. Calibration of the images * is also performed. */ -class NetSource : public RGBDSource { +class NetSource : public detail::Source { public: - explicit NetSource(nlohmann::json &config); - NetSource(nlohmann::json &config, ftl::net::Universe *net); + explicit NetSource(ftl::rgbd::Source *); ~NetSource(); - void grab(); + bool grab(); bool isReady(); - static inline RGBDSource *create(nlohmann::json &config, ftl::net::Universe *net) { - return new NetSource(config, net); - } - void setPose(const Eigen::Matrix4f &pose); + void reset(); + private: bool has_calibration_; ftl::UUID peer_; @@ -37,12 +36,14 @@ class NetSource : public RGBDSource { bool active_; std::string uri_; ftl::net::callback_t h_; + std::mutex mutex_; - bool _getCalibration(ftl::net::Universe &net, const ftl::UUID &peer, const std::string &src, ftl::rgbd::CameraParameters &p); + bool _getCalibration(ftl::net::Universe &net, const ftl::UUID &peer, const std::string &src, ftl::rgbd::Camera &p); void _recv(const std::vector<unsigned char> &jpg, const std::vector<unsigned char> &d); void _updateURI(); }; +} } } diff --git a/components/rgbd-sources/src/rgbd_source.cpp b/components/rgbd-sources/src/rgbd_source.cpp deleted file mode 100644 index d796c02e9..000000000 --- a/components/rgbd-sources/src/rgbd_source.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include <ftl/rgbd_source.hpp> - -using ftl::rgbd::RGBDSource; -using ftl::Configurable; -using std::string; -using std::mutex; -using std::unique_lock; - -std::map<std::string, std::function<RGBDSource*(nlohmann::json&,ftl::net::Universe*)>> *RGBDSource::sources__ = nullptr; - -RGBDSource::RGBDSource(nlohmann::json &config) : Configurable(config), net_(nullptr) { - -} - -RGBDSource::RGBDSource(nlohmann::json &config, ftl::net::Universe *net) : Configurable(config), net_(net) { - -} - -RGBDSource::~RGBDSource() { - -} - -bool RGBDSource::isReady() { - return false; -} - -/*void RGBDSource::setURI(const std::string &uri) { - config_["uri"] = uri; -}*/ - -void RGBDSource::getRGBD(cv::Mat &rgb, cv::Mat &depth) { - unique_lock<mutex> lk(mutex_); - rgb_.copyTo(rgb); - depth_.copyTo(depth); -} - -Eigen::Vector4f RGBDSource::point(uint ux, uint uy) { - const auto ¶ms = getParameters(); - const float x = ((float)ux-params.width/2) / (float)params.fx; - const float y = ((float)uy-params.height/2) / (float)params.fy; - - unique_lock<mutex> lk(mutex_); - const float depth = depth_.at<float>(uy,ux); - return Eigen::Vector4f(x*depth,y*depth,depth,1.0); -} - -bool RGBDSource::snapshot(const std::string &fileprefix) { - cv::Mat rgb; - cv::Mat depth; - getRGBD(rgb, depth); - - cv::Mat d2; - depth.convertTo(d2, CV_16UC1, 16*100); - - cv::imwrite(fileprefix+"-RGB.jpg", rgb); - cv::imwrite(fileprefix+"-DEPTH.png",depth); - return true; -} - -RGBDSource *RGBDSource::create(nlohmann::json &config, ftl::net::Universe *net) { - auto &cfg = ftl::config::resolve(config); - if (!cfg["type"].is_string()) { - LOG(ERROR) << "Missing RGB-D source type: " << cfg["type"].type_name(); - //return nullptr; - } - if (sources__->count(cfg["type"].get<string>()) != 1) return nullptr; - return (*sources__)[cfg["type"].get<string>()](config, net); -} - -void RGBDSource::_register(const std::string &n, - std::function<RGBDSource*(nlohmann::json&,ftl::net::Universe*)> f) { - if (!sources__) sources__ = new std::map<std::string, std::function<RGBDSource*(nlohmann::json&,ftl::net::Universe*)>>; - //LOG(INFO) << "Register RGB-D Source: " << n; - (*sources__)[n] = f; -} - -#include <ftl/net_source.hpp> -static RGBDSource::Register netsource("net", ftl::rgbd::NetSource::create); -#include <ftl/stereovideo_source.hpp> -static RGBDSource::Register svsource("stereovideo", ftl::rgbd::StereoVideoSource::create); diff --git a/components/rgbd-sources/src/snapshot.cpp b/components/rgbd-sources/src/snapshot.cpp index 5142612b9..51b8a635f 100644 --- a/components/rgbd-sources/src/snapshot.cpp +++ b/components/rgbd-sources/src/snapshot.cpp @@ -1,4 +1,4 @@ -#include <ftl/snapshot.hpp> +#include <ftl/rgbd/snapshot.hpp> #include <nlohmann/json.hpp> @@ -16,9 +16,9 @@ using std::vector; using Eigen::Matrix4f; // TODO: move to camera_params -using ftl::rgbd::CameraParameters; +using ftl::rgbd::Camera; -void to_json(nlohmann::json& j, const CameraParameters &p) { +void to_json(nlohmann::json& j, const Camera &p) { j = nlohmann::json{ {"fx", p.fx}, {"fy", p.fy}, @@ -31,7 +31,7 @@ void to_json(nlohmann::json& j, const CameraParameters &p) { }; } -void from_json(const nlohmann::json& j, CameraParameters &p) { +void from_json(const nlohmann::json& j, Camera &p) { j.at("fx").get_to(p.fx); j.at("fy").get_to(p.fy); j.at("cx").get_to(p.cx); @@ -127,7 +127,7 @@ bool SnapshotWriter::addEigenMatrix4f(const string &name, const Matrix4f &m, con } bool SnapshotWriter::addCameraRGBD(const string &name, const Mat &rgb, const Mat &depth, - const Matrix4f &pose, const CameraParameters ¶ms) { + const Matrix4f &pose, const Camera ¶ms) { bool retval = true; retval &= addMat(name + "-RGB", rgb); retval &= addMat(name + "-D", depth); @@ -256,7 +256,7 @@ vector<string> SnapshotReader::getIds() { } bool SnapshotReader::getCameraRGBD(const string &id, Mat &rgb, Mat &depth, - Matrix4f &pose, CameraParameters ¶ms) { + Matrix4f &pose, Camera ¶ms) { if (data_.find(id) == data_.end()) { LOG(ERROR) << "entry not found: " << id; return false; diff --git a/components/rgbd-sources/src/snapshot_source.cpp b/components/rgbd-sources/src/snapshot_source.cpp index 40751e58a..96c16e4d0 100644 --- a/components/rgbd-sources/src/snapshot_source.cpp +++ b/components/rgbd-sources/src/snapshot_source.cpp @@ -1,14 +1,15 @@ -#include "ftl/snapshot_source.hpp" +#include "snapshot_source.hpp" #include <opencv2/opencv.hpp> #include <Eigen/Eigen> #include <opencv2/core/eigen.hpp> using namespace ftl::rgbd; +using ftl::rgbd::detail::SnapshotSource; using std::string; -SnapshotSource::SnapshotSource(nlohmann::json &config, SnapshotReader &reader, const string &id) : RGBDSource(config) { +SnapshotSource::SnapshotSource(ftl::rgbd::Source *host, SnapshotReader &reader, const string &id) : detail::Source(host) { Eigen::Matrix4f pose; reader.getCameraRGBD(id, rgb_, depth_, pose, params_); setPose(pose); diff --git a/components/rgbd-sources/src/snapshot_source.hpp b/components/rgbd-sources/src/snapshot_source.hpp new file mode 100644 index 000000000..3ba9a7f02 --- /dev/null +++ b/components/rgbd-sources/src/snapshot_source.hpp @@ -0,0 +1,31 @@ +#pragma once +#ifndef _FTL_RGBD_SNAPSHOT_SOURCE_HPP_ +#define _FTL_RGBD_SNAPSHOT_SOURCE_HPP_ + +#include <loguru.hpp> + +#include "ftl/rgbd/source.hpp" +#include "ftl/rgbd/snapshot.hpp" + +namespace ftl { +namespace rgbd { +namespace detail { + +class SnapshotSource : public detail::Source { + public: + SnapshotSource(ftl::rgbd::Source *); + SnapshotSource(ftl::rgbd::Source *, ftl::rgbd::SnapshotReader &reader, const std::string &id); + ~SnapshotSource() {}; + + bool grab() override {}; + bool isReady() { return true; } + + //void reset(); + +}; + +} +} +} + +#endif // _FTL_RGBD_SNAPSHOT_SOURCE_HPP_ diff --git a/components/rgbd-sources/src/source.cpp b/components/rgbd-sources/src/source.cpp new file mode 100644 index 000000000..bd68b0ae8 --- /dev/null +++ b/components/rgbd-sources/src/source.cpp @@ -0,0 +1,166 @@ +#include <loguru.hpp> +#include <ftl/rgbd/source.hpp> + +#include "net.hpp" +#include "stereovideo.hpp" +#include "image.hpp" + +#ifdef HAVE_LIBARCHIVE +#include "snapshot_source.hpp" +#endif + +using ftl::rgbd::Source; +using ftl::Configurable; +using std::string; +using std::shared_mutex; +using std::unique_lock; +using std::shared_lock; +using ftl::rgbd::detail::StereoVideoSource; +using ftl::rgbd::detail::NetSource; +using ftl::rgbd::detail::ImageSource; +using ftl::rgbd::capability_t; + +Source::Source(ftl::config::json_t &cfg) : Configurable(cfg), net_(nullptr) { + impl_ = nullptr; + params_ = {0}; + reset(); + + on("uri", [this](const ftl::config::Event &e) { + LOG(INFO) << "URI change for source: " << getURI(); + reset(); + }); +} + +Source::Source(ftl::config::json_t &cfg, ftl::net::Universe *net) : Configurable(cfg), net_(net) { + impl_ = nullptr; + params_ = {0}; + reset(); + + on("uri", [this](const ftl::config::Event &e) { + LOG(INFO) << "URI change for source: " << getURI(); + reset(); + }); +} + +Source::~Source() { + +} + +void Source::customImplementation(ftl::rgbd::detail::Source *impl) { + if (impl_) delete impl_; + impl_ = impl; +} + +ftl::rgbd::detail::Source *Source::_createImplementation() { + auto uristr = get<string>("uri"); + if (!uristr) { + //LOG(WARNING) << "Missing URI for source"; + return nullptr; + } + + ftl::URI uri(*uristr); + if (!uri.isValid()) { + LOG(WARNING) << "Invalid URI for source: " << *uristr; + return nullptr; + } + + switch (uri.getScheme()) { + case ftl::URI::SCHEME_FILE : return _createFileImpl(uri); + case ftl::URI::SCHEME_FTL : return _createNetImpl(uri); + case ftl::URI::SCHEME_DEVICE : return _createDeviceImpl(uri); + default: break; + } + + LOG(WARNING) << "Unrecognised source URI: " << *uristr; + return nullptr; +} + +ftl::rgbd::detail::Source *Source::_createFileImpl(const ftl::URI &uri) { + std::string path = uri.getPath(); + // Note: This is non standard + if (uri.getHost() == "." || uri.getHost() == "~") path = uri.getHost()+path; + + auto eix = path.find_last_of('.'); + + if (eix == string::npos) { + // Might be a directory + if (ftl::is_directory(path)) { + return new StereoVideoSource(this); + } else { + return nullptr; + } + } else if (ftl::is_file(path)) { + string ext = path.substr(eix+1); + + if (ext == "png" || ext == "jpg") { + return new ImageSource(this, path); + } else if (ext == "mp4") { + return new StereoVideoSource(this, path); + } else if (ext == "tar") { + return nullptr; + } else { + LOG(WARNING) << "Unrecognised file type: " << path; + } + } else { + LOG(WARNING) << "File does not exist: " << path; + } + + return nullptr; +} + +ftl::rgbd::detail::Source *Source::_createNetImpl(const ftl::URI &uri) { + LOG(INFO) << "MAKE NET SOURCE"; + return new NetSource(this); +} + +ftl::rgbd::detail::Source *Source::_createDeviceImpl(const ftl::URI &uri) { + if (uri.getPathSegment(0) == "video") { + return new StereoVideoSource(this); + } + return nullptr; +} + +void Source::getFrames(cv::Mat &rgb, cv::Mat &depth) { + shared_lock<shared_mutex> lk(mutex_); + rgb_.copyTo(rgb); + depth_.copyTo(depth); +} + +Eigen::Vector4f Source::point(uint ux, uint uy) { + const auto ¶ms = parameters(); + const float x = ((float)ux-params.width/2) / (float)params.fx; + const float y = ((float)uy-params.height/2) / (float)params.fy; + + shared_lock<shared_mutex> lk(mutex_); + const float depth = depth_.at<float>(uy,ux); + return Eigen::Vector4f(x*depth,y*depth,depth,1.0); +} + +void Source::setPose(const Eigen::Matrix4f &pose) { + pose_ = pose; + if (impl_) impl_->setPose(pose); +} + +const Eigen::Matrix4f &Source::getPose() const { + return pose_; +} + +bool Source::hasCapability(capability_t) { + return false; +} + +void Source::reset() { + unique_lock<shared_mutex> lk(mutex_); + if (impl_) delete impl_; + impl_ = _createImplementation(); +} + +bool Source::grab() { + unique_lock<shared_mutex> lk(mutex_); + if (impl_ && impl_->grab()) { + impl_->rgb_.copyTo(rgb_); + impl_->depth_.copyTo(depth_); + return true; + } + return false; +} diff --git a/components/rgbd-sources/src/stereovideo_source.cpp b/components/rgbd-sources/src/stereovideo.cpp similarity index 75% rename from components/rgbd-sources/src/stereovideo_source.cpp rename to components/rgbd-sources/src/stereovideo.cpp index 047c9be55..07cc16885 100644 --- a/components/rgbd-sources/src/stereovideo_source.cpp +++ b/components/rgbd-sources/src/stereovideo.cpp @@ -1,35 +1,31 @@ #include <loguru.hpp> -#include <ftl/stereovideo_source.hpp> +#include "stereovideo.hpp" #include <ftl/configuration.hpp> #include "calibrate.hpp" #include "local.hpp" #include "disparity.hpp" #include <mutex> -using ftl::Calibrate; -using ftl::LocalSource; -using ftl::rgbd::StereoVideoSource; +using ftl::rgbd::detail::Calibrate; +using ftl::rgbd::detail::LocalSource; +using ftl::rgbd::detail::StereoVideoSource; using std::string; using std::mutex; using std::unique_lock; -StereoVideoSource::StereoVideoSource(nlohmann::json &config, ftl::net::Universe *net) - : RGBDSource(config, net) { +StereoVideoSource::StereoVideoSource(ftl::rgbd::Source *host) + : ftl::rgbd::detail::Source(host) { } -StereoVideoSource::StereoVideoSource(nlohmann::json &config, const string &file) - : RGBDSource(config), ready_(false) { +StereoVideoSource::StereoVideoSource(ftl::rgbd::Source *host, const string &file) + : ftl::rgbd::detail::Source(host), ready_(false) { - REQUIRED({ - {"feed","Details on source video [object]","object"} - }); - if (ftl::is_video(file)) { // Load video file LOG(INFO) << "Using video file..."; //lsrc_ = new LocalSource(file, config["source"]); - lsrc_ = ftl::create<LocalSource>(this, "feed", file); + lsrc_ = ftl::create<LocalSource>(host_, "feed", file); } else if (file != "") { auto vid = ftl::locateFile("video.mp4"); if (!vid) { @@ -37,19 +33,19 @@ StereoVideoSource::StereoVideoSource(nlohmann::json &config, const string &file) } else { LOG(INFO) << "Using test directory..."; //lsrc_ = new LocalSource(*vid, config["source"]); - lsrc_ = ftl::create<LocalSource>(this, "feed", *vid); + lsrc_ = ftl::create<LocalSource>(host_, "feed", *vid); } } else { // Use cameras LOG(INFO) << "Using cameras..."; //lsrc_ = new LocalSource(config["source"]); - lsrc_ = ftl::create<LocalSource>(this, "feed"); + lsrc_ = ftl::create<LocalSource>(host_, "feed"); } //calib_ = new Calibrate(lsrc_, ftl::resolve(config["calibration"])); - calib_ = ftl::create<Calibrate>(this, "calibration", lsrc_); + calib_ = ftl::create<Calibrate>(host_, "calibration", lsrc_); - if (value("calibrate", false)) calib_->recalibrate(); + if (host_->value("calibrate", false)) calib_->recalibrate(); if (!calib_->isCalibrated()) LOG(WARNING) << "Cameras are not calibrated!"; else LOG(INFO) << "Calibration initiated."; @@ -73,8 +69,8 @@ StereoVideoSource::StereoVideoSource(nlohmann::json &config, const string &file) calib_->rectifyStereo(mask_l, mask_r); mask_l_ = (mask_l == 0); - disp_ = Disparity::create(this, "disparity"); - if (!disp_) LOG(FATAL) << "Unknown disparity algorithm : " << *get<ftl::config::json_t>("disparity"); + disp_ = Disparity::create(host_, "disparity"); + if (!disp_) LOG(FATAL) << "Unknown disparity algorithm : " << *host_->get<ftl::config::json_t>("disparity"); disp_->setMask(mask_l_); LOG(INFO) << "StereoVideo source ready..."; @@ -110,15 +106,16 @@ static void disparityToDepth(const cv::Mat &disparity, cv::Mat &depth, const cv: } } -void StereoVideoSource::grab() { +bool StereoVideoSource::grab() { calib_->rectified(left_, right_); cv::Mat disp; disp_->compute(left_, right_, disp); - unique_lock<mutex> lk(mutex_); + //unique_lock<mutex> lk(mutex_); left_.copyTo(rgb_); disparityToDepth(disp, depth_, calib_->getQ()); + return true; } bool StereoVideoSource::isReady() { diff --git a/components/rgbd-sources/include/ftl/stereovideo_source.hpp b/components/rgbd-sources/src/stereovideo.hpp similarity index 56% rename from components/rgbd-sources/include/ftl/stereovideo_source.hpp rename to components/rgbd-sources/src/stereovideo.hpp index 47e2d089c..8b07e8764 100644 --- a/components/rgbd-sources/include/ftl/stereovideo_source.hpp +++ b/components/rgbd-sources/src/stereovideo.hpp @@ -2,48 +2,46 @@ #ifndef _FTL_RGBD_STEREOVIDEO_HPP_ #define _FTL_RGBD_STEREOVIDEO_HPP_ -#include <ftl/rgbd_source.hpp> +#include <ftl/rgbd/source.hpp> #include <string> namespace ftl { +namespace rgbd { +namespace detail { + class LocalSource; class Calibrate; class Disparity; -namespace rgbd { - /** * RGBD source from either a stereo video file with left + right images, or * direct from two camera devices. A variety of algorithms are included for * calculating disparity, before converting to depth. Calibration of the images * is also performed. */ -class StereoVideoSource : public RGBDSource { +class StereoVideoSource : public detail::Source { public: - StereoVideoSource(nlohmann::json &config, ftl::net::Universe *net); - StereoVideoSource(nlohmann::json &config, const std::string &file); + explicit StereoVideoSource(ftl::rgbd::Source*); + StereoVideoSource(ftl::rgbd::Source*, const std::string &); ~StereoVideoSource(); - void grab(); + bool grab(); bool isReady(); - const cv::Mat &getRight() const { return right_; } - - static inline RGBDSource *create(nlohmann::json &config, ftl::net::Universe *net) { - return new StereoVideoSource(config, net); - } + //const cv::Mat &getRight() const { return right_; } private: - ftl::LocalSource *lsrc_; - ftl::Calibrate *calib_; - ftl::Disparity *disp_; + LocalSource *lsrc_; + Calibrate *calib_; + Disparity *disp_; bool ready_; cv::Mat left_; cv::Mat right_; cv::Mat mask_l_; }; +} } } diff --git a/components/rgbd-sources/src/rgbd_streamer.cpp b/components/rgbd-sources/src/streamer.cpp similarity index 93% rename from components/rgbd-sources/src/rgbd_streamer.cpp rename to components/rgbd-sources/src/streamer.cpp index 9ab2d184f..2ea5babc8 100644 --- a/components/rgbd-sources/src/rgbd_streamer.cpp +++ b/components/rgbd-sources/src/streamer.cpp @@ -1,11 +1,11 @@ -#include <ftl/rgbd_streamer.hpp> +#include <ftl/rgbd/streamer.hpp> #include <vector> #include <optional> #include <thread> #include <chrono> using ftl::rgbd::Streamer; -using ftl::rgbd::RGBDSource; +using ftl::rgbd::Source; using ftl::rgbd::detail::StreamSource; using ftl::rgbd::detail::StreamClient; using ftl::net::Universe; @@ -60,9 +60,9 @@ Streamer::Streamer(nlohmann::json &config, Universe *net) shared_lock<shared_mutex> slk(mutex_); if (sources_.find(uri) != sources_.end()) { - buf.resize(sizeof(CameraParameters)); + buf.resize(sizeof(Camera)); LOG(INFO) << "Calib buf size = " << buf.size(); - memcpy(buf.data(), &sources_[uri]->src->getParameters(), buf.size()); + memcpy(buf.data(), &sources_[uri]->src->parameters(), buf.size()); } return buf; }); @@ -90,16 +90,16 @@ Streamer::~Streamer() { pool_.stop(); } -void Streamer::add(RGBDSource *src) { +void Streamer::add(Source *src) { unique_lock<shared_mutex> ulk(mutex_); - if (sources_.find(src->getURI()) != sources_.end()) return; + if (sources_.find(src->getID()) != sources_.end()) return; StreamSource *s = new StreamSource; s->src = src; s->state = 0; - sources_[src->getURI()] = s; + sources_[src->getID()] = s; - LOG(INFO) << "Streaming: " << src->getURI(); + LOG(INFO) << "Streaming: " << src->getID(); } void Streamer::_addClient(const string &source, int N, int rate, const ftl::UUID &peer, const string &dest) { @@ -120,7 +120,7 @@ void Streamer::_addClient(const string &source, int N, int rate, const ftl::UUID s->clients[rate].push_back(c); } -void Streamer::remove(RGBDSource *) { +void Streamer::remove(Source *) { } @@ -170,7 +170,7 @@ void Streamer::run(bool block) { void Streamer::_swap(StreamSource &src) { if (src.state == (ftl::rgbd::detail::kGrabbed | ftl::rgbd::detail::kTransmitted)) { - src.src->getRGBD(src.rgb, src.depth); + src.src->getFrames(src.rgb, src.depth); src.state = 0; } } @@ -293,7 +293,7 @@ void Streamer::_schedule() { job_cv.wait(lk, [&jobs]{ return jobs == 0; }); } -RGBDSource *Streamer::get(const std::string &uri) { +Source *Streamer::get(const std::string &uri) { shared_lock<shared_mutex> slk(mutex_); if (sources_.find(uri) != sources_.end()) return sources_[uri]->src; else return nullptr; diff --git a/components/rgbd-sources/test/CMakeLists.txt b/components/rgbd-sources/test/CMakeLists.txt new file mode 100644 index 000000000..538c4d757 --- /dev/null +++ b/components/rgbd-sources/test/CMakeLists.txt @@ -0,0 +1,10 @@ +### Source Unit ################################################################ +add_executable(source_unit + ./tests.cpp + ./source_unit.cpp +) +target_include_directories(source_unit PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../include") +target_link_libraries(source_unit + ftlcommon Eigen3::Eigen) + +add_test(SourceUnitTest source_unit) diff --git a/components/rgbd-sources/test/catch.hpp b/components/rgbd-sources/test/catch.hpp new file mode 100644 index 000000000..b1b2411d2 --- /dev/null +++ b/components/rgbd-sources/test/catch.hpp @@ -0,0 +1,14362 @@ +/* + * Catch v2.5.0 + * Generated: 2018-11-26 20:46:12.165372 + * ---------------------------------------------------------- + * This file has been merged from multiple headers. Please don't edit it directly + * Copyright (c) 2018 Two Blue Cubes Ltd. All rights reserved. + * + * 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) + */ +#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +// start catch.hpp + + +#define CATCH_VERSION_MAJOR 2 +#define CATCH_VERSION_MINOR 5 +#define CATCH_VERSION_PATCH 0 + +#ifdef __clang__ +# pragma clang system_header +#elif defined __GNUC__ +# pragma GCC system_header +#endif + +// start catch_suppress_warnings.h + +#ifdef __clang__ +# ifdef __ICC // icpc defines the __clang__ macro +# pragma warning(push) +# pragma warning(disable: 161 1682) +# else // __ICC +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wpadded" +# pragma clang diagnostic ignored "-Wswitch-enum" +# pragma clang diagnostic ignored "-Wcovered-switch-default" +# endif +#elif defined __GNUC__ + // GCC likes to warn on REQUIREs, and we cannot suppress them + // locally because g++'s support for _Pragma is lacking in older, + // still supported, versions +# pragma GCC diagnostic ignored "-Wparentheses" +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-variable" +# pragma GCC diagnostic ignored "-Wpadded" +#endif +// end catch_suppress_warnings.h +#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) +# define CATCH_IMPL +# define CATCH_CONFIG_ALL_PARTS +#endif + +// In the impl file, we want to have access to all parts of the headers +// Can also be used to sanely support PCHs +#if defined(CATCH_CONFIG_ALL_PARTS) +# define CATCH_CONFIG_EXTERNAL_INTERFACES +# if defined(CATCH_CONFIG_DISABLE_MATCHERS) +# undef CATCH_CONFIG_DISABLE_MATCHERS +# endif +# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +# endif +#endif + +#if !defined(CATCH_CONFIG_IMPL_ONLY) +// start catch_platform.h + +#ifdef __APPLE__ +# include <TargetConditionals.h> +# if TARGET_OS_OSX == 1 +# define CATCH_PLATFORM_MAC +# elif 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 +#endif + +// end catch_platform.h + +#ifdef CATCH_IMPL +# ifndef CLARA_CONFIG_MAIN +# define CLARA_CONFIG_MAIN_NOT_DEFINED +# define CLARA_CONFIG_MAIN +# endif +#endif + +// start catch_user_interfaces.h + +namespace Catch { + unsigned int rngSeed(); +} + +// end catch_user_interfaces.h +// start catch_tag_alias_autoregistrar.h + +// start catch_common.h + +// start catch_compiler_capabilities.h + +// Detect a number of compiler features - by compiler +// The following features are defined: +// +// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? +// 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_<feature name> 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. + +#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 + +#if defined(CATCH_CPP17_OR_GREATER) +# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif + +#ifdef __clang__ + +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ + _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") +# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_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" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) +# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS \ + _Pragma( "clang diagnostic pop" ) + +#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 +# define CATCH_CONFIG_COLOUR_NONE +#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++ +#ifdef _MSC_VER + +# if _MSC_VER >= 1900 // Visual Studio 2015 or newer +# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +# endif + +// Universal Windows platform does not support SEH +// Or console colours (or console at all...) +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +# define CATCH_CONFIG_COLOUR_NONE +# 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(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) +# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +# endif + +#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 + +//////////////////////////////////////////////////////////////////////////////// +// DJGPP +#ifdef __DJGPP__ +# define CATCH_INTERNAL_CONFIG_NO_WCHAR +#endif // __DJGPP__ + +//////////////////////////////////////////////////////////////////////////////// +// Embarcadero C++Build +#if defined(__BORLANDC__) + #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// Use of __COUNTER__ is suppressed during code analysis in +// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly +// handled by it. +// Otherwise all supported compilers support COUNTER macro, +// but user still might want to turn it off +#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) + #define CATCH_INTERNAL_CONFIG_COUNTER +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Check if string_view is available and usable +// The check is split apart to work around v140 (VS2015) preprocessor issue... +#if defined(__has_include) +#if __has_include(<string_view>) && defined(CATCH_CPP17_OR_GREATER) +# define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW +#endif +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Check if variant is available and usable +#if defined(__has_include) +# if __has_include(<variant>) && 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 <ciso646> +# 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) +# endif // defined(__clang__) && (__clang_major__ < 8) +# endif // __has_include(<variant>) && defined(CATCH_CPP17_OR_GREATER) +#endif // __has_include + +#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) +# define CATCH_CONFIG_COUNTER +#endif +#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 +// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. +#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) +# define CATCH_CONFIG_WCHAR +#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_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) +# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#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_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) +# 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_SUPPRESS_PARENTHESES_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_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 + +// end catch_compiler_capabilities.h +#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 + +#include <iosfwd> +#include <string> +#include <cstdint> + +// 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 { + + struct CaseSensitive { enum Choice { + Yes, + No + }; }; + + class NonCopyable { + NonCopyable( NonCopyable const& ) = delete; + NonCopyable( NonCopyable && ) = delete; + NonCopyable& operator = ( NonCopyable const& ) = delete; + NonCopyable& operator = ( NonCopyable && ) = delete; + + protected: + NonCopyable(); + virtual ~NonCopyable(); + }; + + struct SourceLineInfo { + + SourceLineInfo() = delete; + SourceLineInfo( char const* _file, std::size_t _line ) noexcept + : file( _file ), + line( _line ) + {} + + SourceLineInfo( SourceLineInfo const& other ) = default; + SourceLineInfo( SourceLineInfo && ) = default; + SourceLineInfo& operator = ( SourceLineInfo const& ) = default; + SourceLineInfo& operator = ( SourceLineInfo && ) = default; + + bool empty() const noexcept; + bool operator == ( SourceLineInfo const& other ) const noexcept; + bool operator < ( SourceLineInfo const& other ) const noexcept; + + char const* file; + std::size_t line; + }; + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); + + // Bring in operator<< from global namespace into Catch namespace + // This is necessary because the overload of operator<< above makes + // lookup stop at namespace Catch + using ::operator<<; + + // Use this in variadic streaming macros to allow + // >> +StreamEndStop + // as well as + // >> stuff +StreamEndStop + struct StreamEndStop { + std::string operator+() const; + }; + template<typename T> + T const& operator + ( T const& value, StreamEndStop ) { + return value; + } +} + +#define CATCH_INTERNAL_LINEINFO \ + ::Catch::SourceLineInfo( __FILE__, static_cast<std::size_t>( __LINE__ ) ) + +// end catch_common.h +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_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS + +// end catch_tag_alias_autoregistrar.h +// start catch_test_registry.h + +// start catch_interfaces_testcase.h + +#include <vector> +#include <memory> + +namespace Catch { + + class TestSpec; + + struct ITestInvoker { + virtual void invoke () const = 0; + virtual ~ITestInvoker(); + }; + + using ITestCasePtr = std::shared_ptr<ITestInvoker>; + + class TestCase; + struct IConfig; + + struct ITestCaseRegistry { + virtual ~ITestCaseRegistry(); + virtual std::vector<TestCase> const& getAllTests() const = 0; + virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const = 0; + }; + + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ); + +} + +// end catch_interfaces_testcase.h +// start catch_stringref.h + +#include <cstddef> +#include <string> +#include <iosfwd> + +namespace Catch { + + class StringData; + + /// 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. c_str() must return a null terminated + /// string, however, and so the StringRef will internally take ownership + /// (taking a copy), if necessary. In theory this ownership is not externally + /// visible - but it does mean (substring) StringRefs should not be shared between + /// threads. + class StringRef { + public: + using size_type = std::size_t; + + private: + friend struct StringRefTestAccess; + + char const* m_start; + size_type m_size; + + char* m_data = nullptr; + + void takeOwnership(); + + static constexpr char const* const s_empty = ""; + + public: // construction/ assignment + StringRef() noexcept + : StringRef( s_empty, 0 ) + {} + + StringRef( StringRef const& other ) noexcept + : m_start( other.m_start ), + m_size( other.m_size ) + {} + + StringRef( StringRef&& other ) noexcept + : m_start( other.m_start ), + m_size( other.m_size ), + m_data( other.m_data ) + { + other.m_data = nullptr; + } + + StringRef( char const* rawChars ) noexcept; + + 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() ) + {} + + ~StringRef() noexcept { + delete[] m_data; + } + + auto operator = ( StringRef const &other ) noexcept -> StringRef& { + delete[] m_data; + m_data = nullptr; + m_start = other.m_start; + m_size = other.m_size; + return *this; + } + + operator std::string() const; + + void swap( StringRef& other ) noexcept; + + public: // operators + auto operator == ( StringRef const& other ) const noexcept -> bool; + auto operator != ( StringRef const& other ) const noexcept -> bool; + + auto operator[] ( size_type index ) const noexcept -> char; + + public: // named queries + auto empty() const noexcept -> bool { + return m_size == 0; + } + auto size() const noexcept -> size_type { + return m_size; + } + + auto numberOfCharacters() const noexcept -> size_type; + auto c_str() const -> char const*; + + public: // substrings and searches + auto substr( size_type start, size_type size ) const noexcept -> StringRef; + + // Returns the current start pointer. + // Note that the pointer can change when if the StringRef is a substring + auto currentData() const noexcept -> char const*; + + private: // ownership queries - may not be consistent between calls + auto isOwned() const noexcept -> bool; + auto isSubstring() const noexcept -> bool; + }; + + auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string; + auto operator + ( StringRef const& lhs, char const* rhs ) -> std::string; + auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string; + + auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; + auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; + + inline auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { + return StringRef( rawChars, size ); + } + +} // namespace Catch + +inline auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { + return Catch::StringRef( rawChars, size ); +} + +// end catch_stringref.h +// start catch_type_traits.hpp + + +namespace Catch{ + +#ifdef CATCH_CPP17_OR_GREATER + template <typename...> + inline constexpr auto is_unique = std::true_type{}; + + template <typename T, typename... Rest> + inline constexpr auto is_unique<T, Rest...> = std::bool_constant< + (!std::is_same_v<T, Rest> && ...) && is_unique<Rest...> + >{}; +#else + +template <typename...> +struct is_unique : std::true_type{}; + +template <typename T0, typename T1, typename... Rest> +struct is_unique<T0, T1, Rest...> : std::integral_constant +<bool, + !std::is_same<T0, T1>::value + && is_unique<T0, Rest...>::value + && is_unique<T1, Rest...>::value +>{}; + +#endif +} + +// end catch_type_traits.hpp +// start catch_preprocessor.hpp + + +#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_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__) + +#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name, __VA_ARGS__) +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name,...) Name " - " #__VA_ARGS__ +#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME(Name,...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) +#else +// MSVC is adding extra space and needs more calls to properly remove () +#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name,...) Name " -" #__VA_ARGS__ +#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME1(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, __VA_ARGS__) +#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME1(Name, INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) +#endif + +// end catch_preprocessor.hpp +namespace Catch { + +template<typename C> +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)(); + } +}; + +auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker*; + +template<typename C> +auto makeTestInvoker( void (C::*testAsMethod)() ) noexcept -> ITestInvoker* { + return new(std::nothrow) TestInvokerAsMethod<C>( testAsMethod ); +} + +struct NameAndTags { + NameAndTags( StringRef const& name_ = StringRef(), StringRef const& tags_ = StringRef() ) noexcept; + StringRef name; + StringRef tags; +}; + +struct AutoReg : NonCopyable { + AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept; + ~AutoReg(); +}; + +} // end namespace Catch + +#if defined(CATCH_CONFIG_DISABLE) + #define INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( TestName, ... ) \ + static void TestName() + #define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \ + namespace{ \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ + void test(); \ + }; \ + } \ + void TestName::test() + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION( TestName, ... ) \ + template<typename TestType> \ + static void TestName() + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \ + namespace{ \ + template<typename TestType> \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) { \ + void test(); \ + }; \ + } \ + template<typename TestType> \ + void TestName::test() +#endif + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ + static void TestName(); \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_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, ... ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ + CATCH_INTERNAL_SUPPRESS_GLOBALS_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_UNSUPPRESS_GLOBALS_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_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_2(TestName, TestFunc, Name, Tags, ... )\ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + template<typename TestType> \ + static void TestFunc();\ + namespace {\ + template<typename...Types> \ + struct TestName{\ + template<typename...Ts> \ + TestName(Ts...names){\ + CATCH_INTERNAL_CHECK_UNIQUE_TYPES(CATCH_REC_LIST(INTERNAL_CATCH_REMOVE_PARENS, __VA_ARGS__)) \ + using expander = int[];\ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ names, Tags } ), 0)... };/* NOLINT */ \ + }\ + };\ + INTERNAL_CATCH_TEMPLATE_REGISTRY_INITIATE(TestName, Name, __VA_ARGS__) \ + }\ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + template<typename TestType> \ + static void TestFunc() + +#if defined(CATCH_CPP17_OR_GREATER) +#define CATCH_INTERNAL_CHECK_UNIQUE_TYPES(...) static_assert(Catch::is_unique<__VA_ARGS__>,"Duplicate type detected in declaration of template test case"); +#else +#define CATCH_INTERNAL_CHECK_UNIQUE_TYPES(...) static_assert(Catch::is_unique<__VA_ARGS__>::value,"Duplicate type detected in declaration of template test case"); +#endif + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, __VA_ARGS__ ) +#else + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, __VA_ARGS__ ) ) +#endif + + #define INTERNAL_CATCH_TEMPLATE_REGISTRY_INITIATE(TestName, Name, ...)\ + static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ + TestName<CATCH_REC_LIST(INTERNAL_CATCH_REMOVE_PARENS, __VA_ARGS__)>(CATCH_REC_LIST_UD(INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME,Name, __VA_ARGS__));\ + return 0;\ + }(); + + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, ... ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ \ + template<typename TestType> \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) { \ + void test();\ + };\ + template<typename...Types> \ + struct TestNameClass{\ + template<typename...Ts> \ + TestNameClass(Ts...names){\ + CATCH_INTERNAL_CHECK_UNIQUE_TYPES(CATCH_REC_LIST(INTERNAL_CATCH_REMOVE_PARENS, __VA_ARGS__)) \ + using expander = int[];\ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ names, Tags } ), 0)... };/* NOLINT */ \ + }\ + };\ + INTERNAL_CATCH_TEMPLATE_REGISTRY_INITIATE(TestNameClass, Name, __VA_ARGS__)\ + }\ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS\ + template<typename TestType> \ + void TestName<TestType>::test() + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, __VA_ARGS__ ) +#else + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, __VA_ARGS__ ) ) +#endif + +// end catch_test_registry.h +// start catch_capture.hpp + +// start catch_assertionhandler.h + +// start catch_assertioninfo.h + +// start catch_result_type.h + +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 + +// end catch_result_type.h +namespace Catch { + + struct AssertionInfo + { + StringRef macroName; + SourceLineInfo lineInfo; + StringRef capturedExpression; + ResultDisposition::Flags resultDisposition; + + // We want to delete this constructor but a compiler bug in 4.8 means + // the struct is then treated as non-aggregate + //AssertionInfo() = delete; + }; + +} // end namespace Catch + +// end catch_assertioninfo.h +// start catch_decomposer.h + +// start catch_tostring.h + +#include <vector> +#include <cstddef> +#include <type_traits> +#include <string> +// start catch_stream.h + +#include <iosfwd> +#include <cstddef> +#include <ostream> + +namespace Catch { + + std::ostream& cout(); + std::ostream& cerr(); + std::ostream& clog(); + + class StringRef; + + struct IStream { + virtual ~IStream(); + virtual std::ostream& stream() const = 0; + }; + + auto makeStream( StringRef const &filename ) -> IStream const*; + + class ReusableStringStream { + std::size_t m_index; + std::ostream* m_oss; + public: + ReusableStringStream(); + ~ReusableStringStream(); + + auto str() const -> std::string; + + template<typename T> + auto operator << ( T const& value ) -> ReusableStringStream& { + *m_oss << value; + return *this; + } + auto get() -> std::ostream& { return *m_oss; } + }; +} + +// end catch_stream.h + +#ifdef CATCH_CONFIG_CPP17_STRING_VIEW +#include <string_view> +#endif + +#ifdef __OBJC__ +// start catch_objc_arc.hpp + +#import <Foundation/Foundation.h> + +#ifdef __has_feature +#define CATCH_ARC_ENABLED __has_feature(objc_arc) +#else +#define CATCH_ARC_ENABLED 0 +#endif + +void arcSafeRelease( NSObject* obj ); +id performOptionalSelector( id obj, SEL sel ); + +#if !CATCH_ARC_ENABLED +inline void arcSafeRelease( NSObject* obj ) { + [obj release]; +} +inline id performOptionalSelector( id obj, SEL sel ) { + if( [obj respondsToSelector: sel] ) + return [obj performSelector: sel]; + return nil; +} +#define CATCH_UNSAFE_UNRETAINED +#define CATCH_ARC_STRONG +#else +inline void arcSafeRelease( NSObject* ){} +inline id performOptionalSelector( id obj, SEL sel ) { +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" +#endif + if( [obj respondsToSelector: sel] ) + return [obj performSelector: sel]; +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + return nil; +} +#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained +#define CATCH_ARC_STRONG __strong +#endif + +// end catch_objc_arc.hpp +#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 + +namespace Catch { + namespace Detail { + + extern const std::string unprintableString; + + std::string rawMemoryToString( const void *object, std::size_t size ); + + template<typename T> + std::string rawMemoryToString( const T& object ) { + return rawMemoryToString( &object, sizeof(object) ); + } + + template<typename T> + class IsStreamInsertable { + template<typename SS, typename TT> + static auto test(int) + -> decltype(std::declval<SS&>() << std::declval<TT>(), std::true_type()); + + template<typename, typename> + static auto test(...)->std::false_type; + + public: + static const bool value = decltype(test<std::ostream, const T&>(0))::value; + }; + + template<typename E> + std::string convertUnknownEnumToString( E e ); + + template<typename T> + typename std::enable_if< + !std::is_enum<T>::value && !std::is_base_of<std::exception, T>::value, + std::string>::type convertUnstreamable( T const& ) { + return Detail::unprintableString; + } + template<typename T> + typename std::enable_if< + !std::is_enum<T>::value && std::is_base_of<std::exception, T>::value, + std::string>::type convertUnstreamable(T const& ex) { + return ex.what(); + } + + template<typename T> + typename std::enable_if< + std::is_enum<T>::value + , std::string>::type convertUnstreamable( T const& value ) { + return convertUnknownEnumToString( value ); + } + +#if defined(_MANAGED) + //! Convert a CLR string to a utf8 std::string + template<typename T> + std::string clrReferenceToString( T^ ref ) { + if (ref == nullptr) + return std::string("null"); + auto bytes = System::Text::Encoding::UTF8->GetBytes(ref->ToString()); + cli::pin_ptr<System::Byte> p = &bytes[0]; + return std::string(reinterpret_cast<char const *>(p), bytes->Length); + } +#endif + + } // namespace Detail + + // If we decide for C++14, change these to enable_if_ts + template <typename T, typename = void> + struct StringMaker { + template <typename Fake = T> + static + typename std::enable_if<::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>::type + 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 <typename Fake = T> + static + typename std::enable_if<!::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>::type + 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 <typename T> + std::string stringify(const T& e) { + return ::Catch::StringMaker<typename std::remove_cv<typename std::remove_reference<T>::type>::type>::convert(e); + } + + template<typename E> + std::string convertUnknownEnumToString( E e ) { + return ::Catch::Detail::stringify(static_cast<typename std::underlying_type<E>::type>(e)); + } + +#if defined(_MANAGED) + template <typename T> + std::string stringify( T^ e ) { + return ::Catch::StringMaker<T^>::convert(e); + } +#endif + + } // namespace Detail + + // Some predefined specializations + + template<> + struct StringMaker<std::string> { + static std::string convert(const std::string& str); + }; + +#ifdef CATCH_CONFIG_CPP17_STRING_VIEW + template<> + struct StringMaker<std::string_view> { + static std::string convert(std::string_view str); + }; +#endif + + template<> + struct StringMaker<char const *> { + static std::string convert(char const * str); + }; + template<> + struct StringMaker<char *> { + static std::string convert(char * str); + }; + +#ifdef CATCH_CONFIG_WCHAR + template<> + struct StringMaker<std::wstring> { + static std::string convert(const std::wstring& wstr); + }; + +# ifdef CATCH_CONFIG_CPP17_STRING_VIEW + template<> + struct StringMaker<std::wstring_view> { + static std::string convert(std::wstring_view str); + }; +# endif + + template<> + struct StringMaker<wchar_t const *> { + static std::string convert(wchar_t const * str); + }; + template<> + struct StringMaker<wchar_t *> { + static std::string convert(wchar_t * str); + }; +#endif + + // TBD: Should we use `strnlen` to ensure that we don't go out of the buffer, + // while keeping string semantics? + template<int SZ> + struct StringMaker<char[SZ]> { + static std::string convert(char const* str) { + return ::Catch::Detail::stringify(std::string{ str }); + } + }; + template<int SZ> + struct StringMaker<signed char[SZ]> { + static std::string convert(signed char const* str) { + return ::Catch::Detail::stringify(std::string{ reinterpret_cast<char const *>(str) }); + } + }; + template<int SZ> + struct StringMaker<unsigned char[SZ]> { + static std::string convert(unsigned char const* str) { + return ::Catch::Detail::stringify(std::string{ reinterpret_cast<char const *>(str) }); + } + }; + + template<> + struct StringMaker<int> { + static std::string convert(int value); + }; + template<> + struct StringMaker<long> { + static std::string convert(long value); + }; + template<> + struct StringMaker<long long> { + static std::string convert(long long value); + }; + template<> + struct StringMaker<unsigned int> { + static std::string convert(unsigned int value); + }; + template<> + struct StringMaker<unsigned long> { + static std::string convert(unsigned long value); + }; + template<> + struct StringMaker<unsigned long long> { + static std::string convert(unsigned long long value); + }; + + template<> + struct StringMaker<bool> { + static std::string convert(bool b); + }; + + template<> + struct StringMaker<char> { + static std::string convert(char c); + }; + template<> + struct StringMaker<signed char> { + static std::string convert(signed char c); + }; + template<> + struct StringMaker<unsigned char> { + static std::string convert(unsigned char c); + }; + + template<> + struct StringMaker<std::nullptr_t> { + static std::string convert(std::nullptr_t); + }; + + template<> + struct StringMaker<float> { + static std::string convert(float value); + }; + template<> + struct StringMaker<double> { + static std::string convert(double value); + }; + + template <typename T> + struct StringMaker<T*> { + template <typename U> + static std::string convert(U* p) { + if (p) { + return ::Catch::Detail::rawMemoryToString(p); + } else { + return "nullptr"; + } + } + }; + + template <typename R, typename C> + struct StringMaker<R C::*> { + static std::string convert(R C::* p) { + if (p) { + return ::Catch::Detail::rawMemoryToString(p); + } else { + return "nullptr"; + } + } + }; + +#if defined(_MANAGED) + template <typename T> + struct StringMaker<T^> { + static std::string convert( T^ ref ) { + return ::Catch::Detail::clrReferenceToString(ref); + } + }; +#endif + + namespace Detail { + template<typename InputIterator> + std::string rangeToString(InputIterator first, InputIterator 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(); + } + } + +#ifdef __OBJC__ + template<> + struct StringMaker<NSString*> { + static std::string convert(NSString * nsstring) { + if (!nsstring) + return "nil"; + return std::string("@") + [nsstring UTF8String]; + } + }; + template<> + struct StringMaker<NSObject*> { + static std::string convert(NSObject* nsObject) { + return ::Catch::Detail::stringify([nsObject description]); + } + + }; + namespace Detail { + inline std::string stringify( NSString* nsstring ) { + return StringMaker<NSString*>::convert( nsstring ); + } + + } // namespace Detail +#endif // __OBJC__ + +} // namespace Catch + +////////////////////////////////////////////////////// +// Separate std-lib types stringification, so it can be selectively enabled +// This means that we do not bring in + +#if defined(CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS) +# define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER +# define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER +# define CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER +# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +#endif + +// Separate std::pair specialization +#if defined(CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER) +#include <utility> +namespace Catch { + template<typename T1, typename T2> + struct StringMaker<std::pair<T1, T2> > { + static std::string convert(const std::pair<T1, T2>& pair) { + ReusableStringStream rss; + rss << "{ " + << ::Catch::Detail::stringify(pair.first) + << ", " + << ::Catch::Detail::stringify(pair.second) + << " }"; + return rss.str(); + } + }; +} +#endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER + +// Separate std::tuple specialization +#if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER) +#include <tuple> +namespace Catch { + namespace Detail { + template< + typename Tuple, + std::size_t N = 0, + bool = (N < std::tuple_size<Tuple>::value) + > + struct TupleElementPrinter { + static void print(const Tuple& tuple, std::ostream& os) { + os << (N ? ", " : " ") + << ::Catch::Detail::stringify(std::get<N>(tuple)); + TupleElementPrinter<Tuple, N + 1>::print(tuple, os); + } + }; + + template< + typename Tuple, + std::size_t N + > + struct TupleElementPrinter<Tuple, N, false> { + static void print(const Tuple&, std::ostream&) {} + }; + + } + + template<typename ...Types> + struct StringMaker<std::tuple<Types...>> { + static std::string convert(const std::tuple<Types...>& tuple) { + ReusableStringStream rss; + rss << '{'; + Detail::TupleElementPrinter<std::tuple<Types...>>::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 <variant> +namespace Catch { + template<> + struct StringMaker<std::monostate> { + static std::string convert(const std::monostate&) { + return "{ }"; + } + }; + + template<typename... Elements> + struct StringMaker<std::variant<Elements...>> { + static std::string convert(const std::variant<Elements...>& 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 { + struct not_this_one {}; // Tag type for detecting which begin/ end are being selected + + // Import begin/ end from std here so they are considered alongside the fallback (...) overloads in this namespace + using std::begin; + using std::end; + + not_this_one begin( ... ); + not_this_one end( ... ); + + template <typename T> + struct is_range { + static const bool value = + !std::is_same<decltype(begin(std::declval<T>())), not_this_one>::value && + !std::is_same<decltype(end(std::declval<T>())), not_this_one>::value; + }; + +#if defined(_MANAGED) // Managed types are never ranges + template <typename T> + struct is_range<T^> { + static const bool value = false; + }; +#endif + + template<typename Range> + std::string rangeToString( Range const& range ) { + return ::Catch::Detail::rangeToString( begin( range ), end( range ) ); + } + + // Handle vector<bool> specially + template<typename Allocator> + std::string rangeToString( std::vector<bool, Allocator> 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<typename R> + struct StringMaker<R, typename std::enable_if<is_range<R>::value && !::Catch::Detail::IsStreamInsertable<R>::value>::type> { + static std::string convert( R const& range ) { + return rangeToString( range ); + } + }; + + template <typename T, int SZ> + struct StringMaker<T[SZ]> { + static std::string convert(T const(&arr)[SZ]) { + return rangeToString(arr); + } + }; + +} // namespace Catch + +// Separate std::chrono::duration specialization +#if defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +#include <ctime> +#include <ratio> +#include <chrono> + +namespace Catch { + +template <class Ratio> +struct ratio_string { + static std::string symbol(); +}; + +template <class Ratio> +std::string ratio_string<Ratio>::symbol() { + Catch::ReusableStringStream rss; + rss << '[' << Ratio::num << '/' + << Ratio::den << ']'; + return rss.str(); +} +template <> +struct ratio_string<std::atto> { + static std::string symbol(); +}; +template <> +struct ratio_string<std::femto> { + static std::string symbol(); +}; +template <> +struct ratio_string<std::pico> { + static std::string symbol(); +}; +template <> +struct ratio_string<std::nano> { + static std::string symbol(); +}; +template <> +struct ratio_string<std::micro> { + static std::string symbol(); +}; +template <> +struct ratio_string<std::milli> { + static std::string symbol(); +}; + + //////////// + // std::chrono::duration specializations + template<typename Value, typename Ratio> + struct StringMaker<std::chrono::duration<Value, Ratio>> { + static std::string convert(std::chrono::duration<Value, Ratio> const& duration) { + ReusableStringStream rss; + rss << duration.count() << ' ' << ratio_string<Ratio>::symbol() << 's'; + return rss.str(); + } + }; + template<typename Value> + struct StringMaker<std::chrono::duration<Value, std::ratio<1>>> { + static std::string convert(std::chrono::duration<Value, std::ratio<1>> const& duration) { + ReusableStringStream rss; + rss << duration.count() << " s"; + return rss.str(); + } + }; + template<typename Value> + struct StringMaker<std::chrono::duration<Value, std::ratio<60>>> { + static std::string convert(std::chrono::duration<Value, std::ratio<60>> const& duration) { + ReusableStringStream rss; + rss << duration.count() << " m"; + return rss.str(); + } + }; + template<typename Value> + struct StringMaker<std::chrono::duration<Value, std::ratio<3600>>> { + static std::string convert(std::chrono::duration<Value, std::ratio<3600>> const& duration) { + 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<system_clock> + template<typename Clock, typename Duration> + struct StringMaker<std::chrono::time_point<Clock, Duration>> { + static std::string convert(std::chrono::time_point<Clock, Duration> const& time_point) { + return ::Catch::Detail::stringify(time_point.time_since_epoch()) + " since epoch"; + } + }; + // std::chrono::time_point<system_clock> specialization + template<typename Duration> + struct StringMaker<std::chrono::time_point<std::chrono::system_clock, Duration>> { + static std::string convert(std::chrono::time_point<std::chrono::system_clock, Duration> const& time_point) { + auto converted = std::chrono::system_clock::to_time_t(time_point); + +#ifdef _MSC_VER + std::tm timeInfo = {}; + gmtime_s(&timeInfo, &converted); +#else + std::tm* timeInfo = std::gmtime(&converted); +#endif + + auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); + char timeStamp[timeStampSize]; + const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; + +#ifdef _MSC_VER + std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); +#else + std::strftime(timeStamp, timeStampSize, fmt, timeInfo); +#endif + return std::string(timeStamp); + } + }; +} +#endif // CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +// end catch_tostring.h +#include <iosfwd> + +#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 +#endif + +namespace Catch { + + struct ITransientExpression { + 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 ) + {} + + // We don't actually need a virtual destructor, but many static analysers + // complain if it's not here :-( + virtual ~ITransientExpression(); + + bool m_isBinaryExpression; + bool m_result; + + }; + + void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ); + + template<typename LhsT, typename RhsT> + 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<typename LhsT> + 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, lhs ? true : false }, + m_lhs( lhs ) + {} + }; + + // Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int) + template<typename LhsT, typename RhsT> + auto compareEqual( LhsT const& lhs, RhsT const& rhs ) -> bool { return static_cast<bool>(lhs == rhs); } + template<typename T> + auto compareEqual( T* const& lhs, int rhs ) -> bool { return lhs == reinterpret_cast<void const*>( rhs ); } + template<typename T> + auto compareEqual( T* const& lhs, long rhs ) -> bool { return lhs == reinterpret_cast<void const*>( rhs ); } + template<typename T> + auto compareEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) == rhs; } + template<typename T> + auto compareEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) == rhs; } + + template<typename LhsT, typename RhsT> + auto compareNotEqual( LhsT const& lhs, RhsT&& rhs ) -> bool { return static_cast<bool>(lhs != rhs); } + template<typename T> + auto compareNotEqual( T* const& lhs, int rhs ) -> bool { return lhs != reinterpret_cast<void const*>( rhs ); } + template<typename T> + auto compareNotEqual( T* const& lhs, long rhs ) -> bool { return lhs != reinterpret_cast<void const*>( rhs ); } + template<typename T> + auto compareNotEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) != rhs; } + template<typename T> + auto compareNotEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) != rhs; } + + template<typename LhsT> + class ExprLhs { + LhsT m_lhs; + public: + explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {} + + template<typename RhsT> + auto operator == ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { + return { compareEqual( m_lhs, rhs ), m_lhs, "==", rhs }; + } + auto operator == ( bool rhs ) -> BinaryExpr<LhsT, bool> const { + return { m_lhs == rhs, m_lhs, "==", rhs }; + } + + template<typename RhsT> + auto operator != ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { + return { compareNotEqual( m_lhs, rhs ), m_lhs, "!=", rhs }; + } + auto operator != ( bool rhs ) -> BinaryExpr<LhsT, bool> const { + return { m_lhs != rhs, m_lhs, "!=", rhs }; + } + + template<typename RhsT> + auto operator > ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { + return { static_cast<bool>(m_lhs > rhs), m_lhs, ">", rhs }; + } + template<typename RhsT> + auto operator < ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { + return { static_cast<bool>(m_lhs < rhs), m_lhs, "<", rhs }; + } + template<typename RhsT> + auto operator >= ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { + return { static_cast<bool>(m_lhs >= rhs), m_lhs, ">=", rhs }; + } + template<typename RhsT> + auto operator <= ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const { + return { static_cast<bool>(m_lhs <= rhs), m_lhs, "<=", rhs }; + } + + auto makeUnaryExpr() const -> UnaryExpr<LhsT> { + return UnaryExpr<LhsT>{ m_lhs }; + } + }; + + void handleExpression( ITransientExpression const& expr ); + + template<typename T> + void handleExpression( ExprLhs<T> const& expr ) { + handleExpression( expr.makeUnaryExpr() ); + } + + struct Decomposer { + template<typename T> + auto operator <= ( T const& lhs ) -> ExprLhs<T const&> { + return ExprLhs<T const&>{ lhs }; + } + + auto operator <=( bool value ) -> ExprLhs<bool> { + return ExprLhs<bool>{ value }; + } + }; + +} // end namespace Catch + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +// end catch_decomposer.h +// start catch_interfaces_capture.h + +#include <string> + +namespace Catch { + + class AssertionResult; + struct AssertionInfo; + struct SectionInfo; + struct SectionEndInfo; + struct MessageInfo; + struct Counts; + struct BenchmarkInfo; + struct BenchmarkStats; + struct AssertionReaction; + struct SourceLineInfo; + + struct ITransientExpression; + struct IGeneratorTracker; + + struct IResultCapture { + + 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( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& = 0; + + virtual void benchmarkStarting( BenchmarkInfo const& info ) = 0; + virtual void benchmarkEnded( BenchmarkStats const& stats ) = 0; + + virtual void pushScopedMessage( MessageInfo const& message ) = 0; + virtual void popScopedMessage( MessageInfo const& message ) = 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 const& 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(); +} + +// end catch_interfaces_capture.h +namespace Catch { + + struct TestFailureException{}; + struct AssertionResultData; + struct IResultCapture; + class RunContext; + + class LazyExpression { + friend class AssertionHandler; + friend struct AssertionStats; + friend class RunContext; + + ITransientExpression const* m_transientExpression = nullptr; + bool m_isNegated; + public: + LazyExpression( bool isNegated ); + LazyExpression( LazyExpression const& other ); + LazyExpression& operator = ( LazyExpression const& ) = delete; + + explicit operator bool() const; + + friend auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream&; + }; + + 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 const& macroName, + SourceLineInfo const& lineInfo, + StringRef capturedExpression, + ResultDisposition::Flags resultDisposition ); + ~AssertionHandler() { + if ( !m_completed ) { + m_resultCapture.handleIncomplete( m_assertionInfo ); + } + } + + template<typename T> + void handleExpr( ExprLhs<T> const& expr ) { + handleExpr( expr.makeUnaryExpr() ); + } + void handleExpr( ITransientExpression const& expr ); + + void handleMessage(ResultWas::OfType resultType, StringRef const& 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 const& matcherString ); + +} // namespace Catch + +// end catch_assertionhandler.h +// start catch_message.h + +#include <string> +#include <vector> + +namespace Catch { + + struct MessageInfo { + MessageInfo( StringRef const& _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; + bool operator < ( MessageInfo const& other ) const; + private: + static unsigned int globalCount; + }; + + struct MessageStream { + + template<typename T> + MessageStream& operator << ( T const& value ) { + m_stream << value; + return *this; + } + + ReusableStringStream m_stream; + }; + + struct MessageBuilder : MessageStream { + MessageBuilder( StringRef const& macroName, + SourceLineInfo const& lineInfo, + ResultWas::OfType type ); + + template<typename T> + MessageBuilder& operator << ( T const& value ) { + m_stream << value; + return *this; + } + + MessageInfo m_info; + }; + + class ScopedMessage { + public: + explicit ScopedMessage( MessageBuilder const& builder ); + ~ScopedMessage(); + + MessageInfo m_info; + }; + + class Capturer { + std::vector<MessageInfo> m_messages; + IResultCapture& m_resultCapture = getResultCapture(); + size_t m_captured = 0; + public: + Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ); + ~Capturer(); + + void captureValue( size_t index, std::string const& value ); + + template<typename T> + void captureValues( size_t index, T const& value ) { + captureValue( index, Catch::Detail::stringify( value ) ); + } + + template<typename T, typename... Ts> + void captureValues( size_t index, T const& value, Ts const&... values ) { + captureValue( index, Catch::Detail::stringify(value) ); + captureValues( index+1, values... ); + } + }; + +} // end namespace Catch + +// end catch_message.h +#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 { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ + INTERNAL_CATCH_TRY { \ + CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); \ + CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ + } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( (void)0, false && static_cast<bool>( !!(__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 { \ + static_cast<void>(__VA_ARGS__); \ + 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 { \ + static_cast<void>(__VA_ARGS__); \ + 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 { \ + static_cast<void>(expr); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } \ + catch( exceptionType const& ) { \ + catchAssertionHandler.handleExceptionThrownAsExpected(); \ + } \ + catch( ... ) { \ + catchAssertionHandler.handleUnexpectedInflightException(); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +/////////////////////////////////////////////////////////////////////////////// +#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, ... ) \ + auto varName = Catch::Capturer( 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 ); + +/////////////////////////////////////////////////////////////////////////////// +// 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 { \ + static_cast<void>(__VA_ARGS__); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } \ + catch( ... ) { \ + Catch::handleExceptionMatchExpr( catchAssertionHandler, matcher, #matcher##_catch_sr ); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +#endif // CATCH_CONFIG_DISABLE + +// end catch_capture.hpp +// start catch_section.h + +// start catch_section_info.h + +// start catch_totals.h + +#include <cstddef> + +namespace Catch { + + struct Counts { + Counts operator - ( Counts const& other ) const; + Counts& operator += ( Counts const& other ); + + std::size_t total() const; + bool allPassed() const; + bool allOk() const; + + std::size_t passed = 0; + std::size_t failed = 0; + std::size_t failedButOk = 0; + }; + + struct Totals { + + Totals operator - ( Totals const& other ) const; + Totals& operator += ( Totals const& other ); + + Totals delta( Totals const& prevTotals ) const; + + int error = 0; + Counts assertions; + Counts testCases; + }; +} + +// end catch_totals.h +#include <string> + +namespace Catch { + + struct SectionInfo { + SectionInfo + ( SourceLineInfo const& _lineInfo, + std::string const& _name ); + + // Deprecated + SectionInfo + ( SourceLineInfo const& _lineInfo, + std::string const& _name, + std::string const& ) : SectionInfo( _lineInfo, _name ) {} + + std::string name; + std::string description; // !Deprecated: this will always be empty + SourceLineInfo lineInfo; + }; + + struct SectionEndInfo { + SectionInfo sectionInfo; + Counts prevAssertions; + double durationInSeconds; + }; + +} // end namespace Catch + +// end catch_section_info.h +// start catch_timer.h + +#include <cstdint> + +namespace Catch { + + auto getCurrentNanosecondsSinceEpoch() -> uint64_t; + auto getEstimatedClockResolution() -> uint64_t; + + 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 + +// end catch_timer.h +#include <string> + +namespace Catch { + + class Section : NonCopyable { + public: + Section( SectionInfo const& info ); + ~Section(); + + // This indicates whether the section should be executed or not + explicit operator bool() const; + + private: + SectionInfo m_info; + + std::string m_name; + Counts m_assertions; + bool m_sectionIncluded; + Timer m_timer; + }; + +} // end namespace Catch + +#define INTERNAL_CATCH_SECTION( ... ) \ + CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \ + CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS + +#define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \ + CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, (Catch::ReusableStringStream() << __VA_ARGS__).str() ) ) \ + CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS + +// end catch_section.h +// start catch_benchmark.h + +#include <cstdint> +#include <string> + +namespace Catch { + + class BenchmarkLooper { + + std::string m_name; + std::size_t m_count = 0; + std::size_t m_iterationsToRun = 1; + uint64_t m_resolution; + Timer m_timer; + + static auto getResolution() -> uint64_t; + public: + // Keep most of this inline as it's on the code path that is being timed + BenchmarkLooper( StringRef name ) + : m_name( name ), + m_resolution( getResolution() ) + { + reportStart(); + m_timer.start(); + } + + explicit operator bool() { + if( m_count < m_iterationsToRun ) + return true; + return needsMoreIterations(); + } + + void increment() { + ++m_count; + } + + void reportStart(); + auto needsMoreIterations() -> bool; + }; + +} // end namespace Catch + +#define BENCHMARK( name ) \ + for( Catch::BenchmarkLooper looper( name ); looper; looper.increment() ) + +// end catch_benchmark.h +// start catch_interfaces_exception.h + +// start catch_interfaces_registry_hub.h + +#include <string> +#include <memory> + +namespace Catch { + + class TestCase; + struct ITestCaseRegistry; + struct IExceptionTranslatorRegistry; + struct IExceptionTranslator; + struct IReporterRegistry; + struct IReporterFactory; + struct ITagAliasRegistry; + class StartupExceptionRegistry; + + using IReporterFactoryPtr = std::shared_ptr<IReporterFactory>; + + struct IRegistryHub { + virtual ~IRegistryHub(); + + 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; + }; + + struct IMutableRegistryHub { + virtual ~IMutableRegistryHub(); + virtual void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) = 0; + virtual void registerListener( IReporterFactoryPtr 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; + virtual void registerStartupException() noexcept = 0; + }; + + IRegistryHub const& getRegistryHub(); + IMutableRegistryHub& getMutableRegistryHub(); + void cleanUp(); + std::string translateActiveException(); + +} + +// end catch_interfaces_registry_hub.h +#if defined(CATCH_CONFIG_DISABLE) + #define INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( translatorName, signature) \ + static std::string translatorName( signature ) +#endif + +#include <exception> +#include <string> +#include <vector> + +namespace Catch { + using exceptionTranslateFunction = std::string(*)(); + + struct IExceptionTranslator; + using ExceptionTranslators = std::vector<std::unique_ptr<IExceptionTranslator const>>; + + struct IExceptionTranslator { + virtual ~IExceptionTranslator(); + virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const = 0; + }; + + struct IExceptionTranslatorRegistry { + virtual ~IExceptionTranslatorRegistry(); + + virtual std::string translateActiveException() const = 0; + }; + + class ExceptionTranslatorRegistrar { + template<typename T> + class ExceptionTranslator : public IExceptionTranslator { + public: + + ExceptionTranslator( std::string(*translateFunction)( T& ) ) + : m_translateFunction( translateFunction ) + {} + + std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const override { + try { + if( it == itEnd ) + std::rethrow_exception(std::current_exception()); + else + return (*it)->translate( it+1, itEnd ); + } + catch( T& ex ) { + return m_translateFunction( ex ); + } + } + + protected: + std::string(*m_translateFunction)( T& ); + }; + + public: + template<typename T> + ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) { + getMutableRegistryHub().registerTranslator + ( new ExceptionTranslator<T>( translateFunction ) ); + } + }; +} + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \ + static std::string translatorName( signature ); \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + static std::string translatorName( signature ) + +#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) + +// end catch_interfaces_exception.h +// start catch_approx.h + +#include <type_traits> + +namespace Catch { +namespace Detail { + + class Approx { + private: + bool equalityComparisonImpl(double other) const; + // Validates the new margin (margin >= 0) + // out-of-line to avoid including stdexcept in the header + void setMargin(double margin); + // Validates the new epsilon (0 < epsilon < 1) + // out-of-line to avoid including stdexcept in the header + void setEpsilon(double epsilon); + + public: + explicit Approx ( double value ); + + static Approx custom(); + + Approx operator-() const; + + template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + Approx operator()( T const& value ) { + Approx approx( static_cast<double>(value) ); + approx.m_epsilon = m_epsilon; + approx.m_margin = m_margin; + approx.m_scale = m_scale; + return approx; + } + + template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + explicit Approx( T const& value ): Approx(static_cast<double>(value)) + {} + + template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + friend bool operator == ( const T& lhs, Approx const& rhs ) { + auto lhs_v = static_cast<double>(lhs); + return rhs.equalityComparisonImpl(lhs_v); + } + + template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + friend bool operator == ( Approx const& lhs, const T& rhs ) { + return operator==( rhs, lhs ); + } + + template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + friend bool operator != ( T const& lhs, Approx const& rhs ) { + return !operator==( lhs, rhs ); + } + + template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + friend bool operator != ( Approx const& lhs, T const& rhs ) { + return !operator==( rhs, lhs ); + } + + template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + friend bool operator <= ( T const& lhs, Approx const& rhs ) { + return static_cast<double>(lhs) < rhs.m_value || lhs == rhs; + } + + template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + friend bool operator <= ( Approx const& lhs, T const& rhs ) { + return lhs.m_value < static_cast<double>(rhs) || lhs == rhs; + } + + template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + friend bool operator >= ( T const& lhs, Approx const& rhs ) { + return static_cast<double>(lhs) > rhs.m_value || lhs == rhs; + } + + template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + friend bool operator >= ( Approx const& lhs, T const& rhs ) { + return lhs.m_value > static_cast<double>(rhs) || lhs == rhs; + } + + template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + Approx& epsilon( T const& newEpsilon ) { + double epsilonAsDouble = static_cast<double>(newEpsilon); + setEpsilon(epsilonAsDouble); + return *this; + } + + template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + Approx& margin( T const& newMargin ) { + double marginAsDouble = static_cast<double>(newMargin); + setMargin(marginAsDouble); + return *this; + } + + template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> + Approx& scale( T const& newScale ) { + m_scale = static_cast<double>(newScale); + return *this; + } + + std::string toString() const; + + private: + double m_epsilon; + double m_margin; + double m_scale; + double m_value; + }; +} // end namespace Detail + +namespace literals { + Detail::Approx operator "" _a(long double val); + Detail::Approx operator "" _a(unsigned long long val); +} // end namespace literals + +template<> +struct StringMaker<Catch::Detail::Approx> { + static std::string convert(Catch::Detail::Approx const& value); +}; + +} // end namespace Catch + +// end catch_approx.h +// start catch_string_manip.h + +#include <string> +#include <iosfwd> + +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 ); + std::string trim( std::string const& str ); + bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ); + + struct pluralise { + pluralise( std::size_t count, std::string const& label ); + + friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ); + + std::size_t m_count; + std::string m_label; + }; +} + +// end catch_string_manip.h +#ifndef CATCH_CONFIG_DISABLE_MATCHERS +// start catch_capture_matchers.h + +// start catch_matchers.h + +#include <string> +#include <vector> + +namespace Catch { +namespace Matchers { + namespace Impl { + + template<typename ArgT> struct MatchAllOf; + template<typename ArgT> struct MatchAnyOf; + template<typename ArgT> struct MatchNotOf; + + class MatcherUntypedBase { + public: + MatcherUntypedBase() = default; + MatcherUntypedBase ( MatcherUntypedBase const& ) = default; + MatcherUntypedBase& operator = ( MatcherUntypedBase const& ) = delete; + std::string toString() const; + + protected: + virtual ~MatcherUntypedBase(); + virtual std::string describe() const = 0; + mutable std::string m_cachedToString; + }; + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wnon-virtual-dtor" +#endif + + template<typename ObjectT> + struct MatcherMethod { + virtual bool match( ObjectT const& arg ) const = 0; + }; + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif + + template<typename T> + struct MatcherBase : MatcherUntypedBase, MatcherMethod<T> { + + MatchAllOf<T> operator && ( MatcherBase const& other ) const; + MatchAnyOf<T> operator || ( MatcherBase const& other ) const; + MatchNotOf<T> operator ! () const; + }; + + template<typename ArgT> + struct MatchAllOf : MatcherBase<ArgT> { + bool match( ArgT const& arg ) const override { + for( auto matcher : m_matchers ) { + if (!matcher->match(arg)) + return false; + } + return true; + } + std::string describe() const override { + std::string description; + description.reserve( 4 + m_matchers.size()*32 ); + description += "( "; + bool first = true; + for( auto matcher : m_matchers ) { + if( first ) + first = false; + else + description += " and "; + description += matcher->toString(); + } + description += " )"; + return description; + } + + MatchAllOf<ArgT>& operator && ( MatcherBase<ArgT> const& other ) { + m_matchers.push_back( &other ); + return *this; + } + + std::vector<MatcherBase<ArgT> const*> m_matchers; + }; + template<typename ArgT> + struct MatchAnyOf : MatcherBase<ArgT> { + + bool match( ArgT const& arg ) const override { + for( auto matcher : m_matchers ) { + if (matcher->match(arg)) + return true; + } + return false; + } + std::string describe() const override { + std::string description; + description.reserve( 4 + m_matchers.size()*32 ); + description += "( "; + bool first = true; + for( auto matcher : m_matchers ) { + if( first ) + first = false; + else + description += " or "; + description += matcher->toString(); + } + description += " )"; + return description; + } + + MatchAnyOf<ArgT>& operator || ( MatcherBase<ArgT> const& other ) { + m_matchers.push_back( &other ); + return *this; + } + + std::vector<MatcherBase<ArgT> const*> m_matchers; + }; + + template<typename ArgT> + struct MatchNotOf : MatcherBase<ArgT> { + + MatchNotOf( MatcherBase<ArgT> const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {} + + bool match( ArgT const& arg ) const override { + return !m_underlyingMatcher.match( arg ); + } + + std::string describe() const override { + return "not " + m_underlyingMatcher.toString(); + } + MatcherBase<ArgT> const& m_underlyingMatcher; + }; + + template<typename T> + MatchAllOf<T> MatcherBase<T>::operator && ( MatcherBase const& other ) const { + return MatchAllOf<T>() && *this && other; + } + template<typename T> + MatchAnyOf<T> MatcherBase<T>::operator || ( MatcherBase const& other ) const { + return MatchAnyOf<T>() || *this || other; + } + template<typename T> + MatchNotOf<T> MatcherBase<T>::operator ! () const { + return MatchNotOf<T>( *this ); + } + + } // namespace Impl + +} // namespace Matchers + +using namespace Matchers; +using Matchers::Impl::MatcherBase; + +} // namespace Catch + +// end catch_matchers.h +// start catch_matchers_floating.h + +#include <type_traits> +#include <cmath> + +namespace Catch { +namespace Matchers { + + namespace Floating { + + enum class FloatingPointKind : uint8_t; + + struct WithinAbsMatcher : MatcherBase<double> { + WithinAbsMatcher(double target, double margin); + bool match(double const& matchee) const override; + std::string describe() const override; + private: + double m_target; + double m_margin; + }; + + struct WithinUlpsMatcher : MatcherBase<double> { + WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType); + bool match(double const& matchee) const override; + std::string describe() const override; + private: + double m_target; + int m_ulps; + FloatingPointKind m_type; + }; + + } // namespace Floating + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff); + Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff); + Floating::WithinAbsMatcher WithinAbs(double target, double margin); + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_floating.h +// start catch_matchers_generic.hpp + +#include <functional> +#include <string> + +namespace Catch { +namespace Matchers { +namespace Generic { + +namespace Detail { + std::string finalizeDescription(const std::string& desc); +} + +template <typename T> +class PredicateMatcher : public MatcherBase<T> { + std::function<bool(T const&)> m_predicate; + std::string m_description; +public: + + PredicateMatcher(std::function<bool(T const&)> const& elem, std::string const& descr) + :m_predicate(std::move(elem)), + m_description(Detail::finalizeDescription(descr)) + {} + + bool match( T const& item ) const override { + return m_predicate(item); + } + + std::string describe() const override { + return m_description; + } +}; + +} // namespace Generic + + // The following functions create the actual matcher objects. + // The user has to explicitly specify type to the function, because + // infering std::function<bool(T const&)> is hard (but possible) and + // requires a lot of TMP. + template<typename T> + Generic::PredicateMatcher<T> Predicate(std::function<bool(T const&)> const& predicate, std::string const& description = "") { + return Generic::PredicateMatcher<T>(predicate, description); + } + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_generic.hpp +// start catch_matchers_string.h + +#include <string> + +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<std::string> { + StringMatcherBase( std::string const& operation, CasedString const& comparator ); + std::string describe() const override; + + CasedString m_comparator; + std::string m_operation; + }; + + struct EqualsMatcher : StringMatcherBase { + EqualsMatcher( CasedString const& comparator ); + bool match( std::string const& source ) const override; + }; + struct ContainsMatcher : StringMatcherBase { + ContainsMatcher( CasedString const& comparator ); + bool match( std::string const& source ) const override; + }; + struct StartsWithMatcher : StringMatcherBase { + StartsWithMatcher( CasedString const& comparator ); + bool match( std::string const& source ) const override; + }; + struct EndsWithMatcher : StringMatcherBase { + EndsWithMatcher( CasedString const& comparator ); + bool match( std::string const& source ) const override; + }; + + struct RegexMatcher : MatcherBase<std::string> { + RegexMatcher( std::string regex, CaseSensitive::Choice caseSensitivity ); + bool match( std::string const& matchee ) const override; + std::string describe() const override; + + private: + std::string m_regex; + CaseSensitive::Choice m_caseSensitivity; + }; + + } // 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 ); + StdString::RegexMatcher Matches( std::string const& regex, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_string.h +// start catch_matchers_vector.h + +#include <algorithm> + +namespace Catch { +namespace Matchers { + + namespace Vector { + namespace Detail { + template <typename InputIterator, typename T> + size_t count(InputIterator first, InputIterator last, T const& item) { + size_t cnt = 0; + for (; first != last; ++first) { + if (*first == item) { + ++cnt; + } + } + return cnt; + } + template <typename InputIterator, typename T> + bool contains(InputIterator first, InputIterator last, T const& item) { + for (; first != last; ++first) { + if (*first == item) { + return true; + } + } + return false; + } + } + + template<typename T> + struct ContainsElementMatcher : MatcherBase<std::vector<T>> { + + ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {} + + bool match(std::vector<T> const &v) const override { + for (auto const& el : v) { + if (el == m_comparator) { + return true; + } + } + return false; + } + + std::string describe() const override { + return "Contains: " + ::Catch::Detail::stringify( m_comparator ); + } + + T const& m_comparator; + }; + + template<typename T> + struct ContainsMatcher : MatcherBase<std::vector<T>> { + + ContainsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {} + + bool match(std::vector<T> const &v) const override { + // !TBD: see note in EqualsMatcher + if (m_comparator.size() > v.size()) + return false; + for (auto const& comparator : m_comparator) { + auto present = false; + for (const auto& el : v) { + if (el == comparator) { + present = true; + break; + } + } + if (!present) { + return false; + } + } + return true; + } + std::string describe() const override { + return "Contains: " + ::Catch::Detail::stringify( m_comparator ); + } + + std::vector<T> const& m_comparator; + }; + + template<typename T> + struct EqualsMatcher : MatcherBase<std::vector<T>> { + + EqualsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {} + + bool match(std::vector<T> const &v) const 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<T> etc + // - then just call that directly + if (m_comparator.size() != v.size()) + return false; + for (std::size_t i = 0; i < v.size(); ++i) + if (m_comparator[i] != v[i]) + return false; + return true; + } + std::string describe() const override { + return "Equals: " + ::Catch::Detail::stringify( m_comparator ); + } + std::vector<T> const& m_comparator; + }; + + template<typename T> + struct UnorderedEqualsMatcher : MatcherBase<std::vector<T>> { + UnorderedEqualsMatcher(std::vector<T> const& target) : m_target(target) {} + bool match(std::vector<T> const& vec) const override { + // Note: This is a reimplementation of std::is_permutation, + // because I don't want to include <algorithm> inside the common path + if (m_target.size() != vec.size()) { + return false; + } + auto lfirst = m_target.begin(), llast = m_target.end(); + auto rfirst = vec.begin(), rlast = vec.end(); + // Cut common prefix to optimize checking of permuted parts + while (lfirst != llast && *lfirst == *rfirst) { + ++lfirst; ++rfirst; + } + if (lfirst == llast) { + return true; + } + + for (auto mid = lfirst; mid != llast; ++mid) { + // Skip already counted items + if (Detail::contains(lfirst, mid, *mid)) { + continue; + } + size_t num_vec = Detail::count(rfirst, rlast, *mid); + if (num_vec == 0 || Detail::count(lfirst, llast, *mid) != num_vec) { + return false; + } + } + + return true; + } + + std::string describe() const override { + return "UnorderedEquals: " + ::Catch::Detail::stringify(m_target); + } + private: + std::vector<T> const& m_target; + }; + + } // namespace Vector + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + + template<typename T> + Vector::ContainsMatcher<T> Contains( std::vector<T> const& comparator ) { + return Vector::ContainsMatcher<T>( comparator ); + } + + template<typename T> + Vector::ContainsElementMatcher<T> VectorContains( T const& comparator ) { + return Vector::ContainsElementMatcher<T>( comparator ); + } + + template<typename T> + Vector::EqualsMatcher<T> Equals( std::vector<T> const& comparator ) { + return Vector::EqualsMatcher<T>( comparator ); + } + + template<typename T> + Vector::UnorderedEqualsMatcher<T> UnorderedEquals(std::vector<T> const& target) { + return Vector::UnorderedEqualsMatcher<T>(target); + } + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_vector.h +namespace Catch { + + template<typename ArgT, typename MatcherT> + class MatchExpr : public ITransientExpression { + ArgT const& m_arg; + MatcherT m_matcher; + StringRef m_matcherString; + public: + MatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef const& matcherString ) + : ITransientExpression{ true, matcher.match( arg ) }, + m_arg( arg ), + m_matcher( matcher ), + m_matcherString( matcherString ) + {} + + void streamReconstructedExpression( std::ostream &os ) const override { + auto matcherAsString = m_matcher.toString(); + os << Catch::Detail::stringify( m_arg ) << ' '; + if( matcherAsString == Detail::unprintableString ) + os << m_matcherString; + else + os << matcherAsString; + } + }; + + using StringMatcher = Matchers::Impl::MatcherBase<std::string>; + + void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString ); + + template<typename ArgT, typename MatcherT> + auto makeMatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef const& matcherString ) -> MatchExpr<ArgT, MatcherT> { + return MatchExpr<ArgT, MatcherT>( arg, matcher, matcherString ); + } + +} // namespace Catch + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ + INTERNAL_CATCH_TRY { \ + catchAssertionHandler.handleExpr( Catch::makeMatchExpr( arg, matcher, #matcher##_catch_sr ) ); \ + } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS_MATCHES( macroName, exceptionType, resultDisposition, matcher, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(exceptionType) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ + if( catchAssertionHandler.allowThrows() ) \ + try { \ + static_cast<void>(__VA_ARGS__ ); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } \ + catch( exceptionType const& ex ) { \ + catchAssertionHandler.handleExpr( Catch::makeMatchExpr( ex, matcher, #matcher##_catch_sr ) ); \ + } \ + catch( ... ) { \ + catchAssertionHandler.handleUnexpectedInflightException(); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +// end catch_capture_matchers.h +#endif +// start catch_generators.hpp + +// start catch_interfaces_generatortracker.h + + +#include <memory> + +namespace Catch { + + namespace Generators { + class GeneratorBase { + protected: + size_t m_size = 0; + + public: + GeneratorBase( size_t size ) : m_size( size ) {} + virtual ~GeneratorBase(); + auto size() const -> size_t { return m_size; } + }; + using GeneratorBasePtr = std::unique_ptr<GeneratorBase>; + + } // namespace Generators + + struct IGeneratorTracker { + virtual ~IGeneratorTracker(); + virtual auto hasGenerator() const -> bool = 0; + virtual auto getGenerator() const -> Generators::GeneratorBasePtr const& = 0; + virtual void setGenerator( Generators::GeneratorBasePtr&& generator ) = 0; + virtual auto getIndex() const -> std::size_t = 0; + }; + +} // namespace Catch + +// end catch_interfaces_generatortracker.h +// start catch_enforce.h + +#include <stdexcept> + +namespace Catch { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + template <typename Ex> + [[noreturn]] + void throw_exception(Ex const& e) { + throw e; + } +#else // ^^ Exceptions are enabled // Exceptions are disabled vv + [[noreturn]] + void throw_exception(std::exception const& e); +#endif +} // namespace Catch; + +#define CATCH_PREPARE_EXCEPTION( type, msg ) \ + type( ( Catch::ReusableStringStream() << msg ).str() ) +#define CATCH_INTERNAL_ERROR( msg ) \ + Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::logic_error, CATCH_INTERNAL_LINEINFO << ": Internal Catch error: " << msg)) +#define CATCH_ERROR( msg ) \ + Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::domain_error, msg )) +#define CATCH_RUNTIME_ERROR( msg ) \ + Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::runtime_error, msg )) +#define CATCH_ENFORCE( condition, msg ) \ + do{ if( !(condition) ) CATCH_ERROR( msg ); } while(false) + +// end catch_enforce.h +#include <memory> +#include <vector> +#include <cassert> + +#include <utility> + +namespace Catch { +namespace Generators { + + // !TBD move this into its own location? + namespace pf{ + template<typename T, typename... Args> + std::unique_ptr<T> make_unique( Args&&... args ) { + return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); + } + } + + template<typename T> + struct IGenerator { + virtual ~IGenerator() {} + virtual auto get( size_t index ) const -> T = 0; + }; + + template<typename T> + class SingleValueGenerator : public IGenerator<T> { + T m_value; + public: + SingleValueGenerator( T const& value ) : m_value( value ) {} + + auto get( size_t ) const -> T override { + return m_value; + } + }; + + template<typename T> + class FixedValuesGenerator : public IGenerator<T> { + std::vector<T> m_values; + + public: + FixedValuesGenerator( std::initializer_list<T> values ) : m_values( values ) {} + + auto get( size_t index ) const -> T override { + return m_values[index]; + } + }; + + template<typename T> + class RangeGenerator : public IGenerator<T> { + T const m_first; + T const m_last; + + public: + RangeGenerator( T const& first, T const& last ) : m_first( first ), m_last( last ) { + assert( m_last > m_first ); + } + + auto get( size_t index ) const -> T override { + // ToDo:: introduce a safe cast to catch potential overflows + return static_cast<T>(m_first+index); + } + }; + + template<typename T> + struct NullGenerator : IGenerator<T> { + auto get( size_t ) const -> T override { + CATCH_INTERNAL_ERROR("A Null Generator is always empty"); + } + }; + + template<typename T> + class Generator { + std::unique_ptr<IGenerator<T>> m_generator; + size_t m_size; + + public: + Generator( size_t size, std::unique_ptr<IGenerator<T>> generator ) + : m_generator( std::move( generator ) ), + m_size( size ) + {} + + auto size() const -> size_t { return m_size; } + auto operator[]( size_t index ) const -> T { + assert( index < m_size ); + return m_generator->get( index ); + } + }; + + std::vector<size_t> randomiseIndices( size_t selectionSize, size_t sourceSize ); + + template<typename T> + class GeneratorRandomiser : public IGenerator<T> { + Generator<T> m_baseGenerator; + + std::vector<size_t> m_indices; + public: + GeneratorRandomiser( Generator<T>&& baseGenerator, size_t numberOfItems ) + : m_baseGenerator( std::move( baseGenerator ) ), + m_indices( randomiseIndices( numberOfItems, m_baseGenerator.size() ) ) + {} + + auto get( size_t index ) const -> T override { + return m_baseGenerator[m_indices[index]]; + } + }; + + template<typename T> + struct RequiresASpecialisationFor; + + template<typename T> + auto all() -> Generator<T> { return RequiresASpecialisationFor<T>(); } + + template<> + auto all<int>() -> Generator<int>; + + template<typename T> + auto range( T const& first, T const& last ) -> Generator<T> { + return Generator<T>( (last-first), pf::make_unique<RangeGenerator<T>>( first, last ) ); + } + + template<typename T> + auto random( T const& first, T const& last ) -> Generator<T> { + auto gen = range( first, last ); + auto size = gen.size(); + + return Generator<T>( size, pf::make_unique<GeneratorRandomiser<T>>( std::move( gen ), size ) ); + } + template<typename T> + auto random( size_t size ) -> Generator<T> { + return Generator<T>( size, pf::make_unique<GeneratorRandomiser<T>>( all<T>(), size ) ); + } + + template<typename T> + auto values( std::initializer_list<T> values ) -> Generator<T> { + return Generator<T>( values.size(), pf::make_unique<FixedValuesGenerator<T>>( values ) ); + } + template<typename T> + auto value( T const& val ) -> Generator<T> { + return Generator<T>( 1, pf::make_unique<SingleValueGenerator<T>>( val ) ); + } + + template<typename T> + auto as() -> Generator<T> { + return Generator<T>( 0, pf::make_unique<NullGenerator<T>>() ); + } + + template<typename... Ts> + auto table( std::initializer_list<std::tuple<Ts...>>&& tuples ) -> Generator<std::tuple<Ts...>> { + return values<std::tuple<Ts...>>( std::forward<std::initializer_list<std::tuple<Ts...>>>( tuples ) ); + } + + template<typename T> + struct Generators : GeneratorBase { + std::vector<Generator<T>> m_generators; + + using type = T; + + Generators() : GeneratorBase( 0 ) {} + + void populate( T&& val ) { + m_size += 1; + m_generators.emplace_back( value( std::move( val ) ) ); + } + template<typename U> + void populate( U&& val ) { + populate( T( std::move( val ) ) ); + } + void populate( Generator<T>&& generator ) { + m_size += generator.size(); + m_generators.emplace_back( std::move( generator ) ); + } + + template<typename U, typename... Gs> + void populate( U&& valueOrGenerator, Gs... moreGenerators ) { + populate( std::forward<U>( valueOrGenerator ) ); + populate( std::forward<Gs>( moreGenerators )... ); + } + + auto operator[]( size_t index ) const -> T { + size_t sizes = 0; + for( auto const& gen : m_generators ) { + auto localIndex = index-sizes; + sizes += gen.size(); + if( index < sizes ) + return gen[localIndex]; + } + CATCH_INTERNAL_ERROR("Index '" << index << "' is out of range (" << sizes << ')'); + } + }; + + template<typename T, typename... Gs> + auto makeGenerators( Generator<T>&& generator, Gs... moreGenerators ) -> Generators<T> { + Generators<T> generators; + generators.m_generators.reserve( 1+sizeof...(Gs) ); + generators.populate( std::move( generator ), std::forward<Gs>( moreGenerators )... ); + return generators; + } + template<typename T> + auto makeGenerators( Generator<T>&& generator ) -> Generators<T> { + Generators<T> generators; + generators.populate( std::move( generator ) ); + return generators; + } + template<typename T, typename... Gs> + auto makeGenerators( T&& val, Gs... moreGenerators ) -> Generators<T> { + return makeGenerators( value( std::forward<T>( val ) ), std::forward<Gs>( moreGenerators )... ); + } + template<typename T, typename U, typename... Gs> + auto makeGenerators( U&& val, Gs... moreGenerators ) -> Generators<T> { + return makeGenerators( value( T( std::forward<U>( val ) ) ), std::forward<Gs>( moreGenerators )... ); + } + + auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker&; + + template<typename L> + // Note: The type after -> is weird, because VS2015 cannot parse + // the expression used in the typedef inside, when it is in + // return type. Yeah, ¯\_(ツ)_/¯ + auto generate( SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval<decltype(generatorExpression())>()[0]) { + using UnderlyingType = typename decltype(generatorExpression())::type; + + IGeneratorTracker& tracker = acquireGeneratorTracker( lineInfo ); + if( !tracker.hasGenerator() ) + tracker.setGenerator( pf::make_unique<Generators<UnderlyingType>>( generatorExpression() ) ); + + auto const& generator = static_cast<Generators<UnderlyingType> const&>( *tracker.getGenerator() ); + return generator[tracker.getIndex()]; + } + +} // namespace Generators +} // namespace Catch + +#define GENERATE( ... ) \ + Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, []{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) + +// end catch_generators.hpp + +// These files are included here so the single_include script doesn't put them +// in the conditionally compiled sections +// start catch_test_case_info.h + +#include <string> +#include <vector> +#include <memory> + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +namespace Catch { + + struct ITestInvoker; + + struct TestCaseInfo { + enum SpecialProperties{ + None = 0, + IsHidden = 1 << 1, + ShouldFail = 1 << 2, + MayFail = 1 << 3, + Throws = 1 << 4, + NonPortable = 1 << 5, + Benchmark = 1 << 6 + }; + + TestCaseInfo( std::string const& _name, + std::string const& _className, + std::string const& _description, + std::vector<std::string> const& _tags, + SourceLineInfo const& _lineInfo ); + + friend void setTags( TestCaseInfo& testCaseInfo, std::vector<std::string> tags ); + + bool isHidden() const; + bool throws() const; + bool okToFail() const; + bool expectedToFail() const; + + std::string tagsAsString() const; + + std::string name; + std::string className; + std::string description; + std::vector<std::string> tags; + std::vector<std::string> lcaseTags; + SourceLineInfo lineInfo; + SpecialProperties properties; + }; + + class TestCase : public TestCaseInfo { + public: + + TestCase( ITestInvoker* testCase, TestCaseInfo&& info ); + + TestCase withName( std::string const& _newName ) const; + + void invoke() const; + + TestCaseInfo const& getTestCaseInfo() const; + + bool operator == ( TestCase const& other ) const; + bool operator < ( TestCase const& other ) const; + + private: + std::shared_ptr<ITestInvoker> test; + }; + + TestCase makeTestCase( ITestInvoker* testCase, + std::string const& className, + NameAndTags const& nameAndTags, + SourceLineInfo const& lineInfo ); +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// end catch_test_case_info.h +// start catch_interfaces_runner.h + +namespace Catch { + + struct IRunner { + virtual ~IRunner(); + virtual bool aborting() const = 0; + }; +} + +// end catch_interfaces_runner.h + +#ifdef __OBJC__ +// start catch_objc.hpp + +#import <objc/runtime.h> + +#include <string> + +// NB. Any general catch headers included here must be included +// in catch.hpp first to make sure they are included by the single +// header for non obj-usage + +/////////////////////////////////////////////////////////////////////////////// +// This protocol is really only here for (self) documenting purposes, since +// all its methods are optional. +@protocol OcFixture + +@optional + +-(void) setUp; +-(void) tearDown; + +@end + +namespace Catch { + + class OcMethod : public ITestInvoker { + + public: + OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {} + + virtual void invoke() const { + id obj = [[m_cls alloc] init]; + + performOptionalSelector( obj, @selector(setUp) ); + performOptionalSelector( obj, m_sel ); + performOptionalSelector( obj, @selector(tearDown) ); + + arcSafeRelease( obj ); + } + private: + virtual ~OcMethod() {} + + Class m_cls; + SEL m_sel; + }; + + namespace Detail{ + + inline std::string getAnnotation( Class cls, + std::string const& annotationName, + std::string const& testCaseName ) { + NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()]; + SEL sel = NSSelectorFromString( selStr ); + arcSafeRelease( selStr ); + id value = performOptionalSelector( cls, sel ); + if( value ) + return [(NSString*)value UTF8String]; + return ""; + } + } + + inline std::size_t registerTestMethods() { + std::size_t noTestMethods = 0; + int noClasses = objc_getClassList( nullptr, 0 ); + + Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses); + objc_getClassList( classes, noClasses ); + + for( int c = 0; c < noClasses; c++ ) { + Class cls = classes[c]; + { + u_int count; + Method* methods = class_copyMethodList( cls, &count ); + for( u_int m = 0; m < count ; m++ ) { + SEL selector = method_getName(methods[m]); + std::string methodName = sel_getName(selector); + if( startsWith( methodName, "Catch_TestCase_" ) ) { + std::string testCaseName = methodName.substr( 15 ); + std::string name = Detail::getAnnotation( cls, "Name", testCaseName ); + std::string desc = Detail::getAnnotation( cls, "Description", testCaseName ); + const char* className = class_getName( cls ); + + getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, NameAndTags( name.c_str(), desc.c_str() ), SourceLineInfo("",0) ) ); + noTestMethods++; + } + } + free(methods); + } + } + return noTestMethods; + } + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) + + namespace Matchers { + namespace Impl { + namespace NSStringMatchers { + + struct StringHolder : MatcherBase<NSString*>{ + StringHolder( NSString* substr ) : m_substr( [substr copy] ){} + StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){} + StringHolder() { + arcSafeRelease( m_substr ); + } + + bool match( NSString* arg ) const override { + return false; + } + + NSString* CATCH_ARC_STRONG m_substr; + }; + + struct Equals : StringHolder { + Equals( NSString* substr ) : StringHolder( substr ){} + + bool match( NSString* str ) const override { + return (str != nil || m_substr == nil ) && + [str isEqualToString:m_substr]; + } + + std::string describe() const override { + return "equals string: " + Catch::Detail::stringify( m_substr ); + } + }; + + struct Contains : StringHolder { + Contains( NSString* substr ) : StringHolder( substr ){} + + bool match( NSString* str ) const { + return (str != nil || m_substr == nil ) && + [str rangeOfString:m_substr].location != NSNotFound; + } + + std::string describe() const override { + return "contains string: " + Catch::Detail::stringify( m_substr ); + } + }; + + struct StartsWith : StringHolder { + StartsWith( NSString* substr ) : StringHolder( substr ){} + + bool match( NSString* str ) const override { + return (str != nil || m_substr == nil ) && + [str rangeOfString:m_substr].location == 0; + } + + std::string describe() const override { + return "starts with: " + Catch::Detail::stringify( m_substr ); + } + }; + struct EndsWith : StringHolder { + EndsWith( NSString* substr ) : StringHolder( substr ){} + + bool match( NSString* str ) const override { + return (str != nil || m_substr == nil ) && + [str rangeOfString:m_substr].location == [str length] - [m_substr length]; + } + + std::string describe() const override { + return "ends with: " + Catch::Detail::stringify( m_substr ); + } + }; + + } // namespace NSStringMatchers + } // namespace Impl + + inline Impl::NSStringMatchers::Equals + Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); } + + inline Impl::NSStringMatchers::Contains + Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); } + + inline Impl::NSStringMatchers::StartsWith + StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); } + + inline Impl::NSStringMatchers::EndsWith + EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); } + + } // namespace Matchers + + using namespace Matchers; + +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +} // namespace Catch + +/////////////////////////////////////////////////////////////////////////////// +#define OC_MAKE_UNIQUE_NAME( root, uniqueSuffix ) root##uniqueSuffix +#define OC_TEST_CASE2( name, desc, uniqueSuffix ) \ ++(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Name_test_, uniqueSuffix ) \ +{ \ +return @ name; \ +} \ ++(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Description_test_, uniqueSuffix ) \ +{ \ +return @ desc; \ +} \ +-(void) OC_MAKE_UNIQUE_NAME( Catch_TestCase_test_, uniqueSuffix ) + +#define OC_TEST_CASE( name, desc ) OC_TEST_CASE2( name, desc, __LINE__ ) + +// end catch_objc.hpp +#endif + +#ifdef CATCH_CONFIG_EXTERNAL_INTERFACES +// start catch_external_interfaces.h + +// start catch_reporter_bases.hpp + +// start catch_interfaces_reporter.h + +// start catch_config.hpp + +// start catch_test_spec_parser.h + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +// start catch_test_spec.h + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +// start catch_wildcard_pattern.h + +namespace Catch +{ + class WildcardPattern { + enum WildcardPosition { + NoWildcard = 0, + WildcardAtStart = 1, + WildcardAtEnd = 2, + WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd + }; + + public: + + WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity ); + virtual ~WildcardPattern() = default; + virtual bool matches( std::string const& str ) const; + + private: + std::string adjustCase( std::string const& str ) const; + CaseSensitive::Choice m_caseSensitivity; + WildcardPosition m_wildcard = NoWildcard; + std::string m_pattern; + }; +} + +// end catch_wildcard_pattern.h +#include <string> +#include <vector> +#include <memory> + +namespace Catch { + + class TestSpec { + struct Pattern { + virtual ~Pattern(); + virtual bool matches( TestCaseInfo const& testCase ) const = 0; + }; + using PatternPtr = std::shared_ptr<Pattern>; + + class NamePattern : public Pattern { + public: + NamePattern( std::string const& name ); + virtual ~NamePattern(); + virtual bool matches( TestCaseInfo const& testCase ) const override; + private: + WildcardPattern m_wildcardPattern; + }; + + class TagPattern : public Pattern { + public: + TagPattern( std::string const& tag ); + virtual ~TagPattern(); + virtual bool matches( TestCaseInfo const& testCase ) const override; + private: + std::string m_tag; + }; + + class ExcludedPattern : public Pattern { + public: + ExcludedPattern( PatternPtr const& underlyingPattern ); + virtual ~ExcludedPattern(); + virtual bool matches( TestCaseInfo const& testCase ) const override; + private: + PatternPtr m_underlyingPattern; + }; + + struct Filter { + std::vector<PatternPtr> m_patterns; + + bool matches( TestCaseInfo const& testCase ) const; + }; + + public: + bool hasFilters() const; + bool matches( TestCaseInfo const& testCase ) const; + + private: + std::vector<Filter> m_filters; + + friend class TestSpecParser; + }; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// end catch_test_spec.h +// start catch_interfaces_tag_alias_registry.h + +#include <string> + +namespace Catch { + + struct TagAlias; + + struct ITagAliasRegistry { + virtual ~ITagAliasRegistry(); + // Nullptr if not present + virtual TagAlias const* find( std::string const& alias ) const = 0; + virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0; + + static ITagAliasRegistry const& get(); + }; + +} // end namespace Catch + +// end catch_interfaces_tag_alias_registry.h +namespace Catch { + + class TestSpecParser { + enum Mode{ None, Name, QuotedName, Tag, EscapedName }; + Mode m_mode = None; + bool m_exclusion = false; + std::size_t m_start = std::string::npos, m_pos = 0; + std::string m_arg; + std::vector<std::size_t> m_escapeChars; + TestSpec::Filter m_currentFilter; + TestSpec m_testSpec; + ITagAliasRegistry const* m_tagAliases = nullptr; + + public: + TestSpecParser( ITagAliasRegistry const& tagAliases ); + + TestSpecParser& parse( std::string const& arg ); + TestSpec testSpec(); + + private: + void visitChar( char c ); + void startNewMode( Mode mode, std::size_t start ); + void escape(); + std::string subString() const; + + template<typename T> + void addPattern() { + std::string token = subString(); + for( std::size_t i = 0; i < m_escapeChars.size(); ++i ) + 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; + token = token.substr( 8 ); + } + if( !token.empty() ) { + TestSpec::PatternPtr pattern = std::make_shared<T>( token ); + if( m_exclusion ) + pattern = std::make_shared<TestSpec::ExcludedPattern>( pattern ); + m_currentFilter.m_patterns.push_back( pattern ); + } + m_exclusion = false; + m_mode = None; + } + + void addFilter(); + }; + TestSpec parseTestSpec( std::string const& arg ); + +} // namespace Catch + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// end catch_test_spec_parser.h +// start catch_interfaces_config.h + +#include <iosfwd> +#include <string> +#include <vector> +#include <memory> + +namespace Catch { + + enum class Verbosity { + Quiet = 0, + Normal, + High + }; + + struct WarnAbout { enum What { + Nothing = 0x00, + NoAssertions = 0x01, + NoTests = 0x02 + }; }; + + struct ShowDurations { enum OrNot { + DefaultForReporter, + Always, + Never + }; }; + struct RunTests { enum InWhatOrder { + InDeclarationOrder, + InLexicographicalOrder, + InRandomOrder + }; }; + struct UseColour { enum YesOrNo { + Auto, + Yes, + No + }; }; + struct WaitForKeypress { enum When { + Never, + BeforeStart = 1, + BeforeExit = 2, + BeforeStartAndExit = BeforeStart | BeforeExit + }; }; + + class TestSpec; + + struct IConfig : NonCopyable { + + virtual ~IConfig(); + + virtual bool allowThrows() const = 0; + virtual std::ostream& stream() const = 0; + virtual std::string name() const = 0; + virtual bool includeSuccessfulResults() const = 0; + virtual bool shouldDebugBreak() const = 0; + virtual bool warnAboutMissingAssertions() const = 0; + virtual bool warnAboutNoTests() const = 0; + virtual int abortAfter() const = 0; + virtual bool showInvisibles() const = 0; + virtual ShowDurations::OrNot showDurations() const = 0; + virtual TestSpec const& testSpec() const = 0; + virtual bool hasTestFilters() const = 0; + virtual RunTests::InWhatOrder runOrder() const = 0; + virtual unsigned int rngSeed() const = 0; + virtual int benchmarkResolutionMultiple() const = 0; + virtual UseColour::YesOrNo useColour() const = 0; + virtual std::vector<std::string> const& getSectionsToRun() const = 0; + virtual Verbosity verbosity() const = 0; + }; + + using IConfigPtr = std::shared_ptr<IConfig const>; +} + +// end catch_interfaces_config.h +// Libstdc++ doesn't like incomplete classes for unique_ptr + +#include <memory> +#include <vector> +#include <string> + +#ifndef CATCH_CONFIG_CONSOLE_WIDTH +#define CATCH_CONFIG_CONSOLE_WIDTH 80 +#endif + +namespace Catch { + + struct IStream; + + struct ConfigData { + bool listTests = false; + bool listTags = false; + bool listReporters = false; + bool listTestNamesOnly = false; + + bool showSuccessfulTests = false; + bool shouldDebugBreak = false; + bool noThrow = false; + bool showHelp = false; + bool showInvisibles = false; + bool filenamesAsTags = false; + bool libIdentify = false; + + int abortAfter = -1; + unsigned int rngSeed = 0; + int benchmarkResolutionMultiple = 100; + + Verbosity verbosity = Verbosity::Normal; + WarnAbout::What warnings = WarnAbout::Nothing; + ShowDurations::OrNot showDurations = ShowDurations::DefaultForReporter; + RunTests::InWhatOrder runOrder = RunTests::InDeclarationOrder; + UseColour::YesOrNo useColour = UseColour::Auto; + WaitForKeypress::When waitForKeypress = WaitForKeypress::Never; + + std::string outputFilename; + std::string name; + std::string processName; +#ifndef CATCH_CONFIG_DEFAULT_REPORTER +#define CATCH_CONFIG_DEFAULT_REPORTER "console" +#endif + std::string reporterName = CATCH_CONFIG_DEFAULT_REPORTER; +#undef CATCH_CONFIG_DEFAULT_REPORTER + + std::vector<std::string> testsOrTags; + std::vector<std::string> sectionsToRun; + }; + + class Config : public IConfig { + public: + + Config() = default; + Config( ConfigData const& data ); + virtual ~Config() = default; + + std::string const& getFilename() const; + + bool listTests() const; + bool listTestNamesOnly() const; + bool listTags() const; + bool listReporters() const; + + std::string getProcessName() const; + std::string const& getReporterName() const; + + std::vector<std::string> const& getTestsOrTags() const; + std::vector<std::string> const& getSectionsToRun() const override; + + virtual TestSpec const& testSpec() const override; + bool hasTestFilters() const override; + + bool showHelp() const; + + // IConfig interface + bool allowThrows() const override; + std::ostream& stream() const override; + std::string name() const override; + bool includeSuccessfulResults() const override; + bool warnAboutMissingAssertions() const override; + bool warnAboutNoTests() const override; + ShowDurations::OrNot showDurations() const override; + RunTests::InWhatOrder runOrder() const override; + unsigned int rngSeed() const override; + int benchmarkResolutionMultiple() const override; + UseColour::YesOrNo useColour() const override; + bool shouldDebugBreak() const override; + int abortAfter() const override; + bool showInvisibles() const override; + Verbosity verbosity() const override; + + private: + + IStream const* openStream(); + ConfigData m_data; + + std::unique_ptr<IStream const> m_stream; + TestSpec m_testSpec; + bool m_hasTestFilters = false; + }; + +} // end namespace Catch + +// end catch_config.hpp +// start catch_assertionresult.h + +#include <string> + +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; + std::string getMessage() const; + SourceLineInfo getSourceInfo() const; + StringRef getTestMacroName() const; + + //protected: + AssertionInfo m_info; + AssertionResultData m_resultData; + }; + +} // end namespace Catch + +// end catch_assertionresult.h +// start catch_option.hpp + +namespace Catch { + + // An optional type + template<typename T> + class Option { + public: + Option() : nullableValue( nullptr ) {} + Option( T const& _value ) + : nullableValue( new( storage ) T( _value ) ) + {} + Option( Option const& _other ) + : nullableValue( _other ? new( storage ) T( *_other ) : nullptr ) + {} + + ~Option() { + reset(); + } + + Option& operator= ( Option const& _other ) { + if( &_other != this ) { + reset(); + if( _other ) + nullableValue = new( storage ) T( *_other ); + } + return *this; + } + Option& operator = ( T const& _value ) { + reset(); + nullableValue = new( storage ) T( _value ); + return *this; + } + + void reset() { + if( nullableValue ) + nullableValue->~T(); + nullableValue = nullptr; + } + + T& operator*() { return *nullableValue; } + T const& operator*() const { return *nullableValue; } + T* operator->() { return nullableValue; } + const T* operator->() const { 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(); + } + + private: + T *nullableValue; + alignas(alignof(T)) char storage[sizeof(T)]; + }; + +} // end namespace Catch + +// end catch_option.hpp +#include <string> +#include <iosfwd> +#include <map> +#include <set> +#include <memory> + +namespace Catch { + + struct ReporterConfig { + explicit ReporterConfig( IConfigPtr const& _fullConfig ); + + ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream ); + + std::ostream& stream() const; + IConfigPtr fullConfig() const; + + private: + std::ostream* m_stream; + IConfigPtr m_fullConfig; + }; + + struct ReporterPreferences { + bool shouldRedirectStdOut = false; + bool shouldReportAllAssertions = false; + }; + + template<typename T> + struct LazyStat : Option<T> { + LazyStat& operator=( T const& _value ) { + Option<T>::operator=( _value ); + used = false; + return *this; + } + void reset() { + Option<T>::reset(); + used = false; + } + bool used = false; + }; + + struct TestRunInfo { + TestRunInfo( std::string const& _name ); + std::string name; + }; + struct GroupInfo { + GroupInfo( std::string const& _name, + std::size_t _groupIndex, + std::size_t _groupsCount ); + + std::string name; + std::size_t groupIndex; + std::size_t groupsCounts; + }; + + struct AssertionStats { + AssertionStats( AssertionResult const& _assertionResult, + std::vector<MessageInfo> const& _infoMessages, + Totals const& _totals ); + + AssertionStats( AssertionStats const& ) = default; + AssertionStats( AssertionStats && ) = default; + AssertionStats& operator = ( AssertionStats const& ) = default; + AssertionStats& operator = ( AssertionStats && ) = default; + virtual ~AssertionStats(); + + AssertionResult assertionResult; + std::vector<MessageInfo> infoMessages; + Totals totals; + }; + + struct SectionStats { + SectionStats( SectionInfo const& _sectionInfo, + Counts const& _assertions, + double _durationInSeconds, + bool _missingAssertions ); + SectionStats( SectionStats const& ) = default; + SectionStats( SectionStats && ) = default; + SectionStats& operator = ( SectionStats const& ) = default; + SectionStats& operator = ( SectionStats && ) = default; + virtual ~SectionStats(); + + 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 ); + + TestCaseStats( TestCaseStats const& ) = default; + TestCaseStats( TestCaseStats && ) = default; + TestCaseStats& operator = ( TestCaseStats const& ) = default; + TestCaseStats& operator = ( TestCaseStats && ) = default; + virtual ~TestCaseStats(); + + TestCaseInfo testInfo; + Totals totals; + std::string stdOut; + std::string stdErr; + bool aborting; + }; + + struct TestGroupStats { + TestGroupStats( GroupInfo const& _groupInfo, + Totals const& _totals, + bool _aborting ); + TestGroupStats( GroupInfo const& _groupInfo ); + + TestGroupStats( TestGroupStats const& ) = default; + TestGroupStats( TestGroupStats && ) = default; + TestGroupStats& operator = ( TestGroupStats const& ) = default; + TestGroupStats& operator = ( TestGroupStats && ) = default; + virtual ~TestGroupStats(); + + GroupInfo groupInfo; + Totals totals; + bool aborting; + }; + + struct TestRunStats { + TestRunStats( TestRunInfo const& _runInfo, + Totals const& _totals, + bool _aborting ); + + TestRunStats( TestRunStats const& ) = default; + TestRunStats( TestRunStats && ) = default; + TestRunStats& operator = ( TestRunStats const& ) = default; + TestRunStats& operator = ( TestRunStats && ) = default; + virtual ~TestRunStats(); + + TestRunInfo runInfo; + Totals totals; + bool aborting; + }; + + struct BenchmarkInfo { + std::string name; + }; + struct BenchmarkStats { + BenchmarkInfo info; + std::size_t iterations; + uint64_t elapsedTimeInNanoseconds; + }; + + struct IStreamingReporter { + virtual ~IStreamingReporter() = default; + + // Implementing class must also provide the following static methods: + // static std::string getDescription(); + // static std::set<Verbosity> getSupportedVerbosities() + + virtual ReporterPreferences getPreferences() const = 0; + + virtual void noMatchingTestCases( std::string const& spec ) = 0; + + virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; + virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0; + + virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; + virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; + + // *** experimental *** + virtual void benchmarkStarting( BenchmarkInfo const& ) {} + + virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; + + // The return value indicates if the messages buffer should be cleared: + virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0; + + // *** experimental *** + virtual void benchmarkEnded( BenchmarkStats const& ) {} + + virtual void sectionEnded( SectionStats const& sectionStats ) = 0; + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0; + virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; + + virtual void skipTest( TestCaseInfo const& testInfo ) = 0; + + // Default empty implementation provided + virtual void fatalErrorEncountered( StringRef name ); + + virtual bool isMulti() const; + }; + using IStreamingReporterPtr = std::unique_ptr<IStreamingReporter>; + + struct IReporterFactory { + virtual ~IReporterFactory(); + virtual IStreamingReporterPtr create( ReporterConfig const& config ) const = 0; + virtual std::string getDescription() const = 0; + }; + using IReporterFactoryPtr = std::shared_ptr<IReporterFactory>; + + struct IReporterRegistry { + using FactoryMap = std::map<std::string, IReporterFactoryPtr>; + using Listeners = std::vector<IReporterFactoryPtr>; + + virtual ~IReporterRegistry(); + virtual IStreamingReporterPtr create( std::string const& name, IConfigPtr const& config ) const = 0; + virtual FactoryMap const& getFactories() const = 0; + virtual Listeners const& getListeners() const = 0; + }; + +} // end namespace Catch + +// end catch_interfaces_reporter.h +#include <algorithm> +#include <cstring> +#include <cfloat> +#include <cstdio> +#include <cassert> +#include <memory> +#include <ostream> + +namespace Catch { + void prepareExpandedExpression(AssertionResult& result); + + // Returns double formatted as %.3f (format expected on output) + std::string getFormattedDuration( double duration ); + + template<typename DerivedT> + struct StreamingReporterBase : IStreamingReporter { + + StreamingReporterBase( ReporterConfig const& _config ) + : m_config( _config.fullConfig() ), + stream( _config.stream() ) + { + m_reporterPrefs.shouldRedirectStdOut = false; + if( !DerivedT::getSupportedVerbosities().count( m_config->verbosity() ) ) + CATCH_ERROR( "Verbosity level not supported by this reporter" ); + } + + ReporterPreferences getPreferences() const override { + return m_reporterPrefs; + } + + static std::set<Verbosity> getSupportedVerbosities() { + return { Verbosity::Normal }; + } + + ~StreamingReporterBase() override = default; + + void noMatchingTestCases(std::string const&) override {} + + void testRunStarting(TestRunInfo const& _testRunInfo) override { + currentTestRunInfo = _testRunInfo; + } + void testGroupStarting(GroupInfo const& _groupInfo) override { + currentGroupInfo = _groupInfo; + } + + void testCaseStarting(TestCaseInfo const& _testInfo) override { + currentTestCaseInfo = _testInfo; + } + void sectionStarting(SectionInfo const& _sectionInfo) override { + m_sectionStack.push_back(_sectionInfo); + } + + void sectionEnded(SectionStats const& /* _sectionStats */) override { + m_sectionStack.pop_back(); + } + void testCaseEnded(TestCaseStats const& /* _testCaseStats */) override { + currentTestCaseInfo.reset(); + } + void testGroupEnded(TestGroupStats const& /* _testGroupStats */) override { + currentGroupInfo.reset(); + } + void testRunEnded(TestRunStats const& /* _testRunStats */) override { + currentTestCaseInfo.reset(); + currentGroupInfo.reset(); + currentTestRunInfo.reset(); + } + + void skipTest(TestCaseInfo const&) override { + // Don't do anything with this by default. + // It can optionally be overridden in the derived class. + } + + IConfigPtr m_config; + std::ostream& stream; + + LazyStat<TestRunInfo> currentTestRunInfo; + LazyStat<GroupInfo> currentGroupInfo; + LazyStat<TestCaseInfo> currentTestCaseInfo; + + std::vector<SectionInfo> m_sectionStack; + ReporterPreferences m_reporterPrefs; + }; + + template<typename DerivedT> + struct CumulativeReporterBase : IStreamingReporter { + template<typename T, typename ChildNodeT> + struct Node { + explicit Node( T const& _value ) : value( _value ) {} + virtual ~Node() {} + + using ChildNodes = std::vector<std::shared_ptr<ChildNodeT>>; + T value; + ChildNodes children; + }; + struct SectionNode { + explicit SectionNode(SectionStats const& _stats) : stats(_stats) {} + virtual ~SectionNode() = default; + + bool operator == (SectionNode const& other) const { + return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; + } + bool operator == (std::shared_ptr<SectionNode> const& other) const { + return operator==(*other); + } + + SectionStats stats; + using ChildSections = std::vector<std::shared_ptr<SectionNode>>; + using Assertions = std::vector<AssertionStats>; + ChildSections childSections; + Assertions assertions; + std::string stdOut; + std::string stdErr; + }; + + struct BySectionInfo { + BySectionInfo( SectionInfo const& other ) : m_other( other ) {} + BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} + bool operator() (std::shared_ptr<SectionNode> 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; + }; + + using TestCaseNode = Node<TestCaseStats, SectionNode>; + using TestGroupNode = Node<TestGroupStats, TestCaseNode>; + using TestRunNode = Node<TestRunStats, TestGroupNode>; + + CumulativeReporterBase( ReporterConfig const& _config ) + : m_config( _config.fullConfig() ), + stream( _config.stream() ) + { + m_reporterPrefs.shouldRedirectStdOut = false; + if( !DerivedT::getSupportedVerbosities().count( m_config->verbosity() ) ) + CATCH_ERROR( "Verbosity level not supported by this reporter" ); + } + ~CumulativeReporterBase() override = default; + + ReporterPreferences getPreferences() const override { + return m_reporterPrefs; + } + + static std::set<Verbosity> getSupportedVerbosities() { + return { Verbosity::Normal }; + } + + void testRunStarting( TestRunInfo const& ) override {} + void testGroupStarting( GroupInfo const& ) override {} + + void testCaseStarting( TestCaseInfo const& ) override {} + + void sectionStarting( SectionInfo const& sectionInfo ) override { + SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); + std::shared_ptr<SectionNode> node; + if( m_sectionStack.empty() ) { + if( !m_rootSection ) + m_rootSection = std::make_shared<SectionNode>( incompleteStats ); + node = m_rootSection; + } + else { + SectionNode& parentNode = *m_sectionStack.back(); + auto it = + std::find_if( parentNode.childSections.begin(), + parentNode.childSections.end(), + BySectionInfo( sectionInfo ) ); + if( it == parentNode.childSections.end() ) { + node = std::make_shared<SectionNode>( incompleteStats ); + parentNode.childSections.push_back( node ); + } + else + node = *it; + } + m_sectionStack.push_back( node ); + m_deepestSection = std::move(node); + } + + void assertionStarting(AssertionInfo const&) override {} + + bool assertionEnded(AssertionStats const& assertionStats) override { + 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. + prepareExpandedExpression(const_cast<AssertionResult&>( assertionStats.assertionResult ) ); + SectionNode& sectionNode = *m_sectionStack.back(); + sectionNode.assertions.push_back(assertionStats); + return true; + } + void sectionEnded(SectionStats const& sectionStats) override { + assert(!m_sectionStack.empty()); + SectionNode& node = *m_sectionStack.back(); + node.stats = sectionStats; + m_sectionStack.pop_back(); + } + void testCaseEnded(TestCaseStats const& testCaseStats) override { + auto node = std::make_shared<TestCaseNode>(testCaseStats); + assert(m_sectionStack.size() == 0); + node->children.push_back(m_rootSection); + m_testCases.push_back(node); + m_rootSection.reset(); + + assert(m_deepestSection); + m_deepestSection->stdOut = testCaseStats.stdOut; + m_deepestSection->stdErr = testCaseStats.stdErr; + } + void testGroupEnded(TestGroupStats const& testGroupStats) override { + auto node = std::make_shared<TestGroupNode>(testGroupStats); + node->children.swap(m_testCases); + m_testGroups.push_back(node); + } + void testRunEnded(TestRunStats const& testRunStats) override { + auto node = std::make_shared<TestRunNode>(testRunStats); + node->children.swap(m_testGroups); + m_testRuns.push_back(node); + testRunEndedCumulative(); + } + virtual void testRunEndedCumulative() = 0; + + void skipTest(TestCaseInfo const&) override {} + + IConfigPtr m_config; + std::ostream& stream; + std::vector<AssertionStats> m_assertions; + std::vector<std::vector<std::shared_ptr<SectionNode>>> m_sections; + std::vector<std::shared_ptr<TestCaseNode>> m_testCases; + std::vector<std::shared_ptr<TestGroupNode>> m_testGroups; + + std::vector<std::shared_ptr<TestRunNode>> m_testRuns; + + std::shared_ptr<SectionNode> m_rootSection; + std::shared_ptr<SectionNode> m_deepestSection; + std::vector<std::shared_ptr<SectionNode>> m_sectionStack; + ReporterPreferences m_reporterPrefs; + }; + + template<char C> + char const* getLineOfChars() { + static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; + if( !*line ) { + std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); + line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0; + } + return line; + } + + struct TestEventListenerBase : StreamingReporterBase<TestEventListenerBase> { + TestEventListenerBase( ReporterConfig const& _config ); + + static std::set<Verbosity> getSupportedVerbosities(); + + void assertionStarting(AssertionInfo const&) override; + bool assertionEnded(AssertionStats const&) override; + }; + +} // end namespace Catch + +// end catch_reporter_bases.hpp +// start catch_console_colour.h + +namespace Catch { + + 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 + }; + + // Use constructed object for RAII guard + Colour( Code _colourCode ); + Colour( Colour&& other ) noexcept; + Colour& operator=( Colour&& other ) noexcept; + ~Colour(); + + // Use static method for one-shot changes + static void use( Code _colourCode ); + + private: + bool m_moved = false; + }; + + std::ostream& operator << ( std::ostream& os, Colour const& ); + +} // end namespace Catch + +// end catch_console_colour.h +// start catch_reporter_registrars.hpp + + +namespace Catch { + + template<typename T> + class ReporterRegistrar { + + class ReporterFactory : public IReporterFactory { + + virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override { + return std::unique_ptr<T>( new T( config ) ); + } + + virtual std::string getDescription() const override { + return T::getDescription(); + } + }; + + public: + + explicit ReporterRegistrar( std::string const& name ) { + getMutableRegistryHub().registerReporter( name, std::make_shared<ReporterFactory>() ); + } + }; + + template<typename T> + class ListenerRegistrar { + + class ListenerFactory : public IReporterFactory { + + virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override { + return std::unique_ptr<T>( new T( config ) ); + } + virtual std::string getDescription() const override { + return std::string(); + } + }; + + public: + + ListenerRegistrar() { + getMutableRegistryHub().registerListener( std::make_shared<ListenerFactory>() ); + } + }; +} + +#if !defined(CATCH_CONFIG_DISABLE) + +#define CATCH_REGISTER_REPORTER( name, reporterType ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::ReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS + +#define CATCH_REGISTER_LISTENER( listenerType ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; } \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +#else // CATCH_CONFIG_DISABLE + +#define CATCH_REGISTER_REPORTER(name, reporterType) +#define CATCH_REGISTER_LISTENER(listenerType) + +#endif // CATCH_CONFIG_DISABLE + +// end catch_reporter_registrars.hpp +// Allow users to base their work off existing reporters +// start catch_reporter_compact.h + +namespace Catch { + + struct CompactReporter : StreamingReporterBase<CompactReporter> { + + using StreamingReporterBase::StreamingReporterBase; + + ~CompactReporter() override; + + static std::string getDescription(); + + ReporterPreferences getPreferences() const override; + + void noMatchingTestCases(std::string const& spec) override; + + void assertionStarting(AssertionInfo const&) override; + + bool assertionEnded(AssertionStats const& _assertionStats) override; + + void sectionEnded(SectionStats const& _sectionStats) override; + + void testRunEnded(TestRunStats const& _testRunStats) override; + + }; + +} // end namespace Catch + +// end catch_reporter_compact.h +// start catch_reporter_console.h + +#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 { + // Fwd decls + struct SummaryColumn; + class TablePrinter; + + struct ConsoleReporter : StreamingReporterBase<ConsoleReporter> { + std::unique_ptr<TablePrinter> m_tablePrinter; + + ConsoleReporter(ReporterConfig const& config); + ~ConsoleReporter() override; + static std::string getDescription(); + + void noMatchingTestCases(std::string const& spec) override; + + void assertionStarting(AssertionInfo const&) override; + + bool assertionEnded(AssertionStats const& _assertionStats) override; + + void sectionStarting(SectionInfo const& _sectionInfo) override; + void sectionEnded(SectionStats const& _sectionStats) override; + + void benchmarkStarting(BenchmarkInfo const& info) override; + void benchmarkEnded(BenchmarkStats const& stats) override; + + void testCaseEnded(TestCaseStats const& _testCaseStats) override; + void testGroupEnded(TestGroupStats const& _testGroupStats) override; + void testRunEnded(TestRunStats const& _testRunStats) override; + + private: + + void lazyPrint(); + + void lazyPrintWithoutClosingBenchmarkTable(); + void lazyPrintRunInfo(); + void lazyPrintGroupInfo(); + void printTestCaseAndSectionHeader(); + + void printClosedHeader(std::string const& _name); + void printOpenHeader(std::string const& _name); + + // if string has a : in first line will set indent to follow it on + // subsequent lines + void printHeaderString(std::string const& _string, std::size_t indent = 0); + + void printTotals(Totals const& totals); + void printSummaryRow(std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row); + + void printTotalsDivider(Totals const& totals); + void printSummaryDivider(); + + private: + bool m_headerPrinted = false; + }; + +} // end namespace Catch + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +// end catch_reporter_console.h +// start catch_reporter_junit.h + +// start catch_xmlwriter.h + +#include <vector> + +namespace Catch { + + class XmlEncode { + public: + enum ForWhat { ForTextNodes, ForAttributes }; + + XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes ); + + void encodeTo( std::ostream& os ) const; + + friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ); + + private: + std::string m_str; + ForWhat m_forWhat; + }; + + class XmlWriter { + public: + + class ScopedElement { + public: + ScopedElement( XmlWriter* writer ); + + ScopedElement( ScopedElement&& other ) noexcept; + ScopedElement& operator=( ScopedElement&& other ) noexcept; + + ~ScopedElement(); + + ScopedElement& writeText( std::string const& text, bool indent = true ); + + template<typename T> + ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { + m_writer->writeAttribute( name, attribute ); + return *this; + } + + private: + mutable XmlWriter* m_writer = nullptr; + }; + + XmlWriter( std::ostream& os = Catch::cout() ); + ~XmlWriter(); + + XmlWriter( XmlWriter const& ) = delete; + XmlWriter& operator=( XmlWriter const& ) = delete; + + XmlWriter& startElement( std::string const& name ); + + ScopedElement scopedElement( std::string const& name ); + + XmlWriter& endElement(); + + XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ); + + XmlWriter& writeAttribute( std::string const& name, bool attribute ); + + template<typename T> + XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { + ReusableStringStream rss; + rss << attribute; + return writeAttribute( name, rss.str() ); + } + + XmlWriter& writeText( std::string const& text, bool indent = true ); + + XmlWriter& writeComment( std::string const& text ); + + void writeStylesheetRef( std::string const& url ); + + XmlWriter& writeBlankLine(); + + void ensureTagClosed(); + + private: + + void writeDeclaration(); + + void newlineIfNecessary(); + + bool m_tagIsOpen = false; + bool m_needsNewline = false; + std::vector<std::string> m_tags; + std::string m_indent; + std::ostream& m_os; + }; + +} + +// end catch_xmlwriter.h +namespace Catch { + + class JunitReporter : public CumulativeReporterBase<JunitReporter> { + public: + JunitReporter(ReporterConfig const& _config); + + ~JunitReporter() override; + + static std::string getDescription(); + + void noMatchingTestCases(std::string const& /*spec*/) override; + + void testRunStarting(TestRunInfo const& runInfo) override; + + void testGroupStarting(GroupInfo const& groupInfo) override; + + void testCaseStarting(TestCaseInfo const& testCaseInfo) override; + bool assertionEnded(AssertionStats const& assertionStats) override; + + void testCaseEnded(TestCaseStats const& testCaseStats) override; + + void testGroupEnded(TestGroupStats const& testGroupStats) override; + + void testRunEndedCumulative() override; + + void writeGroup(TestGroupNode const& groupNode, double suiteTime); + + void writeTestCase(TestCaseNode const& testCaseNode); + + void writeSection(std::string const& className, + std::string const& rootName, + SectionNode const& sectionNode); + + void writeAssertions(SectionNode const& sectionNode); + void writeAssertion(AssertionStats const& stats); + + XmlWriter xml; + Timer suiteTimer; + std::string stdOutForSuite; + std::string stdErrForSuite; + unsigned int unexpectedExceptions = 0; + bool m_okToFail = false; + }; + +} // end namespace Catch + +// end catch_reporter_junit.h +// start catch_reporter_xml.h + +namespace Catch { + class XmlReporter : public StreamingReporterBase<XmlReporter> { + public: + XmlReporter(ReporterConfig const& _config); + + ~XmlReporter() override; + + static std::string getDescription(); + + virtual std::string getStylesheetRef() const; + + void writeSourceInfo(SourceLineInfo const& sourceInfo); + + public: // StreamingReporterBase + + void noMatchingTestCases(std::string const& s) override; + + void testRunStarting(TestRunInfo const& testInfo) override; + + void testGroupStarting(GroupInfo const& groupInfo) override; + + void testCaseStarting(TestCaseInfo const& testInfo) override; + + void sectionStarting(SectionInfo const& sectionInfo) override; + + void assertionStarting(AssertionInfo const&) override; + + bool assertionEnded(AssertionStats const& assertionStats) override; + + void sectionEnded(SectionStats const& sectionStats) override; + + void testCaseEnded(TestCaseStats const& testCaseStats) override; + + void testGroupEnded(TestGroupStats const& testGroupStats) override; + + void testRunEnded(TestRunStats const& testRunStats) override; + + private: + Timer m_testCaseTimer; + XmlWriter m_xml; + int m_sectionDepth = 0; + }; + +} // end namespace Catch + +// end catch_reporter_xml.h + +// end catch_external_interfaces.h +#endif + +#endif // ! CATCH_CONFIG_IMPL_ONLY + +#ifdef CATCH_IMPL +// start catch_impl.hpp + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wweak-vtables" +#endif + +// Keep these here for external reporters +// start catch_test_case_tracker.h + +#include <string> +#include <vector> +#include <memory> + +namespace Catch { +namespace TestCaseTracking { + + struct NameAndLocation { + std::string name; + SourceLineInfo location; + + NameAndLocation( std::string const& _name, SourceLineInfo const& _location ); + }; + + struct ITracker; + + using ITrackerPtr = std::shared_ptr<ITracker>; + + struct ITracker { + virtual ~ITracker(); + + // static queries + virtual NameAndLocation const& nameAndLocation() const = 0; + + // dynamic queries + virtual bool isComplete() const = 0; // Successfully completed or failed + virtual bool isSuccessfullyCompleted() const = 0; + virtual bool isOpen() const = 0; // Started but not complete + virtual bool hasChildren() const = 0; + + virtual ITracker& parent() = 0; + + // actions + virtual void close() = 0; // Successfully complete + virtual void fail() = 0; + virtual void markAsNeedingAnotherRun() = 0; + + virtual void addChild( ITrackerPtr const& child ) = 0; + virtual ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) = 0; + virtual void openChild() = 0; + + // Debug/ checking + virtual bool isSectionTracker() const = 0; + virtual bool isIndexTracker() const = 0; + }; + + class TrackerContext { + + enum RunState { + NotStarted, + Executing, + CompletedCycle + }; + + ITrackerPtr m_rootTracker; + ITracker* m_currentTracker = nullptr; + RunState m_runState = NotStarted; + + public: + + static TrackerContext& instance(); + + ITracker& startRun(); + void endRun(); + + void startCycle(); + void completeCycle(); + + bool completedCycle() const; + ITracker& currentTracker(); + void setCurrentTracker( ITracker* tracker ); + }; + + class TrackerBase : public ITracker { + protected: + enum CycleState { + NotStarted, + Executing, + ExecutingChildren, + NeedsAnotherRun, + CompletedSuccessfully, + Failed + }; + + using Children = std::vector<ITrackerPtr>; + NameAndLocation m_nameAndLocation; + TrackerContext& m_ctx; + ITracker* m_parent; + Children m_children; + CycleState m_runState = NotStarted; + + public: + TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); + + NameAndLocation const& nameAndLocation() const override; + bool isComplete() const override; + bool isSuccessfullyCompleted() const override; + bool isOpen() const override; + bool hasChildren() const override; + + void addChild( ITrackerPtr const& child ) override; + + ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) override; + ITracker& parent() override; + + void openChild() override; + + bool isSectionTracker() const override; + bool isIndexTracker() const override; + + void open(); + + void close() override; + void fail() override; + void markAsNeedingAnotherRun() override; + + private: + void moveToParent(); + void moveToThis(); + }; + + class SectionTracker : public TrackerBase { + std::vector<std::string> m_filters; + public: + SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); + + bool isSectionTracker() const override; + + static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ); + + void tryOpen(); + + void addInitialFilters( std::vector<std::string> const& filters ); + void addNextFilters( std::vector<std::string> const& filters ); + }; + + class IndexTracker : public TrackerBase { + int m_size; + int m_index = -1; + public: + IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ); + + bool isIndexTracker() const override; + void close() override; + + static IndexTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ); + + int index() const; + + void moveNext(); + }; + +} // namespace TestCaseTracking + +using TestCaseTracking::ITracker; +using TestCaseTracking::TrackerContext; +using TestCaseTracking::SectionTracker; +using TestCaseTracking::IndexTracker; + +} // namespace Catch + +// end catch_test_case_tracker.h + +// start catch_leak_detector.h + +namespace Catch { + + struct LeakDetector { + LeakDetector(); + ~LeakDetector(); + }; + +} +// end catch_leak_detector.h +// Cpp files will be included in the single-header file here +// start catch_approx.cpp + +#include <cmath> +#include <limits> + +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 { +namespace Detail { + + Approx::Approx ( double value ) + : m_epsilon( std::numeric_limits<float>::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(m_value))); + } + + void Approx::setMargin(double margin) { + CATCH_ENFORCE(margin >= 0, + "Invalid Approx::margin: " << margin << '.' + << " Approx::Margin has to be non-negative."); + m_margin = margin; + } + + void Approx::setEpsilon(double epsilon) { + CATCH_ENFORCE(epsilon >= 0 && epsilon <= 1.0, + "Invalid Approx::epsilon: " << epsilon << '.' + << " Approx::epsilon has to be in [0, 1]"); + m_epsilon = epsilon; + } + +} // end namespace Detail + +namespace literals { + Detail::Approx operator "" _a(long double val) { + return Detail::Approx(val); + } + Detail::Approx operator "" _a(unsigned long long val) { + return Detail::Approx(val); + } +} // end namespace literals + +std::string StringMaker<Catch::Detail::Approx>::convert(Catch::Detail::Approx const& value) { + return value.toString(); +} + +} // end namespace Catch +// end catch_approx.cpp +// start catch_assertionhandler.cpp + +// start catch_context.h + +#include <memory> + +namespace Catch { + + struct IResultCapture; + struct IRunner; + struct IConfig; + struct IMutableContext; + + using IConfigPtr = std::shared_ptr<IConfig const>; + + struct IContext + { + virtual ~IContext(); + + virtual IResultCapture* getResultCapture() = 0; + virtual IRunner* getRunner() = 0; + virtual IConfigPtr const& getConfig() const = 0; + }; + + struct IMutableContext : IContext + { + virtual ~IMutableContext(); + virtual void setResultCapture( IResultCapture* resultCapture ) = 0; + virtual void setRunner( IRunner* runner ) = 0; + virtual void setConfig( IConfigPtr const& config ) = 0; + + private: + static IMutableContext *currentContext; + friend IMutableContext& getCurrentMutableContext(); + friend void cleanUpContext(); + static void createContext(); + }; + + inline IMutableContext& getCurrentMutableContext() + { + if( !IMutableContext::currentContext ) + IMutableContext::createContext(); + return *IMutableContext::currentContext; + } + + inline IContext& getCurrentContext() + { + return getCurrentMutableContext(); + } + + void cleanUpContext(); +} + +// end catch_context.h +// start catch_debugger.h + +namespace Catch { + bool isDebuggerActive(); +} + +#ifdef CATCH_PLATFORM_MAC + + #define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */ + +#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 <signal.h> + + #define CATCH_TRAP() raise(SIGTRAP) + #endif +#elif defined(_MSC_VER) + #define CATCH_TRAP() __debugbreak() +#elif defined(__MINGW32__) + extern "C" __declspec(dllimport) void __stdcall DebugBreak(); + #define CATCH_TRAP() DebugBreak() +#endif + +#ifdef CATCH_TRAP + #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } +#else + namespace Catch { + inline void doNothing() {} + } + #define CATCH_BREAK_INTO_DEBUGGER() Catch::doNothing() +#endif + +// end catch_debugger.h +// start catch_run_context.h + +// start catch_fatal_condition.h + +// start catch_windows_h_proxy.h + + +#if defined(CATCH_PLATFORM_WINDOWS) + +#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX) +# define CATCH_DEFINED_NOMINMAX +# define NOMINMAX +#endif +#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN) +# define CATCH_DEFINED_WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif + +#ifdef __AFXDLL +#include <AfxWin.h> +#else +#include <windows.h> +#endif + +#ifdef CATCH_DEFINED_NOMINMAX +# undef NOMINMAX +#endif +#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN +# undef WIN32_LEAN_AND_MEAN +#endif + +#endif // defined(CATCH_PLATFORM_WINDOWS) + +// end catch_windows_h_proxy.h +#if defined( CATCH_CONFIG_WINDOWS_SEH ) + +namespace Catch { + + struct FatalConditionHandler { + + static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo); + FatalConditionHandler(); + static void reset(); + ~FatalConditionHandler(); + + private: + static bool isSet; + static ULONG guaranteeSize; + static PVOID exceptionHandlerHandle; + }; + +} // namespace Catch + +#elif defined ( CATCH_CONFIG_POSIX_SIGNALS ) + +#include <signal.h> + +namespace Catch { + + struct FatalConditionHandler { + + static bool isSet; + static struct sigaction oldSigActions[]; + static stack_t oldSigStack; + static char altStackMem[]; + + static void handleSignal( int sig ); + + FatalConditionHandler(); + ~FatalConditionHandler(); + static void reset(); + }; + +} // namespace Catch + +#else + +namespace Catch { + struct FatalConditionHandler { + void reset(); + }; +} + +#endif + +// end catch_fatal_condition.h +#include <string> + +namespace Catch { + + struct IMutableContext; + + /////////////////////////////////////////////////////////////////////////// + + class RunContext : public IResultCapture, public IRunner { + + public: + RunContext( RunContext const& ) = delete; + RunContext& operator =( RunContext const& ) = delete; + + explicit RunContext( IConfigPtr const& _config, IStreamingReporterPtr&& reporter ); + + ~RunContext() override; + + void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ); + void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ); + + Totals runTest(TestCase const& testCase); + + IConfigPtr config() const; + IStreamingReporter& reporter() const; + + public: // IResultCapture + + // Assertion handlers + void handleExpr + ( AssertionInfo const& info, + ITransientExpression const& expr, + AssertionReaction& reaction ) override; + void handleMessage + ( AssertionInfo const& info, + ResultWas::OfType resultType, + StringRef const& message, + AssertionReaction& reaction ) override; + void handleUnexpectedExceptionNotThrown + ( AssertionInfo const& info, + AssertionReaction& reaction ) override; + void handleUnexpectedInflightException + ( AssertionInfo const& info, + std::string const& message, + AssertionReaction& reaction ) override; + void handleIncomplete + ( AssertionInfo const& info ) override; + void handleNonExpr + ( AssertionInfo const &info, + ResultWas::OfType resultType, + AssertionReaction &reaction ) override; + + bool sectionStarted( SectionInfo const& sectionInfo, Counts& assertions ) override; + + void sectionEnded( SectionEndInfo const& endInfo ) override; + void sectionEndedEarly( SectionEndInfo const& endInfo ) override; + + auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& override; + + void benchmarkStarting( BenchmarkInfo const& info ) override; + void benchmarkEnded( BenchmarkStats const& stats ) override; + + void pushScopedMessage( MessageInfo const& message ) override; + void popScopedMessage( MessageInfo const& message ) override; + + std::string getCurrentTestName() const override; + + const AssertionResult* getLastResult() const override; + + void exceptionEarlyReported() override; + + void handleFatalErrorCondition( StringRef message ) override; + + bool lastAssertionPassed() override; + + void assertionPassed() override; + + public: + // !TBD We need to do this another way! + bool aborting() const final; + + private: + + void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ); + void invokeActiveTestCase(); + + void resetAssertionInfo(); + bool testForMissingAssertions( Counts& assertions ); + + void assertionEnded( AssertionResult const& result ); + void reportExpr + ( AssertionInfo const &info, + ResultWas::OfType resultType, + ITransientExpression const *expr, + bool negated ); + + void populateReaction( AssertionReaction& reaction ); + + private: + + void handleUnfinishedSections(); + + TestRunInfo m_runInfo; + IMutableContext& m_context; + TestCase const* m_activeTestCase = nullptr; + ITracker* m_testCaseTracker; + Option<AssertionResult> m_lastResult; + + IConfigPtr m_config; + Totals m_totals; + IStreamingReporterPtr m_reporter; + std::vector<MessageInfo> m_messages; + AssertionInfo m_lastAssertionInfo; + std::vector<SectionEndInfo> m_unfinishedSections; + std::vector<ITracker*> m_activeSections; + TrackerContext m_trackerContext; + bool m_lastAssertionPassed = false; + bool m_shouldReportUnexpected = true; + bool m_includeSuccessfulResults; + }; + +} // end namespace Catch + +// end catch_run_context.h +namespace Catch { + + namespace { + auto operator <<( std::ostream& os, ITransientExpression const& expr ) -> std::ostream& { + expr.streamReconstructedExpression( os ); + return os; + } + } + + LazyExpression::LazyExpression( bool isNegated ) + : m_isNegated( isNegated ) + {} + + LazyExpression::LazyExpression( LazyExpression const& other ) : m_isNegated( other.m_isNegated ) {} + + LazyExpression::operator bool() const { + return m_transientExpression != nullptr; + } + + 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; + } + + AssertionHandler::AssertionHandler + ( StringRef const& 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 const& 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 const& matcherString ) { + handleExceptionMatchExpr( handler, Matchers::Equals( str ), matcherString ); + } + +} // namespace Catch +// end catch_assertionhandler.cpp +// start catch_assertionresult.cpp + +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[0] != 0; + } + + bool AssertionResult::hasMessage() const { + return !m_resultData.message.empty(); + } + + std::string AssertionResult::getExpression() const { + if( isFalseTest( m_info.resultDisposition ) ) + return "!(" + m_info.capturedExpression + ")"; + else + return m_info.capturedExpression; + } + + std::string AssertionResult::getExpressionInMacro() const { + std::string expr; + if( m_info.macroName[0] == 0 ) + expr = 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; + } + + std::string 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 +// end catch_assertionresult.cpp +// start catch_benchmark.cpp + +namespace Catch { + + auto BenchmarkLooper::getResolution() -> uint64_t { + return getEstimatedClockResolution() * getCurrentContext().getConfig()->benchmarkResolutionMultiple(); + } + + void BenchmarkLooper::reportStart() { + getResultCapture().benchmarkStarting( { m_name } ); + } + auto BenchmarkLooper::needsMoreIterations() -> bool { + auto elapsed = m_timer.getElapsedNanoseconds(); + + // Exponentially increasing iterations until we're confident in our timer resolution + if( elapsed < m_resolution ) { + m_iterationsToRun *= 10; + return true; + } + + getResultCapture().benchmarkEnded( { { m_name }, m_count, elapsed } ); + return false; + } + +} // end namespace Catch +// end catch_benchmark.cpp +// start catch_capture_matchers.cpp + +namespace Catch { + + using StringMatcher = Matchers::Impl::MatcherBase<std::string>; + + // 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 const& matcherString ) { + std::string exceptionMessage = Catch::translateActiveException(); + MatchExpr<std::string, StringMatcher const&> expr( exceptionMessage, matcher, matcherString ); + handler.handleExpr( expr ); + } + +} // namespace Catch +// end catch_capture_matchers.cpp +// start catch_commandline.cpp + +// start catch_commandline.h + +// start catch_clara.h + +// Use Catch's value for console width (store Clara's off to the side, if present) +#ifdef CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#undef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#endif +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH-1 + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wweak-vtables" +#pragma clang diagnostic ignored "-Wexit-time-destructors" +#pragma clang diagnostic ignored "-Wshadow" +#endif + +// start clara.hpp +// Copyright 2017 Two Blue Cubes Ltd. All rights reserved. +// +// 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) +// +// See https://github.com/philsquared/Clara for more details + +// Clara v1.1.5 + + +#ifndef CATCH_CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_CONFIG_CONSOLE_WIDTH 80 +#endif + +#ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CLARA_CONFIG_CONSOLE_WIDTH +#endif + +#ifndef CLARA_CONFIG_OPTIONAL_TYPE +#ifdef __has_include +#if __has_include(<optional>) && __cplusplus >= 201703L +#include <optional> +#define CLARA_CONFIG_OPTIONAL_TYPE std::optional +#endif +#endif +#endif + +// ----------- #included from clara_textflow.hpp ----------- + +// TextFlowCpp +// +// A single-header library for wrapping and laying out basic text, by Phil Nash +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// This project is hosted at https://github.com/philsquared/textflowcpp + + +#include <cassert> +#include <ostream> +#include <sstream> +#include <vector> + +#ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 80 +#endif + +namespace Catch { +namespace clara { +namespace TextFlow { + +inline auto isWhitespace(char c) -> bool { + static std::string chars = " \t\n\r"; + return chars.find(c) != std::string::npos; +} +inline auto isBreakableBefore(char c) -> bool { + static std::string chars = "[({<|"; + return chars.find(c) != std::string::npos; +} +inline auto isBreakableAfter(char c) -> bool { + static std::string chars = "])}>.,:;*+-=&/\\"; + return chars.find(c) != std::string::npos; +} + +class Columns; + +class Column { + std::vector<std::string> m_strings; + size_t m_width = CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH; + size_t m_indent = 0; + size_t m_initialIndent = std::string::npos; + +public: + class iterator { + friend Column; + + Column const& m_column; + size_t m_stringIndex = 0; + size_t m_pos = 0; + + size_t m_len = 0; + size_t m_end = 0; + bool m_suffix = false; + + iterator(Column const& column, size_t stringIndex) + : m_column(column), + m_stringIndex(stringIndex) {} + + auto line() const -> std::string const& { return m_column.m_strings[m_stringIndex]; } + + auto isBoundary(size_t at) const -> bool { + 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]); + } + + void calcLength() { + assert(m_stringIndex < m_column.m_strings.size()); + + m_suffix = false; + auto width = m_column.m_width - indent(); + m_end = m_pos; + while (m_end < line().size() && line()[m_end] != '\n') + ++m_end; + + if (m_end < m_pos + width) { + m_len = m_end - m_pos; + } else { + size_t len = width; + while (len > 0 && !isBoundary(m_pos + len)) + --len; + while (len > 0 && isWhitespace(line()[m_pos + len - 1])) + --len; + + if (len > 0) { + m_len = len; + } else { + m_suffix = true; + m_len = width - 1; + } + } + } + + auto indent() const -> size_t { + auto initial = m_pos == 0 && m_stringIndex == 0 ? m_column.m_initialIndent : std::string::npos; + return initial == std::string::npos ? m_column.m_indent : initial; + } + + auto addIndentAndSuffix(std::string const &plain) const -> std::string { + return std::string(indent(), ' ') + (m_suffix ? plain + "-" : plain); + } + + public: + using difference_type = std::ptrdiff_t; + using value_type = std::string; + using pointer = value_type * ; + using reference = value_type & ; + using iterator_category = std::forward_iterator_tag; + + explicit 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_len == 0) + m_stringIndex++; // Empty string + } + + auto operator *() const -> std::string { + assert(m_stringIndex < m_column.m_strings.size()); + assert(m_pos <= m_end); + return addIndentAndSuffix(line().substr(m_pos, m_len)); + } + + auto operator ++() -> iterator& { + m_pos += m_len; + if (m_pos < line().size() && line()[m_pos] == '\n') + m_pos += 1; + else + while (m_pos < line().size() && isWhitespace(line()[m_pos])) + ++m_pos; + + if (m_pos == line().size()) { + m_pos = 0; + ++m_stringIndex; + } + if (m_stringIndex < m_column.m_strings.size()) + calcLength(); + return *this; + } + auto operator ++(int) -> iterator { + iterator prev(*this); + operator++(); + return prev; + } + + auto operator ==(iterator const& other) const -> bool { + return + m_pos == other.m_pos && + m_stringIndex == other.m_stringIndex && + &m_column == &other.m_column; + } + auto operator !=(iterator const& other) const -> bool { + return !operator==(other); + } + }; + using const_iterator = iterator; + + explicit Column(std::string const& text) { m_strings.push_back(text); } + + auto width(size_t newWidth) -> Column& { + assert(newWidth > 0); + m_width = newWidth; + return *this; + } + auto indent(size_t newIndent) -> Column& { + m_indent = newIndent; + return *this; + } + auto initialIndent(size_t newIndent) -> Column& { + m_initialIndent = newIndent; + return *this; + } + + auto width() const -> size_t { return m_width; } + auto begin() const -> iterator { return iterator(*this); } + auto end() const -> iterator { return { *this, m_strings.size() }; } + + inline friend 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; + } + + auto operator + (Column const& other)->Columns; + + auto toString() const -> std::string { + std::ostringstream oss; + oss << *this; + return oss.str(); + } +}; + +class Spacer : public Column { + +public: + explicit Spacer(size_t spaceWidth) : Column("") { + width(spaceWidth); + } +}; + +class Columns { + std::vector<Column> m_columns; + +public: + + class iterator { + friend Columns; + struct EndTag {}; + + std::vector<Column> const& m_columns; + std::vector<Column::iterator> m_iterators; + size_t m_activeIterators; + + 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()); + } + + public: + using difference_type = std::ptrdiff_t; + using value_type = std::string; + using pointer = value_type * ; + using reference = value_type & ; + using iterator_category = std::forward_iterator_tag; + + explicit 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()); + } + + auto operator ==(iterator const& other) const -> bool { + return m_iterators == other.m_iterators; + } + auto operator !=(iterator const& other) const -> bool { + return m_iterators != other.m_iterators; + } + auto operator *() const -> std::string { + std::string row, padding; + + for (size_t i = 0; i < m_columns.size(); ++i) { + auto width = m_columns[i].width(); + if (m_iterators[i] != m_columns[i].end()) { + std::string col = *m_iterators[i]; + row += padding + col; + if (col.size() < width) + padding = std::string(width - col.size(), ' '); + else + padding = ""; + } else { + padding += std::string(width, ' '); + } + } + return row; + } + auto operator ++() -> iterator& { + for (size_t i = 0; i < m_columns.size(); ++i) { + if (m_iterators[i] != m_columns[i].end()) + ++m_iterators[i]; + } + return *this; + } + auto operator ++(int) -> iterator { + iterator prev(*this); + operator++(); + return prev; + } + }; + using const_iterator = iterator; + + auto begin() const -> iterator { return iterator(*this); } + auto end() const -> iterator { return { *this, iterator::EndTag() }; } + + auto operator += (Column const& col) -> Columns& { + m_columns.push_back(col); + return *this; + } + auto operator + (Column const& col) -> Columns { + Columns combined = *this; + combined += col; + return combined; + } + + inline friend 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; + } + + auto toString() const -> std::string { + std::ostringstream oss; + oss << *this; + return oss.str(); + } +}; + +inline auto Column::operator + (Column const& other) -> Columns { + Columns cols; + cols += *this; + cols += other; + return cols; +} +} + +} +} + +// ----------- end of #include from clara_textflow.hpp ----------- +// ........... back in clara.hpp + +#include <string> +#include <memory> +#include <set> +#include <algorithm> + +#if !defined(CATCH_PLATFORM_WINDOWS) && ( defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) ) +#define CATCH_PLATFORM_WINDOWS +#endif + +namespace Catch { namespace clara { +namespace detail { + + // Traits for extracting arg and return type of lambdas (for single argument lambdas) + template<typename L> + struct UnaryLambdaTraits : UnaryLambdaTraits<decltype( &L::operator() )> {}; + + template<typename ClassT, typename ReturnT, typename... Args> + struct UnaryLambdaTraits<ReturnT( ClassT::* )( Args... ) const> { + static const bool isValid = false; + }; + + template<typename ClassT, typename ReturnT, typename ArgT> + struct UnaryLambdaTraits<ReturnT( ClassT::* )( ArgT ) const> { + static const bool isValid = true; + using ArgType = typename std::remove_const<typename std::remove_reference<ArgT>::type>::type; + using ReturnType = ReturnT; + }; + + class TokenStream; + + // Transport for raw args (copied from main args, or supplied via init list for testing) + class Args { + friend TokenStream; + std::string m_exeName; + std::vector<std::string> m_args; + + public: + Args( int argc, char const* const* argv ) + : m_exeName(argv[0]), + m_args(argv + 1, argv + argc) {} + + Args( std::initializer_list<std::string> args ) + : m_exeName( *args.begin() ), + m_args( args.begin()+1, args.end() ) + {} + + auto exeName() const -> std::string { + return m_exeName; + } + }; + + // 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; + }; + + inline auto isOptPrefix( char c ) -> bool { + return c == '-' +#ifdef CATCH_PLATFORM_WINDOWS + || c == '/' +#endif + ; + } + + // Abstracts iterators into args as a stream of tokens, with option arguments uniformly handled + class TokenStream { + using Iterator = std::vector<std::string>::const_iterator; + Iterator it; + Iterator itEnd; + std::vector<Token> m_tokenBuffer; + + void loadBuffer() { + m_tokenBuffer.resize( 0 ); + + // 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 } ); + } + } + } + + public: + explicit TokenStream( Args const &args ) : TokenStream( args.m_args.begin(), args.m_args.end() ) {} + + TokenStream( Iterator it, Iterator itEnd ) : it( it ), itEnd( itEnd ) { + loadBuffer(); + } + + explicit operator bool() const { + return !m_tokenBuffer.empty() || it != itEnd; + } + + auto count() const -> size_t { return m_tokenBuffer.size() + (itEnd - it); } + + auto operator*() const -> Token { + assert( !m_tokenBuffer.empty() ); + return m_tokenBuffer.front(); + } + + auto operator->() const -> Token const * { + assert( !m_tokenBuffer.empty() ); + return &m_tokenBuffer.front(); + } + + auto operator++() -> TokenStream & { + if( m_tokenBuffer.size() >= 2 ) { + m_tokenBuffer.erase( m_tokenBuffer.begin() ); + } else { + if( it != itEnd ) + ++it; + loadBuffer(); + } + return *this; + } + }; + + class ResultBase { + public: + enum Type { + Ok, LogicError, RuntimeError + }; + + protected: + ResultBase( Type type ) : m_type( type ) {} + virtual ~ResultBase() = default; + + virtual void enforceOk() const = 0; + + Type m_type; + }; + + template<typename T> + class ResultValueBase : public ResultBase { + public: + auto value() const -> T const & { + enforceOk(); + return m_value; + } + + protected: + ResultValueBase( Type type ) : ResultBase( type ) {} + + ResultValueBase( ResultValueBase const &other ) : ResultBase( other ) { + if( m_type == ResultBase::Ok ) + new( &m_value ) T( other.m_value ); + } + + ResultValueBase( Type, T const &value ) : ResultBase( Ok ) { + new( &m_value ) T( value ); + } + + auto operator=( ResultValueBase const &other ) -> ResultValueBase & { + if( m_type == ResultBase::Ok ) + m_value.~T(); + ResultBase::operator=(other); + if( m_type == ResultBase::Ok ) + new( &m_value ) T( other.m_value ); + return *this; + } + + ~ResultValueBase() override { + if( m_type == Ok ) + m_value.~T(); + } + + union { + T m_value; + }; + }; + + template<> + class ResultValueBase<void> : public ResultBase { + protected: + using ResultBase::ResultBase; + }; + + template<typename T = void> + class BasicResult : public ResultValueBase<T> { + public: + template<typename U> + explicit BasicResult( BasicResult<U> const &other ) + : ResultValueBase<T>( other.type() ), + m_errorMessage( other.errorMessage() ) + { + assert( type() != ResultBase::Ok ); + } + + template<typename U> + static auto ok( U const &value ) -> BasicResult { return { ResultBase::Ok, value }; } + static auto ok() -> BasicResult { return { ResultBase::Ok }; } + static auto logicError( std::string const &message ) -> BasicResult { return { ResultBase::LogicError, message }; } + static auto runtimeError( std::string const &message ) -> BasicResult { return { ResultBase::RuntimeError, message }; } + + explicit operator bool() const { return m_type == ResultBase::Ok; } + auto type() const -> ResultBase::Type { return m_type; } + auto errorMessage() const -> std::string { 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 != ResultBase::LogicError ); + assert( m_type != ResultBase::RuntimeError ); + if( m_type != ResultBase::Ok ) + std::abort(); + } + + std::string m_errorMessage; // Only populated if resultType is an error + + BasicResult( ResultBase::Type type, std::string const &message ) + : ResultValueBase<T>(type), + m_errorMessage(message) + { + assert( m_type != ResultBase::Ok ); + } + + using ResultValueBase<T>::ResultValueBase; + using ResultBase::m_type; + }; + + enum class ParseResultType { + Matched, NoMatch, ShortCircuitAll, ShortCircuitSame + }; + + class ParseState { + public: + + ParseState( ParseResultType type, TokenStream const &remainingTokens ) + : m_type(type), + m_remainingTokens( remainingTokens ) + {} + + auto type() const -> ParseResultType { return m_type; } + auto remainingTokens() const -> TokenStream { return m_remainingTokens; } + + private: + ParseResultType m_type; + TokenStream m_remainingTokens; + }; + + using Result = BasicResult<void>; + using ParserResult = BasicResult<ParseResultType>; + using InternalParseResult = BasicResult<ParseState>; + + struct HelpColumns { + std::string left; + std::string right; + }; + + template<typename T> + inline auto convertInto( std::string const &source, T& target ) -> ParserResult { + std::stringstream ss; + ss << source; + ss >> target; + if( ss.fail() ) + return ParserResult::runtimeError( "Unable to convert '" + source + "' to destination type" ); + else + return ParserResult::ok( ParseResultType::Matched ); + } + inline auto convertInto( std::string const &source, std::string& target ) -> ParserResult { + target = source; + return ParserResult::ok( ParseResultType::Matched ); + } + inline auto convertInto( std::string const &source, bool &target ) -> ParserResult { + std::string srcLC = source; + std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( char c ) { return static_cast<char>( ::tolower(c) ); } ); + 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 ); + } +#ifdef CLARA_CONFIG_OPTIONAL_TYPE + template<typename T> + inline auto convertInto( std::string const &source, CLARA_CONFIG_OPTIONAL_TYPE<T>& target ) -> ParserResult { + T temp; + auto result = convertInto( source, temp ); + if( result ) + target = std::move(temp); + return result; + } +#endif // CLARA_CONFIG_OPTIONAL_TYPE + + struct NonCopyable { + NonCopyable() = default; + NonCopyable( NonCopyable const & ) = delete; + NonCopyable( NonCopyable && ) = delete; + NonCopyable &operator=( NonCopyable const & ) = delete; + NonCopyable &operator=( NonCopyable && ) = delete; + }; + + struct BoundRef : NonCopyable { + virtual ~BoundRef() = default; + virtual auto isContainer() const -> bool { return false; } + virtual auto isFlag() const -> bool { return false; } + }; + struct BoundValueRefBase : BoundRef { + virtual auto setValue( std::string const &arg ) -> ParserResult = 0; + }; + struct BoundFlagRefBase : BoundRef { + virtual auto setFlag( bool flag ) -> ParserResult = 0; + virtual auto isFlag() const -> bool { return true; } + }; + + template<typename T> + struct BoundValueRef : BoundValueRefBase { + T &m_ref; + + explicit BoundValueRef( T &ref ) : m_ref( ref ) {} + + auto setValue( std::string const &arg ) -> ParserResult override { + return convertInto( arg, m_ref ); + } + }; + + template<typename T> + struct BoundValueRef<std::vector<T>> : BoundValueRefBase { + std::vector<T> &m_ref; + + explicit BoundValueRef( std::vector<T> &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 ) {} + + auto setFlag( bool flag ) -> ParserResult override { + m_ref = flag; + return ParserResult::ok( ParseResultType::Matched ); + } + }; + + template<typename ReturnType> + struct LambdaInvoker { + static_assert( std::is_same<ReturnType, ParserResult>::value, "Lambda must return void or clara::ParserResult" ); + + template<typename L, typename ArgType> + static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { + return lambda( arg ); + } + }; + + template<> + struct LambdaInvoker<void> { + template<typename L, typename ArgType> + static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { + lambda( arg ); + return ParserResult::ok( ParseResultType::Matched ); + } + }; + + template<typename ArgType, typename L> + inline auto invokeLambda( L const &lambda, std::string const &arg ) -> ParserResult { + ArgType temp{}; + auto result = convertInto( arg, temp ); + return !result + ? result + : LambdaInvoker<typename UnaryLambdaTraits<L>::ReturnType>::invoke( lambda, temp ); + } + + template<typename L> + struct BoundLambda : BoundValueRefBase { + L m_lambda; + + static_assert( UnaryLambdaTraits<L>::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<typename UnaryLambdaTraits<L>::ArgType>( m_lambda, arg ); + } + }; + + template<typename L> + struct BoundFlagLambda : BoundFlagRefBase { + L m_lambda; + + static_assert( UnaryLambdaTraits<L>::isValid, "Supplied lambda must take exactly one argument" ); + static_assert( std::is_same<typename UnaryLambdaTraits<L>::ArgType, bool>::value, "flags must be boolean" ); + + explicit BoundFlagLambda( L const &lambda ) : m_lambda( lambda ) {} + + auto setFlag( bool flag ) -> ParserResult override { + return LambdaInvoker<typename UnaryLambdaTraits<L>::ReturnType>::invoke( m_lambda, flag ); + } + }; + + enum class Optionality { Optional, Required }; + + struct Parser; + + 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 auto cardinality() const -> size_t { return 1; } + + auto parse( Args const &args ) const -> InternalParseResult { + return parse( args.exeName(), TokenStream( args ) ); + } + }; + + template<typename DerivedT> + class ComposableParserImpl : public ParserBase { + public: + template<typename T> + auto operator|( T const &other ) const -> Parser; + + template<typename T> + auto operator+( T const &other ) const -> Parser; + }; + + // Common code and state for Args and Opts + template<typename DerivedT> + class ParserRefImpl : public ComposableParserImpl<DerivedT> { + protected: + Optionality m_optionality = Optionality::Optional; + std::shared_ptr<BoundRef> m_ref; + std::string m_hint; + std::string m_description; + + explicit ParserRefImpl( std::shared_ptr<BoundRef> const &ref ) : m_ref( ref ) {} + + public: + template<typename T> + ParserRefImpl( T &ref, std::string const &hint ) + : m_ref( std::make_shared<BoundValueRef<T>>( ref ) ), + m_hint( hint ) + {} + + template<typename LambdaT> + ParserRefImpl( LambdaT const &ref, std::string const &hint ) + : m_ref( std::make_shared<BoundLambda<LambdaT>>( ref ) ), + m_hint(hint) + {} + + auto operator()( std::string const &description ) -> DerivedT & { + m_description = description; + return static_cast<DerivedT &>( *this ); + } + + auto optional() -> DerivedT & { + m_optionality = Optionality::Optional; + return static_cast<DerivedT &>( *this ); + }; + + auto required() -> DerivedT & { + m_optionality = Optionality::Required; + return static_cast<DerivedT &>( *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; + } + + auto hint() const -> std::string { return m_hint; } + }; + + class ExeName : public ComposableParserImpl<ExeName> { + std::shared_ptr<std::string> m_name; + std::shared_ptr<BoundValueRefBase> m_ref; + + template<typename LambdaT> + static auto makeRef(LambdaT const &lambda) -> std::shared_ptr<BoundValueRefBase> { + return std::make_shared<BoundLambda<LambdaT>>( lambda) ; + } + + public: + ExeName() : m_name( std::make_shared<std::string>( "<executable>" ) ) {} + + explicit ExeName( std::string &ref ) : ExeName() { + m_ref = std::make_shared<BoundValueRef<std::string>>( ref ); + } + + template<typename LambdaT> + explicit ExeName( LambdaT const& lambda ) : ExeName() { + m_ref = std::make_shared<BoundLambda<LambdaT>>( lambda ); + } + + // The exe name is not parsed out of the normal tokens, but is handled specially + auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { + return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); + } + + auto name() const -> std::string { return *m_name; } + auto set( std::string const& newName ) -> ParserResult { + + 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 ); + } + }; + + class Arg : public ParserRefImpl<Arg> { + public: + using ParserRefImpl::ParserRefImpl; + + auto parse( std::string const &, TokenStream const &tokens ) const -> InternalParseResult override { + auto validationResult = validate(); + if( !validationResult ) + return InternalParseResult( validationResult ); + + auto remainingTokens = tokens; + auto const &token = *remainingTokens; + if( token.type != TokenType::Argument ) + return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); + + assert( !m_ref->isFlag() ); + auto valueRef = static_cast<detail::BoundValueRefBase*>( m_ref.get() ); + + auto result = valueRef->setValue( remainingTokens->token ); + if( !result ) + return InternalParseResult( result ); + else + return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); + } + }; + + inline auto normaliseOpt( std::string const &optName ) -> std::string { +#ifdef CATCH_PLATFORM_WINDOWS + if( optName[0] == '/' ) + return "-" + optName.substr( 1 ); + else +#endif + return optName; + } + + class Opt : public ParserRefImpl<Opt> { + protected: + std::vector<std::string> m_optNames; + + public: + template<typename LambdaT> + explicit Opt( LambdaT const &ref ) : ParserRefImpl( std::make_shared<BoundFlagLambda<LambdaT>>( ref ) ) {} + + explicit Opt( bool &ref ) : ParserRefImpl( std::make_shared<BoundFlagRef>( ref ) ) {} + + template<typename LambdaT> + Opt( LambdaT const &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} + + template<typename T> + Opt( T &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} + + auto operator[]( std::string const &optName ) -> Opt & { + m_optNames.push_back( optName ); + return *this; + } + + auto getHelpColumns() const -> std::vector<HelpColumns> { + 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 } }; + } + + auto isMatch( std::string const &optToken ) const -> bool { + auto normalisedToken = normaliseOpt( optToken ); + for( auto const &name : m_optNames ) { + if( normaliseOpt( name ) == normalisedToken ) + return true; + } + return false; + } + + using ParserBase::parse; + + auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { + auto validationResult = validate(); + if( !validationResult ) + return InternalParseResult( validationResult ); + + auto remainingTokens = tokens; + if( remainingTokens && remainingTokens->type == TokenType::Option ) { + auto const &token = *remainingTokens; + if( isMatch(token.token ) ) { + if( m_ref->isFlag() ) { + auto flagRef = static_cast<detail::BoundFlagRefBase*>( m_ref.get() ); + auto result = flagRef->setFlag( true ); + if( !result ) + return InternalParseResult( result ); + if( result.value() == ParseResultType::ShortCircuitAll ) + return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); + } else { + auto valueRef = static_cast<detail::BoundValueRefBase*>( m_ref.get() ); + ++remainingTokens; + if( !remainingTokens ) + return InternalParseResult::runtimeError( "Expected argument following " + token.token ); + auto const &argToken = *remainingTokens; + if( argToken.type != TokenType::Argument ) + return InternalParseResult::runtimeError( "Expected argument following " + token.token ); + auto result = valueRef->setValue( argToken.token ); + if( !result ) + return InternalParseResult( result ); + if( result.value() == ParseResultType::ShortCircuitAll ) + return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); + } + return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); + } + } + return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); + } + + auto validate() const -> Result override { + if( m_optNames.empty() ) + return Result::logicError( "No options supplied to Opt" ); + for( auto const &name : m_optNames ) { + if( name.empty() ) + return Result::logicError( "Option name cannot be empty" ); +#ifdef CATCH_PLATFORM_WINDOWS + if( name[0] != '-' && name[0] != '/' ) + return Result::logicError( "Option name must begin with '-' or '/'" ); +#else + if( name[0] != '-' ) + return Result::logicError( "Option name must begin with '-'" ); +#endif + } + return ParserRefImpl::validate(); + } + }; + + struct Help : Opt { + Help( bool &showHelpFlag ) + : Opt([&]( bool flag ) { + showHelpFlag = flag; + return ParserResult::ok( ParseResultType::ShortCircuitAll ); + }) + { + static_cast<Opt &>( *this ) + ("display usage information") + ["-?"]["-h"]["--help"] + .optional(); + } + }; + + struct Parser : ParserBase { + + mutable ExeName m_exeName; + std::vector<Opt> m_options; + std::vector<Arg> m_args; + + 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; + } + + auto operator|=( Parser const &other ) -> Parser & { + 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; + } + + template<typename T> + auto operator|( T const &other ) const -> Parser { + return Parser( *this ) |= other; + } + + // Forward deprecated interface with '+' instead of '|' + template<typename T> + auto operator+=( T const &other ) -> Parser & { return operator|=( other ); } + template<typename T> + auto operator+( T const &other ) const -> Parser { return operator|( other ); } + + auto getHelpColumns() const -> std::vector<HelpColumns> { + std::vector<HelpColumns> cols; + for (auto const &o : m_options) { + auto childCols = o.getHelpColumns(); + cols.insert( cols.end(), childCols.begin(), childCols.end() ); + } + return cols; + } + + void 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:" << std::endl; + } + + auto rows = getHelpColumns(); + size_t consoleWidth = CATCH_CLARA_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 << std::endl; + } + } + + friend auto operator<<( std::ostream &os, Parser const &parser ) -> std::ostream& { + parser.writeToStream( os ); + return os; + } + + auto validate() const -> Result override { + 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 Result::ok(); + } + + using ParserBase::parse; + + auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override { + + struct ParserInfo { + ParserBase const* parser = nullptr; + size_t count = 0; + }; + const size_t totalParsers = m_options.size() + m_args.size(); + assert( totalParsers < 512 ); + // ParserInfo parseInfos[totalParsers]; // <-- this is what we really want to do + ParserInfo parseInfos[512]; + + { + size_t i = 0; + for (auto const &opt : m_options) parseInfos[i++].parser = &opt; + for (auto const &arg : m_args) parseInfos[i++].parser = &arg; + } + + m_exeName.set( exeName ); + + auto result = InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); + while( result.value().remainingTokens() ) { + bool tokenParsed = false; + + for( size_t i = 0; i < totalParsers; ++i ) { + auto& parseInfo = parseInfos[i]; + 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 InternalParseResult::runtimeError( "Unrecognised token: " + result.value().remainingTokens()->token ); + } + // !TBD Check missing required options + return result; + } + }; + + template<typename DerivedT> + template<typename T> + auto ComposableParserImpl<DerivedT>::operator|( T const &other ) const -> Parser { + return Parser() | static_cast<DerivedT const &>( *this ) | other; + } +} // namespace detail + +// A Combined parser +using detail::Parser; + +// A parser for options +using detail::Opt; + +// A parser for arguments +using detail::Arg; + +// Wrapper for argc, argv from main() +using detail::Args; + +// Specifies the name of the executable +using detail::ExeName; + +// Convenience wrapper for option parser that specifies the help option +using detail::Help; + +// enum of result types from a parse +using detail::ParseResultType; + +// Result type for parser operation +using detail::ParserResult; + +}} // namespace Catch::clara + +// end clara.hpp +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// Restore Clara's value for console width, if present +#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#endif + +// end catch_clara.h +namespace Catch { + + clara::Parser makeCommandLineParser( ConfigData& config ); + +} // end namespace Catch + +// end catch_commandline.h +#include <fstream> +#include <ctime> + +namespace Catch { + + clara::Parser makeCommandLineParser( ConfigData& config ) { + + using namespace clara; + + auto const setWarning = [&]( std::string const& warning ) { + auto warningSet = [&]() { + if( warning == "NoAssertions" ) + return WarnAbout::NoAssertions; + + if ( warning == "NoTests" ) + return WarnAbout::NoTests; + + return WarnAbout::Nothing; + }(); + + if (warningSet == WarnAbout::Nothing) + return ParserResult::runtimeError( "Unrecognised warning: '" + warning + "'" ); + config.warnings = static_cast<WarnAbout::What>( config.warnings | warningSet ); + return ParserResult::ok( ParseResultType::Matched ); + }; + 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 + ',' ); + } + } + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setTestOrder = [&]( std::string const& order ) { + if( startsWith( "declared", order ) ) + config.runOrder = RunTests::InDeclarationOrder; + else if( startsWith( "lexical", order ) ) + config.runOrder = RunTests::InLexicographicalOrder; + else if( startsWith( "random", order ) ) + config.runOrder = RunTests::InRandomOrder; + else + return clara::ParserResult::runtimeError( "Unrecognised ordering: '" + order + "'" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setRngSeed = [&]( std::string const& seed ) { + if( seed != "time" ) + return clara::detail::convertInto( seed, config.rngSeed ); + config.rngSeed = static_cast<unsigned int>( std::time(nullptr) ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setColourUsage = [&]( std::string const& useColour ) { + auto mode = toLower( useColour ); + + if( mode == "yes" ) + config.useColour = UseColour::Yes; + else if( mode == "no" ) + config.useColour = UseColour::No; + else if( mode == "auto" ) + config.useColour = UseColour::Auto; + else + return ParserResult::runtimeError( "colour mode must be one of: auto, yes or no. '" + useColour + "' not recognised" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setWaitForKeypress = [&]( std::string const& keypress ) { + auto 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 + return ParserResult::runtimeError( "keypress argument must be one of: 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& reporter ) { + IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); + + auto lcReporter = toLower( reporter ); + auto result = factories.find( lcReporter ); + + if( factories.end() != result ) + config.reporterName = lcReporter; + else + return ParserResult::runtimeError( "Unrecognized reporter, '" + reporter + "'. Check available with --list-reporters" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + + auto cli + = ExeName( config.processName ) + | Help( config.showHelp ) + | Opt( config.listTests ) + ["-l"]["--list-tests"] + ( "list all/matching test cases" ) + | Opt( config.listTags ) + ["-t"]["--list-tags"] + ( "list all/matching tags" ) + | 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.outputFilename, "filename" ) + ["-o"]["--out"] + ( "output filename" ) + | Opt( setReporter, "name" ) + ["-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( 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( 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.listTestNamesOnly ) + ["--list-test-names-only"] + ( "list all/matching test cases names only" ) + | Opt( config.listReporters ) + ["--list-reporters"] + ( "list all reporters" ) + | Opt( setTestOrder, "decl|lex|rand" ) + ["--order"] + ( "test case order (defaults to decl)" ) + | Opt( setRngSeed, "'time'|number" ) + ["--rng-seed"] + ( "set a specific seed for random numbers" ) + | Opt( setColourUsage, "yes|no" ) + ["--use-colour"] + ( "should output be colourised" ) + | Opt( config.libIdentify ) + ["--libidentify"] + ( "report name and version according to libidentify standard" ) + | Opt( setWaitForKeypress, "start|exit|both" ) + ["--wait-for-keypress"] + ( "waits for a keypress before exiting" ) + | Opt( config.benchmarkResolutionMultiple, "multiplier" ) + ["--benchmark-resolution-multiple"] + ( "multiple of clock resolution to run benchmarks" ) + + | Arg( config.testsOrTags, "test name|pattern|tags" ) + ( "which test or tests to use" ); + + return cli; + } + +} // end namespace Catch +// end catch_commandline.cpp +// start catch_common.cpp + +#include <cstring> +#include <ostream> + +namespace Catch { + + bool SourceLineInfo::empty() const noexcept { + return file[0] == '\0'; + } + 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; + } + + std::string StreamEndStop::operator+() const { + return std::string(); + } + + NonCopyable::NonCopyable() = default; + NonCopyable::~NonCopyable() = default; + +} +// end catch_common.cpp +// start catch_config.cpp + +namespace Catch { + + Config::Config( ConfigData const& data ) + : m_data( data ), + m_stream( openStream() ) + { + TestSpecParser parser(ITagAliasRegistry::get()); + if (data.testsOrTags.empty()) { + parser.parse("~[.]"); // All not hidden tests + } + else { + m_hasTestFilters = true; + for( auto const& testOrTags : data.testsOrTags ) + parser.parse( testOrTags ); + } + m_testSpec = parser.testSpec(); + } + + std::string const& Config::getFilename() const { + return m_data.outputFilename ; + } + + bool Config::listTests() const { return m_data.listTests; } + bool Config::listTestNamesOnly() const { return m_data.listTestNamesOnly; } + bool Config::listTags() const { return m_data.listTags; } + bool Config::listReporters() const { return m_data.listReporters; } + + std::string Config::getProcessName() const { return m_data.processName; } + std::string const& Config::getReporterName() const { return m_data.reporterName; } + + std::vector<std::string> const& Config::getTestsOrTags() const { return m_data.testsOrTags; } + std::vector<std::string> const& Config::getSectionsToRun() const { return m_data.sectionsToRun; } + + 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; } + std::ostream& Config::stream() const { return m_stream->stream(); } + std::string 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::warnAboutNoTests() const { return !!(m_data.warnings & WarnAbout::NoTests); } + ShowDurations::OrNot Config::showDurations() const { return m_data.showDurations; } + RunTests::InWhatOrder Config::runOrder() const { return m_data.runOrder; } + unsigned int Config::rngSeed() const { return m_data.rngSeed; } + int Config::benchmarkResolutionMultiple() const { return m_data.benchmarkResolutionMultiple; } + UseColour::YesOrNo Config::useColour() const { return m_data.useColour; } + 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; } + + IStream const* Config::openStream() { + return Catch::makeStream(m_data.outputFilename); + } + +} // end namespace Catch +// end catch_config.cpp +// start catch_console_colour.cpp + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +#endif + +// start catch_errno_guard.h + +namespace Catch { + + class ErrnoGuard { + public: + ErrnoGuard(); + ~ErrnoGuard(); + private: + int m_oldErrno; + }; + +} + +// end catch_errno_guard.h +#include <sstream> + +namespace Catch { + namespace { + + struct IColourImpl { + virtual ~IColourImpl() = default; + virtual void use( Colour::Code _colourCode ) = 0; + }; + + struct NoColourImpl : IColourImpl { + void use( Colour::Code ) {} + + static IColourImpl* instance() { + static NoColourImpl s_instance; + return &s_instance; + } + }; + + } // anon namespace +} // namespace Catch + +#if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI ) +# ifdef CATCH_PLATFORM_WINDOWS +# define CATCH_CONFIG_COLOUR_WINDOWS +# else +# define CATCH_CONFIG_COLOUR_ANSI +# endif +#endif + +#if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) ///////////////////////////////////////// + +namespace Catch { +namespace { + + class Win32ColourImpl : public IColourImpl { + public: + Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) ) + { + CONSOLE_SCREEN_BUFFER_INFO csbiInfo; + GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo ); + originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY ); + originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY ); + } + + virtual void use( Colour::Code _colourCode ) 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" ); + } + } + + private: + void setTextAttribute( WORD _textAttribute ) { + SetConsoleTextAttribute( stdoutHandle, _textAttribute | originalBackgroundAttributes ); + } + HANDLE stdoutHandle; + WORD originalForegroundAttributes; + WORD originalBackgroundAttributes; + }; + + IColourImpl* platformColourInstance() { + static Win32ColourImpl s_instance; + + IConfigPtr config = getCurrentContext().getConfig(); + UseColour::YesOrNo colourMode = config + ? config->useColour() + : UseColour::Auto; + if( colourMode == UseColour::Auto ) + colourMode = UseColour::Yes; + return colourMode == UseColour::Yes + ? &s_instance + : NoColourImpl::instance(); + } + +} // end anon namespace +} // end namespace Catch + +#elif defined( CATCH_CONFIG_COLOUR_ANSI ) ////////////////////////////////////// + +#include <unistd.h> + +namespace Catch { +namespace { + + // use POSIX/ ANSI console terminal codes + // Thanks to Adam Strzelecki for original contribution + // (http://github.com/nanoant) + // https://github.com/philsquared/Catch/pull/131 + class PosixColourImpl : public IColourImpl { + public: + virtual void use( Colour::Code _colourCode ) override { + 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" ); + } + } + static IColourImpl* instance() { + static PosixColourImpl s_instance; + return &s_instance; + } + + private: + void setColour( const char* _escapeCode ) { + Catch::cout() << '\033' << _escapeCode; + } + }; + + bool useColourOnPlatform() { + return +#ifdef CATCH_PLATFORM_MAC + !isDebuggerActive() && +#endif +#if !(defined(__DJGPP__) && defined(__STRICT_ANSI__)) + isatty(STDOUT_FILENO) +#else + false +#endif + ; + } + IColourImpl* platformColourInstance() { + ErrnoGuard guard; + IConfigPtr config = getCurrentContext().getConfig(); + UseColour::YesOrNo colourMode = config + ? config->useColour() + : UseColour::Auto; + if( colourMode == UseColour::Auto ) + colourMode = useColourOnPlatform() + ? UseColour::Yes + : UseColour::No; + return colourMode == UseColour::Yes + ? PosixColourImpl::instance() + : NoColourImpl::instance(); + } + +} // end anon namespace +} // end namespace Catch + +#else // not Windows or ANSI /////////////////////////////////////////////// + +namespace Catch { + + static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); } + +} // end namespace Catch + +#endif // Windows/ ANSI/ None + +namespace Catch { + + Colour::Colour( Code _colourCode ) { use( _colourCode ); } + Colour::Colour( Colour&& rhs ) noexcept { + m_moved = rhs.m_moved; + rhs.m_moved = true; + } + Colour& Colour::operator=( Colour&& rhs ) noexcept { + m_moved = rhs.m_moved; + rhs.m_moved = true; + return *this; + } + + Colour::~Colour(){ if( !m_moved ) use( None ); } + + void Colour::use( Code _colourCode ) { + static IColourImpl* impl = platformColourInstance(); + impl->use( _colourCode ); + } + + std::ostream& operator << ( std::ostream& os, Colour const& ) { + return os; + } + +} // end namespace Catch + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif + +// end catch_console_colour.cpp +// start catch_context.cpp + +namespace Catch { + + class Context : public IMutableContext, NonCopyable { + + public: // IContext + virtual IResultCapture* getResultCapture() override { + return m_resultCapture; + } + virtual IRunner* getRunner() override { + return m_runner; + } + + virtual IConfigPtr const& getConfig() const override { + return m_config; + } + + virtual ~Context() override; + + public: // IMutableContext + virtual void setResultCapture( IResultCapture* resultCapture ) override { + m_resultCapture = resultCapture; + } + virtual void setRunner( IRunner* runner ) override { + m_runner = runner; + } + virtual void setConfig( IConfigPtr const& config ) override { + m_config = config; + } + + friend IMutableContext& getCurrentMutableContext(); + + private: + IConfigPtr m_config; + IRunner* m_runner = 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; +} +// end catch_context.cpp +// start catch_debug_console.cpp + +// start catch_debug_console.h + +#include <string> + +namespace Catch { + void writeToDebugConsole( std::string const& text ); +} + +// end catch_debug_console.h +#ifdef 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 +// end catch_debug_console.cpp +// start catch_debugger.cpp + +#ifdef CATCH_PLATFORM_MAC + +# include <assert.h> +# include <stdbool.h> +# include <sys/types.h> +# include <unistd.h> +# include <sys/sysctl.h> +# include <cstddef> +# include <ostream> + +namespace Catch { + + // The following function is taken directly from the following technical note: + // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.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" << std::endl; + return false; + } + + // We're being debugged if the P_TRACED flag is set. + + return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); + } + } // namespace Catch + +#elif defined(CATCH_PLATFORM_LINUX) + #include <fstream> + #include <string> + + 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 +// end catch_debugger.cpp +// start catch_decomposer.cpp + +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; + } +} +// end catch_decomposer.cpp +// start catch_enforce.cpp + +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 +} // namespace Catch; +// end catch_enforce.cpp +// start catch_errno_guard.cpp + +#include <cerrno> + +namespace Catch { + ErrnoGuard::ErrnoGuard():m_oldErrno(errno){} + ErrnoGuard::~ErrnoGuard() { errno = m_oldErrno; } +} +// end catch_errno_guard.cpp +// start catch_exception_translator_registry.cpp + +// start catch_exception_translator_registry.h + +#include <vector> +#include <string> +#include <memory> + +namespace Catch { + + class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { + public: + ~ExceptionTranslatorRegistry(); + virtual void registerTranslator( const IExceptionTranslator* translator ); + virtual std::string translateActiveException() const override; + std::string tryTranslators() const; + + private: + std::vector<std::unique_ptr<IExceptionTranslator const>> m_translators; + }; +} + +// end catch_exception_translator_registry.h +#ifdef __OBJC__ +#import "Foundation/Foundation.h" +#endif + +namespace Catch { + + ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() { + } + + void ExceptionTranslatorRegistry::registerTranslator( const IExceptionTranslator* translator ) { + m_translators.push_back( std::unique_ptr<const IExceptionTranslator>( translator ) ); + } + +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + std::string ExceptionTranslatorRegistry::translateActiveException() const { + try { +#ifdef __OBJC__ + // In Objective-C try objective-c exceptions first + @try { + return tryTranslators(); + } + @catch (NSException *exception) { + return Catch::Detail::stringify( [exception description] ); + } +#else + // Compiling a mixed mode project with MSVC means that CLR + // exceptions will be caught in (...) as well. However, these + // 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."; + } + return tryTranslators(); +#endif + } + catch( TestFailureException& ) { + std::rethrow_exception(std::current_exception()); + } + catch( std::exception& ex ) { + return ex.what(); + } + catch( std::string& msg ) { + return msg; + } + catch( const char* msg ) { + return msg; + } + catch(...) { + return "Unknown exception"; + } + } + +#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!"); + } +#endif + + 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() ); + } +} +// end catch_exception_translator_registry.cpp +// start catch_fatal_condition.cpp + +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif + +#if defined( CATCH_CONFIG_WINDOWS_SEH ) || defined( CATCH_CONFIG_POSIX_SIGNALS ) + +namespace { + // Report the error condition + void reportFatal( char const * const message ) { + Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message ); + } +} + +#endif // signals/SEH handling + +#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" }, + }; + + LONG CALLBACK FatalConditionHandler::handleVectoredException(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; + } + + FatalConditionHandler::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 = nullptr; + // Register as first handler in current chain + exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException); + // Pass in guarantee size to be filled + SetThreadStackGuarantee(&guaranteeSize); + } + + void FatalConditionHandler::reset() { + if (isSet) { + RemoveVectoredExceptionHandler(exceptionHandlerHandle); + SetThreadStackGuarantee(&guaranteeSize); + exceptionHandlerHandle = nullptr; + isSet = false; + } + } + + FatalConditionHandler::~FatalConditionHandler() { + reset(); + } + +bool FatalConditionHandler::isSet = false; +ULONG FatalConditionHandler::guaranteeSize = 0; +PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr; + +} // namespace Catch + +#elif defined( CATCH_CONFIG_POSIX_SIGNALS ) + +namespace Catch { + + struct SignalDefs { + int id; + const char* name; + }; + + // 32kb for the alternate stack seems to be sufficient. However, this value + // is experimentally determined, so that's not guaranteed. + constexpr static std::size_t sigStackSize = 32768 >= MINSIGSTKSZ ? 32768 : MINSIGSTKSZ; + + 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" } + }; + + void FatalConditionHandler::handleSignal( int sig ) { + char const * name = "<unknown signal>"; + for (auto const& def : signalDefs) { + if (sig == def.id) { + name = def.name; + break; + } + } + reset(); + reportFatal(name); + raise( sig ); + } + + FatalConditionHandler::FatalConditionHandler() { + isSet = true; + stack_t sigStack; + sigStack.ss_sp = altStackMem; + sigStack.ss_size = sigStackSize; + 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]); + } + } + + FatalConditionHandler::~FatalConditionHandler() { + reset(); + } + + void FatalConditionHandler::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], nullptr); + } + // Return the old stack + sigaltstack(&oldSigStack, nullptr); + isSet = false; + } + } + + bool FatalConditionHandler::isSet = false; + struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {}; + stack_t FatalConditionHandler::oldSigStack = {}; + char FatalConditionHandler::altStackMem[sigStackSize] = {}; + +} // namespace Catch + +#else + +namespace Catch { + void FatalConditionHandler::reset() {} +} + +#endif // signals/SEH handling + +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif +// end catch_fatal_condition.cpp +// start catch_generators.cpp + +// start catch_random_number_generator.h + +#include <algorithm> +#include <random> + +namespace Catch { + + struct IConfig; + + std::mt19937& rng(); + void seedRng( IConfig const& config ); + unsigned int rngSeed(); + +} + +// end catch_random_number_generator.h +#include <limits> +#include <set> + +namespace Catch { + +IGeneratorTracker::~IGeneratorTracker() {} + +namespace Generators { + + GeneratorBase::~GeneratorBase() {} + + std::vector<size_t> randomiseIndices( size_t selectionSize, size_t sourceSize ) { + + assert( selectionSize <= sourceSize ); + std::vector<size_t> indices; + indices.reserve( selectionSize ); + std::uniform_int_distribution<size_t> uid( 0, sourceSize-1 ); + + std::set<size_t> seen; + // !TBD: improve this algorithm + while( indices.size() < selectionSize ) { + auto index = uid( rng() ); + if( seen.insert( index ).second ) + indices.push_back( index ); + } + return indices; + } + + auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { + return getResultCapture().acquireGeneratorTracker( lineInfo ); + } + + template<> + auto all<int>() -> Generator<int> { + return range( std::numeric_limits<int>::min(), std::numeric_limits<int>::max() ); + } + +} // namespace Generators +} // namespace Catch +// end catch_generators.cpp +// start catch_interfaces_capture.cpp + +namespace Catch { + IResultCapture::~IResultCapture() = default; +} +// end catch_interfaces_capture.cpp +// start catch_interfaces_config.cpp + +namespace Catch { + IConfig::~IConfig() = default; +} +// end catch_interfaces_config.cpp +// start catch_interfaces_exception.cpp + +namespace Catch { + IExceptionTranslator::~IExceptionTranslator() = default; + IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() = default; +} +// end catch_interfaces_exception.cpp +// start catch_interfaces_registry_hub.cpp + +namespace Catch { + IRegistryHub::~IRegistryHub() = default; + IMutableRegistryHub::~IMutableRegistryHub() = default; +} +// end catch_interfaces_registry_hub.cpp +// start catch_interfaces_reporter.cpp + +// start catch_reporter_listening.h + +namespace Catch { + + class ListeningReporter : public IStreamingReporter { + using Reporters = std::vector<IStreamingReporterPtr>; + Reporters m_listeners; + IStreamingReporterPtr m_reporter = nullptr; + ReporterPreferences m_preferences; + + public: + ListeningReporter(); + + void addListener( IStreamingReporterPtr&& listener ); + void addReporter( IStreamingReporterPtr&& reporter ); + + public: // IStreamingReporter + + ReporterPreferences getPreferences() const override; + + void noMatchingTestCases( std::string const& spec ) override; + + static std::set<Verbosity> getSupportedVerbosities(); + + void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) override; + void benchmarkEnded( BenchmarkStats const& benchmarkStats ) override; + + void testRunStarting( TestRunInfo const& testRunInfo ) override; + void testGroupStarting( GroupInfo const& groupInfo ) override; + void testCaseStarting( TestCaseInfo const& testInfo ) override; + void sectionStarting( SectionInfo const& sectionInfo ) override; + void assertionStarting( AssertionInfo const& assertionInfo ) override; + + // The return value indicates if the messages buffer should be cleared: + bool assertionEnded( AssertionStats const& assertionStats ) override; + void sectionEnded( SectionStats const& sectionStats ) override; + void testCaseEnded( TestCaseStats const& testCaseStats ) override; + void testGroupEnded( TestGroupStats const& testGroupStats ) override; + void testRunEnded( TestRunStats const& testRunStats ) override; + + void skipTest( TestCaseInfo const& testInfo ) override; + bool isMulti() const override; + + }; + +} // end namespace Catch + +// end catch_reporter_listening.h +namespace Catch { + + ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig ) + : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} + + ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream ) + : m_stream( &_stream ), m_fullConfig( _fullConfig ) {} + + std::ostream& ReporterConfig::stream() const { return *m_stream; } + IConfigPtr ReporterConfig::fullConfig() const { return m_fullConfig; } + + TestRunInfo::TestRunInfo( std::string const& _name ) : name( _name ) {} + + GroupInfo::GroupInfo( std::string const& _name, + std::size_t _groupIndex, + std::size_t _groupsCount ) + : name( _name ), + groupIndex( _groupIndex ), + groupsCounts( _groupsCount ) + {} + + AssertionStats::AssertionStats( AssertionResult const& _assertionResult, + std::vector<MessageInfo> 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 ); + } + } + + AssertionStats::~AssertionStats() = default; + + SectionStats::SectionStats( SectionInfo const& _sectionInfo, + Counts const& _assertions, + double _durationInSeconds, + bool _missingAssertions ) + : sectionInfo( _sectionInfo ), + assertions( _assertions ), + durationInSeconds( _durationInSeconds ), + missingAssertions( _missingAssertions ) + {} + + SectionStats::~SectionStats() = default; + + 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 ) + {} + + TestCaseStats::~TestCaseStats() = default; + + TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo, + Totals const& _totals, + bool _aborting ) + : groupInfo( _groupInfo ), + totals( _totals ), + aborting( _aborting ) + {} + + TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo ) + : groupInfo( _groupInfo ), + aborting( false ) + {} + + TestGroupStats::~TestGroupStats() = default; + + TestRunStats::TestRunStats( TestRunInfo const& _runInfo, + Totals const& _totals, + bool _aborting ) + : runInfo( _runInfo ), + totals( _totals ), + aborting( _aborting ) + {} + + TestRunStats::~TestRunStats() = default; + + void IStreamingReporter::fatalErrorEncountered( StringRef ) {} + bool IStreamingReporter::isMulti() const { return false; } + + IReporterFactory::~IReporterFactory() = default; + IReporterRegistry::~IReporterRegistry() = default; + +} // end namespace Catch +// end catch_interfaces_reporter.cpp +// start catch_interfaces_runner.cpp + +namespace Catch { + IRunner::~IRunner() = default; +} +// end catch_interfaces_runner.cpp +// start catch_interfaces_testcase.cpp + +namespace Catch { + ITestInvoker::~ITestInvoker() = default; + ITestCaseRegistry::~ITestCaseRegistry() = default; +} +// end catch_interfaces_testcase.cpp +// start catch_leak_detector.cpp + +#ifdef CATCH_CONFIG_WINDOWS_CRTDBG +#include <crtdbg.h> + +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 + + Catch::LeakDetector::LeakDetector() {} + +#endif + +Catch::LeakDetector::~LeakDetector() { + Catch::cleanUp(); +} +// end catch_leak_detector.cpp +// start catch_list.cpp + +// start catch_list.h + +#include <set> + +namespace Catch { + + std::size_t listTests( Config const& config ); + + std::size_t listTestsNamesOnly( Config const& config ); + + struct TagInfo { + void add( std::string const& spelling ); + std::string all() const; + + std::set<std::string> spellings; + std::size_t count = 0; + }; + + std::size_t listTags( Config const& config ); + + std::size_t listReporters(); + + Option<std::size_t> list( Config const& config ); + +} // end namespace Catch + +// end catch_list.h +// start catch_text.h + +namespace Catch { + using namespace clara::TextFlow; +} + +// end catch_text.h +#include <limits> +#include <algorithm> +#include <iomanip> + +namespace Catch { + + std::size_t listTests( Config const& config ) { + TestSpec testSpec = config.testSpec(); + if( config.hasTestFilters() ) + Catch::cout() << "Matching test cases:\n"; + else { + Catch::cout() << "All available test cases:\n"; + } + + auto matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( auto const& testCaseInfo : matchedTestCases ) { + Colour::Code colour = testCaseInfo.isHidden() + ? Colour::SecondaryText + : Colour::None; + Colour colourGuard( colour ); + + Catch::cout() << Column( testCaseInfo.name ).initialIndent( 2 ).indent( 4 ) << "\n"; + if( config.verbosity() >= Verbosity::High ) { + Catch::cout() << Column( Catch::Detail::stringify( testCaseInfo.lineInfo ) ).indent(4) << std::endl; + std::string description = testCaseInfo.description; + if( description.empty() ) + description = "(NO DESCRIPTION)"; + Catch::cout() << Column( description ).indent(4) << std::endl; + } + if( !testCaseInfo.tags.empty() ) + Catch::cout() << Column( testCaseInfo.tagsAsString() ).indent( 6 ) << "\n"; + } + + if( !config.hasTestFilters() ) + Catch::cout() << pluralise( matchedTestCases.size(), "test case" ) << '\n' << std::endl; + else + Catch::cout() << pluralise( matchedTestCases.size(), "matching test case" ) << '\n' << std::endl; + return matchedTestCases.size(); + } + + std::size_t listTestsNamesOnly( Config const& config ) { + TestSpec testSpec = config.testSpec(); + std::size_t matchedTests = 0; + std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( auto const& testCaseInfo : matchedTestCases ) { + matchedTests++; + if( startsWith( testCaseInfo.name, '#' ) ) + Catch::cout() << '"' << testCaseInfo.name << '"'; + else + Catch::cout() << testCaseInfo.name; + if ( config.verbosity() >= Verbosity::High ) + Catch::cout() << "\t@" << testCaseInfo.lineInfo; + Catch::cout() << std::endl; + } + return matchedTests; + } + + void TagInfo::add( std::string const& spelling ) { + ++count; + spellings.insert( spelling ); + } + + std::string TagInfo::all() const { + std::string out; + for( auto const& spelling : spellings ) + out += "[" + spelling + "]"; + return out; + } + + std::size_t listTags( Config const& config ) { + TestSpec testSpec = config.testSpec(); + if( config.hasTestFilters() ) + Catch::cout() << "Tags for matching test cases:\n"; + else { + Catch::cout() << "All available tags:\n"; + } + + std::map<std::string, TagInfo> tagCounts; + + std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( auto const& testCase : matchedTestCases ) { + for( auto const& tagName : testCase.getTestCaseInfo().tags ) { + std::string lcaseTagName = toLower( tagName ); + auto countIt = tagCounts.find( lcaseTagName ); + if( countIt == tagCounts.end() ) + countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first; + countIt->second.add( tagName ); + } + } + + for( auto const& tagCount : tagCounts ) { + ReusableStringStream rss; + rss << " " << std::setw(2) << tagCount.second.count << " "; + auto str = rss.str(); + auto wrapper = Column( tagCount.second.all() ) + .initialIndent( 0 ) + .indent( str.size() ) + .width( CATCH_CONFIG_CONSOLE_WIDTH-10 ); + Catch::cout() << str << wrapper << '\n'; + } + Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl; + return tagCounts.size(); + } + + std::size_t listReporters() { + Catch::cout() << "Available reporters:\n"; + IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); + std::size_t maxNameLen = 0; + for( auto const& factoryKvp : factories ) + maxNameLen = (std::max)( maxNameLen, factoryKvp.first.size() ); + + for( auto const& factoryKvp : factories ) { + Catch::cout() + << Column( factoryKvp.first + ":" ) + .indent(2) + .width( 5+maxNameLen ) + + Column( factoryKvp.second->getDescription() ) + .initialIndent(0) + .indent(2) + .width( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) + << "\n"; + } + Catch::cout() << std::endl; + return factories.size(); + } + + Option<std::size_t> list( Config const& config ) { + Option<std::size_t> listedCount; + if( config.listTests() ) + listedCount = listedCount.valueOr(0) + listTests( config ); + if( config.listTestNamesOnly() ) + listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config ); + if( config.listTags() ) + listedCount = listedCount.valueOr(0) + listTags( config ); + if( config.listReporters() ) + listedCount = listedCount.valueOr(0) + listReporters(); + return listedCount; + } + +} // end namespace Catch +// end catch_list.cpp +// start catch_matchers.cpp + +namespace Catch { +namespace Matchers { + namespace Impl { + + std::string MatcherUntypedBase::toString() const { + if( m_cachedToString.empty() ) + m_cachedToString = describe(); + return m_cachedToString; + } + + MatcherUntypedBase::~MatcherUntypedBase() = default; + + } // namespace Impl +} // namespace Matchers + +using namespace Matchers; +using Matchers::Impl::MatcherBase; + +} // namespace Catch +// end catch_matchers.cpp +// start catch_matchers_floating.cpp + +// start catch_polyfills.hpp + +namespace Catch { + bool isnan(float f); + bool isnan(double d); +} + +// end catch_polyfills.hpp +// start catch_to_string.hpp + +#include <string> + +namespace Catch { + template <typename T> + std::string to_string(T const& t) { +#if defined(CATCH_CONFIG_CPP11_TO_STRING) + return std::to_string(t); +#else + ReusableStringStream rss; + rss << t; + return rss.str(); +#endif + } +} // end namespace Catch + +// end catch_to_string.hpp +#include <cstdlib> +#include <cstdint> +#include <cstring> + +namespace Catch { +namespace Matchers { +namespace Floating { +enum class FloatingPointKind : uint8_t { + Float, + Double +}; +} +} +} + +namespace { + +template <typename T> +struct Converter; + +template <> +struct Converter<float> { + static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated"); + Converter(float f) { + std::memcpy(&i, &f, sizeof(f)); + } + int32_t i; +}; + +template <> +struct Converter<double> { + static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated"); + Converter(double d) { + std::memcpy(&i, &d, sizeof(d)); + } + int64_t i; +}; + +template <typename T> +auto convert(T t) -> Converter<T> { + return Converter<T>(t); +} + +template <typename FP> +bool almostEqualUlps(FP lhs, FP rhs, int 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; + } + + auto lc = convert(lhs); + auto rc = convert(rhs); + + if ((lc.i < 0) != (rc.i < 0)) { + // Potentially we can have +0 and -0 + return lhs == rhs; + } + + auto ulpDiff = std::abs(lc.i - rc.i); + return ulpDiff <= maxUlpDiff; +} + +} + +namespace Catch { +namespace Matchers { +namespace Floating { + 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, int ulps, FloatingPointKind baseType) + :m_target{ target }, m_ulps{ ulps }, m_type{ baseType } { + CATCH_ENFORCE(ulps >= 0, "Invalid ULP setting: " << ulps << '.' + << " ULPs have to be non-negative."); + } + +#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 FloatingPointKind::Float: + return almostEqualUlps<float>(static_cast<float>(matchee), static_cast<float>(m_target), m_ulps); + case FloatingPointKind::Double: + return almostEqualUlps<double>(matchee, m_target, m_ulps); + default: + CATCH_INTERNAL_ERROR( "Unknown FloatingPointKind value" ); + } + } + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + + std::string WithinUlpsMatcher::describe() const { + return "is within " + Catch::to_string(m_ulps) + " ULPs of " + ::Catch::Detail::stringify(m_target) + ((m_type == FloatingPointKind::Float)? "f" : ""); + } + +}// namespace Floating + +Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff) { + return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Double); +} + +Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff) { + return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Float); +} + +Floating::WithinAbsMatcher WithinAbs(double target, double margin) { + return Floating::WithinAbsMatcher(target, margin); +} + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_floating.cpp +// start catch_matchers_generic.cpp + +std::string Catch::Matchers::Generic::Detail::finalizeDescription(const std::string& desc) { + if (desc.empty()) { + return "matches undescribed predicate"; + } else { + return "matches predicate: \"" + desc + '"'; + } +} +// end catch_matchers_generic.cpp +// start catch_matchers_string.cpp + +#include <regex> + +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 ); + } + + RegexMatcher::RegexMatcher(std::string regex, CaseSensitive::Choice caseSensitivity): m_regex(std::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::Choice::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::Choice::Yes)? " case sensitively" : " case insensitively"); + } + + } // 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) ); + } + + StdString::RegexMatcher Matches(std::string const& regex, CaseSensitive::Choice caseSensitivity) { + return StdString::RegexMatcher(regex, caseSensitivity); + } + +} // namespace Matchers +} // namespace Catch +// end catch_matchers_string.cpp +// start catch_message.cpp + +// start catch_uncaught_exceptions.h + +namespace Catch { + bool uncaught_exceptions(); +} // end namespace Catch + +// end catch_uncaught_exceptions.h +#include <cassert> +#include <stack> + +namespace Catch { + + MessageInfo::MessageInfo( StringRef const& _macroName, + SourceLineInfo const& _lineInfo, + ResultWas::OfType _type ) + : macroName( _macroName ), + lineInfo( _lineInfo ), + type( _type ), + sequence( ++globalCount ) + {} + + bool MessageInfo::operator==( MessageInfo const& other ) const { + return sequence == other.sequence; + } + + bool MessageInfo::operator<( MessageInfo const& other ) const { + return sequence < other.sequence; + } + + // This may need protecting if threading support is added + unsigned int MessageInfo::globalCount = 0; + + //////////////////////////////////////////////////////////////////////////// + + Catch::MessageBuilder::MessageBuilder( StringRef const& macroName, + SourceLineInfo const& lineInfo, + ResultWas::OfType type ) + :m_info(macroName, lineInfo, type) {} + + //////////////////////////////////////////////////////////////////////////// + + ScopedMessage::ScopedMessage( MessageBuilder const& builder ) + : m_info( builder.m_info ) + { + m_info.message = builder.m_stream.str(); + getResultCapture().pushScopedMessage( m_info ); + } + + ScopedMessage::~ScopedMessage() { + if ( !uncaught_exceptions() ){ + 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(names[start])) { + ++start; + } + while (names[end] == ',' || isspace(names[end])) { + --end; + } + return names.substr(start, end - start + 1); + }; + + size_t start = 0; + std::stack<char> 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 ',': + if (start != pos && openings.size() == 0) { + m_messages.emplace_back(macroName, lineInfo, resultType); + m_messages.back().message = trimmed(start, pos); + m_messages.back().message += " := "; + start = pos; + } + } + } + assert(openings.size() == 0 && "Mismatched openings"); + m_messages.emplace_back(macroName, lineInfo, resultType); + m_messages.back().message = 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 +// end catch_message.cpp +// start catch_output_redirect.cpp + +// start catch_output_redirect.h +#ifndef TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H +#define TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H + +#include <cstdio> +#include <iosfwd> +#include <string> + +namespace Catch { + + class RedirectedStream { + std::ostream& m_originalStream; + std::ostream& m_redirectionStream; + std::streambuf* m_prevBuf; + + public: + RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream ); + ~RedirectedStream(); + }; + + class RedirectedStdOut { + ReusableStringStream m_rss; + RedirectedStream m_cout; + public: + RedirectedStdOut(); + auto str() const -> std::string; + }; + + // 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 + class RedirectedStdErr { + ReusableStringStream m_rss; + RedirectedStream m_cerr; + RedirectedStream m_clog; + public: + RedirectedStdErr(); + auto str() const -> std::string; + }; + +#if defined(CATCH_CONFIG_NEW_CAPTURE) + + // Windows's implementation of std::tmpfile is terrible (it tries + // to create a file inside system folder, thus requiring elevated + // privileges for the binary), so we have to use tmpnam(_s) and + // create the file ourselves there. + class TempFile { + public: + TempFile(TempFile const&) = delete; + TempFile& operator=(TempFile const&) = delete; + TempFile(TempFile&&) = delete; + TempFile& operator=(TempFile&&) = delete; + + TempFile(); + ~TempFile(); + + std::FILE* getFile(); + std::string getContents(); + + private: + std::FILE* m_file = nullptr; + #if defined(_MSC_VER) + char m_buffer[L_tmpnam] = { 0 }; + #endif + }; + + class OutputRedirect { + public: + OutputRedirect(OutputRedirect const&) = delete; + OutputRedirect& operator=(OutputRedirect const&) = delete; + OutputRedirect(OutputRedirect&&) = delete; + OutputRedirect& operator=(OutputRedirect&&) = delete; + + OutputRedirect(std::string& stdout_dest, std::string& stderr_dest); + ~OutputRedirect(); + + private: + int m_originalStdout = -1; + int m_originalStderr = -1; + TempFile m_stdoutFile; + TempFile m_stderrFile; + std::string& m_stdoutDest; + std::string& m_stderrDest; + }; + +#endif + +} // end namespace Catch + +#endif // TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H +// end catch_output_redirect.h +#include <cstdio> +#include <cstring> +#include <fstream> +#include <sstream> +#include <stdexcept> + +#if defined(CATCH_CONFIG_NEW_CAPTURE) + #if defined(_MSC_VER) + #include <io.h> //_dup and _dup2 + #define dup _dup + #define dup2 _dup2 + #define fileno _fileno + #else + #include <unistd.h> // 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(); } + +#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("Coul dnot 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 +// end catch_output_redirect.cpp +// start catch_polyfills.cpp + +#include <cmath> + +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 +// end catch_polyfills.cpp +// start catch_random_number_generator.cpp + +namespace Catch { + + std::mt19937& rng() { + static std::mt19937 s_rng; + return s_rng; + } + + void seedRng( IConfig const& config ) { + if( config.rngSeed() != 0 ) { + std::srand( config.rngSeed() ); + rng().seed( config.rngSeed() ); + } + } + + unsigned int rngSeed() { + return getCurrentContext().getConfig()->rngSeed(); + } +} +// end catch_random_number_generator.cpp +// start catch_registry_hub.cpp + +// start catch_test_case_registry_impl.h + +#include <vector> +#include <set> +#include <algorithm> +#include <ios> + +namespace Catch { + + class TestCase; + struct IConfig; + + std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ); + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + + void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions ); + + std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ); + + class TestRegistry : public ITestCaseRegistry { + public: + virtual ~TestRegistry() = default; + + virtual void registerTest( TestCase const& testCase ); + + std::vector<TestCase> const& getAllTests() const override; + std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const override; + + private: + std::vector<TestCase> m_functions; + mutable RunTests::InWhatOrder m_currentSortOrder = RunTests::InDeclarationOrder; + mutable std::vector<TestCase> m_sortedFunctions; + std::size_t m_unnamedCount = 0; + std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised + }; + + /////////////////////////////////////////////////////////////////////////// + + class TestInvokerAsFunction : public ITestInvoker { + void(*m_testAsFunction)(); + public: + TestInvokerAsFunction( void(*testAsFunction)() ) noexcept; + + void invoke() const override; + }; + + std::string extractClassName( StringRef const& classOrQualifiedMethodName ); + + /////////////////////////////////////////////////////////////////////////// + +} // end namespace Catch + +// end catch_test_case_registry_impl.h +// start catch_reporter_registry.h + +#include <map> + +namespace Catch { + + class ReporterRegistry : public IReporterRegistry { + + public: + + ~ReporterRegistry() override; + + IStreamingReporterPtr create( std::string const& name, IConfigPtr const& config ) const override; + + void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ); + void registerListener( IReporterFactoryPtr const& factory ); + + FactoryMap const& getFactories() const override; + Listeners const& getListeners() const override; + + private: + FactoryMap m_factories; + Listeners m_listeners; + }; +} + +// end catch_reporter_registry.h +// start catch_tag_alias_registry.h + +// start catch_tag_alias.h + +#include <string> + +namespace Catch { + + struct TagAlias { + TagAlias(std::string const& _tag, SourceLineInfo _lineInfo); + + std::string tag; + SourceLineInfo lineInfo; + }; + +} // end namespace Catch + +// end catch_tag_alias.h +#include <map> + +namespace Catch { + + class TagAliasRegistry : public ITagAliasRegistry { + public: + ~TagAliasRegistry() override; + TagAlias const* find( std::string const& alias ) const override; + std::string expandAliases( std::string const& unexpandedTestSpec ) const override; + void add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ); + + private: + std::map<std::string, TagAlias> m_registry; + }; + +} // end namespace Catch + +// end catch_tag_alias_registry.h +// start catch_startup_exception_registry.h + +#include <vector> +#include <exception> + +namespace Catch { + + class StartupExceptionRegistry { + public: + void add(std::exception_ptr const& exception) noexcept; + std::vector<std::exception_ptr> const& getExceptions() const noexcept; + private: + std::vector<std::exception_ptr> m_exceptions; + }; + +} // end namespace Catch + +// end catch_startup_exception_registry.h +// start catch_singletons.hpp + +namespace Catch { + + struct ISingleton { + virtual ~ISingleton(); + }; + + void addSingleton( ISingleton* singleton ); + void cleanupSingletons(); + + template<typename SingletonImplT, typename InterfaceT = SingletonImplT, typename MutableInterfaceT = InterfaceT> + class Singleton : SingletonImplT, public ISingleton { + + static auto getInternal() -> Singleton* { + static Singleton* s_instance = nullptr; + if( !s_instance ) { + s_instance = new Singleton; + addSingleton( s_instance ); + } + return s_instance; + } + + public: + static auto get() -> InterfaceT const& { + return *getInternal(); + } + static auto getMutable() -> MutableInterfaceT& { + return *getInternal(); + } + }; + +} // namespace Catch + +// end catch_singletons.hpp +namespace Catch { + + namespace { + + class RegistryHub : public IRegistryHub, public IMutableRegistryHub, + private 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 const& factory ) override { + m_reporterRegistry.registerReporter( name, factory ); + } + void registerListener( IReporterFactoryPtr const& factory ) override { + m_reporterRegistry.registerListener( factory ); + } + void registerTest( TestCase const& testInfo ) override { + m_testCaseRegistry.registerTest( testInfo ); + } + void registerTranslator( const IExceptionTranslator* translator ) override { + m_exceptionTranslatorRegistry.registerTranslator( 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 { + m_exceptionRegistry.add(std::current_exception()); + } + + private: + TestRegistry m_testCaseRegistry; + ReporterRegistry m_reporterRegistry; + ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; + TagAliasRegistry m_tagAliasRegistry; + StartupExceptionRegistry m_exceptionRegistry; + }; + } + + using RegistryHubSingleton = Singleton<RegistryHub, IRegistryHub, IMutableRegistryHub>; + + 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 +// end catch_registry_hub.cpp +// start catch_reporter_registry.cpp + +namespace Catch { + + ReporterRegistry::~ReporterRegistry() = default; + + IStreamingReporterPtr ReporterRegistry::create( std::string const& name, IConfigPtr const& config ) const { + auto it = m_factories.find( name ); + if( it == m_factories.end() ) + return nullptr; + return it->second->create( ReporterConfig( config ) ); + } + + void ReporterRegistry::registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) { + m_factories.emplace(name, factory); + } + void ReporterRegistry::registerListener( IReporterFactoryPtr const& factory ) { + m_listeners.push_back( factory ); + } + + IReporterRegistry::FactoryMap const& ReporterRegistry::getFactories() const { + return m_factories; + } + IReporterRegistry::Listeners const& ReporterRegistry::getListeners() const { + return m_listeners; + } + +} +// end catch_reporter_registry.cpp +// start catch_result_type.cpp + +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<ResultDisposition::Flags>( static_cast<int>( lhs ) | static_cast<int>( rhs ) ); + } + + bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } + bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; } + +} // end namespace Catch +// end catch_result_type.cpp +// start catch_run_context.cpp + +#include <cassert> +#include <algorithm> +#include <sstream> + +namespace Catch { + + namespace Generators { + struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker { + size_t m_index = static_cast<size_t>( -1 ); + GeneratorBasePtr m_generator; + + GeneratorTracker( TestCaseTracking::NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : TrackerBase( nameAndLocation, ctx, parent ) + {} + ~GeneratorTracker(); + + static GeneratorTracker& acquire( TrackerContext& ctx, TestCaseTracking::NameAndLocation const& nameAndLocation ) { + std::shared_ptr<GeneratorTracker> tracker; + + ITracker& currentTracker = ctx.currentTracker(); + if( TestCaseTracking::ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isIndexTracker() ); + tracker = std::static_pointer_cast<GeneratorTracker>( childTracker ); + } + else { + tracker = std::make_shared<GeneratorTracker>( nameAndLocation, ctx, ¤tTracker ); + currentTracker.addChild( tracker ); + } + + if( !ctx.completedCycle() && !tracker->isComplete() ) { + if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun ) + tracker->moveNext(); + tracker->open(); + } + + return *tracker; + } + + void moveNext() { + m_index++; + m_children.clear(); + } + + // TrackerBase interface + bool isIndexTracker() const override { return true; } + auto hasGenerator() const -> bool override { + return !!m_generator; + } + void close() override { + TrackerBase::close(); + if( m_runState == CompletedSuccessfully && m_index < m_generator->size()-1 ) + m_runState = Executing; + } + + // IGeneratorTracker interface + auto getGenerator() const -> GeneratorBasePtr const& override { + return m_generator; + } + void setGenerator( GeneratorBasePtr&& generator ) override { + m_generator = std::move( generator ); + } + auto getIndex() const -> size_t override { + return m_index; + } + }; + GeneratorTracker::~GeneratorTracker() {} + } + + RunContext::RunContext(IConfigPtr const& _config, IStreamingReporterPtr&& reporter) + : m_runInfo(_config->name()), + m_context(getCurrentMutableContext()), + m_config(_config), + m_reporter(std::move(reporter)), + m_lastAssertionInfo{ StringRef(), SourceLineInfo("",0), StringRef(), ResultDisposition::Normal }, + m_includeSuccessfulResults( m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions ) + { + m_context.setRunner(this); + m_context.setConfig(m_config); + m_context.setResultCapture(this); + m_reporter->testRunStarting(m_runInfo); + } + + RunContext::~RunContext() { + m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, aborting())); + } + + void RunContext::testGroupStarting(std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount) { + m_reporter->testGroupStarting(GroupInfo(testSpec, groupIndex, groupsCount)); + } + + void RunContext::testGroupEnded(std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount) { + m_reporter->testGroupEnded(TestGroupStats(GroupInfo(testSpec, groupIndex, groupsCount), totals, aborting())); + } + + Totals RunContext::runTest(TestCase const& testCase) { + 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<SectionTracker&>(rootTracker).addInitialFilters(m_config->getSectionsToRun()); + do { + m_trackerContext.startCycle(); + m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(testInfo.name, testInfo.lineInfo)); + runCurrentTest(redirectedCout, redirectedCerr); + } 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; + } + + IConfigPtr RunContext::config() const { + return m_config; + } + + IStreamingReporter& RunContext::reporter() const { + return *m_reporter; + } + + void RunContext::assertionEnded(AssertionResult const & result) { + if (result.getResultType() == ResultWas::Ok) { + m_totals.assertions.passed++; + m_lastAssertionPassed = true; + } else if (!result.isOk()) { + m_lastAssertionPassed = false; + if( m_activeTestCase->getTestCaseInfo().okToFail() ) + m_totals.assertions.failedButOk++; + else + m_totals.assertions.failed++; + } + else { + m_lastAssertionPassed = true; + } + + // 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<void>(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals))); + + // 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( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { + using namespace Generators; + GeneratorTracker& tracker = GeneratorTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( "generator", lineInfo ) ); + assert( tracker.isOpen() ); + 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(); + } + + 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::benchmarkStarting( BenchmarkInfo const& info ) { + m_reporter->benchmarkStarting( info ); + } + void RunContext::benchmarkEnded( BenchmarkStats const& stats ) { + m_reporter->benchmarkEnded( stats ); + } + + 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()); + } + + 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 = 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++; + testGroupEnded(std::string(), m_totals, 1, 1); + 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(); + } + + bool RunContext::aborting() const { + return m_totals.assertions.failed >= static_cast<std::size_t>(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 }; + + seedRng(*m_config); + + Timer timer; + CATCH_TRY { + if (m_reporter->getPreferences().shouldRedirectStdOut) { +#if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) + RedirectedStdOut redirectedStdOut; + RedirectedStdErr redirectedStdErr; + + timer.start(); + invokeActiveTestCase(); + redirectedCout += redirectedStdOut.str(); + redirectedCerr += redirectedStdErr.str(); +#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(); + + SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions); + m_reporter->sectionEnded(testCaseSectionStats); + } + + void RunContext::invokeActiveTestCase() { + FatalConditionHandler fatalConditionHandler; // Handle signals + m_activeTestCase->invoke(); + fatalConditionHandler.reset(); + } + + 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 const& message, + AssertionReaction& reaction + ) { + m_reporter->assertionStarting( info ); + + m_lastAssertionInfo = info; + + AssertionResultData data( resultType, LazyExpression( false ) ); + data.message = 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"); + } +} +// end catch_run_context.cpp +// start catch_section.cpp + +namespace Catch { + + Section::Section( SectionInfo const& info ) + : m_info( info ), + m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) ) + { + 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 +// end catch_section.cpp +// start catch_section_info.cpp + +namespace Catch { + + SectionInfo::SectionInfo + ( SourceLineInfo const& _lineInfo, + std::string const& _name ) + : name( _name ), + lineInfo( _lineInfo ) + {} + +} // end namespace Catch +// end catch_section_info.cpp +// start catch_session.cpp + +// start catch_session.h + +#include <memory> + +namespace Catch { + + class Session : NonCopyable { + public: + + Session(); + ~Session() override; + + 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<typename CharT> + 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; + std::shared_ptr<Config> m_config; + bool m_startupExceptions = false; + }; + +} // end namespace Catch + +// end catch_session.h +// start catch_version.h + +#include <iosfwd> + +namespace Catch { + + // Versioning information + struct Version { + Version( Version const& ) = delete; + Version& operator=( Version const& ) = delete; + Version( unsigned int _majorVersion, + unsigned int _minorVersion, + unsigned int _patchNumber, + char const * const _branchName, + unsigned int _buildNumber ); + + unsigned int const majorVersion; + unsigned int const minorVersion; + unsigned int const patchNumber; + + // buildNumber is only used if branchName is not null + char const * const branchName; + unsigned int const buildNumber; + + friend std::ostream& operator << ( std::ostream& os, Version const& version ); + }; + + Version const& libraryVersion(); +} + +// end catch_version.h +#include <cstdlib> +#include <iomanip> + +namespace Catch { + + namespace { + const int MaxExitCode = 255; + + IStreamingReporterPtr createReporter(std::string const& reporterName, IConfigPtr const& config) { + auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, config); + CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << "'"); + + return reporter; + } + + IStreamingReporterPtr makeReporter(std::shared_ptr<Config> const& config) { + if (Catch::getRegistryHub().getReporterRegistry().getListeners().empty()) { + return createReporter(config->getReporterName(), config); + } + + auto multi = std::unique_ptr<ListeningReporter>(new ListeningReporter); + + auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners(); + for (auto const& listener : listeners) { + multi->addListener(listener->create(Catch::ReporterConfig(config))); + } + multi->addReporter(createReporter(config->getReporterName(), config)); + return std::move(multi); + } + + Catch::Totals runTests(std::shared_ptr<Config> const& config) { + auto reporter = makeReporter(config); + + RunContext context(config, std::move(reporter)); + + Totals totals; + + context.testGroupStarting(config->name(), 1, 1); + + TestSpec testSpec = config->testSpec(); + + auto const& allTestCases = getAllTestCasesSorted(*config); + for (auto const& testCase : allTestCases) { + if (!context.aborting() && matchTest(testCase, testSpec, *config)) + totals += context.runTest(testCase); + else + context.reporter().skipTest(testCase); + } + + if (config->warnAboutNoTests() && totals.testCases.total() == 0) { + ReusableStringStream testConfig; + + bool first = true; + for (const auto& input : config->getTestsOrTags()) { + if (!first) { testConfig << ' '; } + first = false; + testConfig << input; + } + + context.reporter().noMatchingTestCases(testConfig.str()); + totals.error = -1; + } + + context.testGroupEnded(config->name(), totals, 1, 1); + return totals; + } + + void applyFilenamesAsTags(Catch::IConfig const& config) { + auto& tests = const_cast<std::vector<TestCase>&>(getAllTestCasesSorted(config)); + for (auto& testCase : tests) { + auto tags = testCase.tags; + + std::string filename = testCase.lineInfo.file; + auto lastSlash = filename.find_last_of("\\/"); + if (lastSlash != std::string::npos) { + filename.erase(0, lastSlash); + filename[0] = '#'; + } + + auto lastDot = filename.find_last_of('.'); + if (lastDot != std::string::npos) { + filename.erase(lastDot); + } + + tags.push_back(std::move(filename)); + setTags(testCase, tags); + } + } + + } // 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() ) { + m_startupExceptions = true; + Colour colourGuard( Colour::Red ); + Catch::cerr() << "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 ) { + Catch::cerr() << Column( ex.what() ).indent(2) << '\n'; + } + } + } +#endif + + alreadyInstantiated = true; + m_cli = makeCommandLineParser( m_configData ); + } + Session::~Session() { + Catch::cleanUp(); + } + + void Session::showHelp() const { + Catch::cout() + << "\nCatch v" << libraryVersion() << "\n" + << m_cli << std::endl + << "For more detailed usage please see the project docs\n" << std::endl; + } + void Session::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 Session::applyCommandLine( int argc, char const * const * argv ) { + if( m_startupExceptions ) + return 1; + + auto result = m_cli.parse( clara::Args( argc, argv ) ); + if( !result ) { + Catch::cerr() + << Colour( Colour::Red ) + << "\nError(s) in input:\n" + << Column( result.errorMessage() ).indent( 2 ) + << "\n\n"; + Catch::cerr() << "Run with -? for usage\n" << std::endl; + 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, 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 ); + + 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" << std::endl; + static_cast<void>(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<void>(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 = std::make_shared<Config>( m_configData ); + return *m_config; + } + + int Session::runInternal() { + if( m_startupExceptions ) + return 1; + + if (m_configData.showHelp || m_configData.libIdentify) { + return 0; + } + + CATCH_TRY { + config(); // Force config to be constructed + + seedRng( *m_config ); + + if( m_configData.filenamesAsTags ) + applyFilenamesAsTags( *m_config ); + + // Handle list request + if( Option<std::size_t> listed = list( config() ) ) + return static_cast<int>( *listed ); + + auto totals = runTests( m_config ); + // 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, (std::max) (totals.error, static_cast<int>(totals.assertions.failed))); + } +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + catch( std::exception& ex ) { + Catch::cerr() << ex.what() << std::endl; + return MaxExitCode; + } +#endif + } + +} // end namespace Catch +// end catch_session.cpp +// start catch_singletons.cpp + +#include <vector> + +namespace Catch { + + namespace { + static auto getSingletons() -> std::vector<ISingleton*>*& { + static std::vector<ISingleton*>* g_singletons = nullptr; + if( !g_singletons ) + g_singletons = new std::vector<ISingleton*>(); + return g_singletons; + } + } + + ISingleton::~ISingleton() {} + + 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 +// end catch_singletons.cpp +// start catch_startup_exception_registry.cpp + +namespace Catch { +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<std::exception_ptr> const& StartupExceptionRegistry::getExceptions() const noexcept { + return m_exceptions; + } + +} // end namespace Catch +// end catch_startup_exception_registry.cpp +// start catch_stream.cpp + +#include <cstdio> +#include <iostream> +#include <fstream> +#include <sstream> +#include <vector> +#include <memory> + +namespace Catch { + + Catch::IStream::~IStream() = default; + + namespace detail { namespace { + template<typename WriterF, std::size_t bufferSize=256> + class StreamBufImpl : public std::streambuf { + char data[bufferSize]; + WriterF m_writer; + + public: + StreamBufImpl() { + setp( data, data + sizeof(data) ); + } + + ~StreamBufImpl() noexcept { + StreamBufImpl::sync(); + } + + private: + int overflow( int c ) override { + sync(); + + if( c != EOF ) { + if( pbase() == epptr() ) + m_writer( std::string( 1, static_cast<char>( c ) ) ); + else + sputc( static_cast<char>( c ) ); + } + return 0; + } + + int sync() override { + if( pbase() != pptr() ) { + m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) ); + setp( pbase(), epptr() ); + } + return 0; + } + }; + + /////////////////////////////////////////////////////////////////////////// + + struct OutputDebugWriter { + + void operator()( std::string const&str ) { + writeToDebugConsole( str ); + } + }; + + /////////////////////////////////////////////////////////////////////////// + + class FileStream : public IStream { + mutable std::ofstream m_ofs; + public: + FileStream( StringRef filename ) { + m_ofs.open( filename.c_str() ); + CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << "'" ); + } + ~FileStream() override = default; + public: // IStream + std::ostream& stream() const override { + return m_ofs; + } + }; + + /////////////////////////////////////////////////////////////////////////// + + class CoutStream : public IStream { + mutable 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() const override { return m_os; } + }; + + /////////////////////////////////////////////////////////////////////////// + + class DebugOutStream : public IStream { + std::unique_ptr<StreamBufImpl<OutputDebugWriter>> m_streamBuf; + mutable std::ostream m_os; + public: + DebugOutStream() + : m_streamBuf( new StreamBufImpl<OutputDebugWriter>() ), + m_os( m_streamBuf.get() ) + {} + + ~DebugOutStream() override = default; + + public: // IStream + std::ostream& stream() const override { return m_os; } + }; + + }} // namespace anon::detail + + /////////////////////////////////////////////////////////////////////////// + + auto makeStream( StringRef const &filename ) -> IStream const* { + if( filename.empty() ) + return new detail::CoutStream(); + else if( filename[0] == '%' ) { + if( filename == "%debug" ) + return new detail::DebugOutStream(); + else + CATCH_ERROR( "Unrecognised stream: '" << filename << "'" ); + } + else + return new detail::FileStream( filename ); + } + + // This class encapsulates the idea of a pool of ostringstreams that can be reused. + struct StringStreams { + std::vector<std::unique_ptr<std::ostringstream>> m_streams; + std::vector<std::size_t> 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( std::unique_ptr<std::ostringstream>( new std::ostringstream ) ); + 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<StringStreams>::getMutable().add() ), + m_oss( Singleton<StringStreams>::getMutable().m_streams[m_index].get() ) + {} + + ReusableStringStream::~ReusableStringStream() { + static_cast<std::ostringstream*>( m_oss )->str(""); + m_oss->clear(); + Singleton<StringStreams>::getMutable().release( m_index ); + } + + auto ReusableStringStream::str() const -> std::string { + return static_cast<std::ostringstream*>( m_oss )->str(); + } + + /////////////////////////////////////////////////////////////////////////// + +#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions + std::ostream& cout() { return std::cout; } + std::ostream& cerr() { return std::cerr; } + std::ostream& clog() { return std::clog; } +#endif +} +// end catch_stream.cpp +// start catch_string_manip.cpp + +#include <algorithm> +#include <ostream> +#include <cstring> +#include <cctype> + +namespace Catch { + + namespace { + char toLowerCh(char c) { + return static_cast<char>( std::tolower( c ) ); + } + } + + 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( 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() && 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(), toLowerCh ); + } + std::string toLower( std::string const& s ) { + std::string lc = s; + toLowerInPlace( lc ); + return lc; + } + 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(); + } + + 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; + } + + pluralise::pluralise( std::size_t count, std::string const& label ) + : m_count( count ), + m_label( label ) + {} + + 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; + } + +} +// end catch_string_manip.cpp +// start catch_stringref.cpp + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +#endif + +#include <ostream> +#include <cstring> +#include <cstdint> + +namespace { + const uint32_t byte_2_lead = 0xC0; + const uint32_t byte_3_lead = 0xE0; + const uint32_t byte_4_lead = 0xF0; +} + +namespace Catch { + StringRef::StringRef( char const* rawChars ) noexcept + : StringRef( rawChars, static_cast<StringRef::size_type>(std::strlen(rawChars) ) ) + {} + + StringRef::operator std::string() const { + return std::string( m_start, m_size ); + } + + void StringRef::swap( StringRef& other ) noexcept { + std::swap( m_start, other.m_start ); + std::swap( m_size, other.m_size ); + std::swap( m_data, other.m_data ); + } + + auto StringRef::c_str() const -> char const* { + if( isSubstring() ) + const_cast<StringRef*>( this )->takeOwnership(); + return m_start; + } + auto StringRef::currentData() const noexcept -> char const* { + return m_start; + } + + auto StringRef::isOwned() const noexcept -> bool { + return m_data != nullptr; + } + auto StringRef::isSubstring() const noexcept -> bool { + return m_start[m_size] != '\0'; + } + + void StringRef::takeOwnership() { + if( !isOwned() ) { + m_data = new char[m_size+1]; + memcpy( m_data, m_start, m_size ); + m_data[m_size] = '\0'; + m_start = m_data; + } + } + auto StringRef::substr( size_type start, size_type size ) const noexcept -> StringRef { + if( start < m_size ) + return StringRef( m_start+start, size ); + else + return StringRef(); + } + auto StringRef::operator == ( StringRef const& other ) const noexcept -> bool { + return + size() == other.size() && + (std::strncmp( m_start, other.m_start, size() ) == 0); + } + auto StringRef::operator != ( StringRef const& other ) const noexcept -> bool { + return !operator==( other ); + } + + auto StringRef::operator[](size_type index) const noexcept -> char { + return m_start[index]; + } + + auto StringRef::numberOfCharacters() const noexcept -> size_type { + size_type noChars = m_size; + // Make adjustments for uft encodings + for( size_type i=0; i < m_size; ++i ) { + char c = m_start[i]; + if( ( c & byte_2_lead ) == byte_2_lead ) { + noChars--; + if (( c & byte_3_lead ) == byte_3_lead ) + noChars--; + if( ( c & byte_4_lead ) == byte_4_lead ) + noChars--; + } + } + return noChars; + } + + auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string { + std::string str; + str.reserve( lhs.size() + rhs.size() ); + str += lhs; + str += rhs; + return str; + } + auto operator + ( StringRef const& lhs, const char* rhs ) -> std::string { + return std::string( lhs ) + std::string( rhs ); + } + auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string { + return std::string( lhs ) + std::string( rhs ); + } + + auto operator << ( std::ostream& os, StringRef const& str ) -> std::ostream& { + return os.write(str.currentData(), str.size()); + } + + auto operator+=( std::string& lhs, StringRef const& rhs ) -> std::string& { + lhs.append(rhs.currentData(), rhs.size()); + return lhs; + } + +} // namespace Catch + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif +// end catch_stringref.cpp +// start catch_tag_alias.cpp + +namespace Catch { + TagAlias::TagAlias(std::string const & _tag, SourceLineInfo _lineInfo): tag(_tag), lineInfo(_lineInfo) {} +} +// end catch_tag_alias.cpp +// start catch_tag_alias_autoregistrar.cpp + +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(); + } + } + +} +// end catch_tag_alias_autoregistrar.cpp +// start catch_tag_alias_registry.cpp + +#include <sstream> + +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() {} + + ITagAliasRegistry const& ITagAliasRegistry::get() { + return getRegistryHub().getTagAliasRegistry(); + } + +} // end namespace Catch +// end catch_tag_alias_registry.cpp +// start catch_test_case_info.cpp + +#include <cctype> +#include <exception> +#include <algorithm> +#include <sstream> + +namespace Catch { + + namespace { + TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { + if( startsWith( tag, '.' ) || + tag == "!hide" ) + return TestCaseInfo::IsHidden; + else if( tag == "!throws" ) + return TestCaseInfo::Throws; + else if( tag == "!shouldfail" ) + return TestCaseInfo::ShouldFail; + else if( tag == "!mayfail" ) + return TestCaseInfo::MayFail; + else if( tag == "!nonportable" ) + return TestCaseInfo::NonPortable; + else if( tag == "!benchmark" ) + return static_cast<TestCaseInfo::SpecialProperties>( TestCaseInfo::Benchmark | TestCaseInfo::IsHidden ); + else + return TestCaseInfo::None; + } + bool isReservedTag( std::string const& tag ) { + return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( static_cast<unsigned char>(tag[0]) ); + } + void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { + CATCH_ENFORCE( !isReservedTag(tag), + "Tag name: [" << tag << "] is not allowed.\n" + << "Tag names starting with non alpha-numeric characters are reserved\n" + << _lineInfo ); + } + } + + TestCase makeTestCase( ITestInvoker* _testCase, + std::string const& _className, + NameAndTags const& nameAndTags, + SourceLineInfo const& _lineInfo ) + { + bool isHidden = false; + + // Parse out tags + std::vector<std::string> tags; + std::string desc, tag; + bool inTag = false; + std::string _descOrTags = nameAndTags.tags; + for (char c : _descOrTags) { + if( !inTag ) { + if( c == '[' ) + inTag = true; + else + desc += c; + } + else { + if( c == ']' ) { + TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag ); + if( ( prop & TestCaseInfo::IsHidden ) != 0 ) + isHidden = true; + else if( prop == TestCaseInfo::None ) + enforceNotReservedTag( tag, _lineInfo ); + + tags.push_back( tag ); + tag.clear(); + inTag = false; + } + else + tag += c; + } + } + if( isHidden ) { + tags.push_back( "." ); + } + + TestCaseInfo info( nameAndTags.name, _className, desc, tags, _lineInfo ); + return TestCase( _testCase, std::move(info) ); + } + + void setTags( TestCaseInfo& testCaseInfo, std::vector<std::string> tags ) { + std::sort(begin(tags), end(tags)); + tags.erase(std::unique(begin(tags), end(tags)), end(tags)); + testCaseInfo.lcaseTags.clear(); + + for( auto const& tag : tags ) { + std::string lcaseTag = toLower( tag ); + testCaseInfo.properties = static_cast<TestCaseInfo::SpecialProperties>( testCaseInfo.properties | parseSpecialTag( lcaseTag ) ); + testCaseInfo.lcaseTags.push_back( lcaseTag ); + } + testCaseInfo.tags = std::move(tags); + } + + TestCaseInfo::TestCaseInfo( std::string const& _name, + std::string const& _className, + std::string const& _description, + std::vector<std::string> const& _tags, + SourceLineInfo const& _lineInfo ) + : name( _name ), + className( _className ), + description( _description ), + lineInfo( _lineInfo ), + properties( None ) + { + setTags( *this, _tags ); + } + + bool TestCaseInfo::isHidden() const { + return ( properties & IsHidden ) != 0; + } + bool TestCaseInfo::throws() const { + return ( properties & Throws ) != 0; + } + bool TestCaseInfo::okToFail() const { + return ( properties & (ShouldFail | MayFail ) ) != 0; + } + bool TestCaseInfo::expectedToFail() const { + return ( properties & (ShouldFail ) ) != 0; + } + + 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.size(); + } + ret.reserve(full_size); + for (const auto& tag : tags) { + ret.push_back('['); + ret.append(tag); + ret.push_back(']'); + } + + return ret; + } + + TestCase::TestCase( ITestInvoker* testCase, TestCaseInfo&& info ) : TestCaseInfo( std::move(info) ), test( testCase ) {} + + TestCase TestCase::withName( std::string const& _newName ) const { + TestCase other( *this ); + other.name = _newName; + return other; + } + + void TestCase::invoke() const { + test->invoke(); + } + + bool TestCase::operator == ( TestCase const& other ) const { + return test.get() == other.test.get() && + name == other.name && + className == other.className; + } + + bool TestCase::operator < ( TestCase const& other ) const { + return name < other.name; + } + + TestCaseInfo const& TestCase::getTestCaseInfo() const + { + return *this; + } + +} // end namespace Catch +// end catch_test_case_info.cpp +// start catch_test_case_registry_impl.cpp + +#include <sstream> + +namespace Catch { + + std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ) { + + std::vector<TestCase> sorted = unsortedTestCases; + + switch( config.runOrder() ) { + case RunTests::InLexicographicalOrder: + std::sort( sorted.begin(), sorted.end() ); + break; + case RunTests::InRandomOrder: + seedRng( config ); + std::shuffle( sorted.begin(), sorted.end(), rng() ); + break; + case RunTests::InDeclarationOrder: + // already in declaration order + break; + } + return sorted; + } + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) { + return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() ); + } + + void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions ) { + std::set<TestCase> seenFunctions; + for( auto const& function : functions ) { + auto prev = seenFunctions.insert( function ); + CATCH_ENFORCE( prev.second, + "error: TEST_CASE( \"" << function.name << "\" ) already defined.\n" + << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" + << "\tRedefined at " << function.getTestCaseInfo().lineInfo ); + } + } + + std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config ) { + std::vector<TestCase> filtered; + filtered.reserve( testCases.size() ); + for( auto const& testCase : testCases ) + if( matchTest( testCase, testSpec, config ) ) + filtered.push_back( testCase ); + return filtered; + } + std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ) { + return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config ); + } + + void TestRegistry::registerTest( TestCase const& testCase ) { + std::string name = testCase.getTestCaseInfo().name; + if( name.empty() ) { + ReusableStringStream rss; + rss << "Anonymous test case " << ++m_unnamedCount; + return registerTest( testCase.withName( rss.str() ) ); + } + m_functions.push_back( testCase ); + } + + std::vector<TestCase> const& TestRegistry::getAllTests() const { + return m_functions; + } + std::vector<TestCase> const& TestRegistry::getAllTestsSorted( IConfig const& config ) const { + if( m_sortedFunctions.empty() ) + enforceNoDuplicateTestCases( m_functions ); + + if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) { + m_sortedFunctions = sortTests( config, m_functions ); + m_currentSortOrder = config.runOrder(); + } + return m_sortedFunctions; + } + + /////////////////////////////////////////////////////////////////////////// + TestInvokerAsFunction::TestInvokerAsFunction( void(*testAsFunction)() ) noexcept : m_testAsFunction( testAsFunction ) {} + + void TestInvokerAsFunction::invoke() const { + m_testAsFunction(); + } + + std::string extractClassName( StringRef const& classOrQualifiedMethodName ) { + std::string className = classOrQualifiedMethodName; + if( startsWith( className, '&' ) ) + { + std::size_t lastColons = className.rfind( "::" ); + std::size_t penultimateColons = className.rfind( "::", lastColons-1 ); + if( penultimateColons == std::string::npos ) + penultimateColons = 1; + className = className.substr( penultimateColons, lastColons-penultimateColons ); + } + return className; + } + +} // end namespace Catch +// end catch_test_case_registry_impl.cpp +// start catch_test_case_tracker.cpp + +#include <algorithm> +#include <cassert> +#include <stdexcept> +#include <memory> +#include <sstream> + +#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; + + TrackerContext& TrackerContext::instance() { + static TrackerContext s_instance; + return s_instance; + } + + ITracker& TrackerContext::startRun() { + m_rootTracker = std::make_shared<SectionTracker>( NameAndLocation( "{root}", 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 ) + : m_nameAndLocation( nameAndLocation ), + m_ctx( ctx ), + m_parent( parent ) + {} + + NameAndLocation const& TrackerBase::nameAndLocation() const { + return m_nameAndLocation; + } + bool TrackerBase::isComplete() const { + return m_runState == CompletedSuccessfully || m_runState == Failed; + } + bool TrackerBase::isSuccessfullyCompleted() const { + return m_runState == CompletedSuccessfully; + } + bool TrackerBase::isOpen() const { + return m_runState != NotStarted && !isComplete(); + } + bool TrackerBase::hasChildren() const { + return !m_children.empty(); + } + + void TrackerBase::addChild( ITrackerPtr const& child ) { + m_children.push_back( child ); + } + + ITrackerPtr TrackerBase::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 + : nullptr; + } + ITracker& TrackerBase::parent() { + assert( m_parent ); // Should always be non-null except for root + return *m_parent; + } + + void TrackerBase::openChild() { + if( m_runState != ExecutingChildren ) { + m_runState = ExecutingChildren; + if( m_parent ) + m_parent->openChild(); + } + } + + bool TrackerBase::isSectionTracker() const { return false; } + bool TrackerBase::isIndexTracker() const { return false; } + + 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( m_children.empty() || m_children.back()->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::markAsNeedingAnotherRun() { + m_runState = NeedsAnotherRun; + } + + 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 ) + { + if( parent ) { + while( !parent->isSectionTracker() ) + parent = &parent->parent(); + + SectionTracker& parentSection = static_cast<SectionTracker&>( *parent ); + addNextFilters( parentSection.m_filters ); + } + } + + bool SectionTracker::isSectionTracker() const { return true; } + + SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { + std::shared_ptr<SectionTracker> section; + + ITracker& currentTracker = ctx.currentTracker(); + if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isSectionTracker() ); + section = std::static_pointer_cast<SectionTracker>( childTracker ); + } + else { + section = std::make_shared<SectionTracker>( nameAndLocation, ctx, ¤tTracker ); + currentTracker.addChild( section ); + } + if( !ctx.completedCycle() ) + section->tryOpen(); + return *section; + } + + void SectionTracker::tryOpen() { + if( !isComplete() && (m_filters.empty() || m_filters[0].empty() || m_filters[0] == m_nameAndLocation.name ) ) + open(); + } + + void SectionTracker::addInitialFilters( std::vector<std::string> 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 SectionTracker::addNextFilters( std::vector<std::string> const& filters ) { + if( filters.size() > 1 ) + m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() ); + } + + IndexTracker::IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ) + : TrackerBase( nameAndLocation, ctx, parent ), + m_size( size ) + {} + + bool IndexTracker::isIndexTracker() const { return true; } + + IndexTracker& IndexTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) { + std::shared_ptr<IndexTracker> tracker; + + ITracker& currentTracker = ctx.currentTracker(); + if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isIndexTracker() ); + tracker = std::static_pointer_cast<IndexTracker>( childTracker ); + } + else { + tracker = std::make_shared<IndexTracker>( nameAndLocation, ctx, ¤tTracker, size ); + currentTracker.addChild( tracker ); + } + + if( !ctx.completedCycle() && !tracker->isComplete() ) { + if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun ) + tracker->moveNext(); + tracker->open(); + } + + return *tracker; + } + + int IndexTracker::index() const { return m_index; } + + void IndexTracker::moveNext() { + m_index++; + m_children.clear(); + } + + void IndexTracker::close() { + TrackerBase::close(); + if( m_runState == CompletedSuccessfully && m_index < m_size-1 ) + m_runState = Executing; + } + +} // namespace TestCaseTracking + +using TestCaseTracking::ITracker; +using TestCaseTracking::TrackerContext; +using TestCaseTracking::SectionTracker; +using TestCaseTracking::IndexTracker; + +} // namespace Catch + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif +// end catch_test_case_tracker.cpp +// start catch_test_registry.cpp + +namespace Catch { + + auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker* { + return new(std::nothrow) TestInvokerAsFunction( testAsFunction ); + } + + NameAndTags::NameAndTags( StringRef const& name_ , StringRef const& tags_ ) noexcept : name( name_ ), tags( tags_ ) {} + + AutoReg::AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept { + CATCH_TRY { + getMutableRegistryHub() + .registerTest( + makeTestCase( + invoker, + extractClassName( classOrMethod ), + nameAndTags, + lineInfo)); + } CATCH_CATCH_ALL { + // Do not throw when constructing global objects, instead register the exception to be processed later + getMutableRegistryHub().registerStartupException(); + } + } + + AutoReg::~AutoReg() = default; +} +// end catch_test_registry.cpp +// start catch_test_spec.cpp + +#include <algorithm> +#include <string> +#include <vector> +#include <memory> + +namespace Catch { + + TestSpec::Pattern::~Pattern() = default; + TestSpec::NamePattern::~NamePattern() = default; + TestSpec::TagPattern::~TagPattern() = default; + TestSpec::ExcludedPattern::~ExcludedPattern() = default; + + TestSpec::NamePattern::NamePattern( std::string const& name ) + : m_wildcardPattern( toLower( name ), CaseSensitive::No ) + {} + bool TestSpec::NamePattern::matches( TestCaseInfo const& testCase ) const { + return m_wildcardPattern.matches( toLower( testCase.name ) ); + } + + TestSpec::TagPattern::TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {} + bool TestSpec::TagPattern::matches( TestCaseInfo const& testCase ) const { + return std::find(begin(testCase.lcaseTags), + end(testCase.lcaseTags), + m_tag) != end(testCase.lcaseTags); + } + + TestSpec::ExcludedPattern::ExcludedPattern( PatternPtr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {} + bool TestSpec::ExcludedPattern::matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); } + + bool TestSpec::Filter::matches( TestCaseInfo const& testCase ) const { + // All patterns in a filter must match for the filter to be a match + for( auto const& pattern : m_patterns ) { + if( !pattern->matches( testCase ) ) + return false; + } + return true; + } + + bool TestSpec::hasFilters() const { + return !m_filters.empty(); + } + bool TestSpec::matches( TestCaseInfo const& testCase ) const { + // A TestSpec matches if any filter matches + for( auto const& filter : m_filters ) + if( filter.matches( testCase ) ) + return true; + return false; + } +} +// end catch_test_spec.cpp +// start catch_test_spec_parser.cpp + +namespace Catch { + + TestSpecParser::TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {} + + TestSpecParser& TestSpecParser::parse( std::string const& arg ) { + m_mode = None; + m_exclusion = false; + m_start = std::string::npos; + m_arg = m_tagAliases->expandAliases( arg ); + m_escapeChars.clear(); + for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) + visitChar( m_arg[m_pos] ); + if( m_mode == Name ) + addPattern<TestSpec::NamePattern>(); + return *this; + } + TestSpec TestSpecParser::testSpec() { + addFilter(); + return m_testSpec; + } + + void TestSpecParser::visitChar( char c ) { + if( m_mode == None ) { + switch( c ) { + case ' ': return; + case '~': m_exclusion = true; return; + case '[': return startNewMode( Tag, ++m_pos ); + case '"': return startNewMode( QuotedName, ++m_pos ); + case '\\': return escape(); + default: startNewMode( Name, m_pos ); break; + } + } + if( m_mode == Name ) { + if( c == ',' ) { + addPattern<TestSpec::NamePattern>(); + addFilter(); + } + else if( c == '[' ) { + if( subString() == "exclude:" ) + m_exclusion = true; + else + addPattern<TestSpec::NamePattern>(); + startNewMode( Tag, ++m_pos ); + } + else if( c == '\\' ) + escape(); + } + else if( m_mode == EscapedName ) + m_mode = Name; + else if( m_mode == QuotedName && c == '"' ) + addPattern<TestSpec::NamePattern>(); + else if( m_mode == Tag && c == ']' ) + addPattern<TestSpec::TagPattern>(); + } + void TestSpecParser::startNewMode( Mode mode, std::size_t start ) { + m_mode = mode; + m_start = start; + } + void TestSpecParser::escape() { + if( m_mode == None ) + m_start = m_pos; + m_mode = EscapedName; + m_escapeChars.push_back( m_pos ); + } + std::string TestSpecParser::subString() const { return m_arg.substr( m_start, m_pos - m_start ); } + + void TestSpecParser::addFilter() { + if( !m_currentFilter.m_patterns.empty() ) { + m_testSpec.m_filters.push_back( m_currentFilter ); + m_currentFilter = TestSpec::Filter(); + } + } + + TestSpec parseTestSpec( std::string const& arg ) { + return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec(); + } + +} // namespace Catch +// end catch_test_spec_parser.cpp +// start catch_timer.cpp + +#include <chrono> + +static const uint64_t nanosecondsInSecond = 1000000000; + +namespace Catch { + + auto getCurrentNanosecondsSinceEpoch() -> uint64_t { + return std::chrono::duration_cast<std::chrono::nanoseconds>( std::chrono::high_resolution_clock::now().time_since_epoch() ).count(); + } + + namespace { + auto estimateClockResolution() -> uint64_t { + uint64_t sum = 0; + static const uint64_t iterations = 1000000; + + auto startTime = getCurrentNanosecondsSinceEpoch(); + + for( std::size_t i = 0; i < iterations; ++i ) { + + uint64_t ticks; + uint64_t baseTicks = getCurrentNanosecondsSinceEpoch(); + do { + ticks = getCurrentNanosecondsSinceEpoch(); + } while( ticks == baseTicks ); + + auto delta = ticks - baseTicks; + sum += delta; + + // If we have been calibrating for over 3 seconds -- the clock + // is terrible and we should move on. + // TBD: How to signal that the measured resolution is probably wrong? + if (ticks > startTime + 3 * nanosecondsInSecond) { + return sum / i; + } + } + + // We're just taking the mean, here. To do better we could take the std. dev and exclude outliers + // - and potentially do more iterations if there's a high variance. + return sum/iterations; + } + } + auto getEstimatedClockResolution() -> uint64_t { + static auto s_resolution = estimateClockResolution(); + return s_resolution; + } + + 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<unsigned int>(getElapsedMicroseconds()/1000); + } + auto Timer::getElapsedSeconds() const -> double { + return getElapsedMicroseconds()/1000000.0; + } + +} // namespace Catch +// end catch_timer.cpp +// start catch_tostring.cpp + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +# pragma clang diagnostic ignored "-Wglobal-constructors" +#endif + +// Enable specific decls locally +#if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +#define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +#endif + +#include <cmath> +#include <iomanip> + +namespace Catch { + +namespace Detail { + + const std::string unprintableString = "{?}"; + + namespace { + const int hexThreshold = 255; + + struct Endianness { + enum Arch { Big, Little }; + + static Arch which() { + union _{ + int asInt; + char asChar[sizeof (int)]; + } u; + + u.asInt = 1; + return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little; + } + }; + } + + std::string rawMemoryToString( const void *object, std::size_t size ) { + // Reverse order for little endian architectures + int i = 0, end = static_cast<int>( size ), inc = 1; + if( Endianness::which() == Endianness::Little ) { + i = end-1; + end = inc = -1; + } + + unsigned char const *bytes = static_cast<unsigned char const *>(object); + ReusableStringStream rss; + rss << "0x" << std::setfill('0') << std::hex; + for( ; i != end; i += inc ) + rss << std::setw(2) << static_cast<unsigned>(bytes[i]); + return rss.str(); + } +} + +template<typename T> +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; +} + +//// ======================================================= //// +// +// Out-of-line defs for full specialization of StringMaker +// +//// ======================================================= //// + +std::string StringMaker<std::string>::convert(const std::string& str) { + if (!getCurrentContext().getConfig()->showInvisibles()) { + return '"' + str + '"'; + } + + std::string s("\""); + for (char c : str) { + switch (c) { + case '\n': + s.append("\\n"); + break; + case '\t': + s.append("\\t"); + break; + default: + s.push_back(c); + break; + } + } + s.append("\""); + return s; +} + +#ifdef CATCH_CONFIG_CPP17_STRING_VIEW +std::string StringMaker<std::string_view>::convert(std::string_view str) { + return ::Catch::Detail::stringify(std::string{ str }); +} +#endif + +std::string StringMaker<char const*>::convert(char const* str) { + if (str) { + return ::Catch::Detail::stringify(std::string{ str }); + } else { + return{ "{null string}" }; + } +} +std::string StringMaker<char*>::convert(char* str) { + if (str) { + return ::Catch::Detail::stringify(std::string{ str }); + } else { + return{ "{null string}" }; + } +} + +#ifdef CATCH_CONFIG_WCHAR +std::string StringMaker<std::wstring>::convert(const std::wstring& wstr) { + std::string s; + s.reserve(wstr.size()); + for (auto c : wstr) { + s += (c <= 0xff) ? static_cast<char>(c) : '?'; + } + return ::Catch::Detail::stringify(s); +} + +# ifdef CATCH_CONFIG_CPP17_STRING_VIEW +std::string StringMaker<std::wstring_view>::convert(std::wstring_view str) { + return StringMaker<std::wstring>::convert(std::wstring(str)); +} +# endif + +std::string StringMaker<wchar_t const*>::convert(wchar_t const * str) { + if (str) { + return ::Catch::Detail::stringify(std::wstring{ str }); + } else { + return{ "{null string}" }; + } +} +std::string StringMaker<wchar_t *>::convert(wchar_t * str) { + if (str) { + return ::Catch::Detail::stringify(std::wstring{ str }); + } else { + return{ "{null string}" }; + } +} +#endif + +std::string StringMaker<int>::convert(int value) { + return ::Catch::Detail::stringify(static_cast<long long>(value)); +} +std::string StringMaker<long>::convert(long value) { + return ::Catch::Detail::stringify(static_cast<long long>(value)); +} +std::string StringMaker<long long>::convert(long long value) { + ReusableStringStream rss; + rss << value; + if (value > Detail::hexThreshold) { + rss << " (0x" << std::hex << value << ')'; + } + return rss.str(); +} + +std::string StringMaker<unsigned int>::convert(unsigned int value) { + return ::Catch::Detail::stringify(static_cast<unsigned long long>(value)); +} +std::string StringMaker<unsigned long>::convert(unsigned long value) { + return ::Catch::Detail::stringify(static_cast<unsigned long long>(value)); +} +std::string StringMaker<unsigned long long>::convert(unsigned long long value) { + ReusableStringStream rss; + rss << value; + if (value > Detail::hexThreshold) { + rss << " (0x" << std::hex << value << ')'; + } + return rss.str(); +} + +std::string StringMaker<bool>::convert(bool b) { + return b ? "true" : "false"; +} + +std::string StringMaker<signed char>::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<unsigned int>(value)); + } else { + char chstr[] = "' '"; + chstr[1] = value; + return chstr; + } +} +std::string StringMaker<char>::convert(char c) { + return ::Catch::Detail::stringify(static_cast<signed char>(c)); +} +std::string StringMaker<unsigned char>::convert(unsigned char c) { + return ::Catch::Detail::stringify(static_cast<char>(c)); +} + +std::string StringMaker<std::nullptr_t>::convert(std::nullptr_t) { + return "nullptr"; +} + +std::string StringMaker<float>::convert(float value) { + return fpToString(value, 5) + 'f'; +} +std::string StringMaker<double>::convert(double value) { + return fpToString(value, 10); +} + +std::string ratio_string<std::atto>::symbol() { return "a"; } +std::string ratio_string<std::femto>::symbol() { return "f"; } +std::string ratio_string<std::pico>::symbol() { return "p"; } +std::string ratio_string<std::nano>::symbol() { return "n"; } +std::string ratio_string<std::micro>::symbol() { return "u"; } +std::string ratio_string<std::milli>::symbol() { return "m"; } + +} // end namespace Catch + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif + +// end catch_tostring.cpp +// start catch_totals.cpp + +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::size_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; + } + +} +// end catch_totals.cpp +// start catch_uncaught_exceptions.cpp + +#include <exception> + +namespace Catch { + bool uncaught_exceptions() { +#if defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) + return std::uncaught_exceptions() > 0; +#else + return std::uncaught_exception(); +#endif + } +} // end namespace Catch +// end catch_uncaught_exceptions.cpp +// start catch_version.cpp + +#include <ostream> + +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( 2, 5, 0, "", 0 ); + return version; + } + +} +// end catch_version.cpp +// start catch_wildcard_pattern.cpp + +#include <sstream> + +namespace Catch { + + WildcardPattern::WildcardPattern( std::string const& pattern, + CaseSensitive::Choice caseSensitivity ) + : m_caseSensitivity( caseSensitivity ), + m_pattern( adjustCase( 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<WildcardPosition>( m_wildcard | WildcardAtEnd ); + } + } + + bool WildcardPattern::matches( std::string const& str ) const { + switch( m_wildcard ) { + case NoWildcard: + return m_pattern == adjustCase( str ); + case WildcardAtStart: + return endsWith( adjustCase( str ), m_pattern ); + case WildcardAtEnd: + return startsWith( adjustCase( str ), m_pattern ); + case WildcardAtBothEnds: + return contains( adjustCase( str ), m_pattern ); + default: + CATCH_INTERNAL_ERROR( "Unknown enum" ); + } + } + + std::string WildcardPattern::adjustCase( std::string const& str ) const { + return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str; + } +} +// end catch_wildcard_pattern.cpp +// start catch_xmlwriter.cpp + +#include <iomanip> + +using uchar = unsigned char; + +namespace Catch { + +namespace { + + size_t trailingBytes(unsigned char c) { + if ((c & 0xE0) == 0xC0) { + return 2; + } + if ((c & 0xF0) == 0xE0) { + return 3; + } + if ((c & 0xF8) == 0xF0) { + return 4; + } + CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); + } + + uint32_t headerValue(unsigned char c) { + if ((c & 0xE0) == 0xC0) { + return c & 0x1F; + } + if ((c & 0xF0) == 0xE0) { + return c & 0x0F; + } + if ((c & 0xF8) == 0xF0) { + return c & 0x07; + } + CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); + } + + void hexEscapeChar(std::ostream& os, unsigned char c) { + os << "\\x" + << std::uppercase << std::hex << std::setfill('0') << std::setw(2) + << static_cast<int>(c); + } + +} // anonymous namespace + + XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat ) + : 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 ) { + uchar c = 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) { + uchar nc = m_str[idx + n]; + valid &= ((nc & 0xC0) == 0x80); + value = (value << 6) | (nc & 0x3F); + } + + if ( + // Wrong bit pattern of following bytes + (!valid) || + // Overlong encodings + (value < 0x80) || + (0x80 <= value && value < 0x800 && encBytes > 2) || + (0x800 < value && value < 0x10000 && encBytes > 3) || + // Encoded value out of range + (value >= 0x110000) + ) { + hexEscapeChar(os, c); + break; + } + + // If we got here, this is in fact a valid(ish) utf-8 sequence + for (std::size_t n = 0; n < encBytes; ++n) { + os << m_str[idx + n]; + } + idx += encBytes - 1; + break; + } + } + } + + std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) { + xmlEncode.encodeTo( os ); + return os; + } + + XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer ) + : m_writer( writer ) + {} + + XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) noexcept + : m_writer( other.m_writer ){ + other.m_writer = nullptr; + } + XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) noexcept { + if ( m_writer ) { + m_writer->endElement(); + } + m_writer = other.m_writer; + other.m_writer = nullptr; + return *this; + } + + XmlWriter::ScopedElement::~ScopedElement() { + if( m_writer ) + m_writer->endElement(); + } + + XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, bool indent ) { + m_writer->writeText( text, indent ); + return *this; + } + + XmlWriter::XmlWriter( std::ostream& os ) : m_os( os ) + { + writeDeclaration(); + } + + XmlWriter::~XmlWriter() { + while( !m_tags.empty() ) + endElement(); + } + + XmlWriter& XmlWriter::startElement( std::string const& name ) { + ensureTagClosed(); + newlineIfNecessary(); + m_os << m_indent << '<' << name; + m_tags.push_back( name ); + m_indent += " "; + m_tagIsOpen = true; + return *this; + } + + XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name ) { + ScopedElement scoped( this ); + startElement( name ); + return scoped; + } + + XmlWriter& XmlWriter::endElement() { + newlineIfNecessary(); + m_indent = m_indent.substr( 0, m_indent.size()-2 ); + if( m_tagIsOpen ) { + m_os << "/>"; + m_tagIsOpen = false; + } + else { + m_os << m_indent << "</" << m_tags.back() << ">"; + } + m_os << std::endl; + m_tags.pop_back(); + return *this; + } + + XmlWriter& XmlWriter::writeAttribute( std::string const& name, std::string const& attribute ) { + if( !name.empty() && !attribute.empty() ) + m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"'; + return *this; + } + + XmlWriter& XmlWriter::writeAttribute( std::string const& name, bool attribute ) { + m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"'; + return *this; + } + + XmlWriter& XmlWriter::writeText( std::string const& text, bool indent ) { + if( !text.empty() ){ + bool tagWasOpen = m_tagIsOpen; + ensureTagClosed(); + if( tagWasOpen && indent ) + m_os << m_indent; + m_os << XmlEncode( text ); + m_needsNewline = true; + } + return *this; + } + + XmlWriter& XmlWriter::writeComment( std::string const& text ) { + ensureTagClosed(); + m_os << m_indent << "<!--" << text << "-->"; + m_needsNewline = true; + return *this; + } + + void XmlWriter::writeStylesheetRef( std::string const& url ) { + m_os << "<?xml-stylesheet type=\"text/xsl\" href=\"" << url << "\"?>\n"; + } + + XmlWriter& XmlWriter::writeBlankLine() { + ensureTagClosed(); + m_os << '\n'; + return *this; + } + + void XmlWriter::ensureTagClosed() { + if( m_tagIsOpen ) { + m_os << ">" << std::endl; + m_tagIsOpen = false; + } + } + + void XmlWriter::writeDeclaration() { + m_os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; + } + + void XmlWriter::newlineIfNecessary() { + if( m_needsNewline ) { + m_os << std::endl; + m_needsNewline = false; + } + } +} +// end catch_xmlwriter.cpp +// start catch_reporter_bases.cpp + +#include <cstring> +#include <cfloat> +#include <cstdio> +#include <cassert> +#include <memory> + +namespace Catch { + void prepareExpandedExpression(AssertionResult& result) { + result.getExpandedExpression(); + } + + // 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 + sprintf_s(buffer, "%.3f", duration); +#else + sprintf(buffer, "%.3f", duration); +#endif + return std::string(buffer); + } + + TestEventListenerBase::TestEventListenerBase(ReporterConfig const & _config) + :StreamingReporterBase(_config) {} + + std::set<Verbosity> TestEventListenerBase::getSupportedVerbosities() { + return { Verbosity::Quiet, Verbosity::Normal, Verbosity::High }; + } + + void TestEventListenerBase::assertionStarting(AssertionInfo const &) {} + + bool TestEventListenerBase::assertionEnded(AssertionStats const &) { + return false; + } + +} // end namespace Catch +// end catch_reporter_bases.cpp +// start catch_reporter_compact.cpp + +namespace { + +#ifdef CATCH_PLATFORM_MAC + const char* failedString() { return "FAILED"; } + const char* passedString() { return "PASSED"; } +#else + const char* failedString() { return "failed"; } + const char* passedString() { return "passed"; } +#endif + + // Colour::LightGrey + Catch::Colour::Code dimColour() { return Catch::Colour::FileName; } + + std::string bothOrAll( std::size_t count ) { + return count == 1 ? std::string() : + count == 2 ? "both " : "all " ; + } + +} // anon namespace + +namespace Catch { +namespace { +// 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) { + if (totals.testCases.total() == 0) { + out << "No tests ran."; + } else if (totals.testCases.failed == totals.testCases.total()) { + Colour colour(Colour::ResultError); + const std::string qualify_assertions_failed = + totals.assertions.failed == totals.assertions.total() ? + bothOrAll(totals.assertions.failed) : std::string(); + out << + "Failed " << bothOrAll(totals.testCases.failed) + << pluralise(totals.testCases.failed, "test case") << ", " + "failed " << qualify_assertions_failed << + pluralise(totals.assertions.failed, "assertion") << '.'; + } else if (totals.assertions.total() == 0) { + out << + "Passed " << bothOrAll(totals.testCases.total()) + << pluralise(totals.testCases.total(), "test case") + << " (no assertions)."; + } else if (totals.assertions.failed) { + Colour colour(Colour::ResultError); + out << + "Failed " << pluralise(totals.testCases.failed, "test case") << ", " + "failed " << pluralise(totals.assertions.failed, "assertion") << '.'; + } else { + Colour colour(Colour::ResultSuccess); + out << + "Passed " << bothOrAll(totals.testCases.passed) + << pluralise(totals.testCases.passed, "test case") << + " with " << pluralise(totals.assertions.passed, "assertion") << '.'; + } +} + +// 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) + : stream(_stream) + , result(_stats.assertionResult) + , messages(_stats.infoMessages) + , itMessage(_stats.infoMessages.begin()) + , printInfoMessages(_printInfoMessages) {} + + void print() { + printSourceInfo(); + + itMessage = messages.begin(); + + switch (result.getResultType()) { + case ResultWas::Ok: + printResultType(Colour::ResultSuccess, passedString()); + printOriginalExpression(); + printReconstructedExpression(); + if (!result.hasExpression()) + printRemainingMessages(Colour::None); + else + printRemainingMessages(); + break; + case ResultWas::ExpressionFailed: + if (result.isOk()) + printResultType(Colour::ResultSuccess, failedString() + std::string(" - but was ok")); + else + printResultType(Colour::Error, failedString()); + printOriginalExpression(); + printReconstructedExpression(); + printRemainingMessages(); + break; + case ResultWas::ThrewException: + printResultType(Colour::Error, failedString()); + printIssue("unexpected exception with message:"); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::FatalErrorCondition: + printResultType(Colour::Error, failedString()); + printIssue("fatal error condition with message:"); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::DidntThrowException: + printResultType(Colour::Error, failedString()); + printIssue("expected exception, got none"); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::Info: + printResultType(Colour::None, "info"); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::Warning: + printResultType(Colour::None, "warning"); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::ExplicitFailure: + printResultType(Colour::Error, failedString()); + 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 { + Colour colourGuard(Colour::FileName); + stream << result.getSourceInfo() << ':'; + } + + void printResultType(Colour::Code colour, std::string const& passOrFail) const { + if (!passOrFail.empty()) { + { + Colour colourGuard(colour); + stream << ' ' << passOrFail; + } + stream << ':'; + } + } + + void printIssue(std::string const& issue) const { + stream << ' ' << issue; + } + + void printExpressionWas() { + if (result.hasExpression()) { + stream << ';'; + { + Colour colour(dimColour()); + stream << " expression was:"; + } + printOriginalExpression(); + } + } + + void printOriginalExpression() const { + if (result.hasExpression()) { + stream << ' ' << result.getExpression(); + } + } + + void printReconstructedExpression() const { + if (result.hasExpandedExpression()) { + { + Colour colour(dimColour()); + stream << " for: "; + } + stream << result.getExpandedExpression(); + } + } + + void printMessage() { + if (itMessage != messages.end()) { + stream << " '" << itMessage->message << '\''; + ++itMessage; + } + } + + void printRemainingMessages(Colour::Code colour = dimColour()) { + if (itMessage == messages.end()) + return; + + // using messages.end() directly yields (or auto) compilation error: + std::vector<MessageInfo>::const_iterator itEnd = messages.end(); + const std::size_t N = static_cast<std::size_t>(std::distance(itMessage, itEnd)); + + { + Colour colourGuard(colour); + 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 << '\''; + if (++itMessage != itEnd) { + Colour colourGuard(dimColour()); + stream << " and"; + } + } + } + } + +private: + std::ostream& stream; + AssertionResult const& result; + std::vector<MessageInfo> messages; + std::vector<MessageInfo>::const_iterator itMessage; + bool printInfoMessages; +}; + +} // anon namespace + + std::string CompactReporter::getDescription() { + return "Reports test results on a single line, suitable for IDEs"; + } + + ReporterPreferences CompactReporter::getPreferences() const { + return m_reporterPrefs; + } + + void CompactReporter::noMatchingTestCases( std::string const& spec ) { + stream << "No test cases matched '" << spec << '\'' << std::endl; + } + + void CompactReporter::assertionStarting( AssertionInfo const& ) {} + + bool 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 false; + printInfoMessages = false; + } + + AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); + printer.print(); + + stream << std::endl; + return true; + } + + void CompactReporter::sectionEnded(SectionStats const& _sectionStats) { + if (m_config->showDurations() == ShowDurations::Always) { + stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; + } + } + + void CompactReporter::testRunEnded( TestRunStats const& _testRunStats ) { + printTotals( stream, _testRunStats.totals ); + stream << '\n' << std::endl; + StreamingReporterBase::testRunEnded( _testRunStats ); + } + + CompactReporter::~CompactReporter() {} + + CATCH_REGISTER_REPORTER( "compact", CompactReporter ) + +} // end namespace Catch +// end catch_reporter_compact.cpp +// start catch_reporter_console.cpp + +#include <cfloat> +#include <cstdio> + +#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 { + +namespace { + +// Formatter impl for ConsoleReporter +class ConsoleAssertionPrinter { +public: + ConsoleAssertionPrinter& operator= (ConsoleAssertionPrinter const&) = delete; + ConsoleAssertionPrinter(ConsoleAssertionPrinter const&) = delete; + ConsoleAssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages) + : stream(_stream), + stats(_stats), + result(_stats.assertionResult), + colour(Colour::None), + message(result.getMessage()), + messages(_stats.infoMessages), + printInfoMessages(_printInfoMessages) { + switch (result.getResultType()) { + case ResultWas::Ok: + colour = Colour::Success; + passOrFail = "PASSED"; + //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"; + } else { + colour = Colour::Error; + passOrFail = "FAILED"; + } + 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"; + 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"; + messageLabel = "due to a fatal error condition"; + break; + case ResultWas::DidntThrowException: + colour = Colour::Error; + passOrFail = "FAILED"; + 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"; + 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 **"; + 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()) { + Colour colourGuard(colour); + stream << passOrFail << ":\n"; + } + } + void printOriginalExpression() const { + if (result.hasExpression()) { + Colour colourGuard(Colour::OriginalExpression); + stream << " "; + stream << result.getExpressionInMacro(); + stream << '\n'; + } + } + void printReconstructedExpression() const { + if (result.hasExpandedExpression()) { + stream << "with expansion:\n"; + Colour colourGuard(Colour::ReconstructedExpression); + stream << 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 << Column(msg.message).indent(2) << '\n'; + } + } + void printSourceInfo() const { + Colour colourGuard(Colour::FileName); + stream << result.getSourceInfo() << ": "; + } + + std::ostream& stream; + AssertionStats const& stats; + AssertionResult const& result; + Colour::Code colour; + std::string passOrFail; + std::string messageLabel; + std::string message; + std::vector<MessageInfo> messages; + bool printInfoMessages; +}; + +std::size_t makeRatio(std::size_t number, std::size_t total) { + std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number / total : 0; + return (ratio == 0 && number > 0) ? 1 : 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; +} + +struct ColumnInfo { + enum Justification { Left, Right }; + std::string name; + int 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; + + uint64_t m_inNanoseconds; + Unit m_units; + +public: + explicit Duration(uint64_t 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<double>(s_nanosecondsInAMicrosecond); + case Unit::Milliseconds: + return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMillisecond); + case Unit::Seconds: + return m_inNanoseconds / static_cast<double>(s_nanosecondsInASecond); + case Unit::Minutes: + return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMinute); + default: + return static_cast<double>(m_inNanoseconds); + } + } + auto unitsAsString() const -> std::string { + switch (m_units) { + case Unit::Nanoseconds: + return "ns"; + case Unit::Microseconds: + return "µs"; + case Unit::Milliseconds: + return "ms"; + case Unit::Seconds: + return "s"; + case Unit::Minutes: + return "m"; + default: + return "** internal error **"; + } + + } + 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<ColumnInfo> m_columnInfos; + std::ostringstream m_oss; + int m_currentColumn = -1; + bool m_isOpen = false; + +public: + TablePrinter( std::ostream& os, std::vector<ColumnInfo> columnInfos ) + : m_os( os ), + m_columnInfos( std::move( columnInfos ) ) {} + + auto columnInfos() const -> std::vector<ColumnInfo> const& { + return m_columnInfos; + } + + void open() { + if (!m_isOpen) { + m_isOpen = true; + *this << RowBreak(); + for (auto const& info : m_columnInfos) + *this << info.name << ColumnBreak(); + *this << RowBreak(); + m_os << Catch::getLineOfChars<'-'>() << "\n"; + } + } + void close() { + if (m_isOpen) { + *this << RowBreak(); + m_os << std::endl; + m_isOpen = false; + } + } + + template<typename T> + 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(); + // This takes account of utf8 encodings + auto strSize = Catch::StringRef(colStr).numberOfCharacters(); + tp.m_oss.str(""); + tp.open(); + if (tp.m_currentColumn == static_cast<int>(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 + 2 < static_cast<std::size_t>(colInfo.width)) + ? std::string(colInfo.width - (strSize + 2), ' ') + : std::string(); + if (colInfo.justification == ColumnInfo::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 const& config) + : StreamingReporterBase(config), + m_tablePrinter(new TablePrinter(config.stream(), + { + { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 32, ColumnInfo::Left }, + { "iters", 8, ColumnInfo::Right }, + { "elapsed ns", 14, ColumnInfo::Right }, + { "average", 14, ColumnInfo::Right } + })) {} +ConsoleReporter::~ConsoleReporter() = default; + +std::string ConsoleReporter::getDescription() { + return "Reports test results as plain lines of text"; +} + +void ConsoleReporter::noMatchingTestCases(std::string const& spec) { + stream << "No test cases matched '" << spec << '\'' << std::endl; +} + +void ConsoleReporter::assertionStarting(AssertionInfo const&) {} + +bool 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 false; + + lazyPrint(); + + ConsoleAssertionPrinter printer(stream, _assertionStats, includeResults); + printer.print(); + stream << std::endl; + return true; +} + +void ConsoleReporter::sectionStarting(SectionInfo const& _sectionInfo) { + m_headerPrinted = false; + StreamingReporterBase::sectionStarting(_sectionInfo); +} +void ConsoleReporter::sectionEnded(SectionStats const& _sectionStats) { + m_tablePrinter->close(); + if (_sectionStats.missingAssertions) { + lazyPrint(); + Colour colour(Colour::ResultError); + if (m_sectionStack.size() > 1) + stream << "\nNo assertions in section"; + else + stream << "\nNo assertions in test case"; + stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; + } + if (m_config->showDurations() == ShowDurations::Always) { + stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; + } + if (m_headerPrinted) { + m_headerPrinted = false; + } + StreamingReporterBase::sectionEnded(_sectionStats); +} + +void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) { + lazyPrintWithoutClosingBenchmarkTable(); + + auto nameCol = Column( info.name ).width( static_cast<std::size_t>( 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::benchmarkEnded(BenchmarkStats const& stats) { + Duration average(stats.elapsedTimeInNanoseconds / stats.iterations); + (*m_tablePrinter) + << stats.iterations << ColumnBreak() + << stats.elapsedTimeInNanoseconds << ColumnBreak() + << average << ColumnBreak(); +} + +void ConsoleReporter::testCaseEnded(TestCaseStats const& _testCaseStats) { + m_tablePrinter->close(); + StreamingReporterBase::testCaseEnded(_testCaseStats); + m_headerPrinted = false; +} +void ConsoleReporter::testGroupEnded(TestGroupStats const& _testGroupStats) { + if (currentGroupInfo.used) { + printSummaryDivider(); + stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; + printTotals(_testGroupStats.totals); + stream << '\n' << std::endl; + } + StreamingReporterBase::testGroupEnded(_testGroupStats); +} +void ConsoleReporter::testRunEnded(TestRunStats const& _testRunStats) { + printTotalsDivider(_testRunStats.totals); + printTotals(_testRunStats.totals); + stream << std::endl; + StreamingReporterBase::testRunEnded(_testRunStats); +} + +void ConsoleReporter::lazyPrint() { + + m_tablePrinter->close(); + lazyPrintWithoutClosingBenchmarkTable(); +} + +void ConsoleReporter::lazyPrintWithoutClosingBenchmarkTable() { + + if (!currentTestRunInfo.used) + lazyPrintRunInfo(); + if (!currentGroupInfo.used) + lazyPrintGroupInfo(); + + if (!m_headerPrinted) { + printTestCaseAndSectionHeader(); + m_headerPrinted = true; + } +} +void ConsoleReporter::lazyPrintRunInfo() { + stream << '\n' << getLineOfChars<'~'>() << '\n'; + Colour colour(Colour::SecondaryText); + stream << currentTestRunInfo->name + << " is a Catch v" << libraryVersion() << " host application.\n" + << "Run with -? for options\n\n"; + + if (m_config->rngSeed() != 0) + stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n"; + + currentTestRunInfo.used = true; +} +void ConsoleReporter::lazyPrintGroupInfo() { + if (!currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1) { + printClosedHeader("Group: " + currentGroupInfo->name); + currentGroupInfo.used = true; + } +} +void ConsoleReporter::printTestCaseAndSectionHeader() { + assert(!m_sectionStack.empty()); + printOpenHeader(currentTestCaseInfo->name); + + if (m_sectionStack.size() > 1) { + Colour colourGuard(Colour::Headers); + + 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; + + if (!lineInfo.empty()) { + stream << getLineOfChars<'-'>() << '\n'; + Colour colourGuard(Colour::FileName); + stream << lineInfo << '\n'; + } + stream << getLineOfChars<'.'>() << '\n' << std::endl; +} + +void ConsoleReporter::printClosedHeader(std::string const& _name) { + printOpenHeader(_name); + stream << getLineOfChars<'.'>() << '\n'; +} +void ConsoleReporter::printOpenHeader(std::string const& _name) { + stream << getLineOfChars<'-'>() << '\n'; + { + Colour colourGuard(Colour::Headers); + printHeaderString(_name); + } +} + +// if string has a : in first line will set indent to follow it on +// subsequent lines +void ConsoleReporter::printHeaderString(std::string const& _string, std::size_t indent) { + std::size_t i = _string.find(": "); + if (i != std::string::npos) + i += 2; + else + i = 0; + stream << Column(_string).indent(indent + i).initialIndent(indent) << '\n'; +} + +struct SummaryColumn { + + SummaryColumn( std::string _label, Colour::Code _colour ) + : label( std::move( _label ) ), + colour( _colour ) {} + SummaryColumn addRow( std::size_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<std::string> rows; + +}; + +void ConsoleReporter::printTotals( Totals const& totals ) { + if (totals.testCases.total() == 0) { + stream << Colour(Colour::Warning) << "No tests ran\n"; + } else if (totals.assertions.total() > 0 && totals.testCases.allPassed()) { + stream << Colour(Colour::ResultSuccess) << "All tests passed"; + stream << " (" + << pluralise(totals.assertions.passed, "assertion") << " in " + << pluralise(totals.testCases.passed, "test case") << ')' + << '\n'; + } else { + + std::vector<SummaryColumn> 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", columns, 0); + printSummaryRow("assertions", columns, 1); + } +} +void ConsoleReporter::printSummaryRow(std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row) { + for (auto col : cols) { + std::string value = col.rows[row]; + if (col.label.empty()) { + stream << label << ": "; + if (value != "0") + stream << value; + else + stream << Colour(Colour::Warning) << "- none -"; + } else if (value != "0") { + stream << Colour(Colour::LightGrey) << " | "; + stream << Colour(col.colour) + << value << ' ' << col.label; + } + } + 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)--; + + stream << Colour(Colour::Error) << std::string(failedRatio, '='); + stream << Colour(Colour::ResultExpectedFailure) << std::string(failedButOkRatio, '='); + if (totals.testCases.allPassed()) + stream << Colour(Colour::ResultSuccess) << std::string(passedRatio, '='); + else + stream << Colour(Colour::Success) << std::string(passedRatio, '='); + } else { + stream << Colour(Colour::Warning) << std::string(CATCH_CONFIG_CONSOLE_WIDTH - 1, '='); + } + stream << '\n'; +} +void ConsoleReporter::printSummaryDivider() { + stream << getLineOfChars<'-'>() << '\n'; +} + +CATCH_REGISTER_REPORTER("console", ConsoleReporter) + +} // end namespace Catch + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif +// end catch_reporter_console.cpp +// start catch_reporter_junit.cpp + +#include <cassert> +#include <sstream> +#include <ctime> +#include <algorithm> + +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); + auto const 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); + } + + std::string fileNameTag(const std::vector<std::string> &tags) { + auto it = std::find_if(begin(tags), + end(tags), + [] (std::string const& tag) {return tag.front() == '#'; }); + if (it != tags.end()) + return it->substr(1); + return std::string(); + } + } // anonymous namespace + + JunitReporter::JunitReporter( ReporterConfig const& _config ) + : CumulativeReporterBase( _config ), + xml( _config.stream() ) + { + m_reporterPrefs.shouldRedirectStdOut = true; + m_reporterPrefs.shouldReportAllAssertions = true; + } + + JunitReporter::~JunitReporter() {} + + std::string JunitReporter::getDescription() { + return "Reports test results in an XML format that looks like Ant's junitreport target"; + } + + void JunitReporter::noMatchingTestCases( std::string const& /*spec*/ ) {} + + void JunitReporter::testRunStarting( TestRunInfo const& runInfo ) { + CumulativeReporterBase::testRunStarting( runInfo ); + xml.startElement( "testsuites" ); + } + + void JunitReporter::testGroupStarting( GroupInfo const& groupInfo ) { + suiteTimer.start(); + stdOutForSuite.clear(); + stdErrForSuite.clear(); + unexpectedExceptions = 0; + CumulativeReporterBase::testGroupStarting( groupInfo ); + } + + void JunitReporter::testCaseStarting( TestCaseInfo const& testCaseInfo ) { + m_okToFail = testCaseInfo.okToFail(); + } + + bool JunitReporter::assertionEnded( AssertionStats const& assertionStats ) { + if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail ) + unexpectedExceptions++; + return CumulativeReporterBase::assertionEnded( assertionStats ); + } + + void JunitReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { + stdOutForSuite += testCaseStats.stdOut; + stdErrForSuite += testCaseStats.stdErr; + CumulativeReporterBase::testCaseEnded( testCaseStats ); + } + + void JunitReporter::testGroupEnded( TestGroupStats const& testGroupStats ) { + double suiteTime = suiteTimer.getElapsedSeconds(); + CumulativeReporterBase::testGroupEnded( testGroupStats ); + writeGroup( *m_testGroups.back(), suiteTime ); + } + + void JunitReporter::testRunEndedCumulative() { + xml.endElement(); + } + + void JunitReporter::writeGroup( TestGroupNode const& groupNode, double suiteTime ) { + XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" ); + TestGroupStats const& stats = groupNode.value; + xml.writeAttribute( "name", stats.groupInfo.name ); + xml.writeAttribute( "errors", unexpectedExceptions ); + xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions ); + xml.writeAttribute( "tests", stats.totals.assertions.total() ); + xml.writeAttribute( "hostname", "tbd" ); // !TBD + if( m_config->showDurations() == ShowDurations::Never ) + xml.writeAttribute( "time", "" ); + else + xml.writeAttribute( "time", suiteTime ); + xml.writeAttribute( "timestamp", getCurrentTimestamp() ); + + // Write test cases + for( auto const& child : groupNode.children ) + writeTestCase( *child ); + + xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite ), false ); + xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite ), false ); + } + + 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 = stats.testInfo.className; + + if( className.empty() ) { + className = fileNameTag(stats.testInfo.tags); + if ( className.empty() ) + className = "global"; + } + + if ( !m_config->name().empty() ) + className = m_config->name() + "." + className; + + writeSection( className, "", rootSection ); + } + + void JunitReporter::writeSection( std::string const& className, + std::string const& rootName, + SectionNode const& sectionNode ) { + std::string name = trim( sectionNode.stats.sectionInfo.name ); + if( !rootName.empty() ) + name = rootName + '/' + name; + + if( !sectionNode.assertions.empty() || + !sectionNode.stdOut.empty() || + !sectionNode.stdErr.empty() ) { + XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); + if( className.empty() ) { + xml.writeAttribute( "classname", name ); + xml.writeAttribute( "name", "root" ); + } + else { + xml.writeAttribute( "classname", className ); + xml.writeAttribute( "name", name ); + } + xml.writeAttribute( "time", ::Catch::Detail::stringify( sectionNode.stats.durationInSeconds ) ); + + writeAssertions( sectionNode ); + + if( !sectionNode.stdOut.empty() ) + xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false ); + if( !sectionNode.stdErr.empty() ) + xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false ); + } + for( auto const& childNode : sectionNode.childSections ) + if( className.empty() ) + writeSection( name, "", *childNode ); + else + writeSection( className, name, *childNode ); + } + + void JunitReporter::writeAssertions( SectionNode const& sectionNode ) { + for( auto const& assertion : sectionNode.assertions ) + writeAssertion( assertion ); + } + + 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: + 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 ); + + xml.writeAttribute( "message", result.getExpandedExpression() ); + xml.writeAttribute( "type", result.getTestMacroName() ); + + ReusableStringStream rss; + 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(), false ); + } + } + + CATCH_REGISTER_REPORTER( "junit", JunitReporter ) + +} // end namespace Catch +// end catch_reporter_junit.cpp +// start catch_reporter_listening.cpp + +#include <cassert> + +namespace Catch { + + ListeningReporter::ListeningReporter() { + // We will assume that listeners will always want all assertions + m_preferences.shouldReportAllAssertions = true; + } + + void ListeningReporter::addListener( IStreamingReporterPtr&& listener ) { + m_listeners.push_back( std::move( listener ) ); + } + + void ListeningReporter::addReporter(IStreamingReporterPtr&& reporter) { + assert(!m_reporter && "Listening reporter can wrap only 1 real reporter"); + m_reporter = std::move( reporter ); + m_preferences.shouldRedirectStdOut = m_reporter->getPreferences().shouldRedirectStdOut; + } + + ReporterPreferences ListeningReporter::getPreferences() const { + return m_preferences; + } + + std::set<Verbosity> ListeningReporter::getSupportedVerbosities() { + return std::set<Verbosity>{ }; + } + + void ListeningReporter::noMatchingTestCases( std::string const& spec ) { + for ( auto const& listener : m_listeners ) { + listener->noMatchingTestCases( spec ); + } + m_reporter->noMatchingTestCases( spec ); + } + + void ListeningReporter::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) { + for ( auto const& listener : m_listeners ) { + listener->benchmarkStarting( benchmarkInfo ); + } + m_reporter->benchmarkStarting( benchmarkInfo ); + } + void ListeningReporter::benchmarkEnded( BenchmarkStats const& benchmarkStats ) { + for ( auto const& listener : m_listeners ) { + listener->benchmarkEnded( benchmarkStats ); + } + m_reporter->benchmarkEnded( benchmarkStats ); + } + + void ListeningReporter::testRunStarting( TestRunInfo const& testRunInfo ) { + for ( auto const& listener : m_listeners ) { + listener->testRunStarting( testRunInfo ); + } + m_reporter->testRunStarting( testRunInfo ); + } + + void ListeningReporter::testGroupStarting( GroupInfo const& groupInfo ) { + for ( auto const& listener : m_listeners ) { + listener->testGroupStarting( groupInfo ); + } + m_reporter->testGroupStarting( groupInfo ); + } + + void ListeningReporter::testCaseStarting( TestCaseInfo const& testInfo ) { + for ( auto const& listener : m_listeners ) { + listener->testCaseStarting( testInfo ); + } + m_reporter->testCaseStarting( testInfo ); + } + + void ListeningReporter::sectionStarting( SectionInfo const& sectionInfo ) { + for ( auto const& listener : m_listeners ) { + listener->sectionStarting( sectionInfo ); + } + m_reporter->sectionStarting( sectionInfo ); + } + + void ListeningReporter::assertionStarting( AssertionInfo const& assertionInfo ) { + for ( auto const& listener : m_listeners ) { + listener->assertionStarting( assertionInfo ); + } + m_reporter->assertionStarting( assertionInfo ); + } + + // The return value indicates if the messages buffer should be cleared: + bool ListeningReporter::assertionEnded( AssertionStats const& assertionStats ) { + for( auto const& listener : m_listeners ) { + static_cast<void>( listener->assertionEnded( assertionStats ) ); + } + return m_reporter->assertionEnded( assertionStats ); + } + + void ListeningReporter::sectionEnded( SectionStats const& sectionStats ) { + for ( auto const& listener : m_listeners ) { + listener->sectionEnded( sectionStats ); + } + m_reporter->sectionEnded( sectionStats ); + } + + void ListeningReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { + for ( auto const& listener : m_listeners ) { + listener->testCaseEnded( testCaseStats ); + } + m_reporter->testCaseEnded( testCaseStats ); + } + + void ListeningReporter::testGroupEnded( TestGroupStats const& testGroupStats ) { + for ( auto const& listener : m_listeners ) { + listener->testGroupEnded( testGroupStats ); + } + m_reporter->testGroupEnded( testGroupStats ); + } + + void ListeningReporter::testRunEnded( TestRunStats const& testRunStats ) { + for ( auto const& listener : m_listeners ) { + listener->testRunEnded( testRunStats ); + } + m_reporter->testRunEnded( testRunStats ); + } + + void ListeningReporter::skipTest( TestCaseInfo const& testInfo ) { + for ( auto const& listener : m_listeners ) { + listener->skipTest( testInfo ); + } + m_reporter->skipTest( testInfo ); + } + + bool ListeningReporter::isMulti() const { + return true; + } + +} // end namespace Catch +// end catch_reporter_listening.cpp +// start catch_reporter_xml.cpp + +#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 const& _config ) + : StreamingReporterBase( _config ), + m_xml(_config.stream()) + { + m_reporterPrefs.shouldRedirectStdOut = true; + m_reporterPrefs.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", sourceInfo.file ) + .writeAttribute( "line", sourceInfo.line ); + } + + void XmlReporter::noMatchingTestCases( std::string const& s ) { + StreamingReporterBase::noMatchingTestCases( s ); + } + + void XmlReporter::testRunStarting( TestRunInfo const& testInfo ) { + 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() ); + if( m_config->rngSeed() != 0 ) + m_xml.scopedElement( "Randomness" ) + .writeAttribute( "seed", m_config->rngSeed() ); + } + + void XmlReporter::testGroupStarting( GroupInfo const& groupInfo ) { + StreamingReporterBase::testGroupStarting( groupInfo ); + m_xml.startElement( "Group" ) + .writeAttribute( "name", groupInfo.name ); + } + + void XmlReporter::testCaseStarting( TestCaseInfo const& testInfo ) { + StreamingReporterBase::testCaseStarting(testInfo); + 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(); + } + + void XmlReporter::sectionStarting( SectionInfo const& sectionInfo ) { + StreamingReporterBase::sectionStarting( sectionInfo ); + if( m_sectionDepth++ > 0 ) { + m_xml.startElement( "Section" ) + .writeAttribute( "name", trim( sectionInfo.name ) ); + writeSourceInfo( sectionInfo.lineInfo ); + m_xml.ensureTagClosed(); + } + } + + void XmlReporter::assertionStarting( AssertionInfo const& ) { } + + bool 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 <Info> 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 true; + + // Print the expression if there is one. + if( result.hasExpression() ) { + m_xml.startElement( "Expression" ) + .writeAttribute( "success", result.succeeded() ) + .writeAttribute( "type", 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(); + + return true; + } + + void XmlReporter::sectionEnded( SectionStats const& sectionStats ) { + StreamingReporterBase::sectionEnded( sectionStats ); + if( --m_sectionDepth > 0 ) { + XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" ); + e.writeAttribute( "successes", sectionStats.assertions.passed ); + e.writeAttribute( "failures", sectionStats.assertions.failed ); + e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk ); + + if ( m_config->showDurations() == ShowDurations::Always ) + e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds ); + + m_xml.endElement(); + } + } + + void XmlReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { + StreamingReporterBase::testCaseEnded( testCaseStats ); + XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" ); + e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() ); + + 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(); + } + + void XmlReporter::testGroupEnded( TestGroupStats const& testGroupStats ) { + StreamingReporterBase::testGroupEnded( testGroupStats ); + // TODO: Check testGroupStats.aborting and act accordingly. + m_xml.scopedElement( "OverallResults" ) + .writeAttribute( "successes", testGroupStats.totals.assertions.passed ) + .writeAttribute( "failures", testGroupStats.totals.assertions.failed ) + .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk ); + m_xml.endElement(); + } + + void XmlReporter::testRunEnded( TestRunStats const& testRunStats ) { + StreamingReporterBase::testRunEnded( testRunStats ); + m_xml.scopedElement( "OverallResults" ) + .writeAttribute( "successes", testRunStats.totals.assertions.passed ) + .writeAttribute( "failures", testRunStats.totals.assertions.failed ) + .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk ); + m_xml.endElement(); + } + + CATCH_REGISTER_REPORTER( "xml", XmlReporter ) + +} // end namespace Catch + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif +// end catch_reporter_xml.cpp + +namespace Catch { + LeakDetector leakDetector; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// end catch_impl.hpp +#endif + +#ifdef CATCH_CONFIG_MAIN +// start catch_default_main.hpp + +#ifndef __OBJC__ + +#if defined(CATCH_CONFIG_WCHAR) && 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[]) { +#endif + + return Catch::Session().run( argc, argv ); +} + +#else // __OBJC__ + +// Objective-C entry point +int main (int argc, char * const argv[]) { +#if !CATCH_ARC_ENABLED + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; +#endif + + Catch::registerTestMethods(); + int result = Catch::Session().run( argc, (char**)argv ); + +#if !CATCH_ARC_ENABLED + [pool drain]; +#endif + + return result; +} + +#endif // __OBJC__ + +// end catch_default_main.hpp +#endif + +#if !defined(CATCH_CONFIG_IMPL_ONLY) + +#ifdef CLARA_CONFIG_MAIN_NOT_DEFINED +# undef CLARA_CONFIG_MAIN +#endif + +#if !defined(CATCH_CONFIG_DISABLE) +////// +// If this config identifier is defined then all CATCH macros are prefixed with CATCH_ +#ifdef CATCH_CONFIG_PREFIX_ALL + +#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_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr ) +#endif// CATCH_CONFIG_DISABLE_MATCHERS +#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, __VA_ARGS__ ) +#define CATCH_CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __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_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) + +#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#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_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_CAPTURE",__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__ ) + +#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE() + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#else +#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) ) +#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) ) +#endif + +#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__ ) +#else +#define CATCH_STATIC_REQUIRE( ... ) CATCH_REQUIRE( __VA_ARGS__ ) +#define CATCH_STATIC_REQUIRE_FALSE( ... ) CATCH_REQUIRE_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 ) + +// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required +#else + +#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_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#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, __VA_ARGS__ ) +#define CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __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_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) + +#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#define INFO( msg ) INTERNAL_CATCH_INFO( "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__ ) + +#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__ ) +#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE() + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#else +#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) ) +#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) ) +#endif + +#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__ ")" ) +#else +#define STATIC_REQUIRE( ... ) REQUIRE( __VA_ARGS__ ) +#define STATIC_REQUIRE_FALSE( ... ) REQUIRE_FALSE( __VA_ARGS__ ) +#endif + +#endif + +#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) + +// "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 ) + +using Catch::Detail::Approx; + +#else // CATCH_CONFIG_DISABLE + +////// +// If this config identifier is defined then all CATCH macros are prefixed with CATCH_ +#ifdef CATCH_CONFIG_PREFIX_ALL + +#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_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif// CATCH_CONFIG_DISABLE_MATCHERS +#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_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_CHECK_NOTHROW( ... ) (void)(0) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THAT( arg, matcher ) (void)(0) + +#define CATCH_REQUIRE_THAT( arg, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#define CATCH_INFO( msg ) (void)(0) +#define CATCH_WARN( msg ) (void)(0) +#define CATCH_CAPTURE( msg ) (void)(0) + +#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#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_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) ) +#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className ) +#else +#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) ) ) +#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className ) ) +#endif + +// "BDD-style" convenience wrappers +#define CATCH_SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), 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 ) + +#define CATCH_STATIC_REQUIRE( ... ) (void)(0) +#define CATCH_STATIC_REQUIRE_FALSE( ... ) (void)(0) + +// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required +#else + +#define REQUIRE( ... ) (void)(0) +#define REQUIRE_FALSE( ... ) (void)(0) + +#define REQUIRE_THROWS( ... ) (void)(0) +#define REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0) +#define REQUIRE_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#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_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CHECK_NOTHROW( ... ) (void)(0) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THAT( arg, matcher ) (void)(0) + +#define REQUIRE_THAT( arg, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#define INFO( msg ) (void)(0) +#define WARN( msg ) (void)(0) +#define CAPTURE( msg ) (void)(0) + +#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#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 ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) ) +#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className ) +#else +#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) ) ) +#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className ) ) +#endif + +#define STATIC_REQUIRE( ... ) (void)(0) +#define STATIC_REQUIRE_FALSE( ... ) (void)(0) + +#endif + +#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) + +// "BDD-style" convenience wrappers +#define SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) ) +#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className ) + +#define GIVEN( desc ) +#define AND_GIVEN( desc ) +#define WHEN( desc ) +#define AND_WHEN( desc ) +#define THEN( desc ) +#define AND_THEN( desc ) + +using Catch::Detail::Approx; + +#endif + +#endif // ! CATCH_CONFIG_IMPL_ONLY + +// start catch_reenable_warnings.h + + +#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 + +// end catch_reenable_warnings.h +// end catch.hpp +#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED + diff --git a/components/rgbd-sources/test/data/image.png b/components/rgbd-sources/test/data/image.png new file mode 100644 index 0000000000000000000000000000000000000000..31d086c8d4fa1e13b131c9aa2d4e216459bc730f GIT binary patch literal 509 zcmeAS@N?(olHy`uVBq!ia0y~yU^oH7983%h3`$m(Cm9$RI14-?iy0WWg+Z8+Vb&Z8 z1_lQ95>H=O_Q!0TY{F`i$_J|%7#L(TLn2C?^K)}k^GX;%z_}<ju_QG`p**uBL&4qC zHy}kXm7RfsvDnkaF{I+w+e?Om3=AAc3=Zsnew>BbS!v;Qzo3F#l>-qH+{uj_4aANy zB`50iupT=UA;I0#y3s&Pf;%};$3X1Zp@;;Xp4N@@R5+O20uBaJ!sXFyRz)tBUcc`T QpMv7W)78&qol`;+0EkGZ-2eap literal 0 HcmV?d00001 diff --git a/components/rgbd-sources/test/data/video.mp4 b/components/rgbd-sources/test/data/video.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..b8637a3e458d400bb4b744157dfbdac6b52bea6e GIT binary patch literal 179698 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 literal 0 HcmV?d00001 diff --git a/components/rgbd-sources/test/source_unit.cpp b/components/rgbd-sources/test/source_unit.cpp new file mode 100644 index 000000000..8e48a53a0 --- /dev/null +++ b/components/rgbd-sources/test/source_unit.cpp @@ -0,0 +1,171 @@ +#include "catch.hpp" + +//---- Mocks ------------------------------------------------------------------- + +#include <ftl/rgbd/source.hpp> +#include <ftl/config.h> + +static std::string last_type = ""; + +namespace ftl { +namespace rgbd { +namespace detail { + +class ImageSource : public ftl::rgbd::detail::Source { + public: + ImageSource(ftl::rgbd::Source *host) : ftl::rgbd::detail::Source(host) { + last_type = "image"; + } + ImageSource(ftl::rgbd::Source *host, const std::string &f) : ftl::rgbd::detail::Source(host) { + last_type = "image"; + } + + bool grab() { return true; }; + bool isReady() { return true; }; +}; + +class StereoVideoSource : public ftl::rgbd::detail::Source { + public: + StereoVideoSource(ftl::rgbd::Source *host) : ftl::rgbd::detail::Source(host) { + last_type = "video"; + } + StereoVideoSource(ftl::rgbd::Source *host, const std::string &f) : ftl::rgbd::detail::Source(host) { + last_type = "video"; + } + + bool grab() { return true; }; + bool isReady() { return true; }; +}; + +class NetSource : public ftl::rgbd::detail::Source { + public: + NetSource(ftl::rgbd::Source *host) : ftl::rgbd::detail::Source(host) { + last_type = "net"; + } + + bool grab() { return true; }; + bool isReady() { return true; }; +}; + +} +} +} + +//---- Sources ----------------------------------------------------------------- + +// Prevent these headers... +#define _FTL_RGBD_STEREOVIDEO_HPP_ +#define _FTL_RGBD_NET_HPP_ +#define _FTL_RGBD_SNAPSHOT_SOURCE_HPP_ +#define _FTL_RGBD_IMAGE_HPP_ + +#include "../src/source.cpp" + + +//---- Tests ------------------------------------------------------------------- + +using ftl::rgbd::Source; +using ftl::config::json_t; + +TEST_CASE("ftl::create<Source>(cfg)", "[rgbd]") { + json_t global = {{"$id","ftl://test"}}; + ftl::config::configure(global); + + SECTION("with valid image file uri") { + json_t cfg = { + {"$id","ftl://test/1"}, + {"uri","file://" FTL_SOURCE_DIRECTORY "/components/rgbd-sources/test/data/image.png"} + }; + + Source *src = ftl::create<Source>(cfg); + + REQUIRE( src ); + REQUIRE( src->isReady() ); + REQUIRE( last_type == "image"); + } + + SECTION("with valid video file uri") { + json_t cfg = { + {"$id","ftl://test/2"}, + {"uri","file://" FTL_SOURCE_DIRECTORY "/components/rgbd-sources/test/data/video.mp4"} + }; + + Source *src = ftl::create<Source>(cfg); + + REQUIRE( src ); + REQUIRE( src->isReady() ); + REQUIRE( last_type == "video"); + } + + SECTION("with valid net uri") { + json_t cfg = { + {"$id","ftl://test/2"}, + {"uri","ftl://utu.fi/dummy"} + }; + + Source *src = ftl::create<Source>(cfg); + + REQUIRE( src ); + REQUIRE( src->isReady() ); + REQUIRE( last_type == "net"); + } + + SECTION("with an invalid uri") { + json_t cfg = { + {"$id","ftl://test/2"}, + {"uri","not a uri"} + }; + + Source *src = ftl::create<Source>(cfg); + + REQUIRE( src ); + REQUIRE( !src->isReady() ); + } + + SECTION("with an invalid file uri") { + json_t cfg = { + {"$id","ftl://test/2"}, + {"uri","file:///not/a/file"} + }; + + Source *src = ftl::create<Source>(cfg); + + REQUIRE( src ); + REQUIRE( !src->isReady() ); + } + + SECTION("with a missing file") { + json_t cfg = { + {"$id","ftl://test/2"}, + {"uri","file:///data/image2.png"} + }; + + Source *src = ftl::create<Source>(cfg); + + REQUIRE( src ); + REQUIRE( !src->isReady() ); + } +} + +TEST_CASE("Source::set(uri)", "[rgbd]") { + json_t global = {{"$id","ftl://test"}}; + ftl::config::configure(global); + + SECTION("change to different valid URI type") { + json_t cfg = { + {"$id","ftl://test/1"}, + {"uri","file://" FTL_SOURCE_DIRECTORY "/components/rgbd-sources/test/data/image.png"} + }; + + Source *src = ftl::create<Source>(cfg); + + REQUIRE( src ); + REQUIRE( src->isReady() ); + REQUIRE( last_type == "image" ); + + src->set("uri", "file://" FTL_SOURCE_DIRECTORY "/components/rgbd-sources/test/data/video.mp4"); + + REQUIRE( src->isReady() ); + REQUIRE( last_type == "video" ); + } +} diff --git a/components/rgbd-sources/test/tests.cpp b/components/rgbd-sources/test/tests.cpp new file mode 100644 index 000000000..178916eab --- /dev/null +++ b/components/rgbd-sources/test/tests.cpp @@ -0,0 +1,3 @@ +#define CATCH_CONFIG_MAIN +#include "catch.hpp" + -- GitLab