From 56c0b7c9226547d8d6796be5f98ecb3a482ee66c Mon Sep 17 00:00:00 2001 From: RamSaw Date: Sun, 3 Feb 2019 17:09:06 +0300 Subject: [PATCH 01/17] Created skeleton for client app and created protocol description. --- .gitignore | 4 ++- CMakeLists.txt | 11 ++++++ CurrencyApplicationProtocol | 56 +++++++++++++++++++++++++++++ include/Client.h | 14 ++++++++ include/CurrencyClientApplication.h | 14 ++++++++ src/Client.cpp | 5 +++ src/CurrencyClientApplication.cpp | 11 ++++++ 7 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 CMakeLists.txt create mode 100644 CurrencyApplicationProtocol create mode 100644 include/Client.h create mode 100644 include/CurrencyClientApplication.h create mode 100644 src/Client.cpp create mode 100644 src/CurrencyClientApplication.cpp diff --git a/.gitignore b/.gitignore index f2e1fa5..45f40f1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ - # Created by https://www.gitignore.io/api/c++,latex,cmake,netbeans,qtcreator,visualstudio +### Intellij IDEA ### +/.idea/* + ### C++ ### # Prerequisites *.d diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..b58a1bb --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.1) +project(NetworksLab2019HSE) + +set(CMAKE_CXX_STANDARD 14) + +include_directories(.) + +add_executable(NetworksLab2019HSE + src/Client.cpp + src/CurrencyClientApplication.cpp + ) diff --git a/CurrencyApplicationProtocol b/CurrencyApplicationProtocol new file mode 100644 index 0000000..14cf287 --- /dev/null +++ b/CurrencyApplicationProtocol @@ -0,0 +1,56 @@ +Протокол разрабатывается исходя из подхода "запрос-ответ", так как он очень хорошо подходит для задачи: +клиенты запрашивают какое-то действие с валютой, сервер ее выполняет. + +Данные на сервере: + Массив валют, где валюта это: + Название валюты - 16 байта + История курса валюты - N * 4 байта (каждое значение курса это 4 байта) + +Любое сообщение должно кончаться на "\0" - 2 байта символизирующие конец сообщения +... - повторение описанных блоков выше. + +Поддерживаемые типы запросов: + 0. Получение и вывод списка валют с котировками/изменениями + Номер команды: 0. + + Клиент отправляет: "<номер команды: 4 байта>\0", + + Сервер отвечает: + "<название валюты: 16 байт><текущий курс: 4 байта><абсолютное приращение: 4 байта><относительное приращение: 4 байта>"..."\0", + абсолютные/относительные курсы валют равны -1 в случае отсутствия значений предыдущих курсов, + значит повторение таких же кусков для каждой валюты. + + 1. Передача команды на добавление валюты + Номер команды: 1. + + Клиент отправляет: "<номер команды: 4 байта><название валюты: 16 байт><текущий курс: 4 байта>\0", + + Сервер отвечает: + "<успех запроса: 1 байт>\0", + возвращает 1 если такой валюты еще не было, 0 если такая валюта была. + + 2. Передача команды на удаление валюты + Номер команды: 2. + + Клиент отправляет: "<номер команды: 4 байта><название валюты: 16 байт>\0", + + Сервер отвечает: + "<успех запроса: 1 байт>\0", + возвращает 1 если такая валюта была и была удалена успешно, 0 если такой валюты не было. + + 3. Передача команды на добавление курса валюты + Номер команды: 3. + + Клиент отправляет: "<номер команды: 4 байта><название валюты: 16 байт><новый курс: 4 байта>\0", + + Сервер отвечает: + "<успех запроса: 1 байт>\0", + возвращает 1 если успешно выставлен новый курс, 0 иначе. + + 4. Получение истории котировок валюты + Номер команды: 4. + + Клиент отправляет: "<номер команды: 4 байта><название валюты: 16 байт>\0", + + Сервер отвечает: + "<курс1: 4 байта><курс2: 4 байта>"..."\0" \ No newline at end of file diff --git a/include/Client.h b/include/Client.h new file mode 100644 index 0000000..9102d6e --- /dev/null +++ b/include/Client.h @@ -0,0 +1,14 @@ +// +// Created by mikhail on 03.02.19. +// + +#ifndef NETWORKSLAB2019HSE_CLIENT_H +#define NETWORKSLAB2019HSE_CLIENT_H + + +class Client { + +}; + + +#endif //NETWORKSLAB2019HSE_CLIENT_H diff --git a/include/CurrencyClientApplication.h b/include/CurrencyClientApplication.h new file mode 100644 index 0000000..20bd7cd --- /dev/null +++ b/include/CurrencyClientApplication.h @@ -0,0 +1,14 @@ +// +// Created by mikhail on 03.02.19. +// + +#ifndef NETWORKSLAB2019HSE_CURRENCYCLIENTAPPLICATION_H +#define NETWORKSLAB2019HSE_CURRENCYCLIENTAPPLICATION_H + + +class CurrencyClientApplication { + +}; + + +#endif //NETWORKSLAB2019HSE_CURRENCYCLIENTAPPLICATION_H diff --git a/src/Client.cpp b/src/Client.cpp new file mode 100644 index 0000000..11ad7a2 --- /dev/null +++ b/src/Client.cpp @@ -0,0 +1,5 @@ +// +// Created by mikhail on 03.02.19. +// + +#include "include/Client.h" diff --git a/src/CurrencyClientApplication.cpp b/src/CurrencyClientApplication.cpp new file mode 100644 index 0000000..b567684 --- /dev/null +++ b/src/CurrencyClientApplication.cpp @@ -0,0 +1,11 @@ +// +// Created by mikhail on 03.02.19. +// + +#include +#include "include/CurrencyClientApplication.h" + +int main() { + printf("Hello World!\n"); +} + From df7923e7fce712401723edaa417abd7077299df6 Mon Sep 17 00:00:00 2001 From: RamSaw Date: Sun, 3 Feb 2019 17:12:29 +0300 Subject: [PATCH 02/17] Branch creation. --- src/Client.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Client.cpp b/src/Client.cpp index 11ad7a2..992b062 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -2,4 +2,4 @@ // Created by mikhail on 03.02.19. // -#include "include/Client.h" +#include "include/Client.h" \ No newline at end of file From 72b49f978d1efe3c82de74328e1c6ae6a0f450dc Mon Sep 17 00:00:00 2001 From: RamSaw Date: Sun, 3 Feb 2019 18:08:54 +0300 Subject: [PATCH 03/17] Fixed protocol and start list command implementation. --- CMakeLists.txt | 4 +-- CurrencyApplicationProtocol | 9 ++++--- include/Client.h | 3 ++- include/Currency.h | 31 +++++++++++++++++++++ include/CurrencyClientApplication.h | 8 ++++++ src/Client.cpp | 8 +++++- src/Currency.cpp | 42 +++++++++++++++++++++++++++++ src/CurrencyClientApplication.cpp | 34 +++++++++++++++++++++-- 8 files changed, 130 insertions(+), 9 deletions(-) create mode 100644 include/Currency.h create mode 100644 src/Currency.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b58a1bb..07c7d6d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) project(NetworksLab2019HSE) set(CMAKE_CXX_STANDARD 14) @@ -8,4 +8,4 @@ include_directories(.) add_executable(NetworksLab2019HSE src/Client.cpp src/CurrencyClientApplication.cpp - ) + src/Currency.cpp include/Currency.h) diff --git a/CurrencyApplicationProtocol b/CurrencyApplicationProtocol index 14cf287..a742987 100644 --- a/CurrencyApplicationProtocol +++ b/CurrencyApplicationProtocol @@ -16,9 +16,12 @@ Клиент отправляет: "<номер команды: 4 байта>\0", Сервер отвечает: - "<название валюты: 16 байт><текущий курс: 4 байта><абсолютное приращение: 4 байта><относительное приращение: 4 байта>"..."\0", - абсолютные/относительные курсы валют равны -1 в случае отсутствия значений предыдущих курсов, - значит повторение таких же кусков для каждой валюты. + "<название валюты: 16 байт><текущий курс: 4 байта><есть ли предыдущий курс: 1 байт><абсолютное приращение: 4 байта><относительное приращение: 4 байта>"..."\0", + если предыдущего курса нет, то абсолютное и относительное значения равны нулям и игнорируются, + относительное значение выражается в процентах и округляется до целых. + Формулы: + абсолютное = current - previous + относительное = (current * 100.0) / previous 1. Передача команды на добавление валюты Номер команды: 1. diff --git a/include/Client.h b/include/Client.h index 9102d6e..2c003a5 100644 --- a/include/Client.h +++ b/include/Client.h @@ -7,7 +7,8 @@ class Client { - +public: + std::vector list(); }; diff --git a/include/Currency.h b/include/Currency.h new file mode 100644 index 0000000..b789b6e --- /dev/null +++ b/include/Currency.h @@ -0,0 +1,31 @@ +// +// Created by mikhail on 03.02.19. +// + +#ifndef NETWORKSLAB2019HSE_CURRENCY_H +#define NETWORKSLAB2019HSE_CURRENCY_H + + +#include +#include + +class Currency { +public: + Currency(const std::string &name, const std::vector &rates); + Currency(const std::string &name, const int32_t ¤t_rate, const int32_t &absolute_change, const int32_t &relative_change); + + const std::string& get_name(); + const int32_t& get_current_rate(); + const int32_t& get_rate(size_t i); + const int32_t& get_absolute_change(); + const int32_t& get_relative_change(); + +private: + std::string name; + std::vector rates; + int32_t absolute_change; + int32_t relative_change; +}; + + +#endif //NETWORKSLAB2019HSE_CURRENCY_H diff --git a/include/CurrencyClientApplication.h b/include/CurrencyClientApplication.h index 20bd7cd..0e1a9bf 100644 --- a/include/CurrencyClientApplication.h +++ b/include/CurrencyClientApplication.h @@ -6,8 +6,16 @@ #define NETWORKSLAB2019HSE_CURRENCYCLIENTAPPLICATION_H +#include "Client.h" + class CurrencyClientApplication { +public: + void run(); + +private: + Client client = Client(); + void printUsage(); }; diff --git a/src/Client.cpp b/src/Client.cpp index 992b062..bfd1efa 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -2,4 +2,10 @@ // Created by mikhail on 03.02.19. // -#include "include/Client.h" \ No newline at end of file +#include + +#include "include/Client.h" + +std::vector Client::list() { + return nullptr; +} diff --git a/src/Currency.cpp b/src/Currency.cpp new file mode 100644 index 0000000..157645b --- /dev/null +++ b/src/Currency.cpp @@ -0,0 +1,42 @@ +// +// Created by mikhail on 03.02.19. +// + +#include + +#include "include/Currency.h" + +Currency::Currency(const std::string &name, const std::vector &rates) : name(name), rates(rates) { + if (rates.size() >= 2) { + int32_t current = get_current_rate(), previous = get_rate(rates.size() - 2); + absolute_change = current - previous; + relative_change = static_cast((current * 100.0) / previous ); + } +} + +Currency::Currency(const std::string &name, const int32_t ¤t_rate, + const int32_t &absolute_change, const int32_t &relative_change) : + name(name), absolute_change(absolute_change), relative_change(relative_change) { + rates = {current_rate - absolute_change, current_rate}; +} + +const std::string &Currency::get_name() { + return name; +} + +const int32_t &Currency::get_current_rate() { + return get_rate(rates.size() - 1); +} + +const int32_t &Currency::get_rate(size_t i) { + return 0 <= i && i < rates.size() ? rates[i] : nullptr; +} + +const int32_t &Currency::get_absolute_change() { + return absolute_change; +} + +const int32_t &Currency::get_relative_change() { + return relative_change; +} + diff --git a/src/CurrencyClientApplication.cpp b/src/CurrencyClientApplication.cpp index b567684..8643734 100644 --- a/src/CurrencyClientApplication.cpp +++ b/src/CurrencyClientApplication.cpp @@ -2,10 +2,40 @@ // Created by mikhail on 03.02.19. // -#include +#include +#include +#include + #include "include/CurrencyClientApplication.h" int main() { - printf("Hello World!\n"); + CurrencyClientApplication().run(); + return 0; +} + +void CurrencyClientApplication::run() { + printUsage(); + + bool toExit = false; + while (!toExit) { + std::string command; + std::cout << ">"; + std::cin >> command; + if (command == "list") { + + } else if (command == "exit") { + toExit = true; + } + } } +void CurrencyClientApplication::printUsage() { + std::cout << "Commands:\n" + << "list - list currencies with rate and rate differences\n" + << "addCurrency - adds new currency\n" + << "remove \n" + << "addRate - adds new rate for given currency\n" + << "getCurrencyHistory - returns rate history of given currency\n" + << "exit - to exit client application\n" + << "\n"; +} From 867d350ab80f229e75e9a1f1f04ef074b4e732af Mon Sep 17 00:00:00 2001 From: RamSaw Date: Tue, 5 Feb 2019 23:58:27 +0300 Subject: [PATCH 04/17] Fixed protocol and start list command implementation. --- CurrencyApplicationProtocol | 2 +- include/Client.h | 23 ++++- include/Currency.h | 23 +++-- include/CurrencyClientApplication.h | 6 +- src/Client.cpp | 137 +++++++++++++++++++++++++++- src/Currency.cpp | 27 +++--- src/CurrencyClientApplication.cpp | 23 +++-- 7 files changed, 209 insertions(+), 32 deletions(-) diff --git a/CurrencyApplicationProtocol b/CurrencyApplicationProtocol index a742987..52856fa 100644 --- a/CurrencyApplicationProtocol +++ b/CurrencyApplicationProtocol @@ -6,7 +6,7 @@ Название валюты - 16 байта История курса валюты - N * 4 байта (каждое значение курса это 4 байта) -Любое сообщение должно кончаться на "\0" - 2 байта символизирующие конец сообщения +Любое сообщение должно кончаться на <'\'0> - 2 байта (символ '\' и нулейвой байт) символизирующие конец сообщения ... - повторение описанных блоков выше. Поддерживаемые типы запросов: diff --git a/include/Client.h b/include/Client.h index 2c003a5..b5a8d7e 100644 --- a/include/Client.h +++ b/include/Client.h @@ -6,9 +6,30 @@ #define NETWORKSLAB2019HSE_CLIENT_H +#include "Currency.h" + class Client { public: - std::vector list(); + Client(const std::string &hostname, uint16_t portno); + + virtual ~Client(); + + const std::vector list() const; + + void write_end_of_message(std::vector &buffer) const; + + void write_command(std::vector &buffer, int32_t command_no) const; + +private: + const int sockfd; + static const size_t BUFFER_INITIAL_LENGTH = 256; + static const size_t CURRENCY_NAME_SIZE_IN_LIST = 16; + + bool is_message_received(const std::vector &message) const; + + const std::vector translate_list_message(std::vector &message) const; + + void remove_ending_symbols(std::vector &message) const; }; diff --git a/include/Currency.h b/include/Currency.h index b789b6e..bdda7a3 100644 --- a/include/Currency.h +++ b/include/Currency.h @@ -11,14 +11,21 @@ class Currency { public: - Currency(const std::string &name, const std::vector &rates); - Currency(const std::string &name, const int32_t ¤t_rate, const int32_t &absolute_change, const int32_t &relative_change); - - const std::string& get_name(); - const int32_t& get_current_rate(); - const int32_t& get_rate(size_t i); - const int32_t& get_absolute_change(); - const int32_t& get_relative_change(); + Currency(std::string name, std::vector rates); + + Currency(std::string name, int32_t current_rate); + + Currency(std::string name, int32_t current_rate, int32_t absolute_change, int32_t relative_change); + + const std::string &get_name() const; + + int32_t get_current_rate() const; + + int32_t get_rate(size_t i) const; + + int32_t get_absolute_change() const; + + int32_t get_relative_change() const; private: std::string name; diff --git a/include/CurrencyClientApplication.h b/include/CurrencyClientApplication.h index 0e1a9bf..69f7ab4 100644 --- a/include/CurrencyClientApplication.h +++ b/include/CurrencyClientApplication.h @@ -12,8 +12,12 @@ class CurrencyClientApplication { public: void run(); + CurrencyClientApplication(const std::string &host, uint16_t portno); + private: - Client client = Client(); + std::string host; + uint16_t portno; + Client client = Client(host, portno); void printUsage(); }; diff --git a/src/Client.cpp b/src/Client.cpp index bfd1efa..f65c2c3 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -2,10 +2,141 @@ // Created by mikhail on 03.02.19. // +#include +#include +#include +#include +#include #include +#include -#include "include/Client.h" -std::vector Client::list() { - return nullptr; +template +void insert_bytes(std::vector &container, const T &value) { + const auto *bytes = reinterpret_cast(&value); + container.insert(container.end(), bytes, bytes + sizeof(T)); +} + +int32_t read_int32(std::vector::iterator &it) { + int32_t val; + std::memcpy(&val, &it, sizeof(int32_t)); + it += sizeof(int32_t); + return val; +} + +bool read_bool(std::vector::iterator &it) { + bool val; + std::memcpy(&val, &it, sizeof(bool)); + it += sizeof(bool); + return val; +} + +std::string read_string(std::vector::iterator &it, size_t len) { + std::string string(it, it + len); + it += len; + return string; +} + +const std::vector Client::list() const { + auto write_buffer = std::vector(); + int32_t command_no = 0; + + write_command(write_buffer, command_no); + write_end_of_message(write_buffer); + if (write(sockfd, write_buffer.data(), write_buffer.size()) < 0) { + perror("ERROR writing to socket"); + exit(1); + } + + bool message_received = false; + auto message = std::vector(); + while (!message_received) { + auto read_buffer = std::vector(BUFFER_INITIAL_LENGTH); + ssize_t bytes_number = read(sockfd, read_buffer.data(), read_buffer.size()); + + if (bytes_number < 0) { + perror("ERROR reading from socket"); + exit(1); + } + read_buffer.resize(static_cast(bytes_number)); + message.insert(message.end(), read_buffer.begin(), read_buffer.end()); + if (message.size() >= 29) { + message.resize(29); + message.push_back('\\'); + message.push_back(0); + } + message_received = is_message_received(message); + } + return translate_list_message(message); +} + +Client::Client(const std::string &hostname, uint16_t portno) : sockfd(socket(AF_INET, SOCK_STREAM, 0)) { + if (sockfd < 0) { + perror("ERROR opening socket"); + exit(1); + } + + struct hostent *server = gethostbyname(hostname.c_str()); + + if (server == nullptr) { + unsigned int addr = inet_addr(hostname.c_str()); + server = gethostbyaddr((char *) &addr, sizeof(addr), AF_INET); + if (server == nullptr) { + perror("ERROR, no such host"); + exit(1); + } + } + + struct sockaddr_in server_addr{}; + bzero((char *) &server_addr, sizeof(server_addr)); + server_addr.sin_family = AF_INET; + bcopy(server->h_addr, (char *) &server_addr.sin_addr.s_addr, (size_t) server->h_length); + server_addr.sin_port = htons(portno); + + /* Now connect to the server */ + if (connect(sockfd, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) { + perror("ERROR connecting"); + exit(1); + } +} + +Client::~Client() { + close(sockfd); +} + + +void Client::write_end_of_message(std::vector &buffer) const { + insert_bytes(buffer, (int8_t) '\\'); + insert_bytes(buffer, (int8_t) 0); +} + +void Client::write_command(std::vector &buffer, int32_t command_no) const { + insert_bytes(buffer, command_no); +} + +bool Client::is_message_received(const std::vector &message) const { + return message.size() >= 2 && message[message.size() - 2] == '\\' && message[message.size() - 1] == 0; +} + +const std::vector Client::translate_list_message(std::vector &message) const { + remove_ending_symbols(message); + std::vector currencies; + for (auto it = message.begin(); it < message.end();) { + std::string currency_name = read_string(it, CURRENCY_NAME_SIZE_IN_LIST); + int32_t current_rate = read_int32(it); + bool has_change = read_bool(it); + int32_t absolute_change, relative_change; + if (has_change) { + absolute_change = read_int32(it); + relative_change = read_int32(it); + currencies.emplace_back(currency_name, current_rate, absolute_change, relative_change); + } else { + currencies.emplace_back(currency_name, current_rate); + } + } + return currencies; +} + +void Client::remove_ending_symbols(std::vector &message) const { + message.erase(message.end() - 2, message.end()); } diff --git a/src/Currency.cpp b/src/Currency.cpp index 157645b..0034988 100644 --- a/src/Currency.cpp +++ b/src/Currency.cpp @@ -1,12 +1,12 @@ +#include + // // Created by mikhail on 03.02.19. // #include -#include "include/Currency.h" - -Currency::Currency(const std::string &name, const std::vector &rates) : name(name), rates(rates) { +Currency::Currency(std::string name, std::vector rates) : name(std::move(name)), rates(std::move(rates)) { if (rates.size() >= 2) { int32_t current = get_current_rate(), previous = get_rate(rates.size() - 2); absolute_change = current - previous; @@ -14,29 +14,32 @@ Currency::Currency(const std::string &name, const std::vector &rates) : } } -Currency::Currency(const std::string &name, const int32_t ¤t_rate, - const int32_t &absolute_change, const int32_t &relative_change) : - name(name), absolute_change(absolute_change), relative_change(relative_change) { +Currency::Currency(std::string name, int32_t current_rate) : name(std::move(name)) { + rates = {current_rate}; +} + +Currency::Currency(std::string name, int32_t current_rate, int32_t absolute_change, int32_t relative_change) : + name(std::move(name)), absolute_change(absolute_change), relative_change(relative_change) { rates = {current_rate - absolute_change, current_rate}; } -const std::string &Currency::get_name() { +const std::string &Currency::get_name() const { return name; } -const int32_t &Currency::get_current_rate() { +int32_t Currency::get_current_rate() const { return get_rate(rates.size() - 1); } -const int32_t &Currency::get_rate(size_t i) { - return 0 <= i && i < rates.size() ? rates[i] : nullptr; +int32_t Currency::get_rate(size_t i) const { + return 0 <= i && i < rates.size() ? rates[i] : -1; } -const int32_t &Currency::get_absolute_change() { +int32_t Currency::get_absolute_change() const { return absolute_change; } -const int32_t &Currency::get_relative_change() { +int32_t Currency::get_relative_change() const { return relative_change; } diff --git a/src/CurrencyClientApplication.cpp b/src/CurrencyClientApplication.cpp index 8643734..db7fa3c 100644 --- a/src/CurrencyClientApplication.cpp +++ b/src/CurrencyClientApplication.cpp @@ -3,13 +3,16 @@ // #include -#include #include -#include "include/CurrencyClientApplication.h" - -int main() { - CurrencyClientApplication().run(); +int main(int argc, char *argv[]) { + if (argc < 3) { + fprintf(stderr, "usage: .%s \n", argv[0]); + return 0; + } + std::string hostname = argv[1]; + auto portno = static_cast(strtol(argv[2], nullptr, 10)); + CurrencyClientApplication(hostname, portno).run(); return 0; } @@ -22,7 +25,12 @@ void CurrencyClientApplication::run() { std::cout << ">"; std::cin >> command; if (command == "list") { - + auto currencies = client.list(); + std::cout << "RESPONSE:\n"; + for (const auto ¤cy : currencies) { + std::cout << currency.get_name() << " " << currency.get_absolute_change() << + " " << currency.get_relative_change() << "\n"; + } } else if (command == "exit") { toExit = true; } @@ -39,3 +47,6 @@ void CurrencyClientApplication::printUsage() { << "exit - to exit client application\n" << "\n"; } + +CurrencyClientApplication::CurrencyClientApplication(const std::string &host, uint16_t portno) : host(host), + portno(portno) {} \ No newline at end of file From 33a51a52e7c25a81a0ee63ff20a9f870149c0e68 Mon Sep 17 00:00:00 2001 From: RamSaw Date: Fri, 8 Feb 2019 00:54:21 +0300 Subject: [PATCH 05/17] Added unit testing system: catch and written one test. --- CMakeLists.txt | 12 +- include/Client.h | 10 +- include/catch.hpp | 16302 ++++++++++++++++++++++++++++ src/Client.cpp | 7 +- src/Currency.cpp | 4 +- src/CurrencyClientApplication.cpp | 2 +- test/ClientTest.cpp | 82 + 7 files changed, 16403 insertions(+), 16 deletions(-) create mode 100644 include/catch.hpp create mode 100644 test/ClientTest.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 07c7d6d..c52d162 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,9 +3,17 @@ project(NetworksLab2019HSE) set(CMAKE_CXX_STANDARD 14) -include_directories(.) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -Iinclude -pedantic -Wextra -pthread") + +include_directories(./include) add_executable(NetworksLab2019HSE src/Client.cpp src/CurrencyClientApplication.cpp - src/Currency.cpp include/Currency.h) + src/Currency.cpp + ) + +add_executable(runTests test/ClientTest.cpp + src/Client.cpp + src/Currency.cpp + ) \ No newline at end of file diff --git a/include/Client.h b/include/Client.h index b5a8d7e..b56cf30 100644 --- a/include/Client.h +++ b/include/Client.h @@ -12,19 +12,19 @@ class Client { public: Client(const std::string &hostname, uint16_t portno); - virtual ~Client(); - const std::vector list() const; - void write_end_of_message(std::vector &buffer) const; - - void write_command(std::vector &buffer, int32_t command_no) const; + virtual ~Client(); private: const int sockfd; static const size_t BUFFER_INITIAL_LENGTH = 256; static const size_t CURRENCY_NAME_SIZE_IN_LIST = 16; + void write_end_of_message(std::vector &buffer) const; + + void write_command(std::vector &buffer, int32_t command_no) const; + bool is_message_received(const std::vector &message) const; const std::vector translate_list_message(std::vector &message) const; diff --git a/include/catch.hpp b/include/catch.hpp new file mode 100644 index 0000000..ad03f07 --- /dev/null +++ b/include/catch.hpp @@ -0,0 +1,16302 @@ +/* + * Catch v2.6.0 + * Generated: 2019-01-31 22:25:55.560884 + * ---------------------------------------------------------- + * This file has been merged from multiple headers. Please don't edit it directly + * Copyright (c) 2019 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 6 +#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__ +// Because REQUIREs trigger GCC's -Wparentheses, and because still +// supported version of g++ have only buggy support for _Pragmas, +// Wparentheses have to be suppressed globally. +# pragma +GCC diagnostic ignored "-Wparentheses" // See #674 for details + +# 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 + +# 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_ 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() && defined(CATCH_CPP17_OR_GREATER) +# define +CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW +#endif +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Check if optional is available and usable +#if +defined(__has_include) +# if +__has_include() && defined(CATCH_CPP17_OR_GREATER) +# define +CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL +# endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +#endif // __has_include + +//////////////////////////////////////////////////////////////////////////////// +// Check if variant is available and usable +#if +defined(__has_include) +# if +__has_include() && defined(CATCH_CPP17_OR_GREATER) +# if +defined(__clang__) && (__clang_major__ < 8) +// work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 +// fix should be in clang 8, workaround in libstdc++ 8.2 +# include + +# if +defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) +# define +CATCH_CONFIG_NO_CPP17_VARIANT +# else +# define +CATCH_INTERNAL_CONFIG_CPP17_VARIANT +# endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) +# else +# define +CATCH_INTERNAL_CONFIG_CPP17_VARIANT +# endif // defined(__clang__) && (__clang_major__ < 8) +# endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +#endif // __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_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) +# define +CATCH_CONFIG_CPP17_OPTIONAL +#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 + +#include + +#include + + +// 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& operator= ( SourceLineInfo const& ) = default; +SourceLineInfo( SourceLineInfo&& ) noexcept = default; +SourceLineInfo& operator= ( SourceLineInfo&& ) noexcept = 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 +T const& operator+ ( T const& value, StreamEndStop ) { +return value; +} +} + +#define +CATCH_INTERNAL_LINEINFO \ +::Catch::SourceLineInfo( __FILE__, static_cast( __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 + + +namespace Catch { + +class TestSpec; + +struct ITestInvoker { +virtual void invoke () const = 0; +virtual ~ITestInvoker(); +}; + +class TestCase; +struct IConfig; + +struct ITestCaseRegistry { +virtual ~ITestCaseRegistry(); +virtual std::vector const& getAllTests() const = 0; +virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; +}; + +bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); +std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); +std::vector const& getAllTestCasesSorted( IConfig const& config ); + +} + +// end catch_interfaces_testcase.h +// start catch_stringref.h + +#include + +#include + +#include + + +namespace Catch { + +/// A non-owning string class (similar to the forthcoming std::string_view) +/// Note that, because a StringRef may be a substring of another string, +/// it may not be null terminated. 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 + + +#include + + +namespace Catch{ + +#ifdef +CATCH_CPP17_OR_GREATER +template +inline constexpr auto is_unique = std::true_type{ +}; + +template +inline constexpr auto is_unique = std::bool_constant< +(!std::is_same_v && ...) && is_unique +>{ +}; +#else + +template +struct is_unique : std::true_type{ +}; + +template +struct is_unique : std::integral_constant +::value +&& is_unique::value +&& is_unique::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 + +#define +INTERNAL_CATCH_MAKE_TYPE_LIST(types) TypeList + +#define +INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(types)\ + CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST, INTERNAL_CATCH_REMOVE_PARENS(types)) + +// end catch_preprocessor.hpp +// start catch_meta.hpp + + +#include + + +template< typename... > +struct TypeList{ +}; + +template< typename... > +struct append; + +template< +template class L1 +, typename...E1 +, +template class L2 +, typename...E2 +> +struct append< L1, L2 > +{ +using type = L1; +}; + +template< +template class L1 +, typename...E1 +, +template class L2 +, typename...E2 +, typename...Rest +> +struct append< L1, L2, Rest...> +{ +using type = typename append< L1, Rest... >::type; +}; + +template< +template class +, typename... +> +struct rewrap; + +template< +template class Container +, +template class List +, typename...elems +> +struct rewrap> +{ +using type = TypeList< Container< elems... > >; +}; + +template< +template class Container +, +template class List +, class...Elems +, typename...Elements> +struct rewrap, Elements...> +{ +using type = typename append>, typename rewrap::type>::type; +}; + +template< +template class...Containers > +struct combine +{ +template< typename...Types > +struct with_types +{ +template< +template class Final > +struct into +{ +using type = typename append, typename rewrap::type...>::type; +}; +}; +}; + +template +struct always_false : std::false_type { +}; + +// end catch_meta.hpp +namespace Catch { + +template +class TestInvokerAsMethod : +public ITestInvoker { +void (C::*m_testAsMethod)(); +public: +TestInvokerAsMethod( void (C::*testAsMethod)()) noexcept : m_testAsMethod( testAsMethod ) { +} + +void invoke() const override { +C obj; +(obj.*m_testAsMethod)(); +} +}; + +auto makeTestInvoker( void(*testAsFunction)()) noexcept -> ITestInvoker*; + +template +auto makeTestInvoker( void (C::*testAsMethod)()) noexcept -> ITestInvoker* { +return new(std::nothrow) TestInvokerAsMethod( 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 \ + static void TestName() +#define +INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \ + namespace{ +\ + +template \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName ) { +\ + void test(); \ + +}; \ + +} \ + template \ + 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 \ + static void TestFunc();\ + namespace { +\ + template \ + struct TestName{ +\ + +template \ + 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 ), 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 \ + 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_UD(INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME, Name, __VA_ARGS__));\ + return 0;\ + +}(); + +#define +INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(TestName, TestFuncName, Name, Tags, TmplTypes, TypesList) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + template static void TestFuncName(); \ + namespace { +\ + template \ + struct TestName { +\ + TestName() { +\ + CATCH_INTERNAL_CHECK_UNIQUE_TYPES(Types...) \ + int index = 0; \ + using expander = int[]; \ + (void)expander{ +(Catch::AutoReg( Catch::makeTestInvoker( &TestFuncName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ +Name " - " + Catch::StringMaker::convert(index++), Tags } ), 0)... };/* NOLINT */ \ + +} \ + +}; \ + static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) =[](){ +\ + using TestInit = combine \ +::with_types::into::type; \ + TestInit(); \ + return 0; \ + +}(); \ + +} \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + template \ + static void TestFuncName() + +#ifndef +CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define +INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(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_PRODUCT_TEST_CASE(Name, Tags, ...)\ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( 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_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, ... ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ +\ + template \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName ) { +\ + void test();\ + +};\ + template \ + struct TestNameClass{ +\ + +template \ + 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::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 \ + void TestName::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 + +#define +INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(TestNameClass, TestName, ClassName, Name, Tags, TmplTypes, TypesList)\ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + template \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName ) { +\ + void test();\ + +};\ + namespace { +\ + template\ + struct TestNameClass{ +\ + TestNameClass(){ +\ + CATCH_INTERNAL_CHECK_UNIQUE_TYPES(Types...)\ + int index = 0;\ + using expander = int[];\ + (void)expander{ +(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, +# +ClassName, Catch::NameAndTags{ +Name " - " + Catch::StringMaker::convert(index++), Tags } ), 0)... };/* NOLINT */ \ + +}\ + +};\ + static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) =[](){ +\ + using TestInit = combine\ +::with_types::into::type;\ + TestInit();\ + return 0;\ + +}(); \ + +}\ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + template \ + void TestName::test() + +#ifndef +CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define +INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\ + INTERNAL_CATCH_TEMPLATE_PRODUCT_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____ ), 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____ ), ClassName, Name, Tags, __VA_ARGS__ ) +#else +#define +INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_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____ ), 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____ ), 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 + +#include + +#include + +#include + +// start catch_stream.h + +#include + +#include + +#include + + +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 +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 + +#endif + +#ifdef +__OBJC__ +// start catch_objc_arc.hpp + +#import + + +#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 +std::string rawMemoryToString( const T& object ) { +return rawMemoryToString( &object, sizeof(object)); +} + +template +class IsStreamInsertable { +template +static auto test(int) +-> decltype(std::declval() << std::declval(), std::true_type()); + +template +static auto test(...)->std::false_type; + +public: +static const bool value = decltype(test(0))::value; +}; + +template +std::string convertUnknownEnumToString( E e ); + +template +typename std::enable_if< +!std::is_enum::value && !std::is_base_of::value, +std::string>::type convertUnstreamable( T const& ) { +return Detail::unprintableString; +} +template +typename std::enable_if< +!std::is_enum::value && std::is_base_of::value, +std::string>::type convertUnstreamable(T const& ex) { +return ex.what(); +} + +template +typename std::enable_if< +std::is_enum::value +, std::string>::type convertUnstreamable( T const& value ) { +return convertUnknownEnumToString( value ); +} + +#if +defined(_MANAGED) +//! Convert a CLR string to a utf8 std::string +template +std::string clrReferenceToString( T^ ref ) { +if (ref == nullptr) +return std::string("null"); +auto bytes = System::Text::Encoding::UTF8->GetBytes(ref->ToString()); +cli::pin_ptr p = &bytes[0]; +return std::string(reinterpret_cast(p), bytes->Length); +} +#endif + +} // namespace Detail + +// If we decide for C++14, change these to enable_if_ts +template +struct StringMaker { +template +static +typename std::enable_if<::Catch::Detail::IsStreamInsertable::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 +static +typename std::enable_if::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 +std::string stringify(const T& e) { +return::Catch::StringMaker::type>::type>::convert(e); +} + +template +std::string convertUnknownEnumToString( E e ) { +return::Catch::Detail::stringify(static_cast::type>(e)); +} + +#if +defined(_MANAGED) +template +std::string stringify( T^ e ) { +return::Catch::StringMaker::convert(e); +} +#endif + +} // namespace Detail + +// Some predefined specializations + +template<> +struct StringMaker { +static std::string convert(const std::string& str); +}; + +#ifdef +CATCH_CONFIG_CPP17_STRING_VIEW +template<> +struct StringMaker { +static std::string convert(std::string_view str); +}; +#endif + +template<> +struct StringMaker { +static std::string convert(char const * str); +}; +template<> +struct StringMaker { +static std::string convert(char * str); +}; + +#ifdef +CATCH_CONFIG_WCHAR +template<> +struct StringMaker { +static std::string convert(const std::wstring& wstr); +}; + +# ifdef +CATCH_CONFIG_CPP17_STRING_VIEW +template<> +struct StringMaker { +static std::string convert(std::wstring_view str); +}; +# endif + +template<> +struct StringMaker { +static std::string convert(wchar_t const * str); +}; +template<> +struct StringMaker { +static std::string convert(wchar_t * str); +}; +#endif + +// TBD: Should we use `strnlen` to ensure that we don't go out of the buffer, +// while keeping string semantics? +template +struct StringMaker { +static std::string convert(char const* str) { +return::Catch::Detail::stringify(std::string{ +str +}); +} +}; +template +struct StringMaker { +static std::string convert(signed char const* str) { +return::Catch::Detail::stringify(std::string{ +reinterpret_cast(str) +}); +} +}; +template +struct StringMaker { +static std::string convert(unsigned char const* str) { +return::Catch::Detail::stringify(std::string{ +reinterpret_cast(str) +}); +} +}; + +template<> +struct StringMaker { +static std::string convert(int value); +}; +template<> +struct StringMaker { +static std::string convert(long value); +}; +template<> +struct StringMaker { +static std::string convert(long long value); +}; +template<> +struct StringMaker { +static std::string convert(unsigned int value); +}; +template<> +struct StringMaker { +static std::string convert(unsigned long value); +}; +template<> +struct StringMaker { +static std::string convert(unsigned long long value); +}; + +template<> +struct StringMaker { +static std::string convert(bool b); +}; + +template<> +struct StringMaker { +static std::string convert(char c); +}; +template<> +struct StringMaker { +static std::string convert(signed char c); +}; +template<> +struct StringMaker { +static std::string convert(unsigned char c); +}; + +template<> +struct StringMaker { +static std::string convert(std::nullptr_t); +}; + +template<> +struct StringMaker { +static std::string convert(float value); +}; +template<> +struct StringMaker { +static std::string convert(double value); +}; + +template +struct StringMaker { +template +static std::string convert(U* p) { +if (p) { +return::Catch::Detail::rawMemoryToString(p); +} else { +return "nullptr"; +} +} +}; + +template +struct StringMaker { +static std::string convert(R C::* p) { +if (p) { +return::Catch::Detail::rawMemoryToString(p); +} else { +return "nullptr"; +} +} +}; + +#if +defined(_MANAGED) +template +struct StringMaker { +static std::string convert( T^ ref ) { +return::Catch::Detail::clrReferenceToString(ref); +} +}; +#endif + +namespace Detail { +template +std::string rangeToString(InputIterator first, 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 { +static std::string convert(NSString * nsstring) { +if (!nsstring) +return "nil"; +return std::string("@") +[nsstring UTF8String]; +} +}; +template<> +struct StringMaker { +static std::string convert(NSObject* nsObject) { +return::Catch::Detail::stringify([nsObject description]); +} + +}; +namespace Detail { +inline std::string stringify( NSString* nsstring ) { +return StringMaker::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 +# define +CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER +#endif + +// Separate std::pair specialization +#if +defined(CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER) +#include + +namespace Catch { +template +struct StringMaker > { +static std::string convert(const std::pair& pair) { +ReusableStringStream rss; +rss << "{ " +<<::Catch::Detail::stringify(pair.first) +<< ", " +<<::Catch::Detail::stringify(pair.second) +<< " }"; +return rss.str(); +} +}; +} +#endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER + +#if +defined(CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_OPTIONAL) +#include + +namespace Catch { +template +struct StringMaker > { +static std::string convert(const std::optional& optional) { +ReusableStringStream rss; +if (optional.has_value()) { +rss <<::Catch::Detail::stringify(*optional); +} else { +rss << "{ }"; +} +return rss.str(); +} +}; +} +#endif // CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER + +// Separate std::tuple specialization +#if +defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER) +#include + +namespace Catch { +namespace Detail { +template< +typename Tuple, +std::size_t N = 0, +bool = (N < std::tuple_size::value) +> +struct TupleElementPrinter { +static void print(const Tuple& tuple, std::ostream& os) { +os << (N ? ", " : " ") +<<::Catch::Detail::stringify(std::get(tuple)); +TupleElementPrinter::print(tuple, os); +} +}; + +template< +typename Tuple, +std::size_t N +> +struct TupleElementPrinter { +static void print(const Tuple&, std::ostream&) { +} +}; + +} + +template +struct StringMaker> { +static std::string convert(const std::tuple& tuple) { +ReusableStringStream rss; +rss << '{'; +Detail::TupleElementPrinter>::print(tuple, rss.get()); +rss << " }"; +return rss.str(); +} +}; +} +#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER + +#if +defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT) +#include + +namespace Catch { +template<> +struct StringMaker { +static std::string convert(const std::monostate&) { +return "{ }"; +} +}; + +template +struct StringMaker> { +static std::string convert(const std::variant& variant) { +if (variant.valueless_by_exception()) { +return "{valueless variant}"; +} else { +return std::visit( +[](const auto& value) { +return::Catch::Detail::stringify(value); +}, +variant +); +} +} +}; +} +#endif // CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER + +namespace Catch { +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 +struct is_range { +static const bool value = +!std::is_same())), not_this_one>::value && +!std::is_same())), not_this_one>::value; +}; + +#if +defined(_MANAGED) // Managed types are never ranges +template +struct is_range { +static const bool value = false; +}; +#endif + +template +std::string rangeToString( Range const& range ) { +return::Catch::Detail::rangeToString( begin( range ), end( range )); +} + +// Handle vector specially +template +std::string rangeToString( std::vector const& v ) { +ReusableStringStream rss; +rss << "{ "; +bool first = true; +for ( bool b : v ) { +if ( first ) +first = false; +else +rss << ", "; +rss <<::Catch::Detail::stringify( b ); +} +rss << " }"; +return rss.str(); +} + +template +struct StringMaker::value && !::Catch::Detail::IsStreamInsertable::value>::type> { +static std::string convert( R const& range ) { +return rangeToString( range ); +} +}; + +template +struct StringMaker { +static std::string convert(T const(&arr)[SZ]) { +return rangeToString(arr); +} +}; + +} // namespace Catch + +// Separate std::chrono::duration specialization +#if +defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +#include + +#include + +#include + + +namespace Catch { + +template +struct ratio_string { +static std::string symbol(); +}; + +template +std::string ratio_string::symbol() { +Catch::ReusableStringStream rss; +rss << '[' << Ratio::num << '/' +<< Ratio::den << ']'; +return rss.str(); +} +template <> +struct ratio_string { +static std::string symbol(); +}; +template <> +struct ratio_string { +static std::string symbol(); +}; +template <> +struct ratio_string { +static std::string symbol(); +}; +template <> +struct ratio_string { +static std::string symbol(); +}; +template <> +struct ratio_string { +static std::string symbol(); +}; +template <> +struct ratio_string { +static std::string symbol(); +}; + +//////////// +// std::chrono::duration specializations +template +struct StringMaker> { +static std::string convert(std::chrono::duration const& duration) { +ReusableStringStream rss; +rss << duration.count() << ' ' << ratio_string::symbol() << 's'; +return rss.str(); +} +}; +template +struct StringMaker>> { +static std::string convert(std::chrono::duration> const& duration) { +ReusableStringStream rss; +rss << duration.count() << " s"; +return rss.str(); +} +}; +template +struct StringMaker>> { +static std::string convert(std::chrono::duration> const& duration) { +ReusableStringStream rss; +rss << duration.count() << " m"; +return rss.str(); +} +}; +template +struct StringMaker>> { +static std::string convert(std::chrono::duration> const& duration) { +ReusableStringStream rss; +rss << duration.count() << " h"; +return rss.str(); +} +}; + +//////////// +// std::chrono::time_point specialization +// Generic time_point cannot be specialized, only std::chrono::time_point +template +struct StringMaker> { +static std::string convert(std::chrono::time_point const& time_point) { +return::Catch::Detail::stringify(time_point.time_since_epoch()) + " since epoch"; +} +}; +// std::chrono::time_point specialization +template +struct StringMaker> { +static std::string convert(std::chrono::time_point const& time_point) { +auto converted = std::chrono::system_clock::to_time_t(time_point); + +#ifdef +_MSC_VER +std::tm timeInfo = { +}; +gmtime_s(&timeInfo, &converted); +#else +std::tm* timeInfo = std::gmtime(&converted); +#endif + +auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); +char timeStamp[timeStampSize]; +const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; + +#ifdef +_MSC_VER +std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); +#else +std::strftime(timeStamp, timeStampSize, fmt, timeInfo); +#endif +return std::string(timeStamp); +} +}; +} +#endif // CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER + +#ifdef +_MSC_VER +#pragma +warning(pop) +#endif + +// end catch_tostring.h +#include + + +#ifdef +_MSC_VER +#pragma +warning(push) +#pragma +warning(disable:4389) // '==' : signed/unsigned mismatch +#pragma +warning(disable:4018) // more "signed/unsigned mismatch" +#pragma +warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform) +#pragma +warning(disable:4180) // qualifier applied to function type has no meaning +#pragma +warning(disable:4800) // Forcing result to true or false +#endif + +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 +class BinaryExpr : +public ITransientExpression { +LhsT m_lhs; +StringRef m_op; +RhsT m_rhs; + +void streamReconstructedExpression( std::ostream &os ) const override { +formatReconstructedExpression +( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs )); +} + +public: +BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs ) +: ITransientExpression{ +true, comparisonResult +}, +m_lhs( lhs ), +m_op( op ), +m_rhs( rhs ) +{ +} + +template +auto operator&& ( T ) const -> BinaryExpr const { +static_assert(always_false::value, +"chained comparisons are not supported inside assertions, " +"wrap the expression inside parentheses, or decompose it"); +} + +template +auto operator|| ( T ) const -> BinaryExpr const { +static_assert(always_false::value, +"chained comparisons are not supported inside assertions, " +"wrap the expression inside parentheses, or decompose it"); +} + +template +auto operator== ( T ) const -> BinaryExpr const { +static_assert(always_false::value, +"chained comparisons are not supported inside assertions, " +"wrap the expression inside parentheses, or decompose it"); +} + +template +auto operator!= ( T ) const -> BinaryExpr const { +static_assert(always_false::value, +"chained comparisons are not supported inside assertions, " +"wrap the expression inside parentheses, or decompose it"); +} + +template +auto operator> ( T ) const -> BinaryExpr const { +static_assert(always_false::value, +"chained comparisons are not supported inside assertions, " +"wrap the expression inside parentheses, or decompose it"); +} + +template +auto operator< ( T ) const -> BinaryExpr const { +static_assert(always_false::value, +"chained comparisons are not supported inside assertions, " +"wrap the expression inside parentheses, or decompose it"); +} + +template +auto operator>= ( T ) const -> BinaryExpr const { +static_assert(always_false::value, +"chained comparisons are not supported inside assertions, " +"wrap the expression inside parentheses, or decompose it"); +} + +template +auto operator<= ( T ) const -> BinaryExpr const { +static_assert(always_false::value, +"chained comparisons are not supported inside assertions, " +"wrap the expression inside parentheses, or decompose it"); +} +}; + +template +class UnaryExpr : +public ITransientExpression { +LhsT m_lhs; + +void streamReconstructedExpression( std::ostream &os ) const override { +os << Catch::Detail::stringify( m_lhs ); +} + +public: +explicit UnaryExpr( LhsT lhs ) +: ITransientExpression{ +false, static_cast(lhs) +}, +m_lhs( lhs ) +{ +} +}; + +// Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int) +template +auto compareEqual( LhsT const& lhs, RhsT const& rhs ) -> bool { +return static_cast(lhs == rhs); } +template +auto compareEqual( T* const& lhs, int rhs ) -> bool { +return lhs == reinterpret_cast( rhs ); } +template +auto compareEqual( T* const& lhs, long rhs ) -> bool { +return lhs == reinterpret_cast( rhs ); } +template +auto compareEqual( int lhs, T* const& rhs ) -> bool { +return reinterpret_cast( lhs ) == rhs; } +template +auto compareEqual( long lhs, T* const& rhs ) -> bool { +return reinterpret_cast( lhs ) == rhs; } + +template +auto compareNotEqual( LhsT const& lhs, RhsT&& rhs ) -> bool { +return static_cast(lhs != rhs); } +template +auto compareNotEqual( T* const& lhs, int rhs ) -> bool { +return lhs != reinterpret_cast( rhs ); } +template +auto compareNotEqual( T* const& lhs, long rhs ) -> bool { +return lhs != reinterpret_cast( rhs ); } +template +auto compareNotEqual( int lhs, T* const& rhs ) -> bool { +return reinterpret_cast( lhs ) != rhs; } +template +auto compareNotEqual( long lhs, T* const& rhs ) -> bool { +return reinterpret_cast( lhs ) != rhs; } + +template +class ExprLhs { +LhsT m_lhs; +public: +explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) { +} + +template +auto operator== ( RhsT const& rhs ) -> BinaryExpr const { +return { +compareEqual( m_lhs, rhs ), m_lhs, "==", rhs }; +} +auto operator== ( bool rhs ) -> BinaryExpr const { +return { +m_lhs == rhs, m_lhs, "==", rhs +}; +} + +template +auto operator!= ( RhsT const& rhs ) -> BinaryExpr const { +return { +compareNotEqual( m_lhs, rhs ), m_lhs, "!=", rhs +}; +} +auto operator!= ( bool rhs ) -> BinaryExpr const { +return { +m_lhs != rhs, m_lhs, "!=", rhs +}; +} + +template +auto operator> ( RhsT const& rhs ) -> BinaryExpr const { +return { +static_cast(m_lhs > rhs), m_lhs, ">", rhs +}; +} +template +auto operator< ( RhsT const& rhs ) -> BinaryExpr const { +return { +static_cast(m_lhs < rhs), m_lhs, "<", rhs +}; +} +template +auto operator>= ( RhsT const& rhs ) -> BinaryExpr const { +return { +static_cast(m_lhs >= rhs), m_lhs, ">=", rhs +}; +} +template +auto operator<= ( RhsT const& rhs ) -> BinaryExpr const { +return { +static_cast(m_lhs <= rhs), m_lhs, "<=", rhs +}; +} + +template +auto operator&& ( RhsT const& ) -> BinaryExpr const { +static_assert(always_false::value, +"operator&& is not supported inside assertions, " +"wrap the expression inside parentheses, or decompose it"); +} + +template +auto operator|| ( RhsT const& ) -> BinaryExpr const { +static_assert(always_false::value, +"operator|| is not supported inside assertions, " +"wrap the expression inside parentheses, or decompose it"); +} + +auto makeUnaryExpr() const -> UnaryExpr { +return UnaryExpr{ +m_lhs +}; +} +}; + +void handleExpression( ITransientExpression const& expr ); + +template +void handleExpression( ExprLhs const& expr ) { +handleExpression( expr.makeUnaryExpr()); +} + +struct Decomposer { +template +auto operator<= ( T const& lhs ) -> ExprLhs { +return ExprLhs{ +lhs +}; +} + +auto operator<=( bool value ) -> ExprLhs { +return ExprLhs{ +value +}; +} +}; + +} // end namespace Catch + +#ifdef +_MSC_VER +#pragma +warning(pop) +#endif + +// end catch_decomposer.h +// start catch_interfaces_capture.h + +#include + + +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 +void handleExpr( ExprLhs 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 + +#include + + +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 +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 +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 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 +void captureValues( size_t index, T const& value ) { +captureValue( index, Catch::Detail::stringify( value )); +} + +template +void captureValues( size_t index, T const& value, Ts const&... values ) { +captureValue( index, Catch::Detail::stringify(value)); +captureValues( index+1, values... ); +} +}; + +} // end namespace Catch + +// 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( !!(__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(__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(__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(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(__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 + + +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 + + +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 + + +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 + + +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 + +#include + + +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 + +#include + + +namespace Catch { + +class TestCase; +struct ITestCaseRegistry; +struct IExceptionTranslatorRegistry; +struct IExceptionTranslator; +struct IReporterRegistry; +struct IReporterFactory; +struct ITagAliasRegistry; +class StartupExceptionRegistry; + +using IReporterFactoryPtr = std::shared_ptr; + +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 + +#include + +#include + + +namespace Catch { +using exceptionTranslateFunction = std::string(*)(); + +struct IExceptionTranslator; +using ExceptionTranslators = std::vector>; + +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 +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 +ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& )) { +getMutableRegistryHub().registerTranslator +( new ExceptionTranslator( 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 + + +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 ::value>::type> +Approx operator()( T const& value ) { +Approx approx( static_cast(value)); +approx.m_epsilon = m_epsilon; +approx.m_margin = m_margin; +approx.m_scale = m_scale; +return approx; +} + +template ::value>::type> +explicit Approx( T const& value ): Approx(static_cast(value)) +{ +} + +template ::value>::type> +friend bool operator== ( const T& lhs, Approx const& rhs ) { +auto lhs_v = static_cast(lhs); +return rhs.equalityComparisonImpl(lhs_v); +} + +template ::value>::type> +friend bool operator== ( Approx const& lhs, const T& rhs ) { +return operator==( rhs, lhs ); +} + +template ::value>::type> +friend bool operator!= ( T const& lhs, Approx const& rhs ) { +return !operator==( lhs, rhs ); +} + +template ::value>::type> +friend bool operator!= ( Approx const& lhs, T const& rhs ) { +return !operator==( rhs, lhs ); +} + +template ::value>::type> +friend bool operator<= ( T const& lhs, Approx const& rhs ) { +return static_cast(lhs) < rhs.m_value || lhs == rhs; +} + +template ::value>::type> +friend bool operator<= ( Approx const& lhs, T const& rhs ) { +return lhs.m_value < static_cast(rhs) || lhs == rhs; +} + +template ::value>::type> +friend bool operator>= ( T const& lhs, Approx const& rhs ) { +return static_cast(lhs) > rhs.m_value || lhs == rhs; +} + +template ::value>::type> +friend bool operator>= ( Approx const& lhs, T const& rhs ) { +return lhs.m_value > static_cast(rhs) || lhs == rhs; +} + +template ::value>::type> +Approx& epsilon( T const& newEpsilon ) { +double epsilonAsDouble = static_cast(newEpsilon); +setEpsilon(epsilonAsDouble); +return *this; +} + +template ::value>::type> +Approx& margin( T const& newMargin ) { +double marginAsDouble = static_cast(newMargin); +setMargin(marginAsDouble); +return *this; +} + +template ::value>::type> +Approx& scale( T const& newScale ) { +m_scale = static_cast(newScale); +return *this; +} + +std::string toString() const; + +private: +double m_epsilon; +double m_margin; +double m_scale; +double m_value; +}; +} // 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 { +static std::string convert(Catch::Detail::Approx const& value); +}; + +} // end namespace Catch + +// end catch_approx.h +// start catch_string_manip.h + +#include + +#include + + +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 + +#include + + +namespace Catch { +namespace Matchers { +namespace Impl { + +template struct MatchAllOf; +template struct MatchAnyOf; +template 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 +struct MatcherMethod { +virtual bool match( ObjectT const& arg ) const = 0; +}; + +#ifdef +__clang__ +# pragma +clang diagnostic pop +#endif + +template +struct MatcherBase : MatcherUntypedBase, MatcherMethod { + +MatchAllOf operator&& ( MatcherBase const& other ) const; +MatchAnyOf operator|| ( MatcherBase const& other ) const; +MatchNotOf operator! () const; +}; + +template +struct MatchAllOf : MatcherBase { +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& operator&& ( MatcherBase const& other ) { +m_matchers.push_back( &other ); +return *this; +} + +std::vector const*> m_matchers; +}; +template +struct MatchAnyOf : MatcherBase { + +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& operator|| ( MatcherBase const& other ) { +m_matchers.push_back( &other ); +return *this; +} + +std::vector const*> m_matchers; +}; + +template +struct MatchNotOf : MatcherBase { + +MatchNotOf( MatcherBase 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 const& m_underlyingMatcher; +}; + +template +MatchAllOf MatcherBase::operator&& ( MatcherBase const& other ) const { +return MatchAllOf() && *this && other; +} +template +MatchAnyOf MatcherBase::operator|| ( MatcherBase const& other ) const { +return MatchAnyOf() || *this || other; +} +template +MatchNotOf MatcherBase::operator! () const { +return MatchNotOf( *this ); +} + +} // namespace Impl + +} // namespace Matchers + +using namespace Matchers; +using Matchers::Impl::MatcherBase; + +} // namespace Catch + +// end catch_matchers.h +// start catch_matchers_floating.h + +#include + +#include + + +namespace Catch { +namespace Matchers { + +namespace Floating { + +enum class FloatingPointKind : uint8_t; + +struct WithinAbsMatcher : MatcherBase { +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 { +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 + +#include + + +namespace Catch { +namespace Matchers { +namespace Generic { + +namespace Detail { +std::string finalizeDescription(const std::string& desc); +} + +template +class PredicateMatcher : +public MatcherBase { +std::function m_predicate; +std::string m_description; +public: + +PredicateMatcher(std::function 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 is hard (but possible) and +// requires a lot of TMP. +template +Generic::PredicateMatcher Predicate(std::function const& predicate, std::string const& description = "") { +return Generic::PredicateMatcher(predicate, description); +} + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_generic.hpp +// start catch_matchers_string.h + +#include + + +namespace Catch { +namespace Matchers { + +namespace StdString { + +struct CasedString +{ +CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ); +std::string adjustString( std::string const& str ) const; +std::string caseSensitivitySuffix() const; + +CaseSensitive::Choice m_caseSensitivity; +std::string m_str; +}; + +struct StringMatcherBase : MatcherBase { +StringMatcherBase( std::string const& operation, CasedString const& comparator ); +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 { +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 + + +namespace Catch { +namespace Matchers { + +namespace Vector { +namespace Detail { +template +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 +bool contains(InputIterator first, InputIterator last, T const& item) { +for (; first != last; ++first) { +if (*first == item) { +return true; +} +} +return false; +} +} + +template +struct ContainsElementMatcher : MatcherBase> { + +ContainsElementMatcher(T const &comparator) : m_comparator( comparator) { +} + +bool match(std::vector 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 +struct ContainsMatcher : MatcherBase> { + +ContainsMatcher(std::vector const &comparator) : m_comparator( comparator ) { +} + +bool match(std::vector 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 const& m_comparator; +}; + +template +struct EqualsMatcher : MatcherBase> { + +EqualsMatcher(std::vector const &comparator) : m_comparator( comparator ) { +} + +bool match(std::vector 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 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 const& m_comparator; +}; + +template +struct UnorderedEqualsMatcher : MatcherBase> { +UnorderedEqualsMatcher(std::vector const& target) : m_target(target) { +} +bool match(std::vector const& vec) const override { +// Note: This is a reimplementation of std::is_permutation, +// because I don't want to include 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 const& m_target; +}; + +} // namespace Vector + +// The following functions create the actual matcher objects. +// This allows the types to be inferred + +template +Vector::ContainsMatcher Contains( std::vector const& comparator ) { +return Vector::ContainsMatcher( comparator ); +} + +template +Vector::ContainsElementMatcher VectorContains( T const& comparator ) { +return Vector::ContainsElementMatcher( comparator ); +} + +template +Vector::EqualsMatcher Equals( std::vector const& comparator ) { +return Vector::EqualsMatcher( comparator ); +} + +template +Vector::UnorderedEqualsMatcher UnorderedEquals(std::vector const& target) { +return Vector::UnorderedEqualsMatcher(target); +} + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_vector.h +namespace Catch { + +template +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; + +void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString ); + +template +auto makeMatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef const& matcherString ) -> MatchExpr { +return MatchExpr( 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(__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 + + +namespace Catch { + +namespace Generators { +class GeneratorUntypedBase { +public: +GeneratorUntypedBase() = default; +virtual ~GeneratorUntypedBase(); +// Attempts to move the generator to the next element +// +// Returns true iff the move succeeded (and a valid element +// can be retrieved). +virtual bool next() = 0; +}; +using GeneratorBasePtr = std::unique_ptr; + +} // 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; +}; + +} // namespace Catch + +// end catch_interfaces_generatortracker.h +// start catch_enforce.h + +#include + + +namespace Catch { +#if +!defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +template +[[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 + +#include + +#include + + +#include + +#include + + +namespace Catch { + +class GeneratorException : +public std::exception { +const char* const m_msg = ""; + +public: +GeneratorException(const char* msg): +m_msg(msg) +{ +} + +const char* what() const noexcept override final; +}; + +namespace Generators { + +// !TBD move this into its own location? +namespace pf{ +template +std::unique_ptr make_unique( Args&&... args ) { +return std::unique_ptr(new T(std::forward(args)...)); +} +} + +template +struct IGenerator : GeneratorUntypedBase { +virtual ~IGenerator() = default; + +// Returns the current element of the generator +// +// \Precondition The generator is either freshly constructed, +// or the last call to `next()` returned true +virtual T const& get() const = 0; +using type = T; +}; + +template +class SingleValueGenerator final : +public IGenerator { +T m_value; +public: +SingleValueGenerator(T const& value) : m_value( value ) { +} +SingleValueGenerator(T&& value) : m_value(std::move(value)) { +} + +T const& get() const override { +return m_value; +} +bool next() override { +return false; +} +}; + +template +class FixedValuesGenerator final : +public IGenerator { +std::vector m_values; +size_t m_idx = 0; +public: +FixedValuesGenerator( std::initializer_list values ) : m_values( values ) { +} + +T const& get() const override { +return m_values[m_idx]; +} +bool next() override { +++m_idx; +return m_idx < m_values.size(); +} +}; + +template +class GeneratorWrapper final { +std::unique_ptr> m_generator; +public: +GeneratorWrapper(std::unique_ptr> generator): +m_generator(std::move(generator)) +{ +} +T const& get() const { +return m_generator->get(); +} +bool next() { +return m_generator->next(); +} +}; + +template +GeneratorWrapper value(T&& value) { +return GeneratorWrapper(pf::make_unique>(std::forward(value))); +} +template +GeneratorWrapper values(std::initializer_list values) { +return GeneratorWrapper(pf::make_unique>(values)); +} + +template +class Generators : +public IGenerator { +std::vector> m_generators; +size_t m_current = 0; + +void populate(GeneratorWrapper&& generator) { +m_generators.emplace_back(std::move(generator)); +} +void populate(T&& val) { +m_generators.emplace_back(value(std::move(val))); +} +template +void populate(U&& val) { +populate(T(std::move(val))); +} +template +void populate(U&& valueOrGenerator, Gs... moreGenerators) { +populate(std::forward(valueOrGenerator)); +populate(std::forward(moreGenerators)...); +} + +public: +template +Generators(Gs... moreGenerators) { +m_generators.reserve(sizeof...(Gs)); +populate(std::forward(moreGenerators)...); +} + +T const& get() const override { +return m_generators[m_current].get(); +} + +bool next() override { +if (m_current >= m_generators.size()) { +return false; +} +const bool current_status = m_generators[m_current].next(); +if (!current_status) { +++m_current; +} +return m_current < m_generators.size(); +} +}; + +template +GeneratorWrapper> table( std::initializer_list::type...>> tuples ) { +return values>( tuples ); +} + +// Tag type to signal that a generator sequence should convert arguments to a specific type +template +struct as { +}; + +template +auto makeGenerators( GeneratorWrapper&& generator, Gs... moreGenerators ) -> Generators { +return Generators(std::move(generator), std::forward(moreGenerators)...); +} +template +auto makeGenerators( GeneratorWrapper&& generator ) -> Generators { +return Generators(std::move(generator)); +} +template +auto makeGenerators( T&& val, Gs... moreGenerators ) -> Generators { +return makeGenerators( value( std::forward( val )), std::forward( moreGenerators )... ); +} +template +auto makeGenerators( as, U&& val, Gs... moreGenerators ) -> Generators { +return makeGenerators( value( T( std::forward( val ))), std::forward( moreGenerators )... ); +} + +template +class TakeGenerator : +public IGenerator { +GeneratorWrapper m_generator; +size_t m_returned = 0; +size_t m_target; +public: +TakeGenerator(size_t target, GeneratorWrapper&& generator): +m_generator(std::move(generator)), +m_target(target) +{ +assert(target != 0 && "Empty generators are not allowed"); +} +T const& get() const override { +return m_generator.get(); +} +bool next() override { +++m_returned; +if (m_returned >= m_target) { +return false; +} + +const auto success = m_generator.next(); +// If the underlying generator does not contain enough values +// then we cut short as well +if (!success) { +m_returned = m_target; +} +return success; +} +}; + +template +GeneratorWrapper take(size_t target, GeneratorWrapper&& generator) { +return GeneratorWrapper(pf::make_unique>(target, std::move(generator))); +} + +template +class FilterGenerator : +public IGenerator { +GeneratorWrapper m_generator; +Predicate m_predicate; +public: +template +FilterGenerator(P&& pred, GeneratorWrapper&& generator): +m_generator(std::move(generator)), +m_predicate(std::forward

(pred)) +{ +if (!m_predicate(m_generator.get())) { +// It might happen that there are no values that pass the +// filter. In that case we throw an exception. +auto has_initial_value = next(); +if (!has_initial_value) { +Catch::throw_exception(GeneratorException("No valid value found in filtered generator")); +} +} +} + +T const& get() const override { +return m_generator.get(); +} + +bool next() override { +bool success = m_generator.next(); +if (!success) { +return false; +} +while (!m_predicate(m_generator.get()) && (success = m_generator.next()) == true); +return success; +} +}; + +template +GeneratorWrapper filter(Predicate&& pred, GeneratorWrapper&& generator) { +return GeneratorWrapper(std::unique_ptr>(pf::make_unique>(std::forward(pred), std::move(generator)))); +} + +template +class RepeatGenerator : +public IGenerator { +GeneratorWrapper m_generator; +mutable std::vector m_returned; +size_t m_target_repeats; +size_t m_current_repeat = 0; +size_t m_repeat_index = 0; +public: +RepeatGenerator(size_t repeats, GeneratorWrapper&& generator): +m_generator(std::move(generator)), +m_target_repeats(repeats) +{ +assert(m_target_repeats > 0 && "Repeat generator must repeat at least once"); +} + +T const& get() const override { +if (m_current_repeat == 0) { +m_returned.push_back(m_generator.get()); +return m_returned.back(); +} +return m_returned[m_repeat_index]; +} + +bool next() override { +// There are 2 basic cases: +// 1) We are still reading the generator +// 2) We are reading our own cache + +// In the first case, we need to poke the underlying generator. +// If it happily moves, we are left in that state, otherwise it is time to start reading from our cache +if (m_current_repeat == 0) { +const auto success = m_generator.next(); +if (!success) { +++m_current_repeat; +} +return m_current_repeat < m_target_repeats; +} + +// In the second case, we need to move indices forward and check that we haven't run up against the end +++m_repeat_index; +if (m_repeat_index == m_returned.size()) { +m_repeat_index = 0; +++m_current_repeat; +} +return m_current_repeat < m_target_repeats; +} +}; + +template +GeneratorWrapper repeat(size_t repeats, GeneratorWrapper&& generator) { +return GeneratorWrapper(pf::make_unique>(repeats, std::move(generator))); +} + +template +class MapGenerator : +public IGenerator { +// TBD: provide static assert for mapping function, for friendly error message +GeneratorWrapper m_generator; +Func m_function; +// To avoid returning dangling reference, we have to save the values +T m_cache; +public: +template +MapGenerator(F2&& function, GeneratorWrapper&& generator) : +m_generator(std::move(generator)), +m_function(std::forward(function)), +m_cache(m_function(m_generator.get())) +{ +} + +T const& get() const override { +return m_cache; +} +bool next() override { +const auto success = m_generator.next(); +if (success) { +m_cache = m_function(m_generator.get()); +} +return success; +} +}; + +template +GeneratorWrapper map(Func&& function, GeneratorWrapper&& generator) { +return GeneratorWrapper( +pf::make_unique>(std::forward(function), std::move(generator)) +); +} +template +GeneratorWrapper map(Func&& function, GeneratorWrapper&& generator) { +return GeneratorWrapper( +pf::make_unique>(std::forward(function), std::move(generator)) +); +} + +auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker&; + +template +// 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().get()) { +using UnderlyingType = typename decltype(generatorExpression())::type; + +IGeneratorTracker& tracker = acquireGeneratorTracker( lineInfo ); +if (!tracker.hasGenerator()) { +tracker.setGenerator(pf::make_unique>(generatorExpression())); +} + +auto const& generator = static_cast const&>( *tracker.getGenerator()); +return generator.get(); +} + +} // 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 + +#include + +#include + + +#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 const& _tags, +SourceLineInfo const& _lineInfo ); + +friend void setTags( TestCaseInfo& testCaseInfo, std::vector 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 tags; +std::vector 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 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 + + +#include + + +// 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{ +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 + +#include + +#include + + +namespace Catch { + +class TestSpec { +struct Pattern { +virtual ~Pattern(); +virtual bool matches( TestCaseInfo const& testCase ) const = 0; +}; +using PatternPtr = std::shared_ptr; + +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 m_patterns; + +bool matches( TestCaseInfo const& testCase ) const; +}; + +public: +bool hasFilters() const; +bool matches( TestCaseInfo const& testCase ) const; + +private: +std::vector 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 + + +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 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 +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( token ); +if ( m_exclusion ) +pattern = std::make_shared( 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 + +#include + +#include + +#include + + +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 const& getSectionsToRun() const = 0; +virtual Verbosity verbosity() const = 0; +}; + +using IConfigPtr = std::shared_ptr; +} + +// end catch_interfaces_config.h +// Libstdc++ doesn't like incomplete classes for unique_ptr + +#include + +#include + +#include + + +#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 testsOrTags; +std::vector 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 const& getTestsOrTags() const; +std::vector 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 m_stream; +TestSpec m_testSpec; +bool m_hasTestFilters = false; +}; + +} // end namespace Catch + +// end catch_config.hpp +// start catch_assertionresult.h + +#include + + +namespace Catch { + +struct AssertionResultData +{ +AssertionResultData() = delete; + +AssertionResultData( ResultWas::OfType _resultType, LazyExpression const& _lazyExpression ); + +std::string message; +mutable std::string reconstructedExpression; +LazyExpression lazyExpression; +ResultWas::OfType resultType; + +std::string reconstructExpression() const; +}; + +class AssertionResult { +public: +AssertionResult() = delete; +AssertionResult( AssertionInfo const& info, AssertionResultData const& data ); + +bool isOk() const; +bool succeeded() const; +ResultWas::OfType getResultType() const; +bool hasExpression() const; +bool hasMessage() const; +std::string getExpression() const; +std::string getExpressionInMacro() const; +bool hasExpandedExpression() const; +std::string getExpandedExpression() const; +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 +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 + +#include + +#include + +#include + +#include + + +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 +struct LazyStat : Option { +LazyStat& operator=( T const& _value ) { +Option::operator=( _value ); +used = false; +return *this; +} +void reset() { +Option::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 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 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 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; + +struct IReporterFactory { +virtual ~IReporterFactory(); +virtual IStreamingReporterPtr create( ReporterConfig const& config ) const = 0; +virtual std::string getDescription() const = 0; +}; +using IReporterFactoryPtr = std::shared_ptr; + +struct IReporterRegistry { +using FactoryMap = std::map; +using Listeners = std::vector; + +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 + +#include + +#include + +#include + +#include + +#include + +#include + + +namespace Catch { +void prepareExpandedExpression(AssertionResult& result); + +// Returns double formatted as %.3f (format expected on output) +std::string getFormattedDuration( double duration ); + +template +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 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 currentTestRunInfo; +LazyStat currentGroupInfo; +LazyStat currentTestCaseInfo; + +std::vector m_sectionStack; +ReporterPreferences m_reporterPrefs; +}; + +template +struct CumulativeReporterBase : IStreamingReporter { +template +struct Node { +explicit Node( T const& _value ) : value( _value ) { +} +virtual ~Node() { +} + +using ChildNodes = std::vector>; +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 const& other) const { +return operator==(*other); +} + +SectionStats stats; +using ChildSections = std::vector>; +using Assertions = std::vector; +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 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; +using TestGroupNode = Node; +using TestRunNode = Node; + +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 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 node; +if ( m_sectionStack.empty()) { +if ( !m_rootSection ) +m_rootSection = std::make_shared( 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( 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( 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(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(testGroupStats); +node->children.swap(m_testCases); +m_testGroups.push_back(node); +} +void testRunEnded(TestRunStats const& testRunStats) override { +auto node = std::make_shared(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 m_assertions; +std::vector>> m_sections; +std::vector> m_testCases; +std::vector> m_testGroups; + +std::vector> m_testRuns; + +std::shared_ptr m_rootSection; +std::shared_ptr m_deepestSection; +std::vector> m_sectionStack; +ReporterPreferences m_reporterPrefs; +}; + +template +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( ReporterConfig const& _config ); + +static std::set 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 +class ReporterRegistrar { + +class ReporterFactory : +public IReporterFactory { + +virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override { +return std::unique_ptr( 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()); +} +}; + +template +class ListenerRegistrar { + +class ListenerFactory : +public IReporterFactory { + +virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override { +return std::unique_ptr( new T( config )); +} +virtual std::string getDescription() const override { +return std::string(); +} +}; + +public: + +ListenerRegistrar() { +getMutableRegistryHub().registerListener( std::make_shared()); +} +}; +} + +#if +!defined(CATCH_CONFIG_DISABLE) + +#define +CATCH_REGISTER_REPORTER( name, reporterType ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ +Catch::ReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS + +#define +CATCH_REGISTER_LISTENER( listenerType ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ +Catch::ListenerRegistrar 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 { + +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 { +std::unique_ptr 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 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 + + +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 +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 +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 m_tags; +std::string m_indent; +std::ostream& m_os; +}; + +} + +// end catch_xmlwriter.h +namespace Catch { + +class JunitReporter : +public CumulativeReporterBase { +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 { +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 + +#include + +#include + + +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; + +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 isGeneratorTracker() 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; +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 isGeneratorTracker() const override; + +void open(); + +void close() override; +void fail() override; +void markAsNeedingAnotherRun() override; + +private: +void moveToParent(); +void moveToThis(); +}; + +class SectionTracker : +public TrackerBase { +std::vector m_filters; +public: +SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); + +bool isSectionTracker() const override; + +bool isComplete() const override; + +static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ); + +void tryOpen(); + +void addInitialFilters( std::vector const& filters ); +void addNextFilters( std::vector const& filters ); +}; + +} // namespace TestCaseTracking + +using TestCaseTracking::ITracker; +using TestCaseTracking::TrackerContext; +using TestCaseTracking::SectionTracker; + +} // 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 + +#include + + +namespace { + +// Performs equivalent check of std::fabs(lhs - rhs) <= margin +// But without the subtraction to allow for INFINITY in comparison +bool marginComparison(double lhs, double rhs, double margin) { +return (lhs + margin >= rhs) && (rhs + margin >= lhs); +} + +} + +namespace Catch { +namespace Detail { + +Approx::Approx ( double value ) +: m_epsilon( std::numeric_limits::epsilon()*100 ), +m_margin( 0.0 ), +m_scale( 0.0 ), +m_value( value ) +{ +} + +Approx Approx::custom() { +return Approx( 0 ); +} + +Approx Approx::operator-() const { +auto temp(*this); +temp.m_value = -temp.m_value; +return temp; +} + +std::string Approx::toString() const { +ReusableStringStream rss; +rss << "Approx( " <<::Catch::Detail::stringify( m_value ) << " )"; +return rss.str(); +} + +bool Approx::equalityComparisonImpl(const double other) const { +// First try with fixed margin, then compute margin based on epsilon, scale and Approx's value +// Thanks to Richard Harris for his help refining the scaled margin value +return marginComparison(m_value, other, m_margin) || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(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::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 + + +namespace Catch { + +struct IResultCapture; +struct IRunner; +struct IConfig; +struct IMutableContext; + +using IConfigPtr = std::shared_ptr; + +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 + + +#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 +#define +CATCH_BREAK_INTO_DEBUGGER()[]{ +}() +#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 + +#else +#include + +#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 + + +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 + + +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 = nullptr; +Option m_lastResult; + +IConfigPtr m_config; +Totals m_totals; +IStreamingReporterPtr m_reporter; +std::vector m_messages; +AssertionInfo m_lastAssertionInfo; +std::vector m_unfinishedSections; +std::vector 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; + +// 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 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() && __cplusplus >= 201703L +#include + +#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 + +#include + +#include + +#include + + +#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 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 m_columns; + +public: + +class iterator { +friend Columns; +struct EndTag {}; + +std::vector const& m_columns; +std::vector 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 + +#include + +#include + +#include + + +#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 +struct UnaryLambdaTraits : UnaryLambdaTraits { +}; + +template +struct UnaryLambdaTraits { +static const bool isValid = false; +}; + +template +struct UnaryLambdaTraits { +static const bool isValid = true; +using ArgType = typename std::remove_const::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 m_args; + +public: +Args( int argc, char const* const* argv ) +: m_exeName(argv[0]), +m_args(argv + 1, argv + argc) { +} + +Args( std::initializer_list 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::const_iterator; +Iterator it; +Iterator itEnd; +std::vector 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 +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 : +public ResultBase { +protected: +using ResultBase::ResultBase; +}; + +template +class BasicResult : +public ResultValueBase { +public: +template +explicit BasicResult( BasicResult const &other ) +: ResultValueBase( other.type()), +m_errorMessage( other.errorMessage()) +{ +assert( type() != ResultBase::Ok ); +} + +template +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(type), +m_errorMessage(message) +{ +assert( m_type != ResultBase::Ok ); +} + +using ResultValueBase::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; +using ParserResult = BasicResult; +using InternalParseResult = BasicResult; + +struct HelpColumns { +std::string left; +std::string right; +}; + +template +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(::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 +inline auto convertInto( std::string const &source, CLARA_CONFIG_OPTIONAL_TYPE& 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 +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 +struct BoundValueRef> : BoundValueRefBase { +std::vector &m_ref; + +explicit BoundValueRef( std::vector &ref ) : m_ref( ref ) { +} + +auto isContainer() const -> bool override { +return true; } + +auto setValue( std::string const &arg ) -> ParserResult override { +T temp; +auto result = convertInto( arg, temp ); +if ( result ) +m_ref.push_back( temp ); +return result; +} +}; + +struct BoundFlagRef : BoundFlagRefBase { +bool &m_ref; + +explicit BoundFlagRef( bool &ref ) : m_ref( ref ) { +} + +auto setFlag( bool flag ) -> ParserResult override { +m_ref = flag; +return ParserResult::ok( ParseResultType::Matched ); +} +}; + +template +struct LambdaInvoker { +static_assert( std::is_same::value, "Lambda must return void or clara::ParserResult" ); + +template +static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { +return lambda( arg ); +} +}; + +template<> +struct LambdaInvoker { +template +static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { +lambda( arg ); +return ParserResult::ok( ParseResultType::Matched ); +} +}; + +template +inline auto invokeLambda( L const &lambda, std::string const &arg ) -> ParserResult { +ArgType temp{ +}; +auto result = convertInto( arg, temp ); +return !result +? result +: LambdaInvoker::ReturnType>::invoke( lambda, temp ); +} + +template +struct BoundLambda : BoundValueRefBase { +L m_lambda; + +static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); +explicit BoundLambda( L const &lambda ) : m_lambda( lambda ) { +} + +auto setValue( std::string const &arg ) -> ParserResult override { +return invokeLambda::ArgType>( m_lambda, arg ); +} +}; + +template +struct BoundFlagLambda : BoundFlagRefBase { +L m_lambda; + +static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); +static_assert( std::is_same::ArgType, bool>::value, "flags must be boolean" ); + +explicit BoundFlagLambda( L const &lambda ) : m_lambda( lambda ) { +} + +auto setFlag( bool flag ) -> ParserResult override { +return LambdaInvoker::ReturnType>::invoke( m_lambda, flag ); +} +}; + +enum class Optionality { Optional, Required }; + +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 +class ComposableParserImpl : +public ParserBase { +public: +template +auto operator|( T const &other ) const -> Parser; + +template +auto operator+( T const &other ) const -> Parser; +}; + +// Common code and state for Args and Opts +template +class ParserRefImpl : +public ComposableParserImpl { +protected: +Optionality m_optionality = Optionality::Optional; +std::shared_ptr m_ref; +std::string m_hint; +std::string m_description; + +explicit ParserRefImpl( std::shared_ptr const &ref ) : m_ref( ref ) { +} + +public: +template +ParserRefImpl( T &ref, std::string const &hint ) +: m_ref( std::make_shared>( ref )), +m_hint( hint ) +{ +} + +template +ParserRefImpl( LambdaT const &ref, std::string const &hint ) +: m_ref( std::make_shared>( ref )), +m_hint(hint) +{ +} + +auto operator()( std::string const &description ) -> DerivedT & { +m_description = description; +return static_cast( *this ); +} + +auto optional() -> DerivedT & { +m_optionality = Optionality::Optional; +return static_cast( *this ); +}; + +auto required() -> DerivedT & { +m_optionality = Optionality::Required; +return static_cast( *this ); +}; + +auto isOptional() const -> bool { +return m_optionality == Optionality::Optional; +} + +auto cardinality() const -> size_t override { +if ( m_ref->isContainer()) +return 0; +else +return 1; +} + +auto hint() const -> std::string { +return m_hint; } +}; + +class ExeName : +public ComposableParserImpl { +std::shared_ptr m_name; +std::shared_ptr m_ref; + +template +static auto makeRef(LambdaT const &lambda) -> std::shared_ptr { +return std::make_shared>( lambda); +} + +public: +ExeName() : m_name( std::make_shared( "" )) { +} + +explicit ExeName( std::string &ref ) : ExeName() { +m_ref = std::make_shared>( ref ); +} + +template +explicit ExeName( LambdaT const& lambda ) : ExeName() { +m_ref = std::make_shared>( lambda ); +} + +// The exe name is not parsed out of the normal tokens, but is handled specially +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 { +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( 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 { +protected: +std::vector m_optNames; + +public: +template +explicit Opt( LambdaT const &ref ) : ParserRefImpl( std::make_shared>( ref )) { +} + +explicit Opt( bool &ref ) : ParserRefImpl( std::make_shared( ref )) { +} + +template +Opt( LambdaT const &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) { +} + +template +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 { +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( 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( 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( *this ) +("display usage information") +["-?"]["-h"]["--help"] +.optional(); +} +}; + +struct Parser : ParserBase { + +mutable ExeName m_exeName; +std::vector m_options; +std::vector 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 +auto operator|( T const &other ) const -> Parser { +return Parser( *this ) |= other; +} + +// Forward deprecated interface with '+' instead of '|' +template +auto operator+=( T const &other ) -> Parser & { +return operator|=( other ); } +template +auto operator+( T const &other ) const -> Parser { +return operator|( other ); } + +auto getHelpColumns() const -> std::vector { +std::vector 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 +template +auto ComposableParserImpl::operator|( T const &other ) const -> Parser { +return Parser() | static_cast( *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 + +#include + + +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( 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( 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 + +#include + + +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 const& Config::getTestsOrTags() const { +return m_data.testsOrTags; } +std::vector 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 + + +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 + + +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 ) { +getCurrentContext().getConfig()->stream() +<< '\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 + + +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 + +# include + +# include + +# include + +# include + +# include + +# include + + +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 + +#include + + +namespace Catch{ +// The standard POSIX way of detecting a debugger is to attempt to +// ptrace() the process, but this needs to be done from a child and not +// this process itself to still allow attaching to this process later +// if wanted, so is rather heavy. Under Linux we have the PID of the +// "debugger" (which doesn't need to be gdb, of course, it could also +// be strace, for example) in /proc/$PID/status, so just get it from +// there instead. +bool isDebuggerActive(){ +// Libstdc++ has a bug, where std::ifstream sets errno to 0 +// This way our users can properly assert over errno values +ErrnoGuard guard; +std::ifstream in("/proc/self/status"); +for ( std::string line; std::getline(in, line); ) { +static const int PREFIX_LEN = 11; +if ( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) { +// We're traced if the PID is not 0 and no other PID starts +// with 0 digit, so it's enough to check for just a single +// character. +return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; +} +} + +return false; +} +} // namespace Catch +#elif +defined(_MSC_VER) +extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); +namespace Catch { +bool isDebuggerActive() { +return IsDebuggerPresent() != 0; +} +} +#elif +defined(__MINGW32__) +extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); +namespace Catch { +bool isDebuggerActive() { +return IsDebuggerPresent() != 0; +} +} +#else +namespace Catch { +bool isDebuggerActive() { +return false; } +} +#endif // Platform +// 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 + + +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 + +#include + +#include + + +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> 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( 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"; +} +} + +std::string ExceptionTranslatorRegistry::tryTranslators() const { +if (m_translators.empty()) { +std::rethrow_exception(std::current_exception()); +} else { +return m_translators[0]->translate(m_translators.begin() + 1, m_translators.end()); +} +} + +#else // ^^ Exceptions are enabled // Exceptions are disabled vv +std::string ExceptionTranslatorRegistry::translateActiveException() const { +CATCH_INTERNAL_ERROR("Attempted to translate active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); +} + +std::string ExceptionTranslatorRegistry::tryTranslators() const { +CATCH_INTERNAL_ERROR("Attempted to use exception translators under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); +} +#endif + +} +// 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 = ""; +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 + +#include + + +namespace Catch { + +struct IConfig; + +std::mt19937& rng(); +void seedRng( IConfig const& config ); +unsigned int rngSeed(); + +} + +// end catch_random_number_generator.h +#include + +#include + + +namespace Catch { + +IGeneratorTracker::~IGeneratorTracker() { +} + +const char* GeneratorException::what() const noexcept { +return m_msg; +} + +namespace Generators { + +GeneratorUntypedBase::~GeneratorUntypedBase() { +} + +auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { +return getResultCapture().acquireGeneratorTracker( lineInfo ); +} + +} // 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; +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 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 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 + + +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 + + +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 spellings; +std::size_t count = 0; +}; + +std::size_t listTags( Config const& config ); + +std::size_t listReporters(); + +Option 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 + +#include + +#include + + +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 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 tagCounts; + +std::vector 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 list( Config const& config ) { +Option 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 + + +namespace Catch { +template +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 + +#include + +#include + + +namespace Catch { +namespace Matchers { +namespace Floating { +enum class FloatingPointKind : uint8_t { +Float, +Double +}; +} +} +} + +namespace { + +template +struct Converter; + +template <> +struct Converter { +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 { +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 +auto convert(T t) -> Converter { +return Converter(t); +} + +template +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(static_cast(matchee), static_cast(m_target), m_ulps); +case FloatingPointKind::Double: +return almostEqualUlps(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 + + +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 + +#include + + +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 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 + +#include + +#include + + +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 + +#include + +#include + +#include + +#include + + +#if +defined(CATCH_CONFIG_NEW_CAPTURE) +#if +defined(_MSC_VER) +#include + //_dup and _dup2 +#define +dup _dup +#define +dup2 _dup2 +#define +fileno _fileno +#else +#include + // dup and dup2 +#endif +#endif + +namespace Catch { + +RedirectedStream::RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream ) +: m_originalStream( originalStream ), +m_redirectionStream( redirectionStream ), +m_prevBuf( m_originalStream.rdbuf()) +{ +m_originalStream.rdbuf( m_redirectionStream.rdbuf()); +} + +RedirectedStream::~RedirectedStream() { +m_originalStream.rdbuf( m_prevBuf ); +} + +RedirectedStdOut::RedirectedStdOut() : m_cout( Catch::cout(), m_rss.get()) { +} +auto RedirectedStdOut::str() const -> std::string { +return m_rss.str(); } + +RedirectedStdErr::RedirectedStdErr() +: m_cerr( Catch::cerr(), m_rss.get()), +m_clog( Catch::clog(), m_rss.get()) +{ +} +auto RedirectedStdErr::str() const -> std::string { return m_rss.str(); } + +#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 + + +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 + +#include + +#include + +#include + + +namespace Catch { + +class TestCase; +struct IConfig; + +std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ); +bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + +void enforceNoDuplicateTestCases( std::vector const& functions ); + +std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); +std::vector const& getAllTestCasesSorted( IConfig const& config ); + +class TestRegistry : +public ITestCaseRegistry { +public: +virtual ~TestRegistry() = default; + +virtual void registerTest( TestCase const& testCase ); + +std::vector const& getAllTests() const override; +std::vector const& getAllTestsSorted( IConfig const& config ) const override; + +private: +std::vector m_functions; +mutable RunTests::InWhatOrder m_currentSortOrder = RunTests::InDeclarationOrder; +mutable std::vector 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 + + +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 + + +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 + + +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 m_registry; +}; + +} // end namespace Catch + +// end catch_tag_alias_registry.h +// start catch_startup_exception_registry.h + +#include + +#include + + +namespace Catch { + +class StartupExceptionRegistry { +public: +void add(std::exception_ptr const& exception) noexcept; +std::vector const& getExceptions() const noexcept; +private: +std::vector 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 +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; + +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( static_cast( lhs ) | static_cast( rhs )); +} + +bool shouldContinueOnFailure( int flags ) { +return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } +bool shouldSuppressFailure( int flags ) { +return ( flags & ResultDisposition::SuppressFail ) != 0; } + +} // end namespace Catch +// end catch_result_type.cpp +// start catch_run_context.cpp + +#include + +#include + +#include + + +namespace Catch { + +namespace Generators { +struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker { +GeneratorBasePtr m_generator; + +GeneratorTracker( TestCaseTracking::NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) +: TrackerBase( nameAndLocation, ctx, parent ) +{ +} +~GeneratorTracker(); + +static GeneratorTracker& acquire( TrackerContext& ctx, TestCaseTracking::NameAndLocation const& nameAndLocation ) { +std::shared_ptr tracker; + +ITracker& currentTracker = ctx.currentTracker(); +if ( TestCaseTracking::ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation )) { +assert( childTracker ); +assert( childTracker->isGeneratorTracker()); +tracker = std::static_pointer_cast( childTracker ); +} +else { +tracker = std::make_shared( nameAndLocation, ctx, ¤tTracker ); +currentTracker.addChild( tracker ); +} + +if ( !ctx.completedCycle() && !tracker->isComplete()) { +tracker->open(); +} + +return *tracker; +} + +// TrackerBase interface +bool isGeneratorTracker() const override { +return true; } +auto hasGenerator() const -> bool override { +return !!m_generator; +} +void close() override { +TrackerBase::close(); +// Generator interface only finds out if it has another item on atual move +if (m_runState == CompletedSuccessfully && m_generator->next()) { +m_children.clear(); +m_runState = Executing; +} +} + +// IGeneratorTracker interface +auto getGenerator() const -> GeneratorBasePtr const& override { +return m_generator; +} +void setGenerator( GeneratorBasePtr&& generator ) override { +m_generator = std::move( generator ); +} +}; +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(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(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(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 + + +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 +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 m_config; +bool m_startupExceptions = false; +}; + +} // end namespace Catch + +// end catch_session.h +// start catch_version.h + +#include + + +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 + +#include + + +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 const& config) { +if (Catch::getRegistryHub().getReporterRegistry().getListeners().empty()) { +return createReporter(config->getReporterName(), config); +} + +// On older platforms, returning std::unique_ptr +// when the return type is std::unique_ptr +// doesn't compile without a std::move call. However, this causes +// a warning on newer platforms. Thus, we have to work around +// it a bit and downcast the pointer manually. +auto ret = std::unique_ptr(new ListeningReporter); +auto& multi = static_cast(*ret); +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 ret; +} + +Catch::Totals runTests(std::shared_ptr 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&>(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(std::getchar()); +} +int exitCode = runInternal(); +if (( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) { +Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << std::endl; +static_cast(std::getchar()); +} +return exitCode; +} + +clara::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( 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 listed = list( config())) +return static_cast( *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(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 + + +namespace Catch { + +namespace { +static auto getSingletons() -> std::vector*& { +static std::vector* g_singletons = nullptr; +if ( !g_singletons ) +g_singletons = new std::vector(); +return g_singletons; +} +} + +ISingleton::~ISingleton() { +} + +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 const& StartupExceptionRegistry::getExceptions() const noexcept { +return m_exceptions; +} + +} // end namespace Catch +// end catch_startup_exception_registry.cpp +// start catch_stream.cpp + +#include + +#include + +#include + +#include + +#include + +#include + + +namespace Catch { + +Catch::IStream::~IStream() = default; + +namespace detail { +namespace { +template +class StreamBufImpl : +public std::streambuf { +char data[bufferSize]; +WriterF m_writer; + +public: +StreamBufImpl() { +setp( data, data + sizeof(data)); +} + +~StreamBufImpl() noexcept { +StreamBufImpl::sync(); +} + +private: +int overflow( int c ) override { +sync(); + +if ( c != EOF ) { +if ( pbase() == epptr()) +m_writer( std::string( 1, static_cast( c ))); +else +sputc( static_cast( c )); +} +return 0; +} + +int sync() override { +if ( pbase() != pptr()) { +m_writer( std::string( pbase(), static_cast( pptr() - pbase()))); +setp( pbase(), epptr()); +} +return 0; +} +}; + +/////////////////////////////////////////////////////////////////////////// + +struct OutputDebugWriter { + +void operator()( std::string const&str ) { +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> m_streamBuf; +mutable std::ostream m_os; +public: +DebugOutStream() +: m_streamBuf( new StreamBufImpl()), +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> m_streams; +std::vector m_unused; +std::ostringstream m_referenceStream; // Used for copy state/ flags from + +auto add() -> std::size_t { +if ( m_unused.empty()) { +m_streams.push_back( std::unique_ptr( 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::getMutable().add()), +m_oss( Singleton::getMutable().m_streams[m_index].get()) +{ +} + +ReusableStringStream::~ReusableStringStream() { +static_cast( m_oss )->str(""); +m_oss->clear(); +Singleton::getMutable().release( m_index ); +} + +auto ReusableStringStream::str() const -> std::string { +return static_cast( 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 + +#include + +#include + +#include + + +namespace Catch { + +namespace { +char toLowerCh(char c) { +return static_cast( 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 + +#include + +#include + + +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(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( 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 + + +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 + +#include + +#include + +#include + + +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::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(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 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 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.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 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 + + +namespace Catch { + +std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { + +std::vector 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 const& functions ) { +std::set 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 filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ) { +std::vector filtered; +filtered.reserve( testCases.size()); +for ( auto const& testCase : testCases ) +if ( matchTest( testCase, testSpec, config )) +filtered.push_back( testCase ); +return filtered; +} +std::vector 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 const& TestRegistry::getAllTests() const { +return m_functions; +} +std::vector 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 + +#include + +#include + +#include + +#include + + +#if +defined(__clang__) +# pragma +clang diagnostic push +# pragma +clang diagnostic ignored "-Wexit-time-destructors" +#endif + +namespace Catch { +namespace TestCaseTracking { + +NameAndLocation::NameAndLocation( std::string const& _name, SourceLineInfo const& _location ) +: name( _name ), +location( _location ) +{ +} + +ITracker::~ITracker() = default; + +TrackerContext& TrackerContext::instance() { +static TrackerContext s_instance; +return s_instance; +} + +ITracker& TrackerContext::startRun() { +m_rootTracker = std::make_shared( 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::isGeneratorTracker() 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( *parent ); +addNextFilters( parentSection.m_filters ); +} +} + +bool SectionTracker::isComplete() const { +bool complete = true; + +if ((m_filters.empty() || m_filters[0] == "") || +std::find(m_filters.begin(), m_filters.end(), +m_nameAndLocation.name) != m_filters.end()) +complete = TrackerBase::isComplete(); +return complete; + +} + +bool SectionTracker::isSectionTracker() const { +return true; } + +SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { +std::shared_ptr section; + +ITracker& currentTracker = ctx.currentTracker(); +if ( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation )) { +assert( childTracker ); +assert( childTracker->isSectionTracker()); +section = std::static_pointer_cast( childTracker ); +} +else { +section = std::make_shared( 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 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 const& filters ) { +if ( filters.size() > 1 ) +m_filters.insert( m_filters.end(), ++filters.begin(), filters.end()); +} + +} // namespace TestCaseTracking + +using TestCaseTracking::ITracker; +using TestCaseTracking::TrackerContext; +using TestCaseTracking::SectionTracker; + +} // 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 + +#include + +#include + +#include + + +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(); +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(); +addFilter(); +} +else if ( c == '[' ) { +if ( subString() == "exclude:" ) +m_exclusion = true; +else +addPattern(); +startNewMode( Tag, ++m_pos ); +} +else if ( c == '\\' ) +escape(); +} +else if ( m_mode == EscapedName ) +m_mode = Name; +else if ( m_mode == QuotedName && c == '"' ) +addPattern(); +else if ( m_mode == Tag && c == ']' ) +addPattern(); +} +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 + + +static const uint64_t nanosecondsInSecond = 1000000000; + +namespace Catch { + +auto getCurrentNanosecondsSinceEpoch() -> uint64_t { +return std::chrono::duration_cast( 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 + 1u ); +} +} + +// 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(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 + +#include + + +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( size ), inc = 1; +if ( Endianness::which() == Endianness::Little ) { +i = end-1; +end = inc = -1; +} + +unsigned char const *bytes = static_cast(object); +ReusableStringStream rss; +rss << "0x" << std::setfill('0') << std::hex; +for (; i != end; i += inc ) +rss << std::setw(2) << static_cast(bytes[i]); +return rss.str(); +} +} + +template +std::string fpToString( T value, int precision ) { +if (Catch::isnan(value)) { +return "nan"; +} + +ReusableStringStream rss; +rss << std::setprecision( precision ) +<< std::fixed +<< value; +std::string d = rss.str(); +std::size_t i = d.find_last_not_of( '0' ); +if ( i != std::string::npos && i != d.size()-1 ) { +if ( d[i] == '.' ) +i++; +d = d.substr( 0, i+1 ); +} +return d; +} + +//// ======================================================= //// +// +// Out-of-line defs for full specialization of StringMaker +// +//// ======================================================= //// + +std::string StringMaker::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::convert(std::string_view str) { +return::Catch::Detail::stringify(std::string{ +str +}); +} +#endif + +std::string StringMaker::convert(char const* str) { +if (str) { +return::Catch::Detail::stringify(std::string{ +str +}); +} else { +return{ +"{null string}" +}; +} +} +std::string StringMaker::convert(char* str) { +if (str) { +return::Catch::Detail::stringify(std::string{ +str +}); +} else { +return{ +"{null string}" +}; +} +} + +#ifdef +CATCH_CONFIG_WCHAR +std::string StringMaker::convert(const std::wstring& wstr) { +std::string s; +s.reserve(wstr.size()); +for (auto c : wstr) { +s += (c <= 0xff) ? static_cast(c) : '?'; +} +return::Catch::Detail::stringify(s); +} + +# ifdef +CATCH_CONFIG_CPP17_STRING_VIEW +std::string StringMaker::convert(std::wstring_view str) { +return StringMaker::convert(std::wstring(str)); +} +# endif + +std::string StringMaker::convert(wchar_t const * str) { +if (str) { +return::Catch::Detail::stringify(std::wstring{ +str +}); +} else { +return{ +"{null string}" +}; +} +} +std::string StringMaker::convert(wchar_t * str) { +if (str) { +return::Catch::Detail::stringify(std::wstring{ +str +}); +} else { +return{ +"{null string}" +}; +} +} +#endif + +std::string StringMaker::convert(int value) { +return::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(long value) { +return::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(long long value) { +ReusableStringStream rss; +rss << value; +if (value > Detail::hexThreshold) { +rss << " (0x" << std::hex << value << ')'; +} +return rss.str(); +} + +std::string StringMaker::convert(unsigned int value) { +return::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(unsigned long value) { +return::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(unsigned long long value) { +ReusableStringStream rss; +rss << value; +if (value > Detail::hexThreshold) { +rss << " (0x" << std::hex << value << ')'; +} +return rss.str(); +} + +std::string StringMaker::convert(bool b) { +return b ? "true" : "false"; +} + +std::string StringMaker::convert(signed char value) { +if (value == '\r') { +return "'\\r'"; +} else if (value == '\f') { +return "'\\f'"; +} else if (value == '\n') { +return "'\\n'"; +} else if (value == '\t') { +return "'\\t'"; +} else if ('\0' <= value && value < ' ') { +return::Catch::Detail::stringify(static_cast(value)); +} else { +char chstr[] = "' '"; +chstr[1] = value; +return chstr; +} +} +std::string StringMaker::convert(char c) { +return::Catch::Detail::stringify(static_cast(c)); +} +std::string StringMaker::convert(unsigned char c) { +return::Catch::Detail::stringify(static_cast(c)); +} + +std::string StringMaker::convert(std::nullptr_t) { +return "nullptr"; +} + +std::string StringMaker::convert(float value) { +return fpToString(value, 5) + 'f'; +} +std::string StringMaker::convert(double value) { +return fpToString(value, 10); +} + +std::string ratio_string::symbol() { +return "a"; } +std::string ratio_string::symbol() { +return "f"; } +std::string ratio_string::symbol() { +return "p"; } +std::string ratio_string::symbol() { +return "n"; } +std::string ratio_string::symbol() { +return "u"; } +std::string ratio_string::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 + + +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 + + +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, 6, 0, "", 0 ); +return version; +} + +} +// end catch_version.cpp +// start catch_wildcard_pattern.cpp + +#include + + +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( 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 + + +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) { +std::ios_base::fmtflags f(os.flags()); +os << "\\x" +<< std::uppercase << std::hex << std::setfill('0') << std::setw(2) +<< static_cast(c); +os.flags(f); +} + +} // 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_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 << ""; +m_needsNewline = true; +return *this; +} + +void XmlWriter::writeStylesheetRef( std::string const& url ) { +m_os << "\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 << "\n"; +} + +void XmlWriter::newlineIfNecessary() { +if ( m_needsNewline ) { +m_os << std::endl; +m_needsNewline = false; +} +} +} +// end catch_xmlwriter.cpp +// start catch_reporter_bases.cpp + +#include + +#include + +#include + +#include + +#include + + +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 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::const_iterator itEnd = messages.end(); +const std::size_t N = static_cast(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 messages; +std::vector::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 + +#include + + +#if +defined(_MSC_VER) +#pragma +warning(push) +#pragma +warning(disable:4061) // Not all labels are EXPLICITLY handled in switch +// Note that 4062 (not all labels are handled +// and default is missing) is enabled +#endif + +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 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(s_nanosecondsInAMicrosecond); +case Unit::Milliseconds: +return m_inNanoseconds / static_cast(s_nanosecondsInAMillisecond); +case Unit::Seconds: +return m_inNanoseconds / static_cast(s_nanosecondsInASecond); +case Unit::Minutes: +return m_inNanoseconds / static_cast(s_nanosecondsInAMinute); +default: +return static_cast(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 m_columnInfos; +std::ostringstream m_oss; +int m_currentColumn = -1; +bool m_isOpen = false; + +public: +TablePrinter( std::ostream& os, std::vector columnInfos ) +: m_os( os ), +m_columnInfos( std::move( columnInfos )) { +} + +auto columnInfos() const -> std::vector 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 +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(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(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( 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 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 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 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 + +#include + +#include + +#include + + +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 &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 + + +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 ListeningReporter::getSupportedVerbosities() { +return std::set{ +}; +} + +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( 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 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__ ) +#define +CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) +#define +CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_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__ )) +#define +CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ )) +#define +CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_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__ ) +#define +TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) +#define +TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_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__ )) +#define +TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ )) +#define +TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_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 ) +#define +CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define +CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#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 )) +#define +CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define +CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#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 ) +#define +TEMPLATE_PRODUCT_TEST_CASE( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define +TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#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 )) +#define +TEMPLATE_PRODUCT_TEST_CASE( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define +TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#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/src/Client.cpp b/src/Client.cpp index f65c2c3..40b723c 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include @@ -60,11 +60,6 @@ const std::vector Client::list() const { } read_buffer.resize(static_cast(bytes_number)); message.insert(message.end(), read_buffer.begin(), read_buffer.end()); - if (message.size() >= 29) { - message.resize(29); - message.push_back('\\'); - message.push_back(0); - } message_received = is_message_received(message); } return translate_list_message(message); diff --git a/src/Currency.cpp b/src/Currency.cpp index 0034988..5b942bb 100644 --- a/src/Currency.cpp +++ b/src/Currency.cpp @@ -4,7 +4,7 @@ // Created by mikhail on 03.02.19. // -#include +#include Currency::Currency(std::string name, std::vector rates) : name(std::move(name)), rates(std::move(rates)) { if (rates.size() >= 2) { @@ -32,7 +32,7 @@ int32_t Currency::get_current_rate() const { } int32_t Currency::get_rate(size_t i) const { - return 0 <= i && i < rates.size() ? rates[i] : -1; + return i < rates.size() ? rates[i] : -1; } int32_t Currency::get_absolute_change() const { diff --git a/src/CurrencyClientApplication.cpp b/src/CurrencyClientApplication.cpp index db7fa3c..37b27fb 100644 --- a/src/CurrencyClientApplication.cpp +++ b/src/CurrencyClientApplication.cpp @@ -2,7 +2,7 @@ // Created by mikhail on 03.02.19. // -#include +#include #include int main(int argc, char *argv[]) { diff --git a/test/ClientTest.cpp b/test/ClientTest.cpp new file mode 100644 index 0000000..4ca8bbe --- /dev/null +++ b/test/ClientTest.cpp @@ -0,0 +1,82 @@ +#define CATCH_CONFIG_MAIN + +#include +#include +#include +#include "catch.hpp" + +#include "Client.h" + +class ClientTest { +public: + uint16_t PORTNO = 0; + const std::string HOST = "localhost"; +}; + +uint16_t get_current_port(int sockfd); + +void setup_server_returning(const std::vector &return_value, ClientTest &clientTest) { + int sockfd, newsockfd; + unsigned int clilen; + char buffer[256]; + struct sockaddr_in serv_addr{}, cli_addr{}; + sockfd = socket(AF_INET, SOCK_STREAM, 0); + + if (sockfd < 0) { + perror("ERROR opening socket"); + exit(1); + } + + bzero((char *) &serv_addr, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = INADDR_ANY; + serv_addr.sin_port = htons(0); + + if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { + perror("ERROR on binding"); + exit(1); + } + + clientTest.PORTNO = get_current_port(sockfd); + listen(sockfd, 1); + clilen = sizeof(cli_addr); + newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen); + + if (newsockfd < 0) { + perror("ERROR on accept"); + exit(1); + } + + bzero(buffer, 256); + if (read(newsockfd, buffer, 255) < 0) { + perror("ERROR reading from socket"); + exit(1); + } + if (write(newsockfd, return_value.data(), return_value.size()) < 0) { + perror("ERROR writing to socket"); + exit(1); + } +} + +uint16_t get_current_port(int sockfd) { + struct sockaddr_in serv_addr{}; + unsigned int clilen = sizeof(serv_addr); + if (getsockname(sockfd, (struct sockaddr *) &serv_addr, &clilen) < 0) { + perror("ERROR getting current port"); + exit(1); + } + return ntohs(serv_addr.sin_port); +} + +TEST_CASE("list empty currencies") { + ClientTest clientTest; + std::vector server_return = {'\\', 0}; + std::thread server_thread(setup_server_returning, server_return, std::ref(clientTest)); + while (clientTest.PORTNO == 0) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + Client client = Client(clientTest.HOST, clientTest.PORTNO); + std::vector result = client.list(); + REQUIRE(result.empty()); + server_thread.join(); +} \ No newline at end of file From 029cd7480e1cccd56fbaad2dbeb7567bda037bd0 Mon Sep 17 00:00:00 2001 From: RamSaw Date: Fri, 8 Feb 2019 12:07:59 +0300 Subject: [PATCH 06/17] Added two new tests. --- CMakeLists.txt | 4 +- include/Currency.h | 4 + include/catch.hpp | 16302 ------------------------------------------ src/Client.cpp | 17 +- src/Currency.cpp | 13 + test/ClientTest.cpp | 51 + 6 files changed, 79 insertions(+), 16312 deletions(-) delete mode 100644 include/catch.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index c52d162..974b6a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,9 @@ add_executable(NetworksLab2019HSE src/Currency.cpp ) -add_executable(runTests test/ClientTest.cpp +add_executable(runTests + include/catch.hpp + test/ClientTest.cpp src/Client.cpp src/Currency.cpp ) \ No newline at end of file diff --git a/include/Currency.h b/include/Currency.h index bdda7a3..06f6f40 100644 --- a/include/Currency.h +++ b/include/Currency.h @@ -27,6 +27,10 @@ class Currency { int32_t get_relative_change() const; + bool operator==(const Currency &rhs) const; + + bool operator!=(const Currency &rhs) const; + private: std::string name; std::vector rates; diff --git a/include/catch.hpp b/include/catch.hpp deleted file mode 100644 index ad03f07..0000000 --- a/include/catch.hpp +++ /dev/null @@ -1,16302 +0,0 @@ -/* - * Catch v2.6.0 - * Generated: 2019-01-31 22:25:55.560884 - * ---------------------------------------------------------- - * This file has been merged from multiple headers. Please don't edit it directly - * Copyright (c) 2019 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 6 -#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__ -// Because REQUIREs trigger GCC's -Wparentheses, and because still -// supported version of g++ have only buggy support for _Pragmas, -// Wparentheses have to be suppressed globally. -# pragma -GCC diagnostic ignored "-Wparentheses" // See #674 for details - -# 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 - -# 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_ 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() && defined(CATCH_CPP17_OR_GREATER) -# define -CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW -#endif -#endif - -//////////////////////////////////////////////////////////////////////////////// -// Check if optional is available and usable -#if -defined(__has_include) -# if -__has_include() && defined(CATCH_CPP17_OR_GREATER) -# define -CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL -# endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) -#endif // __has_include - -//////////////////////////////////////////////////////////////////////////////// -// Check if variant is available and usable -#if -defined(__has_include) -# if -__has_include() && defined(CATCH_CPP17_OR_GREATER) -# if -defined(__clang__) && (__clang_major__ < 8) -// work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 -// fix should be in clang 8, workaround in libstdc++ 8.2 -# include - -# if -defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) -# define -CATCH_CONFIG_NO_CPP17_VARIANT -# else -# define -CATCH_INTERNAL_CONFIG_CPP17_VARIANT -# endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) -# else -# define -CATCH_INTERNAL_CONFIG_CPP17_VARIANT -# endif // defined(__clang__) && (__clang_major__ < 8) -# endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) -#endif // __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_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) -# define -CATCH_CONFIG_CPP17_OPTIONAL -#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 - -#include - -#include - - -// 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& operator= ( SourceLineInfo const& ) = default; -SourceLineInfo( SourceLineInfo&& ) noexcept = default; -SourceLineInfo& operator= ( SourceLineInfo&& ) noexcept = 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 -T const& operator+ ( T const& value, StreamEndStop ) { -return value; -} -} - -#define -CATCH_INTERNAL_LINEINFO \ -::Catch::SourceLineInfo( __FILE__, static_cast( __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 - - -namespace Catch { - -class TestSpec; - -struct ITestInvoker { -virtual void invoke () const = 0; -virtual ~ITestInvoker(); -}; - -class TestCase; -struct IConfig; - -struct ITestCaseRegistry { -virtual ~ITestCaseRegistry(); -virtual std::vector const& getAllTests() const = 0; -virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; -}; - -bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); -std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); -std::vector const& getAllTestCasesSorted( IConfig const& config ); - -} - -// end catch_interfaces_testcase.h -// start catch_stringref.h - -#include - -#include - -#include - - -namespace Catch { - -/// A non-owning string class (similar to the forthcoming std::string_view) -/// Note that, because a StringRef may be a substring of another string, -/// it may not be null terminated. 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 - - -#include - - -namespace Catch{ - -#ifdef -CATCH_CPP17_OR_GREATER -template -inline constexpr auto is_unique = std::true_type{ -}; - -template -inline constexpr auto is_unique = std::bool_constant< -(!std::is_same_v && ...) && is_unique ->{ -}; -#else - -template -struct is_unique : std::true_type{ -}; - -template -struct is_unique : std::integral_constant -::value -&& is_unique::value -&& is_unique::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 - -#define -INTERNAL_CATCH_MAKE_TYPE_LIST(types) TypeList - -#define -INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(types)\ - CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST, INTERNAL_CATCH_REMOVE_PARENS(types)) - -// end catch_preprocessor.hpp -// start catch_meta.hpp - - -#include - - -template< typename... > -struct TypeList{ -}; - -template< typename... > -struct append; - -template< -template class L1 -, typename...E1 -, -template class L2 -, typename...E2 -> -struct append< L1, L2 > -{ -using type = L1; -}; - -template< -template class L1 -, typename...E1 -, -template class L2 -, typename...E2 -, typename...Rest -> -struct append< L1, L2, Rest...> -{ -using type = typename append< L1, Rest... >::type; -}; - -template< -template class -, typename... -> -struct rewrap; - -template< -template class Container -, -template class List -, typename...elems -> -struct rewrap> -{ -using type = TypeList< Container< elems... > >; -}; - -template< -template class Container -, -template class List -, class...Elems -, typename...Elements> -struct rewrap, Elements...> -{ -using type = typename append>, typename rewrap::type>::type; -}; - -template< -template class...Containers > -struct combine -{ -template< typename...Types > -struct with_types -{ -template< -template class Final > -struct into -{ -using type = typename append, typename rewrap::type...>::type; -}; -}; -}; - -template -struct always_false : std::false_type { -}; - -// end catch_meta.hpp -namespace Catch { - -template -class TestInvokerAsMethod : -public ITestInvoker { -void (C::*m_testAsMethod)(); -public: -TestInvokerAsMethod( void (C::*testAsMethod)()) noexcept : m_testAsMethod( testAsMethod ) { -} - -void invoke() const override { -C obj; -(obj.*m_testAsMethod)(); -} -}; - -auto makeTestInvoker( void(*testAsFunction)()) noexcept -> ITestInvoker*; - -template -auto makeTestInvoker( void (C::*testAsMethod)()) noexcept -> ITestInvoker* { -return new(std::nothrow) TestInvokerAsMethod( 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 \ - static void TestName() -#define -INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \ - namespace{ -\ - -template \ - struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName ) { -\ - void test(); \ - -}; \ - -} \ - template \ - 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 \ - static void TestFunc();\ - namespace { -\ - template \ - struct TestName{ -\ - -template \ - 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 ), 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 \ - 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_UD(INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME, Name, __VA_ARGS__));\ - return 0;\ - -}(); - -#define -INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(TestName, TestFuncName, Name, Tags, TmplTypes, TypesList) \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - template static void TestFuncName(); \ - namespace { -\ - template \ - struct TestName { -\ - TestName() { -\ - CATCH_INTERNAL_CHECK_UNIQUE_TYPES(Types...) \ - int index = 0; \ - using expander = int[]; \ - (void)expander{ -(Catch::AutoReg( Catch::makeTestInvoker( &TestFuncName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ -Name " - " + Catch::StringMaker::convert(index++), Tags } ), 0)... };/* NOLINT */ \ - -} \ - -}; \ - static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) =[](){ -\ - using TestInit = combine \ -::with_types::into::type; \ - TestInit(); \ - return 0; \ - -}(); \ - -} \ - CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ - template \ - static void TestFuncName() - -#ifndef -CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -#define -INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\ - INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(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_PRODUCT_TEST_CASE(Name, Tags, ...)\ - INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( 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_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, ... ) \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - namespace{ -\ - template \ - struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName ) { -\ - void test();\ - -};\ - template \ - struct TestNameClass{ -\ - -template \ - 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::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 \ - void TestName::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 - -#define -INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(TestNameClass, TestName, ClassName, Name, Tags, TmplTypes, TypesList)\ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - template \ - struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName ) { -\ - void test();\ - -};\ - namespace { -\ - template\ - struct TestNameClass{ -\ - TestNameClass(){ -\ - CATCH_INTERNAL_CHECK_UNIQUE_TYPES(Types...)\ - int index = 0;\ - using expander = int[];\ - (void)expander{ -(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, -# -ClassName, Catch::NameAndTags{ -Name " - " + Catch::StringMaker::convert(index++), Tags } ), 0)... };/* NOLINT */ \ - -}\ - -};\ - static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) =[](){ -\ - using TestInit = combine\ -::with_types::into::type;\ - TestInit();\ - return 0;\ - -}(); \ - -}\ - CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ - template \ - void TestName::test() - -#ifndef -CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -#define -INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\ - INTERNAL_CATCH_TEMPLATE_PRODUCT_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____ ), 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____ ), ClassName, Name, Tags, __VA_ARGS__ ) -#else -#define -INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\ - INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_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____ ), 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____ ), 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 - -#include - -#include - -#include - -// start catch_stream.h - -#include - -#include - -#include - - -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 -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 - -#endif - -#ifdef -__OBJC__ -// start catch_objc_arc.hpp - -#import - - -#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 -std::string rawMemoryToString( const T& object ) { -return rawMemoryToString( &object, sizeof(object)); -} - -template -class IsStreamInsertable { -template -static auto test(int) --> decltype(std::declval() << std::declval(), std::true_type()); - -template -static auto test(...)->std::false_type; - -public: -static const bool value = decltype(test(0))::value; -}; - -template -std::string convertUnknownEnumToString( E e ); - -template -typename std::enable_if< -!std::is_enum::value && !std::is_base_of::value, -std::string>::type convertUnstreamable( T const& ) { -return Detail::unprintableString; -} -template -typename std::enable_if< -!std::is_enum::value && std::is_base_of::value, -std::string>::type convertUnstreamable(T const& ex) { -return ex.what(); -} - -template -typename std::enable_if< -std::is_enum::value -, std::string>::type convertUnstreamable( T const& value ) { -return convertUnknownEnumToString( value ); -} - -#if -defined(_MANAGED) -//! Convert a CLR string to a utf8 std::string -template -std::string clrReferenceToString( T^ ref ) { -if (ref == nullptr) -return std::string("null"); -auto bytes = System::Text::Encoding::UTF8->GetBytes(ref->ToString()); -cli::pin_ptr p = &bytes[0]; -return std::string(reinterpret_cast(p), bytes->Length); -} -#endif - -} // namespace Detail - -// If we decide for C++14, change these to enable_if_ts -template -struct StringMaker { -template -static -typename std::enable_if<::Catch::Detail::IsStreamInsertable::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 -static -typename std::enable_if::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 -std::string stringify(const T& e) { -return::Catch::StringMaker::type>::type>::convert(e); -} - -template -std::string convertUnknownEnumToString( E e ) { -return::Catch::Detail::stringify(static_cast::type>(e)); -} - -#if -defined(_MANAGED) -template -std::string stringify( T^ e ) { -return::Catch::StringMaker::convert(e); -} -#endif - -} // namespace Detail - -// Some predefined specializations - -template<> -struct StringMaker { -static std::string convert(const std::string& str); -}; - -#ifdef -CATCH_CONFIG_CPP17_STRING_VIEW -template<> -struct StringMaker { -static std::string convert(std::string_view str); -}; -#endif - -template<> -struct StringMaker { -static std::string convert(char const * str); -}; -template<> -struct StringMaker { -static std::string convert(char * str); -}; - -#ifdef -CATCH_CONFIG_WCHAR -template<> -struct StringMaker { -static std::string convert(const std::wstring& wstr); -}; - -# ifdef -CATCH_CONFIG_CPP17_STRING_VIEW -template<> -struct StringMaker { -static std::string convert(std::wstring_view str); -}; -# endif - -template<> -struct StringMaker { -static std::string convert(wchar_t const * str); -}; -template<> -struct StringMaker { -static std::string convert(wchar_t * str); -}; -#endif - -// TBD: Should we use `strnlen` to ensure that we don't go out of the buffer, -// while keeping string semantics? -template -struct StringMaker { -static std::string convert(char const* str) { -return::Catch::Detail::stringify(std::string{ -str -}); -} -}; -template -struct StringMaker { -static std::string convert(signed char const* str) { -return::Catch::Detail::stringify(std::string{ -reinterpret_cast(str) -}); -} -}; -template -struct StringMaker { -static std::string convert(unsigned char const* str) { -return::Catch::Detail::stringify(std::string{ -reinterpret_cast(str) -}); -} -}; - -template<> -struct StringMaker { -static std::string convert(int value); -}; -template<> -struct StringMaker { -static std::string convert(long value); -}; -template<> -struct StringMaker { -static std::string convert(long long value); -}; -template<> -struct StringMaker { -static std::string convert(unsigned int value); -}; -template<> -struct StringMaker { -static std::string convert(unsigned long value); -}; -template<> -struct StringMaker { -static std::string convert(unsigned long long value); -}; - -template<> -struct StringMaker { -static std::string convert(bool b); -}; - -template<> -struct StringMaker { -static std::string convert(char c); -}; -template<> -struct StringMaker { -static std::string convert(signed char c); -}; -template<> -struct StringMaker { -static std::string convert(unsigned char c); -}; - -template<> -struct StringMaker { -static std::string convert(std::nullptr_t); -}; - -template<> -struct StringMaker { -static std::string convert(float value); -}; -template<> -struct StringMaker { -static std::string convert(double value); -}; - -template -struct StringMaker { -template -static std::string convert(U* p) { -if (p) { -return::Catch::Detail::rawMemoryToString(p); -} else { -return "nullptr"; -} -} -}; - -template -struct StringMaker { -static std::string convert(R C::* p) { -if (p) { -return::Catch::Detail::rawMemoryToString(p); -} else { -return "nullptr"; -} -} -}; - -#if -defined(_MANAGED) -template -struct StringMaker { -static std::string convert( T^ ref ) { -return::Catch::Detail::clrReferenceToString(ref); -} -}; -#endif - -namespace Detail { -template -std::string rangeToString(InputIterator first, 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 { -static std::string convert(NSString * nsstring) { -if (!nsstring) -return "nil"; -return std::string("@") +[nsstring UTF8String]; -} -}; -template<> -struct StringMaker { -static std::string convert(NSObject* nsObject) { -return::Catch::Detail::stringify([nsObject description]); -} - -}; -namespace Detail { -inline std::string stringify( NSString* nsstring ) { -return StringMaker::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 -# define -CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER -#endif - -// Separate std::pair specialization -#if -defined(CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER) -#include - -namespace Catch { -template -struct StringMaker > { -static std::string convert(const std::pair& pair) { -ReusableStringStream rss; -rss << "{ " -<<::Catch::Detail::stringify(pair.first) -<< ", " -<<::Catch::Detail::stringify(pair.second) -<< " }"; -return rss.str(); -} -}; -} -#endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER - -#if -defined(CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_OPTIONAL) -#include - -namespace Catch { -template -struct StringMaker > { -static std::string convert(const std::optional& optional) { -ReusableStringStream rss; -if (optional.has_value()) { -rss <<::Catch::Detail::stringify(*optional); -} else { -rss << "{ }"; -} -return rss.str(); -} -}; -} -#endif // CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER - -// Separate std::tuple specialization -#if -defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER) -#include - -namespace Catch { -namespace Detail { -template< -typename Tuple, -std::size_t N = 0, -bool = (N < std::tuple_size::value) -> -struct TupleElementPrinter { -static void print(const Tuple& tuple, std::ostream& os) { -os << (N ? ", " : " ") -<<::Catch::Detail::stringify(std::get(tuple)); -TupleElementPrinter::print(tuple, os); -} -}; - -template< -typename Tuple, -std::size_t N -> -struct TupleElementPrinter { -static void print(const Tuple&, std::ostream&) { -} -}; - -} - -template -struct StringMaker> { -static std::string convert(const std::tuple& tuple) { -ReusableStringStream rss; -rss << '{'; -Detail::TupleElementPrinter>::print(tuple, rss.get()); -rss << " }"; -return rss.str(); -} -}; -} -#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER - -#if -defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT) -#include - -namespace Catch { -template<> -struct StringMaker { -static std::string convert(const std::monostate&) { -return "{ }"; -} -}; - -template -struct StringMaker> { -static std::string convert(const std::variant& variant) { -if (variant.valueless_by_exception()) { -return "{valueless variant}"; -} else { -return std::visit( -[](const auto& value) { -return::Catch::Detail::stringify(value); -}, -variant -); -} -} -}; -} -#endif // CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER - -namespace Catch { -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 -struct is_range { -static const bool value = -!std::is_same())), not_this_one>::value && -!std::is_same())), not_this_one>::value; -}; - -#if -defined(_MANAGED) // Managed types are never ranges -template -struct is_range { -static const bool value = false; -}; -#endif - -template -std::string rangeToString( Range const& range ) { -return::Catch::Detail::rangeToString( begin( range ), end( range )); -} - -// Handle vector specially -template -std::string rangeToString( std::vector const& v ) { -ReusableStringStream rss; -rss << "{ "; -bool first = true; -for ( bool b : v ) { -if ( first ) -first = false; -else -rss << ", "; -rss <<::Catch::Detail::stringify( b ); -} -rss << " }"; -return rss.str(); -} - -template -struct StringMaker::value && !::Catch::Detail::IsStreamInsertable::value>::type> { -static std::string convert( R const& range ) { -return rangeToString( range ); -} -}; - -template -struct StringMaker { -static std::string convert(T const(&arr)[SZ]) { -return rangeToString(arr); -} -}; - -} // namespace Catch - -// Separate std::chrono::duration specialization -#if -defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) -#include - -#include - -#include - - -namespace Catch { - -template -struct ratio_string { -static std::string symbol(); -}; - -template -std::string ratio_string::symbol() { -Catch::ReusableStringStream rss; -rss << '[' << Ratio::num << '/' -<< Ratio::den << ']'; -return rss.str(); -} -template <> -struct ratio_string { -static std::string symbol(); -}; -template <> -struct ratio_string { -static std::string symbol(); -}; -template <> -struct ratio_string { -static std::string symbol(); -}; -template <> -struct ratio_string { -static std::string symbol(); -}; -template <> -struct ratio_string { -static std::string symbol(); -}; -template <> -struct ratio_string { -static std::string symbol(); -}; - -//////////// -// std::chrono::duration specializations -template -struct StringMaker> { -static std::string convert(std::chrono::duration const& duration) { -ReusableStringStream rss; -rss << duration.count() << ' ' << ratio_string::symbol() << 's'; -return rss.str(); -} -}; -template -struct StringMaker>> { -static std::string convert(std::chrono::duration> const& duration) { -ReusableStringStream rss; -rss << duration.count() << " s"; -return rss.str(); -} -}; -template -struct StringMaker>> { -static std::string convert(std::chrono::duration> const& duration) { -ReusableStringStream rss; -rss << duration.count() << " m"; -return rss.str(); -} -}; -template -struct StringMaker>> { -static std::string convert(std::chrono::duration> const& duration) { -ReusableStringStream rss; -rss << duration.count() << " h"; -return rss.str(); -} -}; - -//////////// -// std::chrono::time_point specialization -// Generic time_point cannot be specialized, only std::chrono::time_point -template -struct StringMaker> { -static std::string convert(std::chrono::time_point const& time_point) { -return::Catch::Detail::stringify(time_point.time_since_epoch()) + " since epoch"; -} -}; -// std::chrono::time_point specialization -template -struct StringMaker> { -static std::string convert(std::chrono::time_point const& time_point) { -auto converted = std::chrono::system_clock::to_time_t(time_point); - -#ifdef -_MSC_VER -std::tm timeInfo = { -}; -gmtime_s(&timeInfo, &converted); -#else -std::tm* timeInfo = std::gmtime(&converted); -#endif - -auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); -char timeStamp[timeStampSize]; -const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; - -#ifdef -_MSC_VER -std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); -#else -std::strftime(timeStamp, timeStampSize, fmt, timeInfo); -#endif -return std::string(timeStamp); -} -}; -} -#endif // CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER - -#ifdef -_MSC_VER -#pragma -warning(pop) -#endif - -// end catch_tostring.h -#include - - -#ifdef -_MSC_VER -#pragma -warning(push) -#pragma -warning(disable:4389) // '==' : signed/unsigned mismatch -#pragma -warning(disable:4018) // more "signed/unsigned mismatch" -#pragma -warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform) -#pragma -warning(disable:4180) // qualifier applied to function type has no meaning -#pragma -warning(disable:4800) // Forcing result to true or false -#endif - -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 -class BinaryExpr : -public ITransientExpression { -LhsT m_lhs; -StringRef m_op; -RhsT m_rhs; - -void streamReconstructedExpression( std::ostream &os ) const override { -formatReconstructedExpression -( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs )); -} - -public: -BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs ) -: ITransientExpression{ -true, comparisonResult -}, -m_lhs( lhs ), -m_op( op ), -m_rhs( rhs ) -{ -} - -template -auto operator&& ( T ) const -> BinaryExpr const { -static_assert(always_false::value, -"chained comparisons are not supported inside assertions, " -"wrap the expression inside parentheses, or decompose it"); -} - -template -auto operator|| ( T ) const -> BinaryExpr const { -static_assert(always_false::value, -"chained comparisons are not supported inside assertions, " -"wrap the expression inside parentheses, or decompose it"); -} - -template -auto operator== ( T ) const -> BinaryExpr const { -static_assert(always_false::value, -"chained comparisons are not supported inside assertions, " -"wrap the expression inside parentheses, or decompose it"); -} - -template -auto operator!= ( T ) const -> BinaryExpr const { -static_assert(always_false::value, -"chained comparisons are not supported inside assertions, " -"wrap the expression inside parentheses, or decompose it"); -} - -template -auto operator> ( T ) const -> BinaryExpr const { -static_assert(always_false::value, -"chained comparisons are not supported inside assertions, " -"wrap the expression inside parentheses, or decompose it"); -} - -template -auto operator< ( T ) const -> BinaryExpr const { -static_assert(always_false::value, -"chained comparisons are not supported inside assertions, " -"wrap the expression inside parentheses, or decompose it"); -} - -template -auto operator>= ( T ) const -> BinaryExpr const { -static_assert(always_false::value, -"chained comparisons are not supported inside assertions, " -"wrap the expression inside parentheses, or decompose it"); -} - -template -auto operator<= ( T ) const -> BinaryExpr const { -static_assert(always_false::value, -"chained comparisons are not supported inside assertions, " -"wrap the expression inside parentheses, or decompose it"); -} -}; - -template -class UnaryExpr : -public ITransientExpression { -LhsT m_lhs; - -void streamReconstructedExpression( std::ostream &os ) const override { -os << Catch::Detail::stringify( m_lhs ); -} - -public: -explicit UnaryExpr( LhsT lhs ) -: ITransientExpression{ -false, static_cast(lhs) -}, -m_lhs( lhs ) -{ -} -}; - -// Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int) -template -auto compareEqual( LhsT const& lhs, RhsT const& rhs ) -> bool { -return static_cast(lhs == rhs); } -template -auto compareEqual( T* const& lhs, int rhs ) -> bool { -return lhs == reinterpret_cast( rhs ); } -template -auto compareEqual( T* const& lhs, long rhs ) -> bool { -return lhs == reinterpret_cast( rhs ); } -template -auto compareEqual( int lhs, T* const& rhs ) -> bool { -return reinterpret_cast( lhs ) == rhs; } -template -auto compareEqual( long lhs, T* const& rhs ) -> bool { -return reinterpret_cast( lhs ) == rhs; } - -template -auto compareNotEqual( LhsT const& lhs, RhsT&& rhs ) -> bool { -return static_cast(lhs != rhs); } -template -auto compareNotEqual( T* const& lhs, int rhs ) -> bool { -return lhs != reinterpret_cast( rhs ); } -template -auto compareNotEqual( T* const& lhs, long rhs ) -> bool { -return lhs != reinterpret_cast( rhs ); } -template -auto compareNotEqual( int lhs, T* const& rhs ) -> bool { -return reinterpret_cast( lhs ) != rhs; } -template -auto compareNotEqual( long lhs, T* const& rhs ) -> bool { -return reinterpret_cast( lhs ) != rhs; } - -template -class ExprLhs { -LhsT m_lhs; -public: -explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) { -} - -template -auto operator== ( RhsT const& rhs ) -> BinaryExpr const { -return { -compareEqual( m_lhs, rhs ), m_lhs, "==", rhs }; -} -auto operator== ( bool rhs ) -> BinaryExpr const { -return { -m_lhs == rhs, m_lhs, "==", rhs -}; -} - -template -auto operator!= ( RhsT const& rhs ) -> BinaryExpr const { -return { -compareNotEqual( m_lhs, rhs ), m_lhs, "!=", rhs -}; -} -auto operator!= ( bool rhs ) -> BinaryExpr const { -return { -m_lhs != rhs, m_lhs, "!=", rhs -}; -} - -template -auto operator> ( RhsT const& rhs ) -> BinaryExpr const { -return { -static_cast(m_lhs > rhs), m_lhs, ">", rhs -}; -} -template -auto operator< ( RhsT const& rhs ) -> BinaryExpr const { -return { -static_cast(m_lhs < rhs), m_lhs, "<", rhs -}; -} -template -auto operator>= ( RhsT const& rhs ) -> BinaryExpr const { -return { -static_cast(m_lhs >= rhs), m_lhs, ">=", rhs -}; -} -template -auto operator<= ( RhsT const& rhs ) -> BinaryExpr const { -return { -static_cast(m_lhs <= rhs), m_lhs, "<=", rhs -}; -} - -template -auto operator&& ( RhsT const& ) -> BinaryExpr const { -static_assert(always_false::value, -"operator&& is not supported inside assertions, " -"wrap the expression inside parentheses, or decompose it"); -} - -template -auto operator|| ( RhsT const& ) -> BinaryExpr const { -static_assert(always_false::value, -"operator|| is not supported inside assertions, " -"wrap the expression inside parentheses, or decompose it"); -} - -auto makeUnaryExpr() const -> UnaryExpr { -return UnaryExpr{ -m_lhs -}; -} -}; - -void handleExpression( ITransientExpression const& expr ); - -template -void handleExpression( ExprLhs const& expr ) { -handleExpression( expr.makeUnaryExpr()); -} - -struct Decomposer { -template -auto operator<= ( T const& lhs ) -> ExprLhs { -return ExprLhs{ -lhs -}; -} - -auto operator<=( bool value ) -> ExprLhs { -return ExprLhs{ -value -}; -} -}; - -} // end namespace Catch - -#ifdef -_MSC_VER -#pragma -warning(pop) -#endif - -// end catch_decomposer.h -// start catch_interfaces_capture.h - -#include - - -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 -void handleExpr( ExprLhs 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 - -#include - - -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 -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 -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 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 -void captureValues( size_t index, T const& value ) { -captureValue( index, Catch::Detail::stringify( value )); -} - -template -void captureValues( size_t index, T const& value, Ts const&... values ) { -captureValue( index, Catch::Detail::stringify(value)); -captureValues( index+1, values... ); -} -}; - -} // end namespace Catch - -// 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( !!(__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(__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(__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(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(__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 - - -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 - - -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 - - -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 - - -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 - -#include - - -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 - -#include - - -namespace Catch { - -class TestCase; -struct ITestCaseRegistry; -struct IExceptionTranslatorRegistry; -struct IExceptionTranslator; -struct IReporterRegistry; -struct IReporterFactory; -struct ITagAliasRegistry; -class StartupExceptionRegistry; - -using IReporterFactoryPtr = std::shared_ptr; - -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 - -#include - -#include - - -namespace Catch { -using exceptionTranslateFunction = std::string(*)(); - -struct IExceptionTranslator; -using ExceptionTranslators = std::vector>; - -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 -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 -ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& )) { -getMutableRegistryHub().registerTranslator -( new ExceptionTranslator( 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 - - -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 ::value>::type> -Approx operator()( T const& value ) { -Approx approx( static_cast(value)); -approx.m_epsilon = m_epsilon; -approx.m_margin = m_margin; -approx.m_scale = m_scale; -return approx; -} - -template ::value>::type> -explicit Approx( T const& value ): Approx(static_cast(value)) -{ -} - -template ::value>::type> -friend bool operator== ( const T& lhs, Approx const& rhs ) { -auto lhs_v = static_cast(lhs); -return rhs.equalityComparisonImpl(lhs_v); -} - -template ::value>::type> -friend bool operator== ( Approx const& lhs, const T& rhs ) { -return operator==( rhs, lhs ); -} - -template ::value>::type> -friend bool operator!= ( T const& lhs, Approx const& rhs ) { -return !operator==( lhs, rhs ); -} - -template ::value>::type> -friend bool operator!= ( Approx const& lhs, T const& rhs ) { -return !operator==( rhs, lhs ); -} - -template ::value>::type> -friend bool operator<= ( T const& lhs, Approx const& rhs ) { -return static_cast(lhs) < rhs.m_value || lhs == rhs; -} - -template ::value>::type> -friend bool operator<= ( Approx const& lhs, T const& rhs ) { -return lhs.m_value < static_cast(rhs) || lhs == rhs; -} - -template ::value>::type> -friend bool operator>= ( T const& lhs, Approx const& rhs ) { -return static_cast(lhs) > rhs.m_value || lhs == rhs; -} - -template ::value>::type> -friend bool operator>= ( Approx const& lhs, T const& rhs ) { -return lhs.m_value > static_cast(rhs) || lhs == rhs; -} - -template ::value>::type> -Approx& epsilon( T const& newEpsilon ) { -double epsilonAsDouble = static_cast(newEpsilon); -setEpsilon(epsilonAsDouble); -return *this; -} - -template ::value>::type> -Approx& margin( T const& newMargin ) { -double marginAsDouble = static_cast(newMargin); -setMargin(marginAsDouble); -return *this; -} - -template ::value>::type> -Approx& scale( T const& newScale ) { -m_scale = static_cast(newScale); -return *this; -} - -std::string toString() const; - -private: -double m_epsilon; -double m_margin; -double m_scale; -double m_value; -}; -} // 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 { -static std::string convert(Catch::Detail::Approx const& value); -}; - -} // end namespace Catch - -// end catch_approx.h -// start catch_string_manip.h - -#include - -#include - - -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 - -#include - - -namespace Catch { -namespace Matchers { -namespace Impl { - -template struct MatchAllOf; -template struct MatchAnyOf; -template 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 -struct MatcherMethod { -virtual bool match( ObjectT const& arg ) const = 0; -}; - -#ifdef -__clang__ -# pragma -clang diagnostic pop -#endif - -template -struct MatcherBase : MatcherUntypedBase, MatcherMethod { - -MatchAllOf operator&& ( MatcherBase const& other ) const; -MatchAnyOf operator|| ( MatcherBase const& other ) const; -MatchNotOf operator! () const; -}; - -template -struct MatchAllOf : MatcherBase { -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& operator&& ( MatcherBase const& other ) { -m_matchers.push_back( &other ); -return *this; -} - -std::vector const*> m_matchers; -}; -template -struct MatchAnyOf : MatcherBase { - -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& operator|| ( MatcherBase const& other ) { -m_matchers.push_back( &other ); -return *this; -} - -std::vector const*> m_matchers; -}; - -template -struct MatchNotOf : MatcherBase { - -MatchNotOf( MatcherBase 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 const& m_underlyingMatcher; -}; - -template -MatchAllOf MatcherBase::operator&& ( MatcherBase const& other ) const { -return MatchAllOf() && *this && other; -} -template -MatchAnyOf MatcherBase::operator|| ( MatcherBase const& other ) const { -return MatchAnyOf() || *this || other; -} -template -MatchNotOf MatcherBase::operator! () const { -return MatchNotOf( *this ); -} - -} // namespace Impl - -} // namespace Matchers - -using namespace Matchers; -using Matchers::Impl::MatcherBase; - -} // namespace Catch - -// end catch_matchers.h -// start catch_matchers_floating.h - -#include - -#include - - -namespace Catch { -namespace Matchers { - -namespace Floating { - -enum class FloatingPointKind : uint8_t; - -struct WithinAbsMatcher : MatcherBase { -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 { -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 - -#include - - -namespace Catch { -namespace Matchers { -namespace Generic { - -namespace Detail { -std::string finalizeDescription(const std::string& desc); -} - -template -class PredicateMatcher : -public MatcherBase { -std::function m_predicate; -std::string m_description; -public: - -PredicateMatcher(std::function 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 is hard (but possible) and -// requires a lot of TMP. -template -Generic::PredicateMatcher Predicate(std::function const& predicate, std::string const& description = "") { -return Generic::PredicateMatcher(predicate, description); -} - -} // namespace Matchers -} // namespace Catch - -// end catch_matchers_generic.hpp -// start catch_matchers_string.h - -#include - - -namespace Catch { -namespace Matchers { - -namespace StdString { - -struct CasedString -{ -CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ); -std::string adjustString( std::string const& str ) const; -std::string caseSensitivitySuffix() const; - -CaseSensitive::Choice m_caseSensitivity; -std::string m_str; -}; - -struct StringMatcherBase : MatcherBase { -StringMatcherBase( std::string const& operation, CasedString const& comparator ); -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 { -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 - - -namespace Catch { -namespace Matchers { - -namespace Vector { -namespace Detail { -template -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 -bool contains(InputIterator first, InputIterator last, T const& item) { -for (; first != last; ++first) { -if (*first == item) { -return true; -} -} -return false; -} -} - -template -struct ContainsElementMatcher : MatcherBase> { - -ContainsElementMatcher(T const &comparator) : m_comparator( comparator) { -} - -bool match(std::vector 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 -struct ContainsMatcher : MatcherBase> { - -ContainsMatcher(std::vector const &comparator) : m_comparator( comparator ) { -} - -bool match(std::vector 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 const& m_comparator; -}; - -template -struct EqualsMatcher : MatcherBase> { - -EqualsMatcher(std::vector const &comparator) : m_comparator( comparator ) { -} - -bool match(std::vector 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 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 const& m_comparator; -}; - -template -struct UnorderedEqualsMatcher : MatcherBase> { -UnorderedEqualsMatcher(std::vector const& target) : m_target(target) { -} -bool match(std::vector const& vec) const override { -// Note: This is a reimplementation of std::is_permutation, -// because I don't want to include 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 const& m_target; -}; - -} // namespace Vector - -// The following functions create the actual matcher objects. -// This allows the types to be inferred - -template -Vector::ContainsMatcher Contains( std::vector const& comparator ) { -return Vector::ContainsMatcher( comparator ); -} - -template -Vector::ContainsElementMatcher VectorContains( T const& comparator ) { -return Vector::ContainsElementMatcher( comparator ); -} - -template -Vector::EqualsMatcher Equals( std::vector const& comparator ) { -return Vector::EqualsMatcher( comparator ); -} - -template -Vector::UnorderedEqualsMatcher UnorderedEquals(std::vector const& target) { -return Vector::UnorderedEqualsMatcher(target); -} - -} // namespace Matchers -} // namespace Catch - -// end catch_matchers_vector.h -namespace Catch { - -template -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; - -void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString ); - -template -auto makeMatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef const& matcherString ) -> MatchExpr { -return MatchExpr( 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(__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 - - -namespace Catch { - -namespace Generators { -class GeneratorUntypedBase { -public: -GeneratorUntypedBase() = default; -virtual ~GeneratorUntypedBase(); -// Attempts to move the generator to the next element -// -// Returns true iff the move succeeded (and a valid element -// can be retrieved). -virtual bool next() = 0; -}; -using GeneratorBasePtr = std::unique_ptr; - -} // 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; -}; - -} // namespace Catch - -// end catch_interfaces_generatortracker.h -// start catch_enforce.h - -#include - - -namespace Catch { -#if -!defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) -template -[[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 - -#include - -#include - - -#include - -#include - - -namespace Catch { - -class GeneratorException : -public std::exception { -const char* const m_msg = ""; - -public: -GeneratorException(const char* msg): -m_msg(msg) -{ -} - -const char* what() const noexcept override final; -}; - -namespace Generators { - -// !TBD move this into its own location? -namespace pf{ -template -std::unique_ptr make_unique( Args&&... args ) { -return std::unique_ptr(new T(std::forward(args)...)); -} -} - -template -struct IGenerator : GeneratorUntypedBase { -virtual ~IGenerator() = default; - -// Returns the current element of the generator -// -// \Precondition The generator is either freshly constructed, -// or the last call to `next()` returned true -virtual T const& get() const = 0; -using type = T; -}; - -template -class SingleValueGenerator final : -public IGenerator { -T m_value; -public: -SingleValueGenerator(T const& value) : m_value( value ) { -} -SingleValueGenerator(T&& value) : m_value(std::move(value)) { -} - -T const& get() const override { -return m_value; -} -bool next() override { -return false; -} -}; - -template -class FixedValuesGenerator final : -public IGenerator { -std::vector m_values; -size_t m_idx = 0; -public: -FixedValuesGenerator( std::initializer_list values ) : m_values( values ) { -} - -T const& get() const override { -return m_values[m_idx]; -} -bool next() override { -++m_idx; -return m_idx < m_values.size(); -} -}; - -template -class GeneratorWrapper final { -std::unique_ptr> m_generator; -public: -GeneratorWrapper(std::unique_ptr> generator): -m_generator(std::move(generator)) -{ -} -T const& get() const { -return m_generator->get(); -} -bool next() { -return m_generator->next(); -} -}; - -template -GeneratorWrapper value(T&& value) { -return GeneratorWrapper(pf::make_unique>(std::forward(value))); -} -template -GeneratorWrapper values(std::initializer_list values) { -return GeneratorWrapper(pf::make_unique>(values)); -} - -template -class Generators : -public IGenerator { -std::vector> m_generators; -size_t m_current = 0; - -void populate(GeneratorWrapper&& generator) { -m_generators.emplace_back(std::move(generator)); -} -void populate(T&& val) { -m_generators.emplace_back(value(std::move(val))); -} -template -void populate(U&& val) { -populate(T(std::move(val))); -} -template -void populate(U&& valueOrGenerator, Gs... moreGenerators) { -populate(std::forward(valueOrGenerator)); -populate(std::forward(moreGenerators)...); -} - -public: -template -Generators(Gs... moreGenerators) { -m_generators.reserve(sizeof...(Gs)); -populate(std::forward(moreGenerators)...); -} - -T const& get() const override { -return m_generators[m_current].get(); -} - -bool next() override { -if (m_current >= m_generators.size()) { -return false; -} -const bool current_status = m_generators[m_current].next(); -if (!current_status) { -++m_current; -} -return m_current < m_generators.size(); -} -}; - -template -GeneratorWrapper> table( std::initializer_list::type...>> tuples ) { -return values>( tuples ); -} - -// Tag type to signal that a generator sequence should convert arguments to a specific type -template -struct as { -}; - -template -auto makeGenerators( GeneratorWrapper&& generator, Gs... moreGenerators ) -> Generators { -return Generators(std::move(generator), std::forward(moreGenerators)...); -} -template -auto makeGenerators( GeneratorWrapper&& generator ) -> Generators { -return Generators(std::move(generator)); -} -template -auto makeGenerators( T&& val, Gs... moreGenerators ) -> Generators { -return makeGenerators( value( std::forward( val )), std::forward( moreGenerators )... ); -} -template -auto makeGenerators( as, U&& val, Gs... moreGenerators ) -> Generators { -return makeGenerators( value( T( std::forward( val ))), std::forward( moreGenerators )... ); -} - -template -class TakeGenerator : -public IGenerator { -GeneratorWrapper m_generator; -size_t m_returned = 0; -size_t m_target; -public: -TakeGenerator(size_t target, GeneratorWrapper&& generator): -m_generator(std::move(generator)), -m_target(target) -{ -assert(target != 0 && "Empty generators are not allowed"); -} -T const& get() const override { -return m_generator.get(); -} -bool next() override { -++m_returned; -if (m_returned >= m_target) { -return false; -} - -const auto success = m_generator.next(); -// If the underlying generator does not contain enough values -// then we cut short as well -if (!success) { -m_returned = m_target; -} -return success; -} -}; - -template -GeneratorWrapper take(size_t target, GeneratorWrapper&& generator) { -return GeneratorWrapper(pf::make_unique>(target, std::move(generator))); -} - -template -class FilterGenerator : -public IGenerator { -GeneratorWrapper m_generator; -Predicate m_predicate; -public: -template -FilterGenerator(P&& pred, GeneratorWrapper&& generator): -m_generator(std::move(generator)), -m_predicate(std::forward

(pred)) -{ -if (!m_predicate(m_generator.get())) { -// It might happen that there are no values that pass the -// filter. In that case we throw an exception. -auto has_initial_value = next(); -if (!has_initial_value) { -Catch::throw_exception(GeneratorException("No valid value found in filtered generator")); -} -} -} - -T const& get() const override { -return m_generator.get(); -} - -bool next() override { -bool success = m_generator.next(); -if (!success) { -return false; -} -while (!m_predicate(m_generator.get()) && (success = m_generator.next()) == true); -return success; -} -}; - -template -GeneratorWrapper filter(Predicate&& pred, GeneratorWrapper&& generator) { -return GeneratorWrapper(std::unique_ptr>(pf::make_unique>(std::forward(pred), std::move(generator)))); -} - -template -class RepeatGenerator : -public IGenerator { -GeneratorWrapper m_generator; -mutable std::vector m_returned; -size_t m_target_repeats; -size_t m_current_repeat = 0; -size_t m_repeat_index = 0; -public: -RepeatGenerator(size_t repeats, GeneratorWrapper&& generator): -m_generator(std::move(generator)), -m_target_repeats(repeats) -{ -assert(m_target_repeats > 0 && "Repeat generator must repeat at least once"); -} - -T const& get() const override { -if (m_current_repeat == 0) { -m_returned.push_back(m_generator.get()); -return m_returned.back(); -} -return m_returned[m_repeat_index]; -} - -bool next() override { -// There are 2 basic cases: -// 1) We are still reading the generator -// 2) We are reading our own cache - -// In the first case, we need to poke the underlying generator. -// If it happily moves, we are left in that state, otherwise it is time to start reading from our cache -if (m_current_repeat == 0) { -const auto success = m_generator.next(); -if (!success) { -++m_current_repeat; -} -return m_current_repeat < m_target_repeats; -} - -// In the second case, we need to move indices forward and check that we haven't run up against the end -++m_repeat_index; -if (m_repeat_index == m_returned.size()) { -m_repeat_index = 0; -++m_current_repeat; -} -return m_current_repeat < m_target_repeats; -} -}; - -template -GeneratorWrapper repeat(size_t repeats, GeneratorWrapper&& generator) { -return GeneratorWrapper(pf::make_unique>(repeats, std::move(generator))); -} - -template -class MapGenerator : -public IGenerator { -// TBD: provide static assert for mapping function, for friendly error message -GeneratorWrapper m_generator; -Func m_function; -// To avoid returning dangling reference, we have to save the values -T m_cache; -public: -template -MapGenerator(F2&& function, GeneratorWrapper&& generator) : -m_generator(std::move(generator)), -m_function(std::forward(function)), -m_cache(m_function(m_generator.get())) -{ -} - -T const& get() const override { -return m_cache; -} -bool next() override { -const auto success = m_generator.next(); -if (success) { -m_cache = m_function(m_generator.get()); -} -return success; -} -}; - -template -GeneratorWrapper map(Func&& function, GeneratorWrapper&& generator) { -return GeneratorWrapper( -pf::make_unique>(std::forward(function), std::move(generator)) -); -} -template -GeneratorWrapper map(Func&& function, GeneratorWrapper&& generator) { -return GeneratorWrapper( -pf::make_unique>(std::forward(function), std::move(generator)) -); -} - -auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker&; - -template -// 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().get()) { -using UnderlyingType = typename decltype(generatorExpression())::type; - -IGeneratorTracker& tracker = acquireGeneratorTracker( lineInfo ); -if (!tracker.hasGenerator()) { -tracker.setGenerator(pf::make_unique>(generatorExpression())); -} - -auto const& generator = static_cast const&>( *tracker.getGenerator()); -return generator.get(); -} - -} // 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 - -#include - -#include - - -#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 const& _tags, -SourceLineInfo const& _lineInfo ); - -friend void setTags( TestCaseInfo& testCaseInfo, std::vector 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 tags; -std::vector 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 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 - - -#include - - -// 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{ -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 - -#include - -#include - - -namespace Catch { - -class TestSpec { -struct Pattern { -virtual ~Pattern(); -virtual bool matches( TestCaseInfo const& testCase ) const = 0; -}; -using PatternPtr = std::shared_ptr; - -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 m_patterns; - -bool matches( TestCaseInfo const& testCase ) const; -}; - -public: -bool hasFilters() const; -bool matches( TestCaseInfo const& testCase ) const; - -private: -std::vector 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 - - -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 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 -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( token ); -if ( m_exclusion ) -pattern = std::make_shared( 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 - -#include - -#include - -#include - - -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 const& getSectionsToRun() const = 0; -virtual Verbosity verbosity() const = 0; -}; - -using IConfigPtr = std::shared_ptr; -} - -// end catch_interfaces_config.h -// Libstdc++ doesn't like incomplete classes for unique_ptr - -#include - -#include - -#include - - -#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 testsOrTags; -std::vector 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 const& getTestsOrTags() const; -std::vector 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 m_stream; -TestSpec m_testSpec; -bool m_hasTestFilters = false; -}; - -} // end namespace Catch - -// end catch_config.hpp -// start catch_assertionresult.h - -#include - - -namespace Catch { - -struct AssertionResultData -{ -AssertionResultData() = delete; - -AssertionResultData( ResultWas::OfType _resultType, LazyExpression const& _lazyExpression ); - -std::string message; -mutable std::string reconstructedExpression; -LazyExpression lazyExpression; -ResultWas::OfType resultType; - -std::string reconstructExpression() const; -}; - -class AssertionResult { -public: -AssertionResult() = delete; -AssertionResult( AssertionInfo const& info, AssertionResultData const& data ); - -bool isOk() const; -bool succeeded() const; -ResultWas::OfType getResultType() const; -bool hasExpression() const; -bool hasMessage() const; -std::string getExpression() const; -std::string getExpressionInMacro() const; -bool hasExpandedExpression() const; -std::string getExpandedExpression() const; -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 -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 - -#include - -#include - -#include - -#include - - -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 -struct LazyStat : Option { -LazyStat& operator=( T const& _value ) { -Option::operator=( _value ); -used = false; -return *this; -} -void reset() { -Option::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 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 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 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; - -struct IReporterFactory { -virtual ~IReporterFactory(); -virtual IStreamingReporterPtr create( ReporterConfig const& config ) const = 0; -virtual std::string getDescription() const = 0; -}; -using IReporterFactoryPtr = std::shared_ptr; - -struct IReporterRegistry { -using FactoryMap = std::map; -using Listeners = std::vector; - -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 - -#include - -#include - -#include - -#include - -#include - -#include - - -namespace Catch { -void prepareExpandedExpression(AssertionResult& result); - -// Returns double formatted as %.3f (format expected on output) -std::string getFormattedDuration( double duration ); - -template -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 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 currentTestRunInfo; -LazyStat currentGroupInfo; -LazyStat currentTestCaseInfo; - -std::vector m_sectionStack; -ReporterPreferences m_reporterPrefs; -}; - -template -struct CumulativeReporterBase : IStreamingReporter { -template -struct Node { -explicit Node( T const& _value ) : value( _value ) { -} -virtual ~Node() { -} - -using ChildNodes = std::vector>; -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 const& other) const { -return operator==(*other); -} - -SectionStats stats; -using ChildSections = std::vector>; -using Assertions = std::vector; -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 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; -using TestGroupNode = Node; -using TestRunNode = Node; - -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 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 node; -if ( m_sectionStack.empty()) { -if ( !m_rootSection ) -m_rootSection = std::make_shared( 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( 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( 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(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(testGroupStats); -node->children.swap(m_testCases); -m_testGroups.push_back(node); -} -void testRunEnded(TestRunStats const& testRunStats) override { -auto node = std::make_shared(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 m_assertions; -std::vector>> m_sections; -std::vector> m_testCases; -std::vector> m_testGroups; - -std::vector> m_testRuns; - -std::shared_ptr m_rootSection; -std::shared_ptr m_deepestSection; -std::vector> m_sectionStack; -ReporterPreferences m_reporterPrefs; -}; - -template -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( ReporterConfig const& _config ); - -static std::set 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 -class ReporterRegistrar { - -class ReporterFactory : -public IReporterFactory { - -virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override { -return std::unique_ptr( 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()); -} -}; - -template -class ListenerRegistrar { - -class ListenerFactory : -public IReporterFactory { - -virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override { -return std::unique_ptr( new T( config )); -} -virtual std::string getDescription() const override { -return std::string(); -} -}; - -public: - -ListenerRegistrar() { -getMutableRegistryHub().registerListener( std::make_shared()); -} -}; -} - -#if -!defined(CATCH_CONFIG_DISABLE) - -#define -CATCH_REGISTER_REPORTER( name, reporterType ) \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - namespace{ -Catch::ReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } \ - CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS - -#define -CATCH_REGISTER_LISTENER( listenerType ) \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - namespace{ -Catch::ListenerRegistrar 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 { - -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 { -std::unique_ptr 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 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 - - -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 -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 -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 m_tags; -std::string m_indent; -std::ostream& m_os; -}; - -} - -// end catch_xmlwriter.h -namespace Catch { - -class JunitReporter : -public CumulativeReporterBase { -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 { -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 - -#include - -#include - - -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; - -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 isGeneratorTracker() 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; -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 isGeneratorTracker() const override; - -void open(); - -void close() override; -void fail() override; -void markAsNeedingAnotherRun() override; - -private: -void moveToParent(); -void moveToThis(); -}; - -class SectionTracker : -public TrackerBase { -std::vector m_filters; -public: -SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); - -bool isSectionTracker() const override; - -bool isComplete() const override; - -static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ); - -void tryOpen(); - -void addInitialFilters( std::vector const& filters ); -void addNextFilters( std::vector const& filters ); -}; - -} // namespace TestCaseTracking - -using TestCaseTracking::ITracker; -using TestCaseTracking::TrackerContext; -using TestCaseTracking::SectionTracker; - -} // 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 - -#include - - -namespace { - -// Performs equivalent check of std::fabs(lhs - rhs) <= margin -// But without the subtraction to allow for INFINITY in comparison -bool marginComparison(double lhs, double rhs, double margin) { -return (lhs + margin >= rhs) && (rhs + margin >= lhs); -} - -} - -namespace Catch { -namespace Detail { - -Approx::Approx ( double value ) -: m_epsilon( std::numeric_limits::epsilon()*100 ), -m_margin( 0.0 ), -m_scale( 0.0 ), -m_value( value ) -{ -} - -Approx Approx::custom() { -return Approx( 0 ); -} - -Approx Approx::operator-() const { -auto temp(*this); -temp.m_value = -temp.m_value; -return temp; -} - -std::string Approx::toString() const { -ReusableStringStream rss; -rss << "Approx( " <<::Catch::Detail::stringify( m_value ) << " )"; -return rss.str(); -} - -bool Approx::equalityComparisonImpl(const double other) const { -// First try with fixed margin, then compute margin based on epsilon, scale and Approx's value -// Thanks to Richard Harris for his help refining the scaled margin value -return marginComparison(m_value, other, m_margin) || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(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::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 - - -namespace Catch { - -struct IResultCapture; -struct IRunner; -struct IConfig; -struct IMutableContext; - -using IConfigPtr = std::shared_ptr; - -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 - - -#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 -#define -CATCH_BREAK_INTO_DEBUGGER()[]{ -}() -#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 - -#else -#include - -#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 - - -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 - - -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 = nullptr; -Option m_lastResult; - -IConfigPtr m_config; -Totals m_totals; -IStreamingReporterPtr m_reporter; -std::vector m_messages; -AssertionInfo m_lastAssertionInfo; -std::vector m_unfinishedSections; -std::vector 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; - -// 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 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() && __cplusplus >= 201703L -#include - -#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 - -#include - -#include - -#include - - -#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 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 m_columns; - -public: - -class iterator { -friend Columns; -struct EndTag {}; - -std::vector const& m_columns; -std::vector 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 - -#include - -#include - -#include - - -#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 -struct UnaryLambdaTraits : UnaryLambdaTraits { -}; - -template -struct UnaryLambdaTraits { -static const bool isValid = false; -}; - -template -struct UnaryLambdaTraits { -static const bool isValid = true; -using ArgType = typename std::remove_const::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 m_args; - -public: -Args( int argc, char const* const* argv ) -: m_exeName(argv[0]), -m_args(argv + 1, argv + argc) { -} - -Args( std::initializer_list 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::const_iterator; -Iterator it; -Iterator itEnd; -std::vector 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 -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 : -public ResultBase { -protected: -using ResultBase::ResultBase; -}; - -template -class BasicResult : -public ResultValueBase { -public: -template -explicit BasicResult( BasicResult const &other ) -: ResultValueBase( other.type()), -m_errorMessage( other.errorMessage()) -{ -assert( type() != ResultBase::Ok ); -} - -template -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(type), -m_errorMessage(message) -{ -assert( m_type != ResultBase::Ok ); -} - -using ResultValueBase::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; -using ParserResult = BasicResult; -using InternalParseResult = BasicResult; - -struct HelpColumns { -std::string left; -std::string right; -}; - -template -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(::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 -inline auto convertInto( std::string const &source, CLARA_CONFIG_OPTIONAL_TYPE& 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 -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 -struct BoundValueRef> : BoundValueRefBase { -std::vector &m_ref; - -explicit BoundValueRef( std::vector &ref ) : m_ref( ref ) { -} - -auto isContainer() const -> bool override { -return true; } - -auto setValue( std::string const &arg ) -> ParserResult override { -T temp; -auto result = convertInto( arg, temp ); -if ( result ) -m_ref.push_back( temp ); -return result; -} -}; - -struct BoundFlagRef : BoundFlagRefBase { -bool &m_ref; - -explicit BoundFlagRef( bool &ref ) : m_ref( ref ) { -} - -auto setFlag( bool flag ) -> ParserResult override { -m_ref = flag; -return ParserResult::ok( ParseResultType::Matched ); -} -}; - -template -struct LambdaInvoker { -static_assert( std::is_same::value, "Lambda must return void or clara::ParserResult" ); - -template -static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { -return lambda( arg ); -} -}; - -template<> -struct LambdaInvoker { -template -static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { -lambda( arg ); -return ParserResult::ok( ParseResultType::Matched ); -} -}; - -template -inline auto invokeLambda( L const &lambda, std::string const &arg ) -> ParserResult { -ArgType temp{ -}; -auto result = convertInto( arg, temp ); -return !result -? result -: LambdaInvoker::ReturnType>::invoke( lambda, temp ); -} - -template -struct BoundLambda : BoundValueRefBase { -L m_lambda; - -static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); -explicit BoundLambda( L const &lambda ) : m_lambda( lambda ) { -} - -auto setValue( std::string const &arg ) -> ParserResult override { -return invokeLambda::ArgType>( m_lambda, arg ); -} -}; - -template -struct BoundFlagLambda : BoundFlagRefBase { -L m_lambda; - -static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); -static_assert( std::is_same::ArgType, bool>::value, "flags must be boolean" ); - -explicit BoundFlagLambda( L const &lambda ) : m_lambda( lambda ) { -} - -auto setFlag( bool flag ) -> ParserResult override { -return LambdaInvoker::ReturnType>::invoke( m_lambda, flag ); -} -}; - -enum class Optionality { Optional, Required }; - -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 -class ComposableParserImpl : -public ParserBase { -public: -template -auto operator|( T const &other ) const -> Parser; - -template -auto operator+( T const &other ) const -> Parser; -}; - -// Common code and state for Args and Opts -template -class ParserRefImpl : -public ComposableParserImpl { -protected: -Optionality m_optionality = Optionality::Optional; -std::shared_ptr m_ref; -std::string m_hint; -std::string m_description; - -explicit ParserRefImpl( std::shared_ptr const &ref ) : m_ref( ref ) { -} - -public: -template -ParserRefImpl( T &ref, std::string const &hint ) -: m_ref( std::make_shared>( ref )), -m_hint( hint ) -{ -} - -template -ParserRefImpl( LambdaT const &ref, std::string const &hint ) -: m_ref( std::make_shared>( ref )), -m_hint(hint) -{ -} - -auto operator()( std::string const &description ) -> DerivedT & { -m_description = description; -return static_cast( *this ); -} - -auto optional() -> DerivedT & { -m_optionality = Optionality::Optional; -return static_cast( *this ); -}; - -auto required() -> DerivedT & { -m_optionality = Optionality::Required; -return static_cast( *this ); -}; - -auto isOptional() const -> bool { -return m_optionality == Optionality::Optional; -} - -auto cardinality() const -> size_t override { -if ( m_ref->isContainer()) -return 0; -else -return 1; -} - -auto hint() const -> std::string { -return m_hint; } -}; - -class ExeName : -public ComposableParserImpl { -std::shared_ptr m_name; -std::shared_ptr m_ref; - -template -static auto makeRef(LambdaT const &lambda) -> std::shared_ptr { -return std::make_shared>( lambda); -} - -public: -ExeName() : m_name( std::make_shared( "" )) { -} - -explicit ExeName( std::string &ref ) : ExeName() { -m_ref = std::make_shared>( ref ); -} - -template -explicit ExeName( LambdaT const& lambda ) : ExeName() { -m_ref = std::make_shared>( lambda ); -} - -// The exe name is not parsed out of the normal tokens, but is handled specially -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 { -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( 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 { -protected: -std::vector m_optNames; - -public: -template -explicit Opt( LambdaT const &ref ) : ParserRefImpl( std::make_shared>( ref )) { -} - -explicit Opt( bool &ref ) : ParserRefImpl( std::make_shared( ref )) { -} - -template -Opt( LambdaT const &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) { -} - -template -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 { -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( 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( 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( *this ) -("display usage information") -["-?"]["-h"]["--help"] -.optional(); -} -}; - -struct Parser : ParserBase { - -mutable ExeName m_exeName; -std::vector m_options; -std::vector 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 -auto operator|( T const &other ) const -> Parser { -return Parser( *this ) |= other; -} - -// Forward deprecated interface with '+' instead of '|' -template -auto operator+=( T const &other ) -> Parser & { -return operator|=( other ); } -template -auto operator+( T const &other ) const -> Parser { -return operator|( other ); } - -auto getHelpColumns() const -> std::vector { -std::vector 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 -template -auto ComposableParserImpl::operator|( T const &other ) const -> Parser { -return Parser() | static_cast( *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 - -#include - - -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( 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( 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 - -#include - - -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 const& Config::getTestsOrTags() const { -return m_data.testsOrTags; } -std::vector 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 - - -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 - - -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 ) { -getCurrentContext().getConfig()->stream() -<< '\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 - - -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 - -# include - -# include - -# include - -# include - -# include - -# include - - -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 - -#include - - -namespace Catch{ -// The standard POSIX way of detecting a debugger is to attempt to -// ptrace() the process, but this needs to be done from a child and not -// this process itself to still allow attaching to this process later -// if wanted, so is rather heavy. Under Linux we have the PID of the -// "debugger" (which doesn't need to be gdb, of course, it could also -// be strace, for example) in /proc/$PID/status, so just get it from -// there instead. -bool isDebuggerActive(){ -// Libstdc++ has a bug, where std::ifstream sets errno to 0 -// This way our users can properly assert over errno values -ErrnoGuard guard; -std::ifstream in("/proc/self/status"); -for ( std::string line; std::getline(in, line); ) { -static const int PREFIX_LEN = 11; -if ( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) { -// We're traced if the PID is not 0 and no other PID starts -// with 0 digit, so it's enough to check for just a single -// character. -return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; -} -} - -return false; -} -} // namespace Catch -#elif -defined(_MSC_VER) -extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); -namespace Catch { -bool isDebuggerActive() { -return IsDebuggerPresent() != 0; -} -} -#elif -defined(__MINGW32__) -extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); -namespace Catch { -bool isDebuggerActive() { -return IsDebuggerPresent() != 0; -} -} -#else -namespace Catch { -bool isDebuggerActive() { -return false; } -} -#endif // Platform -// 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 - - -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 - -#include - -#include - - -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> 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( 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"; -} -} - -std::string ExceptionTranslatorRegistry::tryTranslators() const { -if (m_translators.empty()) { -std::rethrow_exception(std::current_exception()); -} else { -return m_translators[0]->translate(m_translators.begin() + 1, m_translators.end()); -} -} - -#else // ^^ Exceptions are enabled // Exceptions are disabled vv -std::string ExceptionTranslatorRegistry::translateActiveException() const { -CATCH_INTERNAL_ERROR("Attempted to translate active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); -} - -std::string ExceptionTranslatorRegistry::tryTranslators() const { -CATCH_INTERNAL_ERROR("Attempted to use exception translators under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); -} -#endif - -} -// 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 = ""; -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 - -#include - - -namespace Catch { - -struct IConfig; - -std::mt19937& rng(); -void seedRng( IConfig const& config ); -unsigned int rngSeed(); - -} - -// end catch_random_number_generator.h -#include - -#include - - -namespace Catch { - -IGeneratorTracker::~IGeneratorTracker() { -} - -const char* GeneratorException::what() const noexcept { -return m_msg; -} - -namespace Generators { - -GeneratorUntypedBase::~GeneratorUntypedBase() { -} - -auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { -return getResultCapture().acquireGeneratorTracker( lineInfo ); -} - -} // 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; -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 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 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 - - -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 - - -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 spellings; -std::size_t count = 0; -}; - -std::size_t listTags( Config const& config ); - -std::size_t listReporters(); - -Option 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 - -#include - -#include - - -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 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 tagCounts; - -std::vector 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 list( Config const& config ) { -Option 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 - - -namespace Catch { -template -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 - -#include - -#include - - -namespace Catch { -namespace Matchers { -namespace Floating { -enum class FloatingPointKind : uint8_t { -Float, -Double -}; -} -} -} - -namespace { - -template -struct Converter; - -template <> -struct Converter { -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 { -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 -auto convert(T t) -> Converter { -return Converter(t); -} - -template -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(static_cast(matchee), static_cast(m_target), m_ulps); -case FloatingPointKind::Double: -return almostEqualUlps(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 - - -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 - -#include - - -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 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 - -#include - -#include - - -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 - -#include - -#include - -#include - -#include - - -#if -defined(CATCH_CONFIG_NEW_CAPTURE) -#if -defined(_MSC_VER) -#include - //_dup and _dup2 -#define -dup _dup -#define -dup2 _dup2 -#define -fileno _fileno -#else -#include - // dup and dup2 -#endif -#endif - -namespace Catch { - -RedirectedStream::RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream ) -: m_originalStream( originalStream ), -m_redirectionStream( redirectionStream ), -m_prevBuf( m_originalStream.rdbuf()) -{ -m_originalStream.rdbuf( m_redirectionStream.rdbuf()); -} - -RedirectedStream::~RedirectedStream() { -m_originalStream.rdbuf( m_prevBuf ); -} - -RedirectedStdOut::RedirectedStdOut() : m_cout( Catch::cout(), m_rss.get()) { -} -auto RedirectedStdOut::str() const -> std::string { -return m_rss.str(); } - -RedirectedStdErr::RedirectedStdErr() -: m_cerr( Catch::cerr(), m_rss.get()), -m_clog( Catch::clog(), m_rss.get()) -{ -} -auto RedirectedStdErr::str() const -> std::string { return m_rss.str(); } - -#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 - - -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 - -#include - -#include - -#include - - -namespace Catch { - -class TestCase; -struct IConfig; - -std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ); -bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); - -void enforceNoDuplicateTestCases( std::vector const& functions ); - -std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); -std::vector const& getAllTestCasesSorted( IConfig const& config ); - -class TestRegistry : -public ITestCaseRegistry { -public: -virtual ~TestRegistry() = default; - -virtual void registerTest( TestCase const& testCase ); - -std::vector const& getAllTests() const override; -std::vector const& getAllTestsSorted( IConfig const& config ) const override; - -private: -std::vector m_functions; -mutable RunTests::InWhatOrder m_currentSortOrder = RunTests::InDeclarationOrder; -mutable std::vector 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 - - -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 - - -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 - - -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 m_registry; -}; - -} // end namespace Catch - -// end catch_tag_alias_registry.h -// start catch_startup_exception_registry.h - -#include - -#include - - -namespace Catch { - -class StartupExceptionRegistry { -public: -void add(std::exception_ptr const& exception) noexcept; -std::vector const& getExceptions() const noexcept; -private: -std::vector 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 -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; - -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( static_cast( lhs ) | static_cast( rhs )); -} - -bool shouldContinueOnFailure( int flags ) { -return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } -bool shouldSuppressFailure( int flags ) { -return ( flags & ResultDisposition::SuppressFail ) != 0; } - -} // end namespace Catch -// end catch_result_type.cpp -// start catch_run_context.cpp - -#include - -#include - -#include - - -namespace Catch { - -namespace Generators { -struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker { -GeneratorBasePtr m_generator; - -GeneratorTracker( TestCaseTracking::NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) -: TrackerBase( nameAndLocation, ctx, parent ) -{ -} -~GeneratorTracker(); - -static GeneratorTracker& acquire( TrackerContext& ctx, TestCaseTracking::NameAndLocation const& nameAndLocation ) { -std::shared_ptr tracker; - -ITracker& currentTracker = ctx.currentTracker(); -if ( TestCaseTracking::ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation )) { -assert( childTracker ); -assert( childTracker->isGeneratorTracker()); -tracker = std::static_pointer_cast( childTracker ); -} -else { -tracker = std::make_shared( nameAndLocation, ctx, ¤tTracker ); -currentTracker.addChild( tracker ); -} - -if ( !ctx.completedCycle() && !tracker->isComplete()) { -tracker->open(); -} - -return *tracker; -} - -// TrackerBase interface -bool isGeneratorTracker() const override { -return true; } -auto hasGenerator() const -> bool override { -return !!m_generator; -} -void close() override { -TrackerBase::close(); -// Generator interface only finds out if it has another item on atual move -if (m_runState == CompletedSuccessfully && m_generator->next()) { -m_children.clear(); -m_runState = Executing; -} -} - -// IGeneratorTracker interface -auto getGenerator() const -> GeneratorBasePtr const& override { -return m_generator; -} -void setGenerator( GeneratorBasePtr&& generator ) override { -m_generator = std::move( generator ); -} -}; -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(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(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(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 - - -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 -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 m_config; -bool m_startupExceptions = false; -}; - -} // end namespace Catch - -// end catch_session.h -// start catch_version.h - -#include - - -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 - -#include - - -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 const& config) { -if (Catch::getRegistryHub().getReporterRegistry().getListeners().empty()) { -return createReporter(config->getReporterName(), config); -} - -// On older platforms, returning std::unique_ptr -// when the return type is std::unique_ptr -// doesn't compile without a std::move call. However, this causes -// a warning on newer platforms. Thus, we have to work around -// it a bit and downcast the pointer manually. -auto ret = std::unique_ptr(new ListeningReporter); -auto& multi = static_cast(*ret); -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 ret; -} - -Catch::Totals runTests(std::shared_ptr 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&>(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(std::getchar()); -} -int exitCode = runInternal(); -if (( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) { -Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << std::endl; -static_cast(std::getchar()); -} -return exitCode; -} - -clara::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( 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 listed = list( config())) -return static_cast( *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(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 - - -namespace Catch { - -namespace { -static auto getSingletons() -> std::vector*& { -static std::vector* g_singletons = nullptr; -if ( !g_singletons ) -g_singletons = new std::vector(); -return g_singletons; -} -} - -ISingleton::~ISingleton() { -} - -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 const& StartupExceptionRegistry::getExceptions() const noexcept { -return m_exceptions; -} - -} // end namespace Catch -// end catch_startup_exception_registry.cpp -// start catch_stream.cpp - -#include - -#include - -#include - -#include - -#include - -#include - - -namespace Catch { - -Catch::IStream::~IStream() = default; - -namespace detail { -namespace { -template -class StreamBufImpl : -public std::streambuf { -char data[bufferSize]; -WriterF m_writer; - -public: -StreamBufImpl() { -setp( data, data + sizeof(data)); -} - -~StreamBufImpl() noexcept { -StreamBufImpl::sync(); -} - -private: -int overflow( int c ) override { -sync(); - -if ( c != EOF ) { -if ( pbase() == epptr()) -m_writer( std::string( 1, static_cast( c ))); -else -sputc( static_cast( c )); -} -return 0; -} - -int sync() override { -if ( pbase() != pptr()) { -m_writer( std::string( pbase(), static_cast( pptr() - pbase()))); -setp( pbase(), epptr()); -} -return 0; -} -}; - -/////////////////////////////////////////////////////////////////////////// - -struct OutputDebugWriter { - -void operator()( std::string const&str ) { -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> m_streamBuf; -mutable std::ostream m_os; -public: -DebugOutStream() -: m_streamBuf( new StreamBufImpl()), -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> m_streams; -std::vector m_unused; -std::ostringstream m_referenceStream; // Used for copy state/ flags from - -auto add() -> std::size_t { -if ( m_unused.empty()) { -m_streams.push_back( std::unique_ptr( 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::getMutable().add()), -m_oss( Singleton::getMutable().m_streams[m_index].get()) -{ -} - -ReusableStringStream::~ReusableStringStream() { -static_cast( m_oss )->str(""); -m_oss->clear(); -Singleton::getMutable().release( m_index ); -} - -auto ReusableStringStream::str() const -> std::string { -return static_cast( 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 - -#include - -#include - -#include - - -namespace Catch { - -namespace { -char toLowerCh(char c) { -return static_cast( 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 - -#include - -#include - - -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(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( 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 - - -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 - -#include - -#include - -#include - - -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::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(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 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 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.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 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 - - -namespace Catch { - -std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { - -std::vector 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 const& functions ) { -std::set 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 filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ) { -std::vector filtered; -filtered.reserve( testCases.size()); -for ( auto const& testCase : testCases ) -if ( matchTest( testCase, testSpec, config )) -filtered.push_back( testCase ); -return filtered; -} -std::vector 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 const& TestRegistry::getAllTests() const { -return m_functions; -} -std::vector 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 - -#include - -#include - -#include - -#include - - -#if -defined(__clang__) -# pragma -clang diagnostic push -# pragma -clang diagnostic ignored "-Wexit-time-destructors" -#endif - -namespace Catch { -namespace TestCaseTracking { - -NameAndLocation::NameAndLocation( std::string const& _name, SourceLineInfo const& _location ) -: name( _name ), -location( _location ) -{ -} - -ITracker::~ITracker() = default; - -TrackerContext& TrackerContext::instance() { -static TrackerContext s_instance; -return s_instance; -} - -ITracker& TrackerContext::startRun() { -m_rootTracker = std::make_shared( 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::isGeneratorTracker() 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( *parent ); -addNextFilters( parentSection.m_filters ); -} -} - -bool SectionTracker::isComplete() const { -bool complete = true; - -if ((m_filters.empty() || m_filters[0] == "") || -std::find(m_filters.begin(), m_filters.end(), -m_nameAndLocation.name) != m_filters.end()) -complete = TrackerBase::isComplete(); -return complete; - -} - -bool SectionTracker::isSectionTracker() const { -return true; } - -SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { -std::shared_ptr section; - -ITracker& currentTracker = ctx.currentTracker(); -if ( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation )) { -assert( childTracker ); -assert( childTracker->isSectionTracker()); -section = std::static_pointer_cast( childTracker ); -} -else { -section = std::make_shared( 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 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 const& filters ) { -if ( filters.size() > 1 ) -m_filters.insert( m_filters.end(), ++filters.begin(), filters.end()); -} - -} // namespace TestCaseTracking - -using TestCaseTracking::ITracker; -using TestCaseTracking::TrackerContext; -using TestCaseTracking::SectionTracker; - -} // 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 - -#include - -#include - -#include - - -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(); -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(); -addFilter(); -} -else if ( c == '[' ) { -if ( subString() == "exclude:" ) -m_exclusion = true; -else -addPattern(); -startNewMode( Tag, ++m_pos ); -} -else if ( c == '\\' ) -escape(); -} -else if ( m_mode == EscapedName ) -m_mode = Name; -else if ( m_mode == QuotedName && c == '"' ) -addPattern(); -else if ( m_mode == Tag && c == ']' ) -addPattern(); -} -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 - - -static const uint64_t nanosecondsInSecond = 1000000000; - -namespace Catch { - -auto getCurrentNanosecondsSinceEpoch() -> uint64_t { -return std::chrono::duration_cast( 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 + 1u ); -} -} - -// 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(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 - -#include - - -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( size ), inc = 1; -if ( Endianness::which() == Endianness::Little ) { -i = end-1; -end = inc = -1; -} - -unsigned char const *bytes = static_cast(object); -ReusableStringStream rss; -rss << "0x" << std::setfill('0') << std::hex; -for (; i != end; i += inc ) -rss << std::setw(2) << static_cast(bytes[i]); -return rss.str(); -} -} - -template -std::string fpToString( T value, int precision ) { -if (Catch::isnan(value)) { -return "nan"; -} - -ReusableStringStream rss; -rss << std::setprecision( precision ) -<< std::fixed -<< value; -std::string d = rss.str(); -std::size_t i = d.find_last_not_of( '0' ); -if ( i != std::string::npos && i != d.size()-1 ) { -if ( d[i] == '.' ) -i++; -d = d.substr( 0, i+1 ); -} -return d; -} - -//// ======================================================= //// -// -// Out-of-line defs for full specialization of StringMaker -// -//// ======================================================= //// - -std::string StringMaker::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::convert(std::string_view str) { -return::Catch::Detail::stringify(std::string{ -str -}); -} -#endif - -std::string StringMaker::convert(char const* str) { -if (str) { -return::Catch::Detail::stringify(std::string{ -str -}); -} else { -return{ -"{null string}" -}; -} -} -std::string StringMaker::convert(char* str) { -if (str) { -return::Catch::Detail::stringify(std::string{ -str -}); -} else { -return{ -"{null string}" -}; -} -} - -#ifdef -CATCH_CONFIG_WCHAR -std::string StringMaker::convert(const std::wstring& wstr) { -std::string s; -s.reserve(wstr.size()); -for (auto c : wstr) { -s += (c <= 0xff) ? static_cast(c) : '?'; -} -return::Catch::Detail::stringify(s); -} - -# ifdef -CATCH_CONFIG_CPP17_STRING_VIEW -std::string StringMaker::convert(std::wstring_view str) { -return StringMaker::convert(std::wstring(str)); -} -# endif - -std::string StringMaker::convert(wchar_t const * str) { -if (str) { -return::Catch::Detail::stringify(std::wstring{ -str -}); -} else { -return{ -"{null string}" -}; -} -} -std::string StringMaker::convert(wchar_t * str) { -if (str) { -return::Catch::Detail::stringify(std::wstring{ -str -}); -} else { -return{ -"{null string}" -}; -} -} -#endif - -std::string StringMaker::convert(int value) { -return::Catch::Detail::stringify(static_cast(value)); -} -std::string StringMaker::convert(long value) { -return::Catch::Detail::stringify(static_cast(value)); -} -std::string StringMaker::convert(long long value) { -ReusableStringStream rss; -rss << value; -if (value > Detail::hexThreshold) { -rss << " (0x" << std::hex << value << ')'; -} -return rss.str(); -} - -std::string StringMaker::convert(unsigned int value) { -return::Catch::Detail::stringify(static_cast(value)); -} -std::string StringMaker::convert(unsigned long value) { -return::Catch::Detail::stringify(static_cast(value)); -} -std::string StringMaker::convert(unsigned long long value) { -ReusableStringStream rss; -rss << value; -if (value > Detail::hexThreshold) { -rss << " (0x" << std::hex << value << ')'; -} -return rss.str(); -} - -std::string StringMaker::convert(bool b) { -return b ? "true" : "false"; -} - -std::string StringMaker::convert(signed char value) { -if (value == '\r') { -return "'\\r'"; -} else if (value == '\f') { -return "'\\f'"; -} else if (value == '\n') { -return "'\\n'"; -} else if (value == '\t') { -return "'\\t'"; -} else if ('\0' <= value && value < ' ') { -return::Catch::Detail::stringify(static_cast(value)); -} else { -char chstr[] = "' '"; -chstr[1] = value; -return chstr; -} -} -std::string StringMaker::convert(char c) { -return::Catch::Detail::stringify(static_cast(c)); -} -std::string StringMaker::convert(unsigned char c) { -return::Catch::Detail::stringify(static_cast(c)); -} - -std::string StringMaker::convert(std::nullptr_t) { -return "nullptr"; -} - -std::string StringMaker::convert(float value) { -return fpToString(value, 5) + 'f'; -} -std::string StringMaker::convert(double value) { -return fpToString(value, 10); -} - -std::string ratio_string::symbol() { -return "a"; } -std::string ratio_string::symbol() { -return "f"; } -std::string ratio_string::symbol() { -return "p"; } -std::string ratio_string::symbol() { -return "n"; } -std::string ratio_string::symbol() { -return "u"; } -std::string ratio_string::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 - - -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 - - -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, 6, 0, "", 0 ); -return version; -} - -} -// end catch_version.cpp -// start catch_wildcard_pattern.cpp - -#include - - -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( 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 - - -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) { -std::ios_base::fmtflags f(os.flags()); -os << "\\x" -<< std::uppercase << std::hex << std::setfill('0') << std::setw(2) -<< static_cast(c); -os.flags(f); -} - -} // 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_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 << ""; -m_needsNewline = true; -return *this; -} - -void XmlWriter::writeStylesheetRef( std::string const& url ) { -m_os << "\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 << "\n"; -} - -void XmlWriter::newlineIfNecessary() { -if ( m_needsNewline ) { -m_os << std::endl; -m_needsNewline = false; -} -} -} -// end catch_xmlwriter.cpp -// start catch_reporter_bases.cpp - -#include - -#include - -#include - -#include - -#include - - -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 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::const_iterator itEnd = messages.end(); -const std::size_t N = static_cast(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 messages; -std::vector::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 - -#include - - -#if -defined(_MSC_VER) -#pragma -warning(push) -#pragma -warning(disable:4061) // Not all labels are EXPLICITLY handled in switch -// Note that 4062 (not all labels are handled -// and default is missing) is enabled -#endif - -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 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(s_nanosecondsInAMicrosecond); -case Unit::Milliseconds: -return m_inNanoseconds / static_cast(s_nanosecondsInAMillisecond); -case Unit::Seconds: -return m_inNanoseconds / static_cast(s_nanosecondsInASecond); -case Unit::Minutes: -return m_inNanoseconds / static_cast(s_nanosecondsInAMinute); -default: -return static_cast(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 m_columnInfos; -std::ostringstream m_oss; -int m_currentColumn = -1; -bool m_isOpen = false; - -public: -TablePrinter( std::ostream& os, std::vector columnInfos ) -: m_os( os ), -m_columnInfos( std::move( columnInfos )) { -} - -auto columnInfos() const -> std::vector 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 -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(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(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( 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 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 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 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 - -#include - -#include - -#include - - -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 &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 - - -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 ListeningReporter::getSupportedVerbosities() { -return std::set{ -}; -} - -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( 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 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__ ) -#define -CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) -#define -CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_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__ )) -#define -CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ )) -#define -CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_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__ ) -#define -TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) -#define -TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_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__ )) -#define -TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ )) -#define -TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_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 ) -#define -CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) -#define -CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) -#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 )) -#define -CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) -#define -CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) -#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 ) -#define -TEMPLATE_PRODUCT_TEST_CASE( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ ) -#define -TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) -#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 )) -#define -TEMPLATE_PRODUCT_TEST_CASE( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ ) -#define -TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) -#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/src/Client.cpp b/src/Client.cpp index 40b723c..ade0005 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -9,6 +9,7 @@ #include #include #include +#include template @@ -18,23 +19,22 @@ void insert_bytes(std::vector &container, const T &value) { } int32_t read_int32(std::vector::iterator &it) { - int32_t val; - std::memcpy(&val, &it, sizeof(int32_t)); + int32_t val = it[0] | (it[1] << 8) | (it[2] << 16) | (it[3] << 24); it += sizeof(int32_t); return val; } bool read_bool(std::vector::iterator &it) { - bool val; - std::memcpy(&val, &it, sizeof(bool)); + bool val = it[0]; it += sizeof(bool); return val; } std::string read_string(std::vector::iterator &it, size_t len) { - std::string string(it, it + len); + std::string str(it, it + len); + str.erase(std::find(str.begin(), str.end(), '\0'), str.end()); it += len; - return string; + return str; } const std::vector Client::list() const { @@ -120,10 +120,9 @@ const std::vector Client::translate_list_message(std::vector & std::string currency_name = read_string(it, CURRENCY_NAME_SIZE_IN_LIST); int32_t current_rate = read_int32(it); bool has_change = read_bool(it); - int32_t absolute_change, relative_change; + int32_t absolute_change = read_int32(it); + int32_t relative_change = read_int32(it); if (has_change) { - absolute_change = read_int32(it); - relative_change = read_int32(it); currencies.emplace_back(currency_name, current_rate, absolute_change, relative_change); } else { currencies.emplace_back(currency_name, current_rate); diff --git a/src/Currency.cpp b/src/Currency.cpp index 5b942bb..d6bdcee 100644 --- a/src/Currency.cpp +++ b/src/Currency.cpp @@ -16,6 +16,8 @@ Currency::Currency(std::string name, std::vector rates) : name(std::mov Currency::Currency(std::string name, int32_t current_rate) : name(std::move(name)) { rates = {current_rate}; + absolute_change = -1; + relative_change = -1; } Currency::Currency(std::string name, int32_t current_rate, int32_t absolute_change, int32_t relative_change) : @@ -43,3 +45,14 @@ int32_t Currency::get_relative_change() const { return relative_change; } +bool Currency::operator==(const Currency &rhs) const { + return name == rhs.name && + rates == rhs.rates && + absolute_change == rhs.absolute_change && + relative_change == rhs.relative_change; +} + +bool Currency::operator!=(const Currency &rhs) const { + return !(rhs == *this); +} + diff --git a/test/ClientTest.cpp b/test/ClientTest.cpp index 4ca8bbe..24a5e79 100644 --- a/test/ClientTest.cpp +++ b/test/ClientTest.cpp @@ -79,4 +79,55 @@ TEST_CASE("list empty currencies") { std::vector result = client.list(); REQUIRE(result.empty()); server_thread.join(); +} + +TEST_CASE("list one currency") { + ClientTest clientTest; + std::vector server_return = {'c', 'u', 'r', 'r', 'e', 'n', 'c', 'y','N', 'a', 'm', 'e', '1', 0, 0, 0, + 1, 1, 1, 1, + 1, + 1, 0, 1, 0, + 12, 0, 0, 0, + '\\', 0}; + std::thread server_thread(setup_server_returning, server_return, std::ref(clientTest)); + while (clientTest.PORTNO == 0) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + Client client = Client(clientTest.HOST, clientTest.PORTNO); + std::vector result = client.list(); + std::vector expected = {Currency("currencyName1", + (1 << 24) + (1 << 16) + (1 << 8) + (1 << 0), + (1 << 16) + (1 << 0), + 12)}; + REQUIRE(result == expected); + server_thread.join(); +} + + +TEST_CASE("list two currencies") { + ClientTest clientTest; + std::vector server_return = {'c', 'u', 'r', 'r', 'e', 'n', 'c', 'y','N', 'a', 'm', 'e', '1', 0, 0, 0, + 1, 1, 1, 1, + 1, + 1, 0, 1, 0, + 12, 0, 0, 0, + 'c', 'u', 'r', 'r', 'e', 'n', 'c', 'y','N', 'a', 'm', 'e', '2', 'e', 'n', 'd', + 1, 0, 0, 0, + 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + '\\', 0}; + std::thread server_thread(setup_server_returning, server_return, std::ref(clientTest)); + while (clientTest.PORTNO == 0) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + Client client = Client(clientTest.HOST, clientTest.PORTNO); + std::vector result = client.list(); + std::vector expected = {Currency("currencyName1", + (1 << 24) + (1 << 16) + (1 << 8) + (1 << 0), + (1 << 16) + (1 << 0), + 12), + Currency("currencyName2end", 1)}; + REQUIRE(result == expected); + server_thread.join(); } \ No newline at end of file From 48af768f7c35b5da78c3f3c3296592154a964146 Mon Sep 17 00:00:00 2001 From: RamSaw Date: Fri, 8 Feb 2019 14:06:09 +0300 Subject: [PATCH 07/17] Added two new tests. --- include/Client.h | 20 + include/Currency.h | 4 + include/catch.hpp | 14688 ++++++++++++++++++++++++++++ src/Client.cpp | 163 +- src/Currency.cpp | 13 + src/CurrencyClientApplication.cpp | 41 +- test/ClientTest.cpp | 208 +- 7 files changed, 15103 insertions(+), 34 deletions(-) create mode 100644 include/catch.hpp diff --git a/include/Client.h b/include/Client.h index b56cf30..4de8b19 100644 --- a/include/Client.h +++ b/include/Client.h @@ -14,6 +14,14 @@ class Client { const std::vector list() const; + bool addCurrency(const Currency ¤cy) const; + + bool addRate(const Currency ¤cy, int32_t new_rate) const; + + bool remove(const Currency ¤cy) const; + + Currency getCurrencyWithHistory(const Currency ¤cy) const; + virtual ~Client(); private: @@ -30,6 +38,18 @@ class Client { const std::vector translate_list_message(std::vector &message) const; void remove_ending_symbols(std::vector &message) const; + + void write_string(std::vector &buffer, const std::string ¤cy_name) const; + + void write_int32(std::vector &buffer, int32_t rate) const; + + std::vector read_response() const; + + bool translate_add_message(std::vector &message) const; + + bool translate_remove_message(std::vector &message) const; + + std::vector translate_get_currency_history_message(std::vector &message) const; }; diff --git a/include/Currency.h b/include/Currency.h index 06f6f40..95ff5f8 100644 --- a/include/Currency.h +++ b/include/Currency.h @@ -11,6 +11,8 @@ class Currency { public: + Currency(std::string name); + Currency(std::string name, std::vector rates); Currency(std::string name, int32_t current_rate); @@ -23,6 +25,8 @@ class Currency { int32_t get_rate(size_t i) const; + const std::vector& get_rates() const; + int32_t get_absolute_change() const; int32_t get_relative_change() const; diff --git a/include/catch.hpp b/include/catch.hpp new file mode 100644 index 0000000..02d766e --- /dev/null +++ b/include/catch.hpp @@ -0,0 +1,14688 @@ +/* + * Catch v2.6.0 + * Generated: 2019-01-31 22:25:55.560884 + * ---------------------------------------------------------- + * This file has been merged from multiple headers. Please don't edit it directly + * Copyright (c) 2019 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 6 +#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__ + // Because REQUIREs trigger GCC's -Wparentheses, and because still + // supported version of g++ have only buggy support for _Pragmas, + // Wparentheses have to be suppressed globally. +# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details + +# 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 +# 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_ 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() && defined(CATCH_CPP17_OR_GREATER) +# define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW +#endif +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Check if optional is available and usable +#if defined(__has_include) +# if __has_include() && defined(CATCH_CPP17_OR_GREATER) +# define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL +# endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +#endif // __has_include + +//////////////////////////////////////////////////////////////////////////////// +// Check if variant is available and usable +#if defined(__has_include) +# if __has_include() && defined(CATCH_CPP17_OR_GREATER) +# if defined(__clang__) && (__clang_major__ < 8) + // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 + // fix should be in clang 8, workaround in libstdc++ 8.2 +# include +# if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) +# define CATCH_CONFIG_NO_CPP17_VARIANT +# else +# define CATCH_INTERNAL_CONFIG_CPP17_VARIANT +# endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) +# else +# define CATCH_INTERNAL_CONFIG_CPP17_VARIANT +# endif // defined(__clang__) && (__clang_major__ < 8) +# endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +#endif // __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_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) +# define CATCH_CONFIG_CPP17_OPTIONAL +#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 +#include +#include + +// 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& operator = ( SourceLineInfo const& ) = default; + SourceLineInfo( SourceLineInfo&& ) noexcept = default; + SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = 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 + T const& operator + ( T const& value, StreamEndStop ) { + return value; + } +} + +#define CATCH_INTERNAL_LINEINFO \ + ::Catch::SourceLineInfo( __FILE__, static_cast( __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 + +namespace Catch { + + class TestSpec; + + struct ITestInvoker { + virtual void invoke () const = 0; + virtual ~ITestInvoker(); + }; + + class TestCase; + struct IConfig; + + struct ITestCaseRegistry { + virtual ~ITestCaseRegistry(); + virtual std::vector const& getAllTests() const = 0; + virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; + }; + + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector const& getAllTestCasesSorted( IConfig const& config ); + +} + +// end catch_interfaces_testcase.h +// start catch_stringref.h + +#include +#include +#include + +namespace Catch { + + /// A non-owning string class (similar to the forthcoming std::string_view) + /// Note that, because a StringRef may be a substring of another string, + /// it may not be null terminated. 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 + + +#include + +namespace Catch{ + +#ifdef CATCH_CPP17_OR_GREATER + template + inline constexpr auto is_unique = std::true_type{}; + + template + inline constexpr auto is_unique = std::bool_constant< + (!std::is_same_v && ...) && is_unique + >{}; +#else + +template +struct is_unique : std::true_type{}; + +template +struct is_unique : std::integral_constant +::value + && is_unique::value + && is_unique::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 + +#define INTERNAL_CATCH_MAKE_TYPE_LIST(types) TypeList + +#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(types)\ + CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,INTERNAL_CATCH_REMOVE_PARENS(types)) + +// end catch_preprocessor.hpp +// start catch_meta.hpp + + +#include + +template< typename... > +struct TypeList{}; + +template< typename... > +struct append; + +template< template class L1 + , typename...E1 + , template class L2 + , typename...E2 + > +struct append< L1, L2 > +{ + using type = L1; +}; + +template< template class L1 + , typename...E1 + , template class L2 + , typename...E2 + , typename...Rest + > +struct append< L1, L2, Rest...> +{ + using type = typename append< L1, Rest... >::type; +}; + +template< template class + , typename... + > +struct rewrap; + +template< template class Container + , template class List + , typename...elems + > +struct rewrap> +{ + using type = TypeList< Container< elems... > >; +}; + +template< template class Container + , template class List + , class...Elems + , typename...Elements> +struct rewrap, Elements...> +{ + using type = typename append>, typename rewrap::type>::type; +}; + +template< template class...Containers > +struct combine +{ + template< typename...Types > + struct with_types + { + template< template class Final > + struct into + { + using type = typename append, typename rewrap::type...>::type; + }; + }; +}; + +template +struct always_false : std::false_type {}; + +// end catch_meta.hpp +namespace Catch { + +template +class TestInvokerAsMethod : public ITestInvoker { + void (C::*m_testAsMethod)(); +public: + TestInvokerAsMethod( void (C::*testAsMethod)() ) noexcept : m_testAsMethod( testAsMethod ) {} + + void invoke() const override { + C obj; + (obj.*m_testAsMethod)(); + } +}; + +auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker*; + +template +auto makeTestInvoker( void (C::*testAsMethod)() ) noexcept -> ITestInvoker* { + return new(std::nothrow) TestInvokerAsMethod( 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 \ + static void TestName() + #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \ + namespace{ \ + template \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName ) { \ + void test(); \ + }; \ + } \ + template \ + 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 \ + static void TestFunc();\ + namespace {\ + template \ + struct TestName{\ + template \ + 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 ), 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 \ + 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_UD(INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME,Name, __VA_ARGS__));\ + return 0;\ + }(); + + #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(TestName, TestFuncName, Name, Tags, TmplTypes, TypesList) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + template static void TestFuncName(); \ + namespace { \ + template \ + struct TestName { \ + TestName() { \ + CATCH_INTERNAL_CHECK_UNIQUE_TYPES(Types...) \ + int index = 0; \ + using expander = int[]; \ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFuncName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + Catch::StringMaker::convert(index++), Tags } ), 0)... };/* NOLINT */ \ + } \ + }; \ + static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \ + using TestInit = combine \ + ::with_types::into::type; \ + TestInit(); \ + return 0; \ + }(); \ + } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + template \ + static void TestFuncName() + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(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_PRODUCT_TEST_CASE(Name, Tags, ...)\ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( 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_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, ... ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ \ + template \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName ) { \ + void test();\ + };\ + template \ + struct TestNameClass{\ + template \ + 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::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 \ + void TestName::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 + + #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(TestNameClass, TestName, ClassName, Name, Tags, TmplTypes, TypesList)\ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + template \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName ) { \ + void test();\ + };\ + namespace {\ + template\ + struct TestNameClass{\ + TestNameClass(){\ + CATCH_INTERNAL_CHECK_UNIQUE_TYPES(Types...)\ + int index = 0;\ + using expander = int[];\ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + Catch::StringMaker::convert(index++), Tags } ), 0)... };/* NOLINT */ \ + }\ + };\ + static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ + using TestInit = combine\ + ::with_types::into::type;\ + TestInit();\ + return 0;\ + }(); \ + }\ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + template \ + void TestName::test() + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR + #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\ + INTERNAL_CATCH_TEMPLATE_PRODUCT_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____ ), 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____ ), ClassName, Name, Tags, __VA_ARGS__ ) +#else + #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\ + INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_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____ ), 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____ ), 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 +#include +#include +#include +// start catch_stream.h + +#include +#include +#include + +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 + 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 +#endif + +#ifdef __OBJC__ +// start catch_objc_arc.hpp + +#import + +#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 + std::string rawMemoryToString( const T& object ) { + return rawMemoryToString( &object, sizeof(object) ); + } + + template + class IsStreamInsertable { + template + static auto test(int) + -> decltype(std::declval() << std::declval(), std::true_type()); + + template + static auto test(...)->std::false_type; + + public: + static const bool value = decltype(test(0))::value; + }; + + template + std::string convertUnknownEnumToString( E e ); + + template + typename std::enable_if< + !std::is_enum::value && !std::is_base_of::value, + std::string>::type convertUnstreamable( T const& ) { + return Detail::unprintableString; + } + template + typename std::enable_if< + !std::is_enum::value && std::is_base_of::value, + std::string>::type convertUnstreamable(T const& ex) { + return ex.what(); + } + + template + typename std::enable_if< + std::is_enum::value + , std::string>::type convertUnstreamable( T const& value ) { + return convertUnknownEnumToString( value ); + } + +#if defined(_MANAGED) + //! Convert a CLR string to a utf8 std::string + template + std::string clrReferenceToString( T^ ref ) { + if (ref == nullptr) + return std::string("null"); + auto bytes = System::Text::Encoding::UTF8->GetBytes(ref->ToString()); + cli::pin_ptr p = &bytes[0]; + return std::string(reinterpret_cast(p), bytes->Length); + } +#endif + + } // namespace Detail + + // If we decide for C++14, change these to enable_if_ts + template + struct StringMaker { + template + static + typename std::enable_if<::Catch::Detail::IsStreamInsertable::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 + static + typename std::enable_if::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 + std::string stringify(const T& e) { + return ::Catch::StringMaker::type>::type>::convert(e); + } + + template + std::string convertUnknownEnumToString( E e ) { + return ::Catch::Detail::stringify(static_cast::type>(e)); + } + +#if defined(_MANAGED) + template + std::string stringify( T^ e ) { + return ::Catch::StringMaker::convert(e); + } +#endif + + } // namespace Detail + + // Some predefined specializations + + template<> + struct StringMaker { + static std::string convert(const std::string& str); + }; + +#ifdef CATCH_CONFIG_CPP17_STRING_VIEW + template<> + struct StringMaker { + static std::string convert(std::string_view str); + }; +#endif + + template<> + struct StringMaker { + static std::string convert(char const * str); + }; + template<> + struct StringMaker { + static std::string convert(char * str); + }; + +#ifdef CATCH_CONFIG_WCHAR + template<> + struct StringMaker { + static std::string convert(const std::wstring& wstr); + }; + +# ifdef CATCH_CONFIG_CPP17_STRING_VIEW + template<> + struct StringMaker { + static std::string convert(std::wstring_view str); + }; +# endif + + template<> + struct StringMaker { + static std::string convert(wchar_t const * str); + }; + template<> + struct StringMaker { + static std::string convert(wchar_t * str); + }; +#endif + + // TBD: Should we use `strnlen` to ensure that we don't go out of the buffer, + // while keeping string semantics? + template + struct StringMaker { + static std::string convert(char const* str) { + return ::Catch::Detail::stringify(std::string{ str }); + } + }; + template + struct StringMaker { + static std::string convert(signed char const* str) { + return ::Catch::Detail::stringify(std::string{ reinterpret_cast(str) }); + } + }; + template + struct StringMaker { + static std::string convert(unsigned char const* str) { + return ::Catch::Detail::stringify(std::string{ reinterpret_cast(str) }); + } + }; + + template<> + struct StringMaker { + static std::string convert(int value); + }; + template<> + struct StringMaker { + static std::string convert(long value); + }; + template<> + struct StringMaker { + static std::string convert(long long value); + }; + template<> + struct StringMaker { + static std::string convert(unsigned int value); + }; + template<> + struct StringMaker { + static std::string convert(unsigned long value); + }; + template<> + struct StringMaker { + static std::string convert(unsigned long long value); + }; + + template<> + struct StringMaker { + static std::string convert(bool b); + }; + + template<> + struct StringMaker { + static std::string convert(char c); + }; + template<> + struct StringMaker { + static std::string convert(signed char c); + }; + template<> + struct StringMaker { + static std::string convert(unsigned char c); + }; + + template<> + struct StringMaker { + static std::string convert(std::nullptr_t); + }; + + template<> + struct StringMaker { + static std::string convert(float value); + }; + template<> + struct StringMaker { + static std::string convert(double value); + }; + + template + struct StringMaker { + template + static std::string convert(U* p) { + if (p) { + return ::Catch::Detail::rawMemoryToString(p); + } else { + return "nullptr"; + } + } + }; + + template + struct StringMaker { + static std::string convert(R C::* p) { + if (p) { + return ::Catch::Detail::rawMemoryToString(p); + } else { + return "nullptr"; + } + } + }; + +#if defined(_MANAGED) + template + struct StringMaker { + static std::string convert( T^ ref ) { + return ::Catch::Detail::clrReferenceToString(ref); + } + }; +#endif + + namespace Detail { + template + std::string rangeToString(InputIterator first, 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 { + static std::string convert(NSString * nsstring) { + if (!nsstring) + return "nil"; + return std::string("@") + [nsstring UTF8String]; + } + }; + template<> + struct StringMaker { + static std::string convert(NSObject* nsObject) { + return ::Catch::Detail::stringify([nsObject description]); + } + + }; + namespace Detail { + inline std::string stringify( NSString* nsstring ) { + return StringMaker::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 +# define CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER +#endif + +// Separate std::pair specialization +#if defined(CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER) +#include +namespace Catch { + template + struct StringMaker > { + static std::string convert(const std::pair& pair) { + ReusableStringStream rss; + rss << "{ " + << ::Catch::Detail::stringify(pair.first) + << ", " + << ::Catch::Detail::stringify(pair.second) + << " }"; + return rss.str(); + } + }; +} +#endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER + +#if defined(CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_OPTIONAL) +#include +namespace Catch { + template + struct StringMaker > { + static std::string convert(const std::optional& optional) { + ReusableStringStream rss; + if (optional.has_value()) { + rss << ::Catch::Detail::stringify(*optional); + } else { + rss << "{ }"; + } + return rss.str(); + } + }; +} +#endif // CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER + +// Separate std::tuple specialization +#if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER) +#include +namespace Catch { + namespace Detail { + template< + typename Tuple, + std::size_t N = 0, + bool = (N < std::tuple_size::value) + > + struct TupleElementPrinter { + static void print(const Tuple& tuple, std::ostream& os) { + os << (N ? ", " : " ") + << ::Catch::Detail::stringify(std::get(tuple)); + TupleElementPrinter::print(tuple, os); + } + }; + + template< + typename Tuple, + std::size_t N + > + struct TupleElementPrinter { + static void print(const Tuple&, std::ostream&) {} + }; + + } + + template + struct StringMaker> { + static std::string convert(const std::tuple& tuple) { + ReusableStringStream rss; + rss << '{'; + Detail::TupleElementPrinter>::print(tuple, rss.get()); + rss << " }"; + return rss.str(); + } + }; +} +#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER + +#if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT) +#include +namespace Catch { + template<> + struct StringMaker { + static std::string convert(const std::monostate&) { + return "{ }"; + } + }; + + template + struct StringMaker> { + static std::string convert(const std::variant& variant) { + if (variant.valueless_by_exception()) { + return "{valueless variant}"; + } else { + return std::visit( + [](const auto& value) { + return ::Catch::Detail::stringify(value); + }, + variant + ); + } + } + }; +} +#endif // CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER + +namespace Catch { + 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 + struct is_range { + static const bool value = + !std::is_same())), not_this_one>::value && + !std::is_same())), not_this_one>::value; + }; + +#if defined(_MANAGED) // Managed types are never ranges + template + struct is_range { + static const bool value = false; + }; +#endif + + template + std::string rangeToString( Range const& range ) { + return ::Catch::Detail::rangeToString( begin( range ), end( range ) ); + } + + // Handle vector specially + template + std::string rangeToString( std::vector const& v ) { + ReusableStringStream rss; + rss << "{ "; + bool first = true; + for( bool b : v ) { + if( first ) + first = false; + else + rss << ", "; + rss << ::Catch::Detail::stringify( b ); + } + rss << " }"; + return rss.str(); + } + + template + struct StringMaker::value && !::Catch::Detail::IsStreamInsertable::value>::type> { + static std::string convert( R const& range ) { + return rangeToString( range ); + } + }; + + template + struct StringMaker { + static std::string convert(T const(&arr)[SZ]) { + return rangeToString(arr); + } + }; + +} // namespace Catch + +// Separate std::chrono::duration specialization +#if defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +#include +#include +#include + +namespace Catch { + +template +struct ratio_string { + static std::string symbol(); +}; + +template +std::string ratio_string::symbol() { + Catch::ReusableStringStream rss; + rss << '[' << Ratio::num << '/' + << Ratio::den << ']'; + return rss.str(); +} +template <> +struct ratio_string { + static std::string symbol(); +}; +template <> +struct ratio_string { + static std::string symbol(); +}; +template <> +struct ratio_string { + static std::string symbol(); +}; +template <> +struct ratio_string { + static std::string symbol(); +}; +template <> +struct ratio_string { + static std::string symbol(); +}; +template <> +struct ratio_string { + static std::string symbol(); +}; + + //////////// + // std::chrono::duration specializations + template + struct StringMaker> { + static std::string convert(std::chrono::duration const& duration) { + ReusableStringStream rss; + rss << duration.count() << ' ' << ratio_string::symbol() << 's'; + return rss.str(); + } + }; + template + struct StringMaker>> { + static std::string convert(std::chrono::duration> const& duration) { + ReusableStringStream rss; + rss << duration.count() << " s"; + return rss.str(); + } + }; + template + struct StringMaker>> { + static std::string convert(std::chrono::duration> const& duration) { + ReusableStringStream rss; + rss << duration.count() << " m"; + return rss.str(); + } + }; + template + struct StringMaker>> { + static std::string convert(std::chrono::duration> const& duration) { + ReusableStringStream rss; + rss << duration.count() << " h"; + return rss.str(); + } + }; + + //////////// + // std::chrono::time_point specialization + // Generic time_point cannot be specialized, only std::chrono::time_point + template + struct StringMaker> { + static std::string convert(std::chrono::time_point const& time_point) { + return ::Catch::Detail::stringify(time_point.time_since_epoch()) + " since epoch"; + } + }; + // std::chrono::time_point specialization + template + struct StringMaker> { + static std::string convert(std::chrono::time_point const& time_point) { + auto converted = std::chrono::system_clock::to_time_t(time_point); + +#ifdef _MSC_VER + std::tm timeInfo = {}; + gmtime_s(&timeInfo, &converted); +#else + std::tm* timeInfo = std::gmtime(&converted); +#endif + + auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); + char timeStamp[timeStampSize]; + const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; + +#ifdef _MSC_VER + std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); +#else + std::strftime(timeStamp, timeStampSize, fmt, timeInfo); +#endif + return std::string(timeStamp); + } + }; +} +#endif // CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +// end catch_tostring.h +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4389) // '==' : signed/unsigned mismatch +#pragma warning(disable:4018) // more "signed/unsigned mismatch" +#pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform) +#pragma warning(disable:4180) // qualifier applied to function type has no meaning +#pragma warning(disable:4800) // Forcing result to true or false +#endif + +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 + class BinaryExpr : public ITransientExpression { + LhsT m_lhs; + StringRef m_op; + RhsT m_rhs; + + void streamReconstructedExpression( std::ostream &os ) const override { + formatReconstructedExpression + ( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) ); + } + + public: + BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs ) + : ITransientExpression{ true, comparisonResult }, + m_lhs( lhs ), + m_op( op ), + m_rhs( rhs ) + {} + + template + auto operator && ( T ) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator || ( T ) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator == ( T ) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator != ( T ) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator > ( T ) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator < ( T ) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator >= ( T ) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator <= ( T ) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + }; + + template + class UnaryExpr : public ITransientExpression { + LhsT m_lhs; + + void streamReconstructedExpression( std::ostream &os ) const override { + os << Catch::Detail::stringify( m_lhs ); + } + + public: + explicit UnaryExpr( LhsT lhs ) + : ITransientExpression{ false, static_cast(lhs) }, + m_lhs( lhs ) + {} + }; + + // Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int) + template + auto compareEqual( LhsT const& lhs, RhsT const& rhs ) -> bool { return static_cast(lhs == rhs); } + template + auto compareEqual( T* const& lhs, int rhs ) -> bool { return lhs == reinterpret_cast( rhs ); } + template + auto compareEqual( T* const& lhs, long rhs ) -> bool { return lhs == reinterpret_cast( rhs ); } + template + auto compareEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) == rhs; } + template + auto compareEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) == rhs; } + + template + auto compareNotEqual( LhsT const& lhs, RhsT&& rhs ) -> bool { return static_cast(lhs != rhs); } + template + auto compareNotEqual( T* const& lhs, int rhs ) -> bool { return lhs != reinterpret_cast( rhs ); } + template + auto compareNotEqual( T* const& lhs, long rhs ) -> bool { return lhs != reinterpret_cast( rhs ); } + template + auto compareNotEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) != rhs; } + template + auto compareNotEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) != rhs; } + + template + class ExprLhs { + LhsT m_lhs; + public: + explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {} + + template + auto operator == ( RhsT const& rhs ) -> BinaryExpr const { + return { compareEqual( m_lhs, rhs ), m_lhs, "==", rhs }; + } + auto operator == ( bool rhs ) -> BinaryExpr const { + return { m_lhs == rhs, m_lhs, "==", rhs }; + } + + template + auto operator != ( RhsT const& rhs ) -> BinaryExpr const { + return { compareNotEqual( m_lhs, rhs ), m_lhs, "!=", rhs }; + } + auto operator != ( bool rhs ) -> BinaryExpr const { + return { m_lhs != rhs, m_lhs, "!=", rhs }; + } + + template + auto operator > ( RhsT const& rhs ) -> BinaryExpr const { + return { static_cast(m_lhs > rhs), m_lhs, ">", rhs }; + } + template + auto operator < ( RhsT const& rhs ) -> BinaryExpr const { + return { static_cast(m_lhs < rhs), m_lhs, "<", rhs }; + } + template + auto operator >= ( RhsT const& rhs ) -> BinaryExpr const { + return { static_cast(m_lhs >= rhs), m_lhs, ">=", rhs }; + } + template + auto operator <= ( RhsT const& rhs ) -> BinaryExpr const { + return { static_cast(m_lhs <= rhs), m_lhs, "<=", rhs }; + } + + template + auto operator && ( RhsT const& ) -> BinaryExpr const { + static_assert(always_false::value, + "operator&& is not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator || ( RhsT const& ) -> BinaryExpr const { + static_assert(always_false::value, + "operator|| is not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + auto makeUnaryExpr() const -> UnaryExpr { + return UnaryExpr{ m_lhs }; + } + }; + + void handleExpression( ITransientExpression const& expr ); + + template + void handleExpression( ExprLhs const& expr ) { + handleExpression( expr.makeUnaryExpr() ); + } + + struct Decomposer { + template + auto operator <= ( T const& lhs ) -> ExprLhs { + return ExprLhs{ lhs }; + } + + auto operator <=( bool value ) -> ExprLhs { + return ExprLhs{ value }; + } + }; + +} // end namespace Catch + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +// end catch_decomposer.h +// start catch_interfaces_capture.h + +#include + +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 + void handleExpr( ExprLhs 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 +#include + +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 + 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 + 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 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 + void captureValues( size_t index, T const& value ) { + captureValue( index, Catch::Detail::stringify( value ) ); + } + + template + void captureValues( size_t index, T const& value, Ts const&... values ) { + captureValue( index, Catch::Detail::stringify(value) ); + captureValues( index+1, values... ); + } + }; + +} // end namespace Catch + +// 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( !!(__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(__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(__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(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(__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 + +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 + +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 + +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 + +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 +#include + +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 +#include + +namespace Catch { + + class TestCase; + struct ITestCaseRegistry; + struct IExceptionTranslatorRegistry; + struct IExceptionTranslator; + struct IReporterRegistry; + struct IReporterFactory; + struct ITagAliasRegistry; + class StartupExceptionRegistry; + + using IReporterFactoryPtr = std::shared_ptr; + + 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 +#include +#include + +namespace Catch { + using exceptionTranslateFunction = std::string(*)(); + + struct IExceptionTranslator; + using ExceptionTranslators = std::vector>; + + 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 + 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 + ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) { + getMutableRegistryHub().registerTranslator + ( new ExceptionTranslator( 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 + +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 ::value>::type> + Approx operator()( T const& value ) { + Approx approx( static_cast(value) ); + approx.m_epsilon = m_epsilon; + approx.m_margin = m_margin; + approx.m_scale = m_scale; + return approx; + } + + template ::value>::type> + explicit Approx( T const& value ): Approx(static_cast(value)) + {} + + template ::value>::type> + friend bool operator == ( const T& lhs, Approx const& rhs ) { + auto lhs_v = static_cast(lhs); + return rhs.equalityComparisonImpl(lhs_v); + } + + template ::value>::type> + friend bool operator == ( Approx const& lhs, const T& rhs ) { + return operator==( rhs, lhs ); + } + + template ::value>::type> + friend bool operator != ( T const& lhs, Approx const& rhs ) { + return !operator==( lhs, rhs ); + } + + template ::value>::type> + friend bool operator != ( Approx const& lhs, T const& rhs ) { + return !operator==( rhs, lhs ); + } + + template ::value>::type> + friend bool operator <= ( T const& lhs, Approx const& rhs ) { + return static_cast(lhs) < rhs.m_value || lhs == rhs; + } + + template ::value>::type> + friend bool operator <= ( Approx const& lhs, T const& rhs ) { + return lhs.m_value < static_cast(rhs) || lhs == rhs; + } + + template ::value>::type> + friend bool operator >= ( T const& lhs, Approx const& rhs ) { + return static_cast(lhs) > rhs.m_value || lhs == rhs; + } + + template ::value>::type> + friend bool operator >= ( Approx const& lhs, T const& rhs ) { + return lhs.m_value > static_cast(rhs) || lhs == rhs; + } + + template ::value>::type> + Approx& epsilon( T const& newEpsilon ) { + double epsilonAsDouble = static_cast(newEpsilon); + setEpsilon(epsilonAsDouble); + return *this; + } + + template ::value>::type> + Approx& margin( T const& newMargin ) { + double marginAsDouble = static_cast(newMargin); + setMargin(marginAsDouble); + return *this; + } + + template ::value>::type> + Approx& scale( T const& newScale ) { + m_scale = static_cast(newScale); + return *this; + } + + std::string toString() const; + + private: + double m_epsilon; + double m_margin; + double m_scale; + double m_value; + }; +} // 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 { + static std::string convert(Catch::Detail::Approx const& value); +}; + +} // end namespace Catch + +// end catch_approx.h +// start catch_string_manip.h + +#include +#include + +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 +#include + +namespace Catch { +namespace Matchers { + namespace Impl { + + template struct MatchAllOf; + template struct MatchAnyOf; + template 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 + struct MatcherMethod { + virtual bool match( ObjectT const& arg ) const = 0; + }; + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif + + template + struct MatcherBase : MatcherUntypedBase, MatcherMethod { + + MatchAllOf operator && ( MatcherBase const& other ) const; + MatchAnyOf operator || ( MatcherBase const& other ) const; + MatchNotOf operator ! () const; + }; + + template + struct MatchAllOf : MatcherBase { + 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& operator && ( MatcherBase const& other ) { + m_matchers.push_back( &other ); + return *this; + } + + std::vector const*> m_matchers; + }; + template + struct MatchAnyOf : MatcherBase { + + 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& operator || ( MatcherBase const& other ) { + m_matchers.push_back( &other ); + return *this; + } + + std::vector const*> m_matchers; + }; + + template + struct MatchNotOf : MatcherBase { + + MatchNotOf( MatcherBase 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 const& m_underlyingMatcher; + }; + + template + MatchAllOf MatcherBase::operator && ( MatcherBase const& other ) const { + return MatchAllOf() && *this && other; + } + template + MatchAnyOf MatcherBase::operator || ( MatcherBase const& other ) const { + return MatchAnyOf() || *this || other; + } + template + MatchNotOf MatcherBase::operator ! () const { + return MatchNotOf( *this ); + } + + } // namespace Impl + +} // namespace Matchers + +using namespace Matchers; +using Matchers::Impl::MatcherBase; + +} // namespace Catch + +// end catch_matchers.h +// start catch_matchers_floating.h + +#include +#include + +namespace Catch { +namespace Matchers { + + namespace Floating { + + enum class FloatingPointKind : uint8_t; + + struct WithinAbsMatcher : MatcherBase { + 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 { + 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 +#include + +namespace Catch { +namespace Matchers { +namespace Generic { + +namespace Detail { + std::string finalizeDescription(const std::string& desc); +} + +template +class PredicateMatcher : public MatcherBase { + std::function m_predicate; + std::string m_description; +public: + + PredicateMatcher(std::function 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 is hard (but possible) and + // requires a lot of TMP. + template + Generic::PredicateMatcher Predicate(std::function const& predicate, std::string const& description = "") { + return Generic::PredicateMatcher(predicate, description); + } + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_generic.hpp +// start catch_matchers_string.h + +#include + +namespace Catch { +namespace Matchers { + + namespace StdString { + + struct CasedString + { + CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ); + std::string adjustString( std::string const& str ) const; + std::string caseSensitivitySuffix() const; + + CaseSensitive::Choice m_caseSensitivity; + std::string m_str; + }; + + struct StringMatcherBase : MatcherBase { + StringMatcherBase( std::string const& operation, CasedString const& comparator ); + 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 { + 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 + +namespace Catch { +namespace Matchers { + + namespace Vector { + namespace Detail { + template + 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 + bool contains(InputIterator first, InputIterator last, T const& item) { + for (; first != last; ++first) { + if (*first == item) { + return true; + } + } + return false; + } + } + + template + struct ContainsElementMatcher : MatcherBase> { + + ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {} + + bool match(std::vector 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 + struct ContainsMatcher : MatcherBase> { + + ContainsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} + + bool match(std::vector 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 const& m_comparator; + }; + + template + struct EqualsMatcher : MatcherBase> { + + EqualsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} + + bool match(std::vector 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 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 const& m_comparator; + }; + + template + struct UnorderedEqualsMatcher : MatcherBase> { + UnorderedEqualsMatcher(std::vector const& target) : m_target(target) {} + bool match(std::vector const& vec) const override { + // Note: This is a reimplementation of std::is_permutation, + // because I don't want to include 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 const& m_target; + }; + + } // namespace Vector + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + + template + Vector::ContainsMatcher Contains( std::vector const& comparator ) { + return Vector::ContainsMatcher( comparator ); + } + + template + Vector::ContainsElementMatcher VectorContains( T const& comparator ) { + return Vector::ContainsElementMatcher( comparator ); + } + + template + Vector::EqualsMatcher Equals( std::vector const& comparator ) { + return Vector::EqualsMatcher( comparator ); + } + + template + Vector::UnorderedEqualsMatcher UnorderedEquals(std::vector const& target) { + return Vector::UnorderedEqualsMatcher(target); + } + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_vector.h +namespace Catch { + + template + 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; + + void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString ); + + template + auto makeMatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef const& matcherString ) -> MatchExpr { + return MatchExpr( 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(__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 + +namespace Catch { + + namespace Generators { + class GeneratorUntypedBase { + public: + GeneratorUntypedBase() = default; + virtual ~GeneratorUntypedBase(); + // Attempts to move the generator to the next element + // + // Returns true iff the move succeeded (and a valid element + // can be retrieved). + virtual bool next() = 0; + }; + using GeneratorBasePtr = std::unique_ptr; + + } // 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; + }; + +} // namespace Catch + +// end catch_interfaces_generatortracker.h +// start catch_enforce.h + +#include + +namespace Catch { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + template + [[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 +#include +#include + +#include +#include + +namespace Catch { + +class GeneratorException : public std::exception { + const char* const m_msg = ""; + +public: + GeneratorException(const char* msg): + m_msg(msg) + {} + + const char* what() const noexcept override final; +}; + +namespace Generators { + + // !TBD move this into its own location? + namespace pf{ + template + std::unique_ptr make_unique( Args&&... args ) { + return std::unique_ptr(new T(std::forward(args)...)); + } + } + + template + struct IGenerator : GeneratorUntypedBase { + virtual ~IGenerator() = default; + + // Returns the current element of the generator + // + // \Precondition The generator is either freshly constructed, + // or the last call to `next()` returned true + virtual T const& get() const = 0; + using type = T; + }; + + template + class SingleValueGenerator final : public IGenerator { + T m_value; + public: + SingleValueGenerator(T const& value) : m_value( value ) {} + SingleValueGenerator(T&& value) : m_value(std::move(value)) {} + + T const& get() const override { + return m_value; + } + bool next() override { + return false; + } + }; + + template + class FixedValuesGenerator final : public IGenerator { + std::vector m_values; + size_t m_idx = 0; + public: + FixedValuesGenerator( std::initializer_list values ) : m_values( values ) {} + + T const& get() const override { + return m_values[m_idx]; + } + bool next() override { + ++m_idx; + return m_idx < m_values.size(); + } + }; + + template + class GeneratorWrapper final { + std::unique_ptr> m_generator; + public: + GeneratorWrapper(std::unique_ptr> generator): + m_generator(std::move(generator)) + {} + T const& get() const { + return m_generator->get(); + } + bool next() { + return m_generator->next(); + } + }; + + template + GeneratorWrapper value(T&& value) { + return GeneratorWrapper(pf::make_unique>(std::forward(value))); + } + template + GeneratorWrapper values(std::initializer_list values) { + return GeneratorWrapper(pf::make_unique>(values)); + } + + template + class Generators : public IGenerator { + std::vector> m_generators; + size_t m_current = 0; + + void populate(GeneratorWrapper&& generator) { + m_generators.emplace_back(std::move(generator)); + } + void populate(T&& val) { + m_generators.emplace_back(value(std::move(val))); + } + template + void populate(U&& val) { + populate(T(std::move(val))); + } + template + void populate(U&& valueOrGenerator, Gs... moreGenerators) { + populate(std::forward(valueOrGenerator)); + populate(std::forward(moreGenerators)...); + } + + public: + template + Generators(Gs... moreGenerators) { + m_generators.reserve(sizeof...(Gs)); + populate(std::forward(moreGenerators)...); + } + + T const& get() const override { + return m_generators[m_current].get(); + } + + bool next() override { + if (m_current >= m_generators.size()) { + return false; + } + const bool current_status = m_generators[m_current].next(); + if (!current_status) { + ++m_current; + } + return m_current < m_generators.size(); + } + }; + + template + GeneratorWrapper> table( std::initializer_list::type...>> tuples ) { + return values>( tuples ); + } + + // Tag type to signal that a generator sequence should convert arguments to a specific type + template + struct as {}; + + template + auto makeGenerators( GeneratorWrapper&& generator, Gs... moreGenerators ) -> Generators { + return Generators(std::move(generator), std::forward(moreGenerators)...); + } + template + auto makeGenerators( GeneratorWrapper&& generator ) -> Generators { + return Generators(std::move(generator)); + } + template + auto makeGenerators( T&& val, Gs... moreGenerators ) -> Generators { + return makeGenerators( value( std::forward( val ) ), std::forward( moreGenerators )... ); + } + template + auto makeGenerators( as, U&& val, Gs... moreGenerators ) -> Generators { + return makeGenerators( value( T( std::forward( val ) ) ), std::forward( moreGenerators )... ); + } + + template + class TakeGenerator : public IGenerator { + GeneratorWrapper m_generator; + size_t m_returned = 0; + size_t m_target; + public: + TakeGenerator(size_t target, GeneratorWrapper&& generator): + m_generator(std::move(generator)), + m_target(target) + { + assert(target != 0 && "Empty generators are not allowed"); + } + T const& get() const override { + return m_generator.get(); + } + bool next() override { + ++m_returned; + if (m_returned >= m_target) { + return false; + } + + const auto success = m_generator.next(); + // If the underlying generator does not contain enough values + // then we cut short as well + if (!success) { + m_returned = m_target; + } + return success; + } + }; + + template + GeneratorWrapper take(size_t target, GeneratorWrapper&& generator) { + return GeneratorWrapper(pf::make_unique>(target, std::move(generator))); + } + + template + class FilterGenerator : public IGenerator { + GeneratorWrapper m_generator; + Predicate m_predicate; + public: + template + FilterGenerator(P&& pred, GeneratorWrapper&& generator): + m_generator(std::move(generator)), + m_predicate(std::forward

(pred)) + { + if (!m_predicate(m_generator.get())) { + // It might happen that there are no values that pass the + // filter. In that case we throw an exception. + auto has_initial_value = next(); + if (!has_initial_value) { + Catch::throw_exception(GeneratorException("No valid value found in filtered generator")); + } + } + } + + T const& get() const override { + return m_generator.get(); + } + + bool next() override { + bool success = m_generator.next(); + if (!success) { + return false; + } + while (!m_predicate(m_generator.get()) && (success = m_generator.next()) == true); + return success; + } + }; + + template + GeneratorWrapper filter(Predicate&& pred, GeneratorWrapper&& generator) { + return GeneratorWrapper(std::unique_ptr>(pf::make_unique>(std::forward(pred), std::move(generator)))); + } + + template + class RepeatGenerator : public IGenerator { + GeneratorWrapper m_generator; + mutable std::vector m_returned; + size_t m_target_repeats; + size_t m_current_repeat = 0; + size_t m_repeat_index = 0; + public: + RepeatGenerator(size_t repeats, GeneratorWrapper&& generator): + m_generator(std::move(generator)), + m_target_repeats(repeats) + { + assert(m_target_repeats > 0 && "Repeat generator must repeat at least once"); + } + + T const& get() const override { + if (m_current_repeat == 0) { + m_returned.push_back(m_generator.get()); + return m_returned.back(); + } + return m_returned[m_repeat_index]; + } + + bool next() override { + // There are 2 basic cases: + // 1) We are still reading the generator + // 2) We are reading our own cache + + // In the first case, we need to poke the underlying generator. + // If it happily moves, we are left in that state, otherwise it is time to start reading from our cache + if (m_current_repeat == 0) { + const auto success = m_generator.next(); + if (!success) { + ++m_current_repeat; + } + return m_current_repeat < m_target_repeats; + } + + // In the second case, we need to move indices forward and check that we haven't run up against the end + ++m_repeat_index; + if (m_repeat_index == m_returned.size()) { + m_repeat_index = 0; + ++m_current_repeat; + } + return m_current_repeat < m_target_repeats; + } + }; + + template + GeneratorWrapper repeat(size_t repeats, GeneratorWrapper&& generator) { + return GeneratorWrapper(pf::make_unique>(repeats, std::move(generator))); + } + + template + class MapGenerator : public IGenerator { + // TBD: provide static assert for mapping function, for friendly error message + GeneratorWrapper m_generator; + Func m_function; + // To avoid returning dangling reference, we have to save the values + T m_cache; + public: + template + MapGenerator(F2&& function, GeneratorWrapper&& generator) : + m_generator(std::move(generator)), + m_function(std::forward(function)), + m_cache(m_function(m_generator.get())) + {} + + T const& get() const override { + return m_cache; + } + bool next() override { + const auto success = m_generator.next(); + if (success) { + m_cache = m_function(m_generator.get()); + } + return success; + } + }; + + template + GeneratorWrapper map(Func&& function, GeneratorWrapper&& generator) { + return GeneratorWrapper( + pf::make_unique>(std::forward(function), std::move(generator)) + ); + } + template + GeneratorWrapper map(Func&& function, GeneratorWrapper&& generator) { + return GeneratorWrapper( + pf::make_unique>(std::forward(function), std::move(generator)) + ); + } + + auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker&; + + template + // 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().get()) { + using UnderlyingType = typename decltype(generatorExpression())::type; + + IGeneratorTracker& tracker = acquireGeneratorTracker( lineInfo ); + if (!tracker.hasGenerator()) { + tracker.setGenerator(pf::make_unique>(generatorExpression())); + } + + auto const& generator = static_cast const&>( *tracker.getGenerator() ); + return generator.get(); + } + +} // 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 +#include +#include + +#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 const& _tags, + SourceLineInfo const& _lineInfo ); + + friend void setTags( TestCaseInfo& testCaseInfo, std::vector 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 tags; + std::vector 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 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 + +#include + +// 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{ + 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 +#include +#include + +namespace Catch { + + class TestSpec { + struct Pattern { + virtual ~Pattern(); + virtual bool matches( TestCaseInfo const& testCase ) const = 0; + }; + using PatternPtr = std::shared_ptr; + + 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 m_patterns; + + bool matches( TestCaseInfo const& testCase ) const; + }; + + public: + bool hasFilters() const; + bool matches( TestCaseInfo const& testCase ) const; + + private: + std::vector 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 + +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 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 + 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( token ); + if( m_exclusion ) + pattern = std::make_shared( 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 +#include +#include +#include + +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 const& getSectionsToRun() const = 0; + virtual Verbosity verbosity() const = 0; + }; + + using IConfigPtr = std::shared_ptr; +} + +// end catch_interfaces_config.h +// Libstdc++ doesn't like incomplete classes for unique_ptr + +#include +#include +#include + +#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 testsOrTags; + std::vector 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 const& getTestsOrTags() const; + std::vector 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 m_stream; + TestSpec m_testSpec; + bool m_hasTestFilters = false; + }; + +} // end namespace Catch + +// end catch_config.hpp +// start catch_assertionresult.h + +#include + +namespace Catch { + + struct AssertionResultData + { + AssertionResultData() = delete; + + AssertionResultData( ResultWas::OfType _resultType, LazyExpression const& _lazyExpression ); + + std::string message; + mutable std::string reconstructedExpression; + LazyExpression lazyExpression; + ResultWas::OfType resultType; + + std::string reconstructExpression() const; + }; + + class AssertionResult { + public: + AssertionResult() = delete; + AssertionResult( AssertionInfo const& info, AssertionResultData const& data ); + + bool isOk() const; + bool succeeded() const; + ResultWas::OfType getResultType() const; + bool hasExpression() const; + bool hasMessage() const; + std::string getExpression() const; + std::string getExpressionInMacro() const; + bool hasExpandedExpression() const; + std::string getExpandedExpression() const; + 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 + 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 +#include +#include +#include +#include + +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 + struct LazyStat : Option { + LazyStat& operator=( T const& _value ) { + Option::operator=( _value ); + used = false; + return *this; + } + void reset() { + Option::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 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 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 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; + + struct IReporterFactory { + virtual ~IReporterFactory(); + virtual IStreamingReporterPtr create( ReporterConfig const& config ) const = 0; + virtual std::string getDescription() const = 0; + }; + using IReporterFactoryPtr = std::shared_ptr; + + struct IReporterRegistry { + using FactoryMap = std::map; + using Listeners = std::vector; + + 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 +#include +#include +#include +#include +#include +#include + +namespace Catch { + void prepareExpandedExpression(AssertionResult& result); + + // Returns double formatted as %.3f (format expected on output) + std::string getFormattedDuration( double duration ); + + template + 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 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 currentTestRunInfo; + LazyStat currentGroupInfo; + LazyStat currentTestCaseInfo; + + std::vector m_sectionStack; + ReporterPreferences m_reporterPrefs; + }; + + template + struct CumulativeReporterBase : IStreamingReporter { + template + struct Node { + explicit Node( T const& _value ) : value( _value ) {} + virtual ~Node() {} + + using ChildNodes = std::vector>; + 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 const& other) const { + return operator==(*other); + } + + SectionStats stats; + using ChildSections = std::vector>; + using Assertions = std::vector; + 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 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; + using TestGroupNode = Node; + using TestRunNode = Node; + + 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 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 node; + if( m_sectionStack.empty() ) { + if( !m_rootSection ) + m_rootSection = std::make_shared( 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( 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( 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(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(testGroupStats); + node->children.swap(m_testCases); + m_testGroups.push_back(node); + } + void testRunEnded(TestRunStats const& testRunStats) override { + auto node = std::make_shared(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 m_assertions; + std::vector>> m_sections; + std::vector> m_testCases; + std::vector> m_testGroups; + + std::vector> m_testRuns; + + std::shared_ptr m_rootSection; + std::shared_ptr m_deepestSection; + std::vector> m_sectionStack; + ReporterPreferences m_reporterPrefs; + }; + + template + 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( ReporterConfig const& _config ); + + static std::set 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 + class ReporterRegistrar { + + class ReporterFactory : public IReporterFactory { + + virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override { + return std::unique_ptr( 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() ); + } + }; + + template + class ListenerRegistrar { + + class ListenerFactory : public IReporterFactory { + + virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override { + return std::unique_ptr( new T( config ) ); + } + virtual std::string getDescription() const override { + return std::string(); + } + }; + + public: + + ListenerRegistrar() { + getMutableRegistryHub().registerListener( std::make_shared() ); + } + }; +} + +#if !defined(CATCH_CONFIG_DISABLE) + +#define CATCH_REGISTER_REPORTER( name, reporterType ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::ReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS + +#define CATCH_REGISTER_LISTENER( listenerType ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::ListenerRegistrar 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 { + + 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 { + std::unique_ptr 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 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 + +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 + 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 + 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 m_tags; + std::string m_indent; + std::ostream& m_os; + }; + +} + +// end catch_xmlwriter.h +namespace Catch { + + class JunitReporter : public CumulativeReporterBase { + 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 { + 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 +#include +#include + +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; + + 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 isGeneratorTracker() 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; + 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 isGeneratorTracker() const override; + + void open(); + + void close() override; + void fail() override; + void markAsNeedingAnotherRun() override; + + private: + void moveToParent(); + void moveToThis(); + }; + + class SectionTracker : public TrackerBase { + std::vector m_filters; + public: + SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); + + bool isSectionTracker() const override; + + bool isComplete() const override; + + static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ); + + void tryOpen(); + + void addInitialFilters( std::vector const& filters ); + void addNextFilters( std::vector const& filters ); + }; + +} // namespace TestCaseTracking + +using TestCaseTracking::ITracker; +using TestCaseTracking::TrackerContext; +using TestCaseTracking::SectionTracker; + +} // 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 +#include + +namespace { + +// Performs equivalent check of std::fabs(lhs - rhs) <= margin +// But without the subtraction to allow for INFINITY in comparison +bool marginComparison(double lhs, double rhs, double margin) { + return (lhs + margin >= rhs) && (rhs + margin >= lhs); +} + +} + +namespace Catch { +namespace Detail { + + Approx::Approx ( double value ) + : m_epsilon( std::numeric_limits::epsilon()*100 ), + m_margin( 0.0 ), + m_scale( 0.0 ), + m_value( value ) + {} + + Approx Approx::custom() { + return Approx( 0 ); + } + + Approx Approx::operator-() const { + auto temp(*this); + temp.m_value = -temp.m_value; + return temp; + } + + std::string Approx::toString() const { + ReusableStringStream rss; + rss << "Approx( " << ::Catch::Detail::stringify( m_value ) << " )"; + return rss.str(); + } + + bool Approx::equalityComparisonImpl(const double other) const { + // First try with fixed margin, then compute margin based on epsilon, scale and Approx's value + // Thanks to Richard Harris for his help refining the scaled margin value + return marginComparison(m_value, other, m_margin) || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(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::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 + +namespace Catch { + + struct IResultCapture; + struct IRunner; + struct IConfig; + struct IMutableContext; + + using IConfigPtr = std::shared_ptr; + + 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 + + #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 + #define CATCH_BREAK_INTO_DEBUGGER() []{}() +#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 +#else +#include +#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 + +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 + +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 = nullptr; + Option m_lastResult; + + IConfigPtr m_config; + Totals m_totals; + IStreamingReporterPtr m_reporter; + std::vector m_messages; + AssertionInfo m_lastAssertionInfo; + std::vector m_unfinishedSections; + std::vector 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; + + // 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 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() && __cplusplus >= 201703L +#include +#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 +#include +#include +#include + +#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 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 m_columns; + +public: + + class iterator { + friend Columns; + struct EndTag {}; + + std::vector const& m_columns; + std::vector 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 +#include +#include +#include + +#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 + struct UnaryLambdaTraits : UnaryLambdaTraits {}; + + template + struct UnaryLambdaTraits { + static const bool isValid = false; + }; + + template + struct UnaryLambdaTraits { + static const bool isValid = true; + using ArgType = typename std::remove_const::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 m_args; + + public: + Args( int argc, char const* const* argv ) + : m_exeName(argv[0]), + m_args(argv + 1, argv + argc) {} + + Args( std::initializer_list 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::const_iterator; + Iterator it; + Iterator itEnd; + std::vector 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 + 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 : public ResultBase { + protected: + using ResultBase::ResultBase; + }; + + template + class BasicResult : public ResultValueBase { + public: + template + explicit BasicResult( BasicResult const &other ) + : ResultValueBase( other.type() ), + m_errorMessage( other.errorMessage() ) + { + assert( type() != ResultBase::Ok ); + } + + template + 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(type), + m_errorMessage(message) + { + assert( m_type != ResultBase::Ok ); + } + + using ResultValueBase::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; + using ParserResult = BasicResult; + using InternalParseResult = BasicResult; + + struct HelpColumns { + std::string left; + std::string right; + }; + + template + 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( ::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 + inline auto convertInto( std::string const &source, CLARA_CONFIG_OPTIONAL_TYPE& 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 + 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 + struct BoundValueRef> : BoundValueRefBase { + std::vector &m_ref; + + explicit BoundValueRef( std::vector &ref ) : m_ref( ref ) {} + + auto isContainer() const -> bool override { return true; } + + auto setValue( std::string const &arg ) -> ParserResult override { + T temp; + auto result = convertInto( arg, temp ); + if( result ) + m_ref.push_back( temp ); + return result; + } + }; + + struct BoundFlagRef : BoundFlagRefBase { + bool &m_ref; + + explicit BoundFlagRef( bool &ref ) : m_ref( ref ) {} + + auto setFlag( bool flag ) -> ParserResult override { + m_ref = flag; + return ParserResult::ok( ParseResultType::Matched ); + } + }; + + template + struct LambdaInvoker { + static_assert( std::is_same::value, "Lambda must return void or clara::ParserResult" ); + + template + static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { + return lambda( arg ); + } + }; + + template<> + struct LambdaInvoker { + template + static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { + lambda( arg ); + return ParserResult::ok( ParseResultType::Matched ); + } + }; + + template + inline auto invokeLambda( L const &lambda, std::string const &arg ) -> ParserResult { + ArgType temp{}; + auto result = convertInto( arg, temp ); + return !result + ? result + : LambdaInvoker::ReturnType>::invoke( lambda, temp ); + } + + template + struct BoundLambda : BoundValueRefBase { + L m_lambda; + + static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); + explicit BoundLambda( L const &lambda ) : m_lambda( lambda ) {} + + auto setValue( std::string const &arg ) -> ParserResult override { + return invokeLambda::ArgType>( m_lambda, arg ); + } + }; + + template + struct BoundFlagLambda : BoundFlagRefBase { + L m_lambda; + + static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); + static_assert( std::is_same::ArgType, bool>::value, "flags must be boolean" ); + + explicit BoundFlagLambda( L const &lambda ) : m_lambda( lambda ) {} + + auto setFlag( bool flag ) -> ParserResult override { + return LambdaInvoker::ReturnType>::invoke( m_lambda, flag ); + } + }; + + enum class Optionality { Optional, Required }; + + 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 + class ComposableParserImpl : public ParserBase { + public: + template + auto operator|( T const &other ) const -> Parser; + + template + auto operator+( T const &other ) const -> Parser; + }; + + // Common code and state for Args and Opts + template + class ParserRefImpl : public ComposableParserImpl { + protected: + Optionality m_optionality = Optionality::Optional; + std::shared_ptr m_ref; + std::string m_hint; + std::string m_description; + + explicit ParserRefImpl( std::shared_ptr const &ref ) : m_ref( ref ) {} + + public: + template + ParserRefImpl( T &ref, std::string const &hint ) + : m_ref( std::make_shared>( ref ) ), + m_hint( hint ) + {} + + template + ParserRefImpl( LambdaT const &ref, std::string const &hint ) + : m_ref( std::make_shared>( ref ) ), + m_hint(hint) + {} + + auto operator()( std::string const &description ) -> DerivedT & { + m_description = description; + return static_cast( *this ); + } + + auto optional() -> DerivedT & { + m_optionality = Optionality::Optional; + return static_cast( *this ); + }; + + auto required() -> DerivedT & { + m_optionality = Optionality::Required; + return static_cast( *this ); + }; + + auto isOptional() const -> bool { + return m_optionality == Optionality::Optional; + } + + auto cardinality() const -> size_t override { + if( m_ref->isContainer() ) + return 0; + else + return 1; + } + + auto hint() const -> std::string { return m_hint; } + }; + + class ExeName : public ComposableParserImpl { + std::shared_ptr m_name; + std::shared_ptr m_ref; + + template + static auto makeRef(LambdaT const &lambda) -> std::shared_ptr { + return std::make_shared>( lambda) ; + } + + public: + ExeName() : m_name( std::make_shared( "" ) ) {} + + explicit ExeName( std::string &ref ) : ExeName() { + m_ref = std::make_shared>( ref ); + } + + template + explicit ExeName( LambdaT const& lambda ) : ExeName() { + m_ref = std::make_shared>( lambda ); + } + + // The exe name is not parsed out of the normal tokens, but is handled specially + 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 { + 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( 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 { + protected: + std::vector m_optNames; + + public: + template + explicit Opt( LambdaT const &ref ) : ParserRefImpl( std::make_shared>( ref ) ) {} + + explicit Opt( bool &ref ) : ParserRefImpl( std::make_shared( ref ) ) {} + + template + Opt( LambdaT const &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} + + template + 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 { + 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( 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( 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( *this ) + ("display usage information") + ["-?"]["-h"]["--help"] + .optional(); + } + }; + + struct Parser : ParserBase { + + mutable ExeName m_exeName; + std::vector m_options; + std::vector 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 + auto operator|( T const &other ) const -> Parser { + return Parser( *this ) |= other; + } + + // Forward deprecated interface with '+' instead of '|' + template + auto operator+=( T const &other ) -> Parser & { return operator|=( other ); } + template + auto operator+( T const &other ) const -> Parser { return operator|( other ); } + + auto getHelpColumns() const -> std::vector { + std::vector 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 + template + auto ComposableParserImpl::operator|( T const &other ) const -> Parser { + return Parser() | static_cast( *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 +#include + +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( 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( 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 +#include + +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 const& Config::getTestsOrTags() const { return m_data.testsOrTags; } + std::vector 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 + +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 + +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 ) { + getCurrentContext().getConfig()->stream() + << '\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 + +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 +# include +# include +# include +# include +# include +# include + +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 + #include + + namespace Catch{ + // The standard POSIX way of detecting a debugger is to attempt to + // ptrace() the process, but this needs to be done from a child and not + // this process itself to still allow attaching to this process later + // if wanted, so is rather heavy. Under Linux we have the PID of the + // "debugger" (which doesn't need to be gdb, of course, it could also + // be strace, for example) in /proc/$PID/status, so just get it from + // there instead. + bool isDebuggerActive(){ + // Libstdc++ has a bug, where std::ifstream sets errno to 0 + // This way our users can properly assert over errno values + ErrnoGuard guard; + std::ifstream in("/proc/self/status"); + for( std::string line; std::getline(in, line); ) { + static const int PREFIX_LEN = 11; + if( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) { + // We're traced if the PID is not 0 and no other PID starts + // with 0 digit, so it's enough to check for just a single + // character. + return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; + } + } + + return false; + } + } // namespace Catch +#elif defined(_MSC_VER) + extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); + namespace Catch { + bool isDebuggerActive() { + return IsDebuggerPresent() != 0; + } + } +#elif defined(__MINGW32__) + extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); + namespace Catch { + bool isDebuggerActive() { + return IsDebuggerPresent() != 0; + } + } +#else + namespace Catch { + bool isDebuggerActive() { return false; } + } +#endif // Platform +// 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 + +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 +#include +#include + +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> 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( 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"; + } + } + + std::string ExceptionTranslatorRegistry::tryTranslators() const { + if (m_translators.empty()) { + std::rethrow_exception(std::current_exception()); + } else { + return m_translators[0]->translate(m_translators.begin() + 1, m_translators.end()); + } + } + +#else // ^^ Exceptions are enabled // Exceptions are disabled vv + std::string ExceptionTranslatorRegistry::translateActiveException() const { + CATCH_INTERNAL_ERROR("Attempted to translate active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); + } + + std::string ExceptionTranslatorRegistry::tryTranslators() const { + CATCH_INTERNAL_ERROR("Attempted to use exception translators under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); + } +#endif + +} +// 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 = ""; + 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 +#include + +namespace Catch { + + struct IConfig; + + std::mt19937& rng(); + void seedRng( IConfig const& config ); + unsigned int rngSeed(); + +} + +// end catch_random_number_generator.h +#include +#include + +namespace Catch { + +IGeneratorTracker::~IGeneratorTracker() {} + +const char* GeneratorException::what() const noexcept { + return m_msg; +} + +namespace Generators { + + GeneratorUntypedBase::~GeneratorUntypedBase() {} + + auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { + return getResultCapture().acquireGeneratorTracker( lineInfo ); + } + +} // 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; + 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 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 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 + +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 + +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 spellings; + std::size_t count = 0; + }; + + std::size_t listTags( Config const& config ); + + std::size_t listReporters(); + + Option 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 +#include +#include + +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 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 tagCounts; + + std::vector 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 list( Config const& config ) { + Option 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 + +namespace Catch { + template + 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 +#include +#include + +namespace Catch { +namespace Matchers { +namespace Floating { +enum class FloatingPointKind : uint8_t { + Float, + Double +}; +} +} +} + +namespace { + +template +struct Converter; + +template <> +struct Converter { + 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 { + 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 +auto convert(T t) -> Converter { + return Converter(t); +} + +template +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(static_cast(matchee), static_cast(m_target), m_ulps); + case FloatingPointKind::Double: + return almostEqualUlps(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 + +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 +#include + +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 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 +#include +#include + +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 +#include +#include +#include +#include + +#if defined(CATCH_CONFIG_NEW_CAPTURE) + #if defined(_MSC_VER) + #include //_dup and _dup2 + #define dup _dup + #define dup2 _dup2 + #define fileno _fileno + #else + #include // dup and dup2 + #endif +#endif + +namespace Catch { + + RedirectedStream::RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream ) + : m_originalStream( originalStream ), + m_redirectionStream( redirectionStream ), + m_prevBuf( m_originalStream.rdbuf() ) + { + m_originalStream.rdbuf( m_redirectionStream.rdbuf() ); + } + + RedirectedStream::~RedirectedStream() { + m_originalStream.rdbuf( m_prevBuf ); + } + + RedirectedStdOut::RedirectedStdOut() : m_cout( Catch::cout(), m_rss.get() ) {} + auto RedirectedStdOut::str() const -> std::string { return m_rss.str(); } + + RedirectedStdErr::RedirectedStdErr() + : m_cerr( Catch::cerr(), m_rss.get() ), + m_clog( Catch::clog(), m_rss.get() ) + {} + auto RedirectedStdErr::str() const -> std::string { return m_rss.str(); } + +#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 + +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 +#include +#include +#include + +namespace Catch { + + class TestCase; + struct IConfig; + + std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ); + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + + void enforceNoDuplicateTestCases( std::vector const& functions ); + + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector const& getAllTestCasesSorted( IConfig const& config ); + + class TestRegistry : public ITestCaseRegistry { + public: + virtual ~TestRegistry() = default; + + virtual void registerTest( TestCase const& testCase ); + + std::vector const& getAllTests() const override; + std::vector const& getAllTestsSorted( IConfig const& config ) const override; + + private: + std::vector m_functions; + mutable RunTests::InWhatOrder m_currentSortOrder = RunTests::InDeclarationOrder; + mutable std::vector 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 + +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 + +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 + +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 m_registry; + }; + +} // end namespace Catch + +// end catch_tag_alias_registry.h +// start catch_startup_exception_registry.h + +#include +#include + +namespace Catch { + + class StartupExceptionRegistry { + public: + void add(std::exception_ptr const& exception) noexcept; + std::vector const& getExceptions() const noexcept; + private: + std::vector 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 + 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; + + 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( static_cast( lhs ) | static_cast( rhs ) ); + } + + bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } + bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; } + +} // end namespace Catch +// end catch_result_type.cpp +// start catch_run_context.cpp + +#include +#include +#include + +namespace Catch { + + namespace Generators { + struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker { + GeneratorBasePtr m_generator; + + GeneratorTracker( TestCaseTracking::NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : TrackerBase( nameAndLocation, ctx, parent ) + {} + ~GeneratorTracker(); + + static GeneratorTracker& acquire( TrackerContext& ctx, TestCaseTracking::NameAndLocation const& nameAndLocation ) { + std::shared_ptr tracker; + + ITracker& currentTracker = ctx.currentTracker(); + if( TestCaseTracking::ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isGeneratorTracker() ); + tracker = std::static_pointer_cast( childTracker ); + } + else { + tracker = std::make_shared( nameAndLocation, ctx, ¤tTracker ); + currentTracker.addChild( tracker ); + } + + if( !ctx.completedCycle() && !tracker->isComplete() ) { + tracker->open(); + } + + return *tracker; + } + + // TrackerBase interface + bool isGeneratorTracker() const override { return true; } + auto hasGenerator() const -> bool override { + return !!m_generator; + } + void close() override { + TrackerBase::close(); + // Generator interface only finds out if it has another item on atual move + if (m_runState == CompletedSuccessfully && m_generator->next()) { + m_children.clear(); + m_runState = Executing; + } + } + + // IGeneratorTracker interface + auto getGenerator() const -> GeneratorBasePtr const& override { + return m_generator; + } + void setGenerator( GeneratorBasePtr&& generator ) override { + m_generator = std::move( generator ); + } + }; + 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(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(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(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 + +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 + 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 m_config; + bool m_startupExceptions = false; + }; + +} // end namespace Catch + +// end catch_session.h +// start catch_version.h + +#include + +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 +#include + +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 const& config) { + if (Catch::getRegistryHub().getReporterRegistry().getListeners().empty()) { + return createReporter(config->getReporterName(), config); + } + + // On older platforms, returning std::unique_ptr + // when the return type is std::unique_ptr + // doesn't compile without a std::move call. However, this causes + // a warning on newer platforms. Thus, we have to work around + // it a bit and downcast the pointer manually. + auto ret = std::unique_ptr(new ListeningReporter); + auto& multi = static_cast(*ret); + 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 ret; + } + + Catch::Totals runTests(std::shared_ptr 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&>(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(std::getchar()); + } + int exitCode = runInternal(); + if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) { + Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << std::endl; + static_cast(std::getchar()); + } + return exitCode; + } + + clara::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( 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 listed = list( config() ) ) + return static_cast( *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(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 + +namespace Catch { + + namespace { + static auto getSingletons() -> std::vector*& { + static std::vector* g_singletons = nullptr; + if( !g_singletons ) + g_singletons = new std::vector(); + return g_singletons; + } + } + + ISingleton::~ISingleton() {} + + 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 const& StartupExceptionRegistry::getExceptions() const noexcept { + return m_exceptions; + } + +} // end namespace Catch +// end catch_startup_exception_registry.cpp +// start catch_stream.cpp + +#include +#include +#include +#include +#include +#include + +namespace Catch { + + Catch::IStream::~IStream() = default; + + namespace detail { namespace { + template + class StreamBufImpl : public std::streambuf { + char data[bufferSize]; + WriterF m_writer; + + public: + StreamBufImpl() { + setp( data, data + sizeof(data) ); + } + + ~StreamBufImpl() noexcept { + StreamBufImpl::sync(); + } + + private: + int overflow( int c ) override { + sync(); + + if( c != EOF ) { + if( pbase() == epptr() ) + m_writer( std::string( 1, static_cast( c ) ) ); + else + sputc( static_cast( c ) ); + } + return 0; + } + + int sync() override { + if( pbase() != pptr() ) { + m_writer( std::string( pbase(), static_cast( pptr() - pbase() ) ) ); + setp( pbase(), epptr() ); + } + return 0; + } + }; + + /////////////////////////////////////////////////////////////////////////// + + struct OutputDebugWriter { + + void operator()( std::string const&str ) { + 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> m_streamBuf; + mutable std::ostream m_os; + public: + DebugOutStream() + : m_streamBuf( new StreamBufImpl() ), + 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> m_streams; + std::vector m_unused; + std::ostringstream m_referenceStream; // Used for copy state/ flags from + + auto add() -> std::size_t { + if( m_unused.empty() ) { + m_streams.push_back( std::unique_ptr( 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::getMutable().add() ), + m_oss( Singleton::getMutable().m_streams[m_index].get() ) + {} + + ReusableStringStream::~ReusableStringStream() { + static_cast( m_oss )->str(""); + m_oss->clear(); + Singleton::getMutable().release( m_index ); + } + + auto ReusableStringStream::str() const -> std::string { + return static_cast( 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 +#include +#include +#include + +namespace Catch { + + namespace { + char toLowerCh(char c) { + return static_cast( 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 +#include +#include + +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(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( 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 + +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 +#include +#include +#include + +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::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(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 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 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.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 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 + +namespace Catch { + + std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { + + std::vector 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 const& functions ) { + std::set 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 filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ) { + std::vector filtered; + filtered.reserve( testCases.size() ); + for( auto const& testCase : testCases ) + if( matchTest( testCase, testSpec, config ) ) + filtered.push_back( testCase ); + return filtered; + } + std::vector 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 const& TestRegistry::getAllTests() const { + return m_functions; + } + std::vector 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 +#include +#include +#include +#include + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +#endif + +namespace Catch { +namespace TestCaseTracking { + + NameAndLocation::NameAndLocation( std::string const& _name, SourceLineInfo const& _location ) + : name( _name ), + location( _location ) + {} + + ITracker::~ITracker() = default; + + TrackerContext& TrackerContext::instance() { + static TrackerContext s_instance; + return s_instance; + } + + ITracker& TrackerContext::startRun() { + m_rootTracker = std::make_shared( 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::isGeneratorTracker() 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( *parent ); + addNextFilters( parentSection.m_filters ); + } + } + + bool SectionTracker::isComplete() const { + bool complete = true; + + if ((m_filters.empty() || m_filters[0] == "") || + std::find(m_filters.begin(), m_filters.end(), + m_nameAndLocation.name) != m_filters.end()) + complete = TrackerBase::isComplete(); + return complete; + + } + + bool SectionTracker::isSectionTracker() const { return true; } + + SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { + std::shared_ptr section; + + ITracker& currentTracker = ctx.currentTracker(); + if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isSectionTracker() ); + section = std::static_pointer_cast( childTracker ); + } + else { + section = std::make_shared( 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 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 const& filters ) { + if( filters.size() > 1 ) + m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() ); + } + +} // namespace TestCaseTracking + +using TestCaseTracking::ITracker; +using TestCaseTracking::TrackerContext; +using TestCaseTracking::SectionTracker; + +} // 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 +#include +#include +#include + +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(); + 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(); + addFilter(); + } + else if( c == '[' ) { + if( subString() == "exclude:" ) + m_exclusion = true; + else + addPattern(); + startNewMode( Tag, ++m_pos ); + } + else if( c == '\\' ) + escape(); + } + else if( m_mode == EscapedName ) + m_mode = Name; + else if( m_mode == QuotedName && c == '"' ) + addPattern(); + else if( m_mode == Tag && c == ']' ) + addPattern(); + } + 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 + +static const uint64_t nanosecondsInSecond = 1000000000; + +namespace Catch { + + auto getCurrentNanosecondsSinceEpoch() -> uint64_t { + return std::chrono::duration_cast( 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 + 1u ); + } + } + + // 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(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 +#include + +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( size ), inc = 1; + if( Endianness::which() == Endianness::Little ) { + i = end-1; + end = inc = -1; + } + + unsigned char const *bytes = static_cast(object); + ReusableStringStream rss; + rss << "0x" << std::setfill('0') << std::hex; + for( ; i != end; i += inc ) + rss << std::setw(2) << static_cast(bytes[i]); + return rss.str(); + } +} + +template +std::string fpToString( T value, int precision ) { + if (Catch::isnan(value)) { + return "nan"; + } + + ReusableStringStream rss; + rss << std::setprecision( precision ) + << std::fixed + << value; + std::string d = rss.str(); + std::size_t i = d.find_last_not_of( '0' ); + if( i != std::string::npos && i != d.size()-1 ) { + if( d[i] == '.' ) + i++; + d = d.substr( 0, i+1 ); + } + return d; +} + +//// ======================================================= //// +// +// Out-of-line defs for full specialization of StringMaker +// +//// ======================================================= //// + +std::string StringMaker::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::convert(std::string_view str) { + return ::Catch::Detail::stringify(std::string{ str }); +} +#endif + +std::string StringMaker::convert(char const* str) { + if (str) { + return ::Catch::Detail::stringify(std::string{ str }); + } else { + return{ "{null string}" }; + } +} +std::string StringMaker::convert(char* str) { + if (str) { + return ::Catch::Detail::stringify(std::string{ str }); + } else { + return{ "{null string}" }; + } +} + +#ifdef CATCH_CONFIG_WCHAR +std::string StringMaker::convert(const std::wstring& wstr) { + std::string s; + s.reserve(wstr.size()); + for (auto c : wstr) { + s += (c <= 0xff) ? static_cast(c) : '?'; + } + return ::Catch::Detail::stringify(s); +} + +# ifdef CATCH_CONFIG_CPP17_STRING_VIEW +std::string StringMaker::convert(std::wstring_view str) { + return StringMaker::convert(std::wstring(str)); +} +# endif + +std::string StringMaker::convert(wchar_t const * str) { + if (str) { + return ::Catch::Detail::stringify(std::wstring{ str }); + } else { + return{ "{null string}" }; + } +} +std::string StringMaker::convert(wchar_t * str) { + if (str) { + return ::Catch::Detail::stringify(std::wstring{ str }); + } else { + return{ "{null string}" }; + } +} +#endif + +std::string StringMaker::convert(int value) { + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(long value) { + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(long long value) { + ReusableStringStream rss; + rss << value; + if (value > Detail::hexThreshold) { + rss << " (0x" << std::hex << value << ')'; + } + return rss.str(); +} + +std::string StringMaker::convert(unsigned int value) { + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(unsigned long value) { + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(unsigned long long value) { + ReusableStringStream rss; + rss << value; + if (value > Detail::hexThreshold) { + rss << " (0x" << std::hex << value << ')'; + } + return rss.str(); +} + +std::string StringMaker::convert(bool b) { + return b ? "true" : "false"; +} + +std::string StringMaker::convert(signed char value) { + if (value == '\r') { + return "'\\r'"; + } else if (value == '\f') { + return "'\\f'"; + } else if (value == '\n') { + return "'\\n'"; + } else if (value == '\t') { + return "'\\t'"; + } else if ('\0' <= value && value < ' ') { + return ::Catch::Detail::stringify(static_cast(value)); + } else { + char chstr[] = "' '"; + chstr[1] = value; + return chstr; + } +} +std::string StringMaker::convert(char c) { + return ::Catch::Detail::stringify(static_cast(c)); +} +std::string StringMaker::convert(unsigned char c) { + return ::Catch::Detail::stringify(static_cast(c)); +} + +std::string StringMaker::convert(std::nullptr_t) { + return "nullptr"; +} + +std::string StringMaker::convert(float value) { + return fpToString(value, 5) + 'f'; +} +std::string StringMaker::convert(double value) { + return fpToString(value, 10); +} + +std::string ratio_string::symbol() { return "a"; } +std::string ratio_string::symbol() { return "f"; } +std::string ratio_string::symbol() { return "p"; } +std::string ratio_string::symbol() { return "n"; } +std::string ratio_string::symbol() { return "u"; } +std::string ratio_string::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 + +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 + +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, 6, 0, "", 0 ); + return version; + } + +} +// end catch_version.cpp +// start catch_wildcard_pattern.cpp + +#include + +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( 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 + +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) { + std::ios_base::fmtflags f(os.flags()); + os << "\\x" + << std::uppercase << std::hex << std::setfill('0') << std::setw(2) + << static_cast(c); + os.flags(f); + } + +} // 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_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 << ""; + m_needsNewline = true; + return *this; + } + + void XmlWriter::writeStylesheetRef( std::string const& url ) { + m_os << "\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 << "\n"; + } + + void XmlWriter::newlineIfNecessary() { + if( m_needsNewline ) { + m_os << std::endl; + m_needsNewline = false; + } + } +} +// end catch_xmlwriter.cpp +// start catch_reporter_bases.cpp + +#include +#include +#include +#include +#include + +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 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::const_iterator itEnd = messages.end(); + const std::size_t N = static_cast(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 messages; + std::vector::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 +#include + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch + // Note that 4062 (not all labels are handled + // and default is missing) is enabled +#endif + +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 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(s_nanosecondsInAMicrosecond); + case Unit::Milliseconds: + return m_inNanoseconds / static_cast(s_nanosecondsInAMillisecond); + case Unit::Seconds: + return m_inNanoseconds / static_cast(s_nanosecondsInASecond); + case Unit::Minutes: + return m_inNanoseconds / static_cast(s_nanosecondsInAMinute); + default: + return static_cast(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 m_columnInfos; + std::ostringstream m_oss; + int m_currentColumn = -1; + bool m_isOpen = false; + +public: + TablePrinter( std::ostream& os, std::vector columnInfos ) + : m_os( os ), + m_columnInfos( std::move( columnInfos ) ) {} + + auto columnInfos() const -> std::vector 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 + 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(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(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( 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 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 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 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 +#include +#include +#include + +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 &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 + +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 ListeningReporter::getSupportedVerbosities() { + return std::set{ }; + } + + 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( 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 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__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_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__ ) ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_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__ ) +#define TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_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__ ) ) +#define TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) ) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_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 ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#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 ) ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#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 ) +#define TEMPLATE_PRODUCT_TEST_CASE( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#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 ) ) +#define TEMPLATE_PRODUCT_TEST_CASE( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ ) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#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/src/Client.cpp b/src/Client.cpp index ade0005..65d1cf8 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -11,32 +11,6 @@ #include #include - -template -void insert_bytes(std::vector &container, const T &value) { - const auto *bytes = reinterpret_cast(&value); - container.insert(container.end(), bytes, bytes + sizeof(T)); -} - -int32_t read_int32(std::vector::iterator &it) { - int32_t val = it[0] | (it[1] << 8) | (it[2] << 16) | (it[3] << 24); - it += sizeof(int32_t); - return val; -} - -bool read_bool(std::vector::iterator &it) { - bool val = it[0]; - it += sizeof(bool); - return val; -} - -std::string read_string(std::vector::iterator &it, size_t len) { - std::string str(it, it + len); - str.erase(std::find(str.begin(), str.end(), '\0'), str.end()); - it += len; - return str; -} - const std::vector Client::list() const { auto write_buffer = std::vector(); int32_t command_no = 0; @@ -65,6 +39,69 @@ const std::vector Client::list() const { return translate_list_message(message); } + +bool Client::addCurrency(const Currency ¤cy) const { + auto write_buffer = std::vector(); + int32_t command_no = 1; + + write_command(write_buffer, command_no); + write_string(write_buffer, currency.get_name()); + write_int32(write_buffer, currency.get_current_rate()); + write_end_of_message(write_buffer); + if (write(sockfd, write_buffer.data(), write_buffer.size()) < 0) { + perror("ERROR writing to socket"); + exit(1); + } + std::vector response = read_response(); + return translate_add_message(response); +} + +bool Client::remove(const Currency ¤cy) const { + auto write_buffer = std::vector(); + int32_t command_no = 2; + + write_command(write_buffer, command_no); + write_string(write_buffer, currency.get_name()); + write_end_of_message(write_buffer); + if (write(sockfd, write_buffer.data(), write_buffer.size()) < 0) { + perror("ERROR writing to socket"); + exit(1); + } + std::vector response = read_response(); + return translate_remove_message(response); +} + +bool Client::addRate(const Currency ¤cy, int32_t new_rate) const { + auto write_buffer = std::vector(); + int32_t command_no = 3; + + write_command(write_buffer, command_no); + write_string(write_buffer, currency.get_name()); + write_int32(write_buffer, new_rate); + write_end_of_message(write_buffer); + if (write(sockfd, write_buffer.data(), write_buffer.size()) < 0) { + perror("ERROR writing to socket"); + exit(1); + } + std::vector response = read_response(); + return translate_add_message(response); +} + +Currency Client::getCurrencyWithHistory(const Currency ¤cy) const { + auto write_buffer = std::vector(); + int32_t command_no = 4; + + write_command(write_buffer, command_no); + write_string(write_buffer, currency.get_name()); + write_end_of_message(write_buffer); + if (write(sockfd, write_buffer.data(), write_buffer.size()) < 0) { + perror("ERROR writing to socket"); + exit(1); + } + std::vector response = read_response(); + return Currency(currency.get_name(), translate_get_currency_history_message(response)); +} + Client::Client(const std::string &hostname, uint16_t portno) : sockfd(socket(AF_INET, SOCK_STREAM, 0)) { if (sockfd < 0) { perror("ERROR opening socket"); @@ -88,7 +125,6 @@ Client::Client(const std::string &hostname, uint16_t portno) : sockfd(socket(AF_ bcopy(server->h_addr, (char *) &server_addr.sin_addr.s_addr, (size_t) server->h_length); server_addr.sin_port = htons(portno); - /* Now connect to the server */ if (connect(sockfd, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) { perror("ERROR connecting"); exit(1); @@ -99,6 +135,30 @@ Client::~Client() { close(sockfd); } +template +void insert_bytes(std::vector &container, const T &value) { + const auto *bytes = reinterpret_cast(&value); + container.insert(container.end(), bytes, bytes + sizeof(T)); +} + +int32_t read_int32(std::vector::iterator &it) { + int32_t val = it[0] | (it[1] << 8) | (it[2] << 16) | (it[3] << 24); + it += sizeof(int32_t); + return val; +} + +bool read_bool(std::vector::iterator &it) { + bool val = it[0]; + it += sizeof(bool); + return val; +} + +std::string read_string(std::vector::iterator &it, size_t len) { + std::string str(it, it + len); + str.erase(std::find(str.begin(), str.end(), '\0'), str.end()); + it += len; + return str; +} void Client::write_end_of_message(std::vector &buffer) const { insert_bytes(buffer, (int8_t) '\\'); @@ -113,6 +173,27 @@ bool Client::is_message_received(const std::vector &message) const { return message.size() >= 2 && message[message.size() - 2] == '\\' && message[message.size() - 1] == 0; } +bool Client::translate_remove_message(std::vector &message) const { + remove_ending_symbols(message); + auto it = message.begin(); + return read_bool(it); +} + +std::vector Client::translate_get_currency_history_message(std::vector &message) const { + remove_ending_symbols(message); + std::vector history; + for (auto it = message.begin(); it < message.end();) { + history.push_back(read_int32(it)); + } + return history; +} + +bool Client::translate_add_message(std::vector &message) const { + remove_ending_symbols(message); + auto it = message.begin(); + return read_bool(it); +} + const std::vector Client::translate_list_message(std::vector &message) const { remove_ending_symbols(message); std::vector currencies; @@ -134,3 +215,31 @@ const std::vector Client::translate_list_message(std::vector & void Client::remove_ending_symbols(std::vector &message) const { message.erase(message.end() - 2, message.end()); } + +void Client::write_string(std::vector &buffer, const std::string ¤cy_name) const { + std::string string_to_write(currency_name); + string_to_write.resize(CURRENCY_NAME_SIZE_IN_LIST, 0); + buffer.insert(buffer.end(), string_to_write.begin(), string_to_write.end()); +} + +void Client::write_int32(std::vector &buffer, int32_t rate) const { + insert_bytes(buffer, rate); +} + +std::vector Client::read_response() const { + bool message_received = false; + auto message = std::vector(); + while (!message_received) { + auto read_buffer = std::vector(BUFFER_INITIAL_LENGTH); + ssize_t bytes_number = read(sockfd, read_buffer.data(), read_buffer.size()); + + if (bytes_number < 0) { + perror("ERROR reading from socket"); + exit(1); + } + read_buffer.resize(static_cast(bytes_number)); + message.insert(message.end(), read_buffer.begin(), read_buffer.end()); + message_received = is_message_received(message); + } + return message; +} diff --git a/src/Currency.cpp b/src/Currency.cpp index d6bdcee..83279ab 100644 --- a/src/Currency.cpp +++ b/src/Currency.cpp @@ -11,9 +11,18 @@ Currency::Currency(std::string name, std::vector rates) : name(std::mov int32_t current = get_current_rate(), previous = get_rate(rates.size() - 2); absolute_change = current - previous; relative_change = static_cast((current * 100.0) / previous ); + } else { + absolute_change = -1; + relative_change = -1; } } +Currency::Currency(std::string name) : name(std::move(name)) { + rates = {}; + absolute_change = -1; + relative_change = -1; +} + Currency::Currency(std::string name, int32_t current_rate) : name(std::move(name)) { rates = {current_rate}; absolute_change = -1; @@ -37,6 +46,10 @@ int32_t Currency::get_rate(size_t i) const { return i < rates.size() ? rates[i] : -1; } +const std::vector& Currency::get_rates() const { + return rates; +} + int32_t Currency::get_absolute_change() const { return absolute_change; } diff --git a/src/CurrencyClientApplication.cpp b/src/CurrencyClientApplication.cpp index 37b27fb..7ecc849 100644 --- a/src/CurrencyClientApplication.cpp +++ b/src/CurrencyClientApplication.cpp @@ -31,8 +31,45 @@ void CurrencyClientApplication::run() { std::cout << currency.get_name() << " " << currency.get_absolute_change() << " " << currency.get_relative_change() << "\n"; } + } else if (command == "addCurrency") { + std::string currency_name; + int32_t currency_rate; + std::cin >> currency_name >> currency_rate; + if (client.addCurrency(Currency(currency_name, currency_rate))) { + std::cout << "Done\n"; + } else { + std::cout << "Currency already exists\n"; + } + } else if (command == "removeCurrency") { + std::string currency_name; + std::cin >> currency_name; + if (client.remove(Currency(currency_name))) { + std::cout << "Done\n"; + } else { + std::cout << "Currency doesn't exist\n"; + } + } else if (command == "addRate") { + std::string currency_name; + int32_t new_rate; + std::cin >> currency_name >> new_rate; + if (client.addRate(Currency(currency_name), new_rate)) { + std::cout << "Done\n"; + } else { + std::cout << "Currency doesn't exist\n"; + } + } else if (command == "getCurrencyHistory") { + std::string currency_name; + std::cin >> currency_name; + Currency currency_with_history = client.getCurrencyWithHistory(Currency(currency_name)); + std::cout << "rates: "; + for (auto rate : currency_with_history.get_rates()) { + std::cout << rate << " "; + } + std::cout << "\n"; } else if (command == "exit") { - toExit = true; + toExit = true; + } else { + std::cout << "UNKNOWN COMMAND\n"; } } } @@ -41,7 +78,7 @@ void CurrencyClientApplication::printUsage() { std::cout << "Commands:\n" << "list - list currencies with rate and rate differences\n" << "addCurrency - adds new currency\n" - << "remove \n" + << "removeCurrency \n" << "addRate - adds new rate for given currency\n" << "getCurrencyHistory - returns rate history of given currency\n" << "exit - to exit client application\n" diff --git a/test/ClientTest.cpp b/test/ClientTest.cpp index 24a5e79..192ac34 100644 --- a/test/ClientTest.cpp +++ b/test/ClientTest.cpp @@ -15,7 +15,8 @@ class ClientTest { uint16_t get_current_port(int sockfd); -void setup_server_returning(const std::vector &return_value, ClientTest &clientTest) { +void setup_server_returning(const std::vector &expected_to_receive, + const std::vector &return_value, ClientTest &clientTest) { int sockfd, newsockfd; unsigned int clilen; char buffer[256]; @@ -48,10 +49,13 @@ void setup_server_returning(const std::vector &return_value, ClientTest } bzero(buffer, 256); - if (read(newsockfd, buffer, 255) < 0) { + ssize_t n_read = read(newsockfd, buffer, 255); + if (n_read < 0) { perror("ERROR reading from socket"); exit(1); } + std::vector received(buffer, buffer + n_read); + REQUIRE(received == expected_to_receive); if (write(newsockfd, return_value.data(), return_value.size()) < 0) { perror("ERROR writing to socket"); exit(1); @@ -70,8 +74,9 @@ uint16_t get_current_port(int sockfd) { TEST_CASE("list empty currencies") { ClientTest clientTest; + std::vector expected_to_receive = {0, 0, 0, 0, '\\', 0}; std::vector server_return = {'\\', 0}; - std::thread server_thread(setup_server_returning, server_return, std::ref(clientTest)); + std::thread server_thread(setup_server_returning, expected_to_receive, server_return, std::ref(clientTest)); while (clientTest.PORTNO == 0) { std::this_thread::sleep_for(std::chrono::seconds(1)); } @@ -83,13 +88,14 @@ TEST_CASE("list empty currencies") { TEST_CASE("list one currency") { ClientTest clientTest; + std::vector expected_to_receive = {0, 0, 0, 0, '\\', 0}; std::vector server_return = {'c', 'u', 'r', 'r', 'e', 'n', 'c', 'y','N', 'a', 'm', 'e', '1', 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 12, 0, 0, 0, '\\', 0}; - std::thread server_thread(setup_server_returning, server_return, std::ref(clientTest)); + std::thread server_thread(setup_server_returning, expected_to_receive, server_return, std::ref(clientTest)); while (clientTest.PORTNO == 0) { std::this_thread::sleep_for(std::chrono::seconds(1)); } @@ -106,6 +112,7 @@ TEST_CASE("list one currency") { TEST_CASE("list two currencies") { ClientTest clientTest; + std::vector expected_to_receive = {0, 0, 0, 0, '\\', 0}; std::vector server_return = {'c', 'u', 'r', 'r', 'e', 'n', 'c', 'y','N', 'a', 'm', 'e', '1', 0, 0, 0, 1, 1, 1, 1, 1, @@ -117,7 +124,7 @@ TEST_CASE("list two currencies") { 0, 0, 0, 0, 0, 0, 0, 0, '\\', 0}; - std::thread server_thread(setup_server_returning, server_return, std::ref(clientTest)); + std::thread server_thread(setup_server_returning, expected_to_receive, server_return, std::ref(clientTest)); while (clientTest.PORTNO == 0) { std::this_thread::sleep_for(std::chrono::seconds(1)); } @@ -130,4 +137,195 @@ TEST_CASE("list two currencies") { Currency("currencyName2end", 1)}; REQUIRE(result == expected); server_thread.join(); +} + +TEST_CASE("list three currencies") { + ClientTest clientTest; + std::vector expected_to_receive = {0, 0, 0, 0, '\\', 0}; + std::vector server_return = {'c', 'u', 'r', 'r', 'e', 'n', 'c', 'y','N', 'a', 'm', 'e', '1', 0, 0, 0, + 1, 1, 1, 1, + 1, + 1, 0, 1, 0, + 12, 0, 0, 0, + 'c', 'u', 'r', 'r', 'e', 'n', 'c', 'y','N', 'a', 'm', 'e', '2', 'e', 'n', 'd', + 1, 0, 0, 0, + 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 'c', 'u', 'r', 'r', 'e', 'n', 'c', 'y','N', 'a', 'm', 'e', '3', 0, 0, 0, + 1, 0, 1, 0, + 1, + 1, 0, 0, 0, + 1, 0, 0, 0, + '\\', 0}; + std::thread server_thread(setup_server_returning, expected_to_receive, server_return, std::ref(clientTest)); + while (clientTest.PORTNO == 0) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + Client client = Client(clientTest.HOST, clientTest.PORTNO); + std::vector result = client.list(); + std::vector expected = {Currency("currencyName1", + (1 << 0) + (1 << 8) + (1 << 16) + (1 << 24), + (1 << 0) + (1 << 16), + 12), + Currency("currencyName2end", 1), + Currency("currencyName3", + (1 << 0) + (1 << 16), + (1 << 0), + (1 << 0))}; + REQUIRE(result == expected); + server_thread.join(); +} + +TEST_CASE("add non-existing currency") { + ClientTest clientTest; + Currency currency_to_add("currencyName", (3 << 0) + (44 << 8) + (22 << 24)); + std::vector expected_to_receive = {1, 0, 0, 0, + 'c', 'u', 'r', 'r', 'e', 'n', 'c', 'y', 'N', 'a', 'm', 'e', 0, 0, 0, 0, + 3, 44, 0, 22, + '\\', 0}; + std::vector server_return = {1, '\\', 0}; + std::thread server_thread(setup_server_returning, expected_to_receive, server_return, std::ref(clientTest)); + while (clientTest.PORTNO == 0) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + Client client = Client(clientTest.HOST, clientTest.PORTNO); + REQUIRE(client.addCurrency(currency_to_add)); + server_thread.join(); +} + +TEST_CASE("add existing currency") { + ClientTest clientTest; + Currency currency_to_add("currencyName", (3 << 0) + (44 << 8) + (22 << 24)); + std::vector expected_to_receive = {1, 0, 0, 0, + 'c', 'u', 'r', 'r', 'e', 'n', 'c', 'y', 'N', 'a', 'm', 'e', 0, 0, 0, 0, + 3, 44, 0, 22, + '\\', 0}; + std::vector server_return = {0, '\\', 0}; + std::thread server_thread(setup_server_returning, expected_to_receive, server_return, std::ref(clientTest)); + while (clientTest.PORTNO == 0) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + Client client = Client(clientTest.HOST, clientTest.PORTNO); + REQUIRE(!client.addCurrency(currency_to_add)); + server_thread.join(); +} + +TEST_CASE("remove non-existing currency") { + ClientTest clientTest; + Currency currency_to_remove("currencyName"); + std::vector expected_to_receive = {2, 0, 0, 0, + 'c', 'u', 'r', 'r', 'e', 'n', 'c', 'y', 'N', 'a', 'm', 'e', 0, 0, 0, 0, + '\\', 0}; + std::vector server_return = {0, '\\', 0}; + std::thread server_thread(setup_server_returning, expected_to_receive, server_return, std::ref(clientTest)); + while (clientTest.PORTNO == 0) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + Client client = Client(clientTest.HOST, clientTest.PORTNO); + REQUIRE(!client.remove(currency_to_remove)); + server_thread.join(); +} + +TEST_CASE("remove existing currency") { + ClientTest clientTest; + Currency currency_to_remove("currencyName"); + std::vector expected_to_receive = {2, 0, 0, 0, + 'c', 'u', 'r', 'r', 'e', 'n', 'c', 'y', 'N', 'a', 'm', 'e', 0, 0, 0, 0, + '\\', 0}; + std::vector server_return = {1, '\\', 0}; + std::thread server_thread(setup_server_returning, expected_to_receive, server_return, std::ref(clientTest)); + while (clientTest.PORTNO == 0) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + Client client = Client(clientTest.HOST, clientTest.PORTNO); + REQUIRE(client.remove(currency_to_remove)); + server_thread.join(); +} + +TEST_CASE("add rate to non-existing currency") { + ClientTest clientTest; + Currency currency_to_add("currencyName"); + int32_t new_rate = (3 << 0) + (44 << 8) + (11 << 16) + (22 << 24); + std::vector expected_to_receive = {3, 0, 0, 0, + 'c', 'u', 'r', 'r', 'e', 'n', 'c', 'y', 'N', 'a', 'm', 'e', 0, 0, 0, 0, + 3, 44, 11, 22, + '\\', 0}; + std::vector server_return = {0, '\\', 0}; + std::thread server_thread(setup_server_returning, expected_to_receive, server_return, std::ref(clientTest)); + while (clientTest.PORTNO == 0) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + Client client = Client(clientTest.HOST, clientTest.PORTNO); + REQUIRE(!client.addRate(currency_to_add, new_rate)); + server_thread.join(); +} + +TEST_CASE("add rate to existing currency") { + ClientTest clientTest; + Currency currency_to_add("currencyNameExit"); + int32_t new_rate = (3 << 0) + (44 << 8) + (11 << 16) + (22 << 24); + std::vector expected_to_receive = {3, 0, 0, 0, + 'c', 'u', 'r', 'r', 'e', 'n', 'c', 'y', 'N', 'a', 'm', 'e', 'E', 'x', 'i', 't', + 3, 44, 11, 22, + '\\', 0}; + std::vector server_return = {1, '\\', 0}; + std::thread server_thread(setup_server_returning, expected_to_receive, server_return, std::ref(clientTest)); + while (clientTest.PORTNO == 0) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + Client client = Client(clientTest.HOST, clientTest.PORTNO); + REQUIRE(client.addRate(currency_to_add, new_rate)); + server_thread.join(); +} + +TEST_CASE("get empty history for non-existing currency") { + ClientTest clientTest; + Currency currency_to_get_history("currencyNameExit"); + std::vector expected_to_receive = {4, 0, 0, 0, + 'c', 'u', 'r', 'r', 'e', 'n', 'c', 'y', 'N', 'a', 'm', 'e', 'E', 'x', 'i', 't', + '\\', 0}; + std::vector server_return = {'\\', 0}; + std::thread server_thread(setup_server_returning, expected_to_receive, server_return, std::ref(clientTest)); + while (clientTest.PORTNO == 0) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + Client client = Client(clientTest.HOST, clientTest.PORTNO); + auto result = client.getCurrencyWithHistory(currency_to_get_history); + REQUIRE(result == currency_to_get_history); + server_thread.join(); +} + +TEST_CASE("get one rate history for currency") { + ClientTest clientTest; + Currency currency_to_get_history("currencyName", (33 << 0) + (3 << 8)); + std::vector expected_to_receive = {4, 0, 0, 0, + 'c', 'u', 'r', 'r', 'e', 'n', 'c', 'y', 'N', 'a', 'm', 'e', 0, 0, 0, 0, + '\\', 0}; + std::vector server_return = {33, 3, 0, 0, '\\', 0}; + std::thread server_thread(setup_server_returning, expected_to_receive, server_return, std::ref(clientTest)); + while (clientTest.PORTNO == 0) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + Client client = Client(clientTest.HOST, clientTest.PORTNO); + auto result = client.getCurrencyWithHistory(currency_to_get_history); + REQUIRE(result == currency_to_get_history); + server_thread.join(); +} + +TEST_CASE("get two rates history for currency") { + ClientTest clientTest; + Currency currency_to_get_history("currencyName", {(33 << 0) + (3 << 8), 0}); + std::vector expected_to_receive = {4, 0, 0, 0, + 'c', 'u', 'r', 'r', 'e', 'n', 'c', 'y', 'N', 'a', 'm', 'e', 0, 0, 0, 0, + '\\', 0}; + std::vector server_return = {33, 3, 0, 0, 0, 0, 0, 0, '\\', 0}; + std::thread server_thread(setup_server_returning, expected_to_receive, server_return, std::ref(clientTest)); + while (clientTest.PORTNO == 0) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + Client client = Client(clientTest.HOST, clientTest.PORTNO); + auto result = client.getCurrencyWithHistory(currency_to_get_history); + REQUIRE(result == currency_to_get_history); + server_thread.join(); } \ No newline at end of file From 639a405f2681a08b057951bffa6ce510099b5b31 Mon Sep 17 00:00:00 2001 From: RamSaw Date: Fri, 8 Feb 2019 14:08:26 +0300 Subject: [PATCH 08/17] Added closing sockets in tests. --- test/ClientTest.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/ClientTest.cpp b/test/ClientTest.cpp index 192ac34..0f5f980 100644 --- a/test/ClientTest.cpp +++ b/test/ClientTest.cpp @@ -60,6 +60,8 @@ void setup_server_returning(const std::vector &expected_to_receive, perror("ERROR writing to socket"); exit(1); } + close(newsockfd); + close(sockfd); } uint16_t get_current_port(int sockfd) { From 2ee825b7cb05a3aa525678530de3653d67ab567d Mon Sep 17 00:00:00 2001 From: RamSaw Date: Fri, 8 Feb 2019 14:28:30 +0300 Subject: [PATCH 09/17] Added 30 seconds wait for response, if more than program stops. --- include/Client.h | 1 + src/Client.cpp | 26 ++++++++++---------------- src/CurrencyClientApplication.cpp | 2 +- 3 files changed, 12 insertions(+), 17 deletions(-) diff --git a/include/Client.h b/include/Client.h index 4de8b19..bb86a74 100644 --- a/include/Client.h +++ b/include/Client.h @@ -28,6 +28,7 @@ class Client { const int sockfd; static const size_t BUFFER_INITIAL_LENGTH = 256; static const size_t CURRENCY_NAME_SIZE_IN_LIST = 16; + static const int SECONDS_TO_WAIT_FOR_RESPONSE = 30; void write_end_of_message(std::vector &buffer) const; diff --git a/src/Client.cpp b/src/Client.cpp index 65d1cf8..3a7a167 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -17,26 +17,14 @@ const std::vector Client::list() const { write_command(write_buffer, command_no); write_end_of_message(write_buffer); - if (write(sockfd, write_buffer.data(), write_buffer.size()) < 0) { + auto written = write(sockfd, write_buffer.data(), write_buffer.size()); + if (written < 0) { perror("ERROR writing to socket"); exit(1); } - bool message_received = false; - auto message = std::vector(); - while (!message_received) { - auto read_buffer = std::vector(BUFFER_INITIAL_LENGTH); - ssize_t bytes_number = read(sockfd, read_buffer.data(), read_buffer.size()); - - if (bytes_number < 0) { - perror("ERROR reading from socket"); - exit(1); - } - read_buffer.resize(static_cast(bytes_number)); - message.insert(message.end(), read_buffer.begin(), read_buffer.end()); - message_received = is_message_received(message); - } - return translate_list_message(message); + std::vector response = read_response(); + return translate_list_message(response); } @@ -229,7 +217,13 @@ void Client::write_int32(std::vector &buffer, int32_t rate) const { std::vector Client::read_response() const { bool message_received = false; auto message = std::vector(); + + const clock_t begin_time = clock(); while (!message_received) { + if ((float(clock() - begin_time) / CLOCKS_PER_SEC) > SECONDS_TO_WAIT_FOR_RESPONSE) { + perror("Connection is to slow, please try later"); + exit(1); + } auto read_buffer = std::vector(BUFFER_INITIAL_LENGTH); ssize_t bytes_number = read(sockfd, read_buffer.data(), read_buffer.size()); diff --git a/src/CurrencyClientApplication.cpp b/src/CurrencyClientApplication.cpp index 7ecc849..cd9c58f 100644 --- a/src/CurrencyClientApplication.cpp +++ b/src/CurrencyClientApplication.cpp @@ -61,7 +61,7 @@ void CurrencyClientApplication::run() { std::string currency_name; std::cin >> currency_name; Currency currency_with_history = client.getCurrencyWithHistory(Currency(currency_name)); - std::cout << "rates: "; + std::cout << "RATES: "; for (auto rate : currency_with_history.get_rates()) { std::cout << rate << " "; } From 3ab5a1f7ec2124c232aa575a17b2827946f8b4d7 Mon Sep 17 00:00:00 2001 From: RamSaw Date: Fri, 8 Feb 2019 15:18:44 +0300 Subject: [PATCH 10/17] Added closed connection by server processing and added correct write by pieces. --- include/Client.h | 3 ++- src/Client.cpp | 53 ++++++++++++++++++++++++------------------------ 2 files changed, 29 insertions(+), 27 deletions(-) diff --git a/include/Client.h b/include/Client.h index bb86a74..f4c9022 100644 --- a/include/Client.h +++ b/include/Client.h @@ -28,7 +28,6 @@ class Client { const int sockfd; static const size_t BUFFER_INITIAL_LENGTH = 256; static const size_t CURRENCY_NAME_SIZE_IN_LIST = 16; - static const int SECONDS_TO_WAIT_FOR_RESPONSE = 30; void write_end_of_message(std::vector &buffer) const; @@ -51,6 +50,8 @@ class Client { bool translate_remove_message(std::vector &message) const; std::vector translate_get_currency_history_message(std::vector &message) const; + + void write_request(const std::vector &buffer) const; }; diff --git a/src/Client.cpp b/src/Client.cpp index 3a7a167..3841d00 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -17,11 +17,7 @@ const std::vector Client::list() const { write_command(write_buffer, command_no); write_end_of_message(write_buffer); - auto written = write(sockfd, write_buffer.data(), write_buffer.size()); - if (written < 0) { - perror("ERROR writing to socket"); - exit(1); - } + write_request(write_buffer); std::vector response = read_response(); return translate_list_message(response); @@ -36,10 +32,8 @@ bool Client::addCurrency(const Currency ¤cy) const { write_string(write_buffer, currency.get_name()); write_int32(write_buffer, currency.get_current_rate()); write_end_of_message(write_buffer); - if (write(sockfd, write_buffer.data(), write_buffer.size()) < 0) { - perror("ERROR writing to socket"); - exit(1); - } + write_request(write_buffer); + std::vector response = read_response(); return translate_add_message(response); } @@ -51,10 +45,8 @@ bool Client::remove(const Currency ¤cy) const { write_command(write_buffer, command_no); write_string(write_buffer, currency.get_name()); write_end_of_message(write_buffer); - if (write(sockfd, write_buffer.data(), write_buffer.size()) < 0) { - perror("ERROR writing to socket"); - exit(1); - } + write_request(write_buffer); + std::vector response = read_response(); return translate_remove_message(response); } @@ -67,10 +59,8 @@ bool Client::addRate(const Currency ¤cy, int32_t new_rate) const { write_string(write_buffer, currency.get_name()); write_int32(write_buffer, new_rate); write_end_of_message(write_buffer); - if (write(sockfd, write_buffer.data(), write_buffer.size()) < 0) { - perror("ERROR writing to socket"); - exit(1); - } + write_request(write_buffer); + std::vector response = read_response(); return translate_add_message(response); } @@ -82,10 +72,8 @@ Currency Client::getCurrencyWithHistory(const Currency ¤cy) const { write_command(write_buffer, command_no); write_string(write_buffer, currency.get_name()); write_end_of_message(write_buffer); - if (write(sockfd, write_buffer.data(), write_buffer.size()) < 0) { - perror("ERROR writing to socket"); - exit(1); - } + write_request(write_buffer); + std::vector response = read_response(); return Currency(currency.get_name(), translate_get_currency_history_message(response)); } @@ -218,15 +206,14 @@ std::vector Client::read_response() const { bool message_received = false; auto message = std::vector(); - const clock_t begin_time = clock(); while (!message_received) { - if ((float(clock() - begin_time) / CLOCKS_PER_SEC) > SECONDS_TO_WAIT_FOR_RESPONSE) { - perror("Connection is to slow, please try later"); - exit(1); - } auto read_buffer = std::vector(BUFFER_INITIAL_LENGTH); ssize_t bytes_number = read(sockfd, read_buffer.data(), read_buffer.size()); + if (bytes_number == 0) { + perror("Connection was closed by server, please try later"); + exit(1); + } if (bytes_number < 0) { perror("ERROR reading from socket"); exit(1); @@ -237,3 +224,17 @@ std::vector Client::read_response() const { } return message; } + +void Client::write_request(const std::vector &buffer) const { + ssize_t written = 0; + ssize_t must_be_written = buffer.size(); + while (written < must_be_written) { + auto written_piece = write(sockfd, buffer.data() + written, + static_cast(must_be_written - written)); + if (written_piece < 0) { + perror("ERROR writing to socket"); + exit(1); + } + written += written_piece; + } +} From 3031a8773911d3e5815c732d2869affddb64f911 Mon Sep 17 00:00:00 2001 From: RamSaw Date: Fri, 8 Feb 2019 15:26:04 +0300 Subject: [PATCH 11/17] Writing by pieces not needed in blocking mode. --- src/Client.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/Client.cpp b/src/Client.cpp index 3841d00..d9f8659 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -226,15 +226,9 @@ std::vector Client::read_response() const { } void Client::write_request(const std::vector &buffer) const { - ssize_t written = 0; - ssize_t must_be_written = buffer.size(); - while (written < must_be_written) { - auto written_piece = write(sockfd, buffer.data() + written, - static_cast(must_be_written - written)); - if (written_piece < 0) { - perror("ERROR writing to socket"); - exit(1); - } - written += written_piece; + auto written_piece = write(sockfd, buffer.data(), buffer.size()); + if (written_piece < 0) { + perror("ERROR writing to socket"); + exit(1); } } From be42015d7666a01e60e8bc836b8143d2f74c67dd Mon Sep 17 00:00:00 2001 From: RamSaw Date: Sun, 17 Feb 2019 23:23:14 +0300 Subject: [PATCH 12/17] Fixed reading int32. Error was discovered during writing second part of the task (when I was writing server side, not client side). --- src/Client.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Client.cpp b/src/Client.cpp index d9f8659..fe91c15 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -118,7 +118,8 @@ void insert_bytes(std::vector &container, const T &value) { } int32_t read_int32(std::vector::iterator &it) { - int32_t val = it[0] | (it[1] << 8) | (it[2] << 16) | (it[3] << 24); + int8_t bytes[] = {it[0], it[1], it[2], it[3]}; + int32_t val = *((int*)bytes); it += sizeof(int32_t); return val; } From 319e9daecedbee3236cbb67836eb1ca7b11b8298 Mon Sep 17 00:00:00 2001 From: RamSaw Date: Fri, 15 Mar 2019 12:01:01 +0300 Subject: [PATCH 13/17] Started developing udp client. --- include/Client.h | 3 ++- src/Client.cpp | 26 ++++++-------------------- 2 files changed, 8 insertions(+), 21 deletions(-) diff --git a/include/Client.h b/include/Client.h index f4c9022..0a05dea 100644 --- a/include/Client.h +++ b/include/Client.h @@ -10,7 +10,7 @@ class Client { public: - Client(const std::string &hostname, uint16_t portno); + Client(const std::string &server_ip, uint16_t portno); const std::vector list() const; @@ -25,6 +25,7 @@ class Client { virtual ~Client(); private: + struct sockaddr_in si_other; const int sockfd; static const size_t BUFFER_INITIAL_LENGTH = 256; static const size_t CURRENCY_NAME_SIZE_IN_LIST = 16; diff --git a/src/Client.cpp b/src/Client.cpp index fe91c15..6219016 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -78,31 +78,17 @@ Currency Client::getCurrencyWithHistory(const Currency ¤cy) const { return Currency(currency.get_name(), translate_get_currency_history_message(response)); } -Client::Client(const std::string &hostname, uint16_t portno) : sockfd(socket(AF_INET, SOCK_STREAM, 0)) { +Client::Client(const std::string &server_ip, uint16_t portno) : sockfd(socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) { if (sockfd < 0) { perror("ERROR opening socket"); exit(1); } - struct hostent *server = gethostbyname(hostname.c_str()); - - if (server == nullptr) { - unsigned int addr = inet_addr(hostname.c_str()); - server = gethostbyaddr((char *) &addr, sizeof(addr), AF_INET); - if (server == nullptr) { - perror("ERROR, no such host"); - exit(1); - } - } - - struct sockaddr_in server_addr{}; - bzero((char *) &server_addr, sizeof(server_addr)); - server_addr.sin_family = AF_INET; - bcopy(server->h_addr, (char *) &server_addr.sin_addr.s_addr, (size_t) server->h_length); - server_addr.sin_port = htons(portno); - - if (connect(sockfd, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) { - perror("ERROR connecting"); + bzero((char *) &si_other, sizeof(si_other)); + si_other.sin_family = AF_INET; + si_other.sin_port = htons(portno); + if (inet_aton(server_ip.c_str(), &si_other.sin_addr)==0) { + perror("inet_aton() failed\n"); exit(1); } } From 0457f74ebde16c35f4ad99d0e99d57369b8ec0a8 Mon Sep 17 00:00:00 2001 From: RamSaw Date: Fri, 15 Mar 2019 14:20:20 +0300 Subject: [PATCH 14/17] Implemented udp client. Tests are needed. --- CurrencyApplicationProtocol | 2 +- include/Client.h | 32 ++++--- src/Client.cpp | 145 ++++++++++++++++++++---------- src/CurrencyClientApplication.cpp | 2 +- 4 files changed, 123 insertions(+), 58 deletions(-) diff --git a/CurrencyApplicationProtocol b/CurrencyApplicationProtocol index 52856fa..8096521 100644 --- a/CurrencyApplicationProtocol +++ b/CurrencyApplicationProtocol @@ -13,7 +13,7 @@ 0. Получение и вывод списка валют с котировками/изменениями Номер команды: 0. - Клиент отправляет: "<номер команды: 4 байта>\0", + Клиент отправляет: "<номер команды: 4 байта>\0", Сервер отвечает: "<название валюты: 16 байт><текущий курс: 4 байта><есть ли предыдущий курс: 1 байт><абсолютное приращение: 4 байта><относительное приращение: 4 байта>"..."\0", diff --git a/include/Client.h b/include/Client.h index 0a05dea..ddd4504 100644 --- a/include/Client.h +++ b/include/Client.h @@ -6,35 +6,45 @@ #define NETWORKSLAB2019HSE_CLIENT_H +#include +#include #include "Currency.h" class Client { public: Client(const std::string &server_ip, uint16_t portno); - const std::vector list() const; + const std::vector list(); - bool addCurrency(const Currency ¤cy) const; + bool addCurrency(const Currency ¤cy); - bool addRate(const Currency ¤cy, int32_t new_rate) const; + bool addRate(const Currency ¤cy, int32_t new_rate); - bool remove(const Currency ¤cy) const; + bool remove(const Currency ¤cy); - Currency getCurrencyWithHistory(const Currency ¤cy) const; + Currency getCurrencyWithHistory(const Currency ¤cy); virtual ~Client(); private: - struct sockaddr_in si_other; + struct sockaddr_in *si_other; const int sockfd; - static const size_t BUFFER_INITIAL_LENGTH = 256; + int32_t request_id = 0; + int si_other_len = sizeof(si_other); + static const size_t BUFFER_INITIAL_LENGTH = 1024; static const size_t CURRENCY_NAME_SIZE_IN_LIST = 16; + static const size_t MAX_SIZE_OF_PACKET = 508; + static const size_t TIMOUT_SECONDS = 30; - void write_end_of_message(std::vector &buffer) const; + const std::vector send_and_receive(const std::vector &send_buffer) const; - void write_command(std::vector &buffer, int32_t command_no) const; + void write_request_id(std::vector &buffer); + + bool is_all_packets_received(const std::map> &packets, int number_of_packets) const; - bool is_message_received(const std::vector &message) const; + std::vector construct_message_from_packets(const std::map> &packets) const; + + void write_command(std::vector &buffer, int32_t command_no) const; const std::vector translate_list_message(std::vector &message) const; @@ -52,7 +62,7 @@ class Client { std::vector translate_get_currency_history_message(std::vector &message) const; - void write_request(const std::vector &buffer) const; + void send_request(const std::vector &buffer) const; }; diff --git a/src/Client.cpp b/src/Client.cpp index 6219016..d6cb356 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -9,72 +9,85 @@ #include #include #include +#include #include +#include + +const std::vector Client::send_and_receive(const std::vector &send_buffer) const { + bool got_all_message = false; + std::vector response; + while(!got_all_message) { + send_request(send_buffer); + + try { + response = read_response(); + } catch (char *c) { + continue; + } + got_all_message = true; + } + return response; +} -const std::vector Client::list() const { - auto write_buffer = std::vector(); +const std::vector Client::list() { int32_t command_no = 0; + auto write_buffer = std::vector(); + write_request_id(write_buffer); write_command(write_buffer, command_no); - write_end_of_message(write_buffer); - write_request(write_buffer); - std::vector response = read_response(); + auto response = send_and_receive(write_buffer); return translate_list_message(response); } -bool Client::addCurrency(const Currency ¤cy) const { - auto write_buffer = std::vector(); +bool Client::addCurrency(const Currency ¤cy) { int32_t command_no = 1; + auto write_buffer = std::vector(); + write_request_id(write_buffer); write_command(write_buffer, command_no); write_string(write_buffer, currency.get_name()); write_int32(write_buffer, currency.get_current_rate()); - write_end_of_message(write_buffer); - write_request(write_buffer); - std::vector response = read_response(); + auto response = send_and_receive(write_buffer); return translate_add_message(response); } -bool Client::remove(const Currency ¤cy) const { - auto write_buffer = std::vector(); +bool Client::remove(const Currency ¤cy) { int32_t command_no = 2; + auto write_buffer = std::vector(); + write_request_id(write_buffer); write_command(write_buffer, command_no); write_string(write_buffer, currency.get_name()); - write_end_of_message(write_buffer); - write_request(write_buffer); - std::vector response = read_response(); + auto response = send_and_receive(write_buffer); return translate_remove_message(response); } -bool Client::addRate(const Currency ¤cy, int32_t new_rate) const { - auto write_buffer = std::vector(); +bool Client::addRate(const Currency ¤cy, int32_t new_rate) { int32_t command_no = 3; + auto write_buffer = std::vector(); + write_request_id(write_buffer); write_command(write_buffer, command_no); write_string(write_buffer, currency.get_name()); write_int32(write_buffer, new_rate); - write_end_of_message(write_buffer); - write_request(write_buffer); - std::vector response = read_response(); + auto response = send_and_receive(write_buffer); return translate_add_message(response); } -Currency Client::getCurrencyWithHistory(const Currency ¤cy) const { +Currency Client::getCurrencyWithHistory(const Currency ¤cy) { auto write_buffer = std::vector(); int32_t command_no = 4; + write_request_id(write_buffer); write_command(write_buffer, command_no); write_string(write_buffer, currency.get_name()); - write_end_of_message(write_buffer); - write_request(write_buffer); - std::vector response = read_response(); + auto response = send_and_receive(write_buffer); return Currency(currency.get_name(), translate_get_currency_history_message(response)); } @@ -84,10 +97,19 @@ Client::Client(const std::string &server_ip, uint16_t portno) : sockfd(socket(AF exit(1); } - bzero((char *) &si_other, sizeof(si_other)); - si_other.sin_family = AF_INET; - si_other.sin_port = htons(portno); - if (inet_aton(server_ip.c_str(), &si_other.sin_addr)==0) { + struct timeval tv{}; + tv.tv_sec = TIMOUT_SECONDS; + tv.tv_usec = 0; + if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) { + perror("Error: cannot set timeout for socket"); + exit(1); + } + + si_other = new sockaddr_in(); + bzero((char *) si_other, sizeof(si_other)); + si_other->sin_family = AF_INET; + si_other->sin_port = htons(portno); + if (inet_aton(server_ip.c_str(), &si_other->sin_addr)==0) { perror("inet_aton() failed\n"); exit(1); } @@ -95,6 +117,7 @@ Client::Client(const std::string &server_ip, uint16_t portno) : sockfd(socket(AF Client::~Client() { close(sockfd); + delete(si_other); } template @@ -123,19 +146,15 @@ std::string read_string(std::vector::iterator &it, size_t len) { return str; } -void Client::write_end_of_message(std::vector &buffer) const { - insert_bytes(buffer, (int8_t) '\\'); - insert_bytes(buffer, (int8_t) 0); +void Client::write_request_id(std::vector &buffer) { + request_id++; + insert_bytes(buffer, request_id); } void Client::write_command(std::vector &buffer, int32_t command_no) const { insert_bytes(buffer, command_no); } -bool Client::is_message_received(const std::vector &message) const { - return message.size() >= 2 && message[message.size() - 2] == '\\' && message[message.size() - 1] == 0; -} - bool Client::translate_remove_message(std::vector &message) const { remove_ending_symbols(message); auto it = message.begin(); @@ -190,13 +209,18 @@ void Client::write_int32(std::vector &buffer, int32_t rate) const { } std::vector Client::read_response() const { - bool message_received = false; - auto message = std::vector(); + bool all_packets_received = false; + auto packets = std::map>(); - while (!message_received) { + while (!all_packets_received) { auto read_buffer = std::vector(BUFFER_INITIAL_LENGTH); - ssize_t bytes_number = read(sockfd, read_buffer.data(), read_buffer.size()); + ssize_t bytes_number = recvfrom(sockfd, read_buffer.data(), read_buffer.size(), 0, + (struct sockaddr *) si_other, (socklen_t *) &si_other_len); + if (errno == EAGAIN || errno == EWOULDBLOCK) { + perror("Timout reached for recvfrom, probably packages was lost, sending again"); + throw "TIMEOUT"; + } if (bytes_number == 0) { perror("Connection was closed by server, please try later"); exit(1); @@ -206,16 +230,47 @@ std::vector Client::read_response() const { exit(1); } read_buffer.resize(static_cast(bytes_number)); - message.insert(message.end(), read_buffer.begin(), read_buffer.end()); - message_received = is_message_received(message); + auto it = read_buffer.begin(); + int32_t read_request_id = read_int32(it); + if (read_request_id != request_id) { + continue; + } + int32_t read_number_of_packets = read_int32(it); + int32_t packet_id = read_int32(it); + auto packet_message = std::vector(it, read_buffer.end()); + packets[packet_id] = packet_message; + all_packets_received = is_all_packets_received(packets, read_number_of_packets); + } + return construct_message_from_packets(packets); +} + +std::vector Client::construct_message_from_packets(const std::map> &packets) const { + std::vector message; + for (const auto& packet : packets) { + message.insert(message.end(), packet.second.begin(), packet.second.end()); } return message; } -void Client::write_request(const std::vector &buffer) const { - auto written_piece = write(sockfd, buffer.data(), buffer.size()); - if (written_piece < 0) { - perror("ERROR writing to socket"); +bool Client::is_all_packets_received +(const std::map> &packets, int number_of_packets) const { + for (int packet_id = 0; packet_id < number_of_packets; packet_id++) { + if (packets.count(packet_id) != 0) { + return false; + } + } + return true; +} + +void Client::send_request(const std::vector &buffer) const { + if (buffer.size() > MAX_SIZE_OF_PACKET) { + perror("ERROR sending data, packet size is more then maximum size"); + exit(1); + } + auto return_code = sendto(sockfd, buffer.data(), buffer.size(), 0, (struct sockaddr *) &si_other, + static_cast(si_other_len)); + if (return_code < 0) { + perror("ERROR sending data to socket"); exit(1); } } diff --git a/src/CurrencyClientApplication.cpp b/src/CurrencyClientApplication.cpp index cd9c58f..7b5b44d 100644 --- a/src/CurrencyClientApplication.cpp +++ b/src/CurrencyClientApplication.cpp @@ -7,7 +7,7 @@ int main(int argc, char *argv[]) { if (argc < 3) { - fprintf(stderr, "usage: .%s \n", argv[0]); + fprintf(stderr, "usage: .%s \n", argv[0]); return 0; } std::string hostname = argv[1]; From 84faa1098e80ef80a458b0d4279144ffc86a8fd0 Mon Sep 17 00:00:00 2001 From: RamSaw Date: Fri, 15 Mar 2019 15:09:49 +0300 Subject: [PATCH 15/17] Fixed establishing socket. --- include/Client.h | 8 +++++--- src/Client.cpp | 28 ++++++++++++++++------------ 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/include/Client.h b/include/Client.h index ddd4504..0cf6660 100644 --- a/include/Client.h +++ b/include/Client.h @@ -30,13 +30,15 @@ class Client { struct sockaddr_in *si_other; const int sockfd; int32_t request_id = 0; - int si_other_len = sizeof(si_other); + int si_other_len; static const size_t BUFFER_INITIAL_LENGTH = 1024; static const size_t CURRENCY_NAME_SIZE_IN_LIST = 16; static const size_t MAX_SIZE_OF_PACKET = 508; - static const size_t TIMOUT_SECONDS = 30; + static const size_t TIMOUT_SECONDS = 3; - const std::vector send_and_receive(const std::vector &send_buffer) const; + const std::vector get_message_with_request_id(const std::vector &message); + + const std::vector send_and_receive(const std::vector &message); void write_request_id(std::vector &buffer); diff --git a/src/Client.cpp b/src/Client.cpp index d6cb356..c867af7 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -13,15 +13,22 @@ #include #include -const std::vector Client::send_and_receive(const std::vector &send_buffer) const { +const std::vector Client::get_message_with_request_id(const std::vector &message) { + std::vector send_buffer; + write_request_id(send_buffer); + send_buffer.insert(send_buffer.end(), message.begin(), message.end()); + return send_buffer; +} + +const std::vector Client::send_and_receive(const std::vector &message) { bool got_all_message = false; std::vector response; while(!got_all_message) { - send_request(send_buffer); + send_request(get_message_with_request_id(message)); try { response = read_response(); - } catch (char *c) { + } catch (const char *timeout_exception) { continue; } got_all_message = true; @@ -33,7 +40,6 @@ const std::vector Client::list() { int32_t command_no = 0; auto write_buffer = std::vector(); - write_request_id(write_buffer); write_command(write_buffer, command_no); auto response = send_and_receive(write_buffer); @@ -45,7 +51,6 @@ bool Client::addCurrency(const Currency ¤cy) { int32_t command_no = 1; auto write_buffer = std::vector(); - write_request_id(write_buffer); write_command(write_buffer, command_no); write_string(write_buffer, currency.get_name()); write_int32(write_buffer, currency.get_current_rate()); @@ -58,7 +63,6 @@ bool Client::remove(const Currency ¤cy) { int32_t command_no = 2; auto write_buffer = std::vector(); - write_request_id(write_buffer); write_command(write_buffer, command_no); write_string(write_buffer, currency.get_name()); @@ -70,7 +74,6 @@ bool Client::addRate(const Currency ¤cy, int32_t new_rate) { int32_t command_no = 3; auto write_buffer = std::vector(); - write_request_id(write_buffer); write_command(write_buffer, command_no); write_string(write_buffer, currency.get_name()); write_int32(write_buffer, new_rate); @@ -83,7 +86,6 @@ Currency Client::getCurrencyWithHistory(const Currency ¤cy) { auto write_buffer = std::vector(); int32_t command_no = 4; - write_request_id(write_buffer); write_command(write_buffer, command_no); write_string(write_buffer, currency.get_name()); @@ -109,10 +111,11 @@ Client::Client(const std::string &server_ip, uint16_t portno) : sockfd(socket(AF bzero((char *) si_other, sizeof(si_other)); si_other->sin_family = AF_INET; si_other->sin_port = htons(portno); - if (inet_aton(server_ip.c_str(), &si_other->sin_addr)==0) { + if (inet_aton(server_ip.c_str(), &(si_other->sin_addr)) == 0) { perror("inet_aton() failed\n"); exit(1); } + si_other_len = sizeof(*si_other); } Client::~Client() { @@ -218,7 +221,8 @@ std::vector Client::read_response() const { (struct sockaddr *) si_other, (socklen_t *) &si_other_len); if (errno == EAGAIN || errno == EWOULDBLOCK) { - perror("Timout reached for recvfrom, probably packages was lost, sending again"); + perror("Timout reached for recvfrom, probably packages was lost or request wasn't received, sending again." + " Error:"); throw "TIMEOUT"; } if (bytes_number == 0) { @@ -267,8 +271,8 @@ void Client::send_request(const std::vector &buffer) const { perror("ERROR sending data, packet size is more then maximum size"); exit(1); } - auto return_code = sendto(sockfd, buffer.data(), buffer.size(), 0, (struct sockaddr *) &si_other, - static_cast(si_other_len)); + auto return_code = sendto(sockfd, buffer.data(), buffer.size(), 0, + (struct sockaddr *) si_other, static_cast(si_other_len)); if (return_code < 0) { perror("ERROR sending data to socket"); exit(1); From f7de8cacea2ef93b0c300106104d6cb6c188aa84 Mon Sep 17 00:00:00 2001 From: RamSaw Date: Fri, 15 Mar 2019 15:26:53 +0300 Subject: [PATCH 16/17] Erased unused files and corrected protocol description file. --- CurrencyApplicationProtocol | 31 ++++++---- include/Client.h | 2 +- tcp/client/.gitkeep | 0 tcp/server/.gitkeep | 0 tcp_template/client_linux/CMakeLists.txt | 7 --- tcp_template/client_linux/main.c | 78 ------------------------ tcp_template/server_linux/CMakeLists.txt | 7 --- tcp_template/server_linux/main.c | 75 ----------------------- udp/client/.gitkeep | 0 udp/server/.gitkeep | 0 10 files changed, 21 insertions(+), 179 deletions(-) delete mode 100644 tcp/client/.gitkeep delete mode 100644 tcp/server/.gitkeep delete mode 100644 tcp_template/client_linux/CMakeLists.txt delete mode 100644 tcp_template/client_linux/main.c delete mode 100644 tcp_template/server_linux/CMakeLists.txt delete mode 100644 tcp_template/server_linux/main.c delete mode 100644 udp/client/.gitkeep delete mode 100644 udp/server/.gitkeep diff --git a/CurrencyApplicationProtocol b/CurrencyApplicationProtocol index 8096521..18a8199 100644 --- a/CurrencyApplicationProtocol +++ b/CurrencyApplicationProtocol @@ -6,17 +6,26 @@ Название валюты - 16 байта История курса валюты - N * 4 байта (каждое значение курса это 4 байта) -Любое сообщение должно кончаться на <'\'0> - 2 байта (символ '\' и нулейвой байт) символизирующие конец сообщения ... - повторение описанных блоков выше. +Клиент и сервер обмениваются пакетами размера 508 байт (либо меньше, если размер сообщения небольшой). +В начале каждого пакета с запросом клиент передает -- номер его сессии, +и при получении ответа от сервера обрабатывает только те пакеты, которые соответствуют этой сессии. +Сделано это для того, чтобы если пакет от сервера задержался и запрос от клиента был отправлен повторно, +ошибочно не учитывать уже невалидный пакет. Таким образом, пакеты с неправильным id игнорируются. +Перед отправкой ответа с сервера содержимое сообщения разбивается на пакеты по 508 байт. +В начало каждого записывается служебная информация: + -- номер сессии, количество пакетов и порядковый номер текущего соответственно. +Клиент расставляет пришедшие пакеты в правильном порядке, при нехватке пакетов отправляет запрос заново с новым номером сессии. + Поддерживаемые типы запросов: 0. Получение и вывод списка валют с котировками/изменениями Номер команды: 0. - Клиент отправляет: "<номер команды: 4 байта>\0", + Клиент отправляет: "<номер команды: 4 байта>", Сервер отвечает: - "<название валюты: 16 байт><текущий курс: 4 байта><есть ли предыдущий курс: 1 байт><абсолютное приращение: 4 байта><относительное приращение: 4 байта>"..."\0", + "<название валюты: 16 байт><текущий курс: 4 байта><есть ли предыдущий курс: 1 байт><абсолютное приращение: 4 байта><относительное приращение: 4 байта>"..., если предыдущего курса нет, то абсолютное и относительное значения равны нулям и игнорируются, относительное значение выражается в процентах и округляется до целых. Формулы: @@ -26,34 +35,34 @@ 1. Передача команды на добавление валюты Номер команды: 1. - Клиент отправляет: "<номер команды: 4 байта><название валюты: 16 байт><текущий курс: 4 байта>\0", + Клиент отправляет: "<номер команды: 4 байта><название валюты: 16 байт><текущий курс: 4 байта>", Сервер отвечает: - "<успех запроса: 1 байт>\0", + "<успех запроса: 1 байт>", возвращает 1 если такой валюты еще не было, 0 если такая валюта была. 2. Передача команды на удаление валюты Номер команды: 2. - Клиент отправляет: "<номер команды: 4 байта><название валюты: 16 байт>\0", + Клиент отправляет: "<номер команды: 4 байта><название валюты: 16 байт>", Сервер отвечает: - "<успех запроса: 1 байт>\0", + "<успех запроса: 1 байт>", возвращает 1 если такая валюта была и была удалена успешно, 0 если такой валюты не было. 3. Передача команды на добавление курса валюты Номер команды: 3. - Клиент отправляет: "<номер команды: 4 байта><название валюты: 16 байт><новый курс: 4 байта>\0", + Клиент отправляет: "<номер команды: 4 байта><название валюты: 16 байт><новый курс: 4 байта>", Сервер отвечает: - "<успех запроса: 1 байт>\0", + "<успех запроса: 1 байт>", возвращает 1 если успешно выставлен новый курс, 0 иначе. 4. Получение истории котировок валюты Номер команды: 4. - Клиент отправляет: "<номер команды: 4 байта><название валюты: 16 байт>\0", + Клиент отправляет: "<номер команды: 4 байта><название валюты: 16 байт>", Сервер отвечает: - "<курс1: 4 байта><курс2: 4 байта>"..."\0" \ No newline at end of file +"<курс1: 4 байта><курс2: 4 байта>"... \ No newline at end of file diff --git a/include/Client.h b/include/Client.h index 0cf6660..92618b1 100644 --- a/include/Client.h +++ b/include/Client.h @@ -34,7 +34,7 @@ class Client { static const size_t BUFFER_INITIAL_LENGTH = 1024; static const size_t CURRENCY_NAME_SIZE_IN_LIST = 16; static const size_t MAX_SIZE_OF_PACKET = 508; - static const size_t TIMOUT_SECONDS = 3; + static const size_t TIMOUT_SECONDS = 30; const std::vector get_message_with_request_id(const std::vector &message); diff --git a/tcp/client/.gitkeep b/tcp/client/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/tcp/server/.gitkeep b/tcp/server/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/tcp_template/client_linux/CMakeLists.txt b/tcp_template/client_linux/CMakeLists.txt deleted file mode 100644 index 311326d..0000000 --- a/tcp_template/client_linux/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -cmake_minimum_required(VERSION 2.8) -project(client_linux) - -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra") - -set(SOURCE_FILES main.c) -add_executable(client_linux ${SOURCE_FILES}) \ No newline at end of file diff --git a/tcp_template/client_linux/main.c b/tcp_template/client_linux/main.c deleted file mode 100644 index ad0c290..0000000 --- a/tcp_template/client_linux/main.c +++ /dev/null @@ -1,78 +0,0 @@ -#include -#include - -#include -#include -#include - -#include - -int main(int argc, char *argv[]) { - int sockfd, n; - uint16_t portno; - struct sockaddr_in serv_addr; - struct hostent *server; - - char buffer[256]; - - if (argc < 3) { - fprintf(stderr, "usage %s hostname port\n", argv[0]); - exit(0); - } - - portno = (uint16_t) atoi(argv[2]); - - /* Create a socket point */ - sockfd = socket(AF_INET, SOCK_STREAM, 0); - - if (sockfd < 0) { - perror("ERROR opening socket"); - exit(1); - } - - server = gethostbyname(argv[1]); - - if (server == NULL) { - fprintf(stderr, "ERROR, no such host\n"); - exit(0); - } - - bzero((char *) &serv_addr, sizeof(serv_addr)); - serv_addr.sin_family = AF_INET; - bcopy(server->h_addr, (char *) &serv_addr.sin_addr.s_addr, (size_t) server->h_length); - serv_addr.sin_port = htons(portno); - - /* Now connect to the server */ - if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { - perror("ERROR connecting"); - exit(1); - } - - /* Now ask for a message from the user, this message - * will be read by server - */ - - printf("Please enter the message: "); - bzero(buffer, 256); - fgets(buffer, 255, stdin); - - /* Send message to the server */ - n = write(sockfd, buffer, strlen(buffer)); - - if (n < 0) { - perror("ERROR writing to socket"); - exit(1); - } - - /* Now read server response */ - bzero(buffer, 256); - n = read(sockfd, buffer, 255); - - if (n < 0) { - perror("ERROR reading from socket"); - exit(1); - } - - printf("%s\n", buffer); - return 0; -} \ No newline at end of file diff --git a/tcp_template/server_linux/CMakeLists.txt b/tcp_template/server_linux/CMakeLists.txt deleted file mode 100644 index ac88ba4..0000000 --- a/tcp_template/server_linux/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -cmake_minimum_required(VERSION 2.8) -project(server_linux) - -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra") - -set(SOURCE_FILES main.c) -add_executable(server_linux ${SOURCE_FILES}) \ No newline at end of file diff --git a/tcp_template/server_linux/main.c b/tcp_template/server_linux/main.c deleted file mode 100644 index 30c7084..0000000 --- a/tcp_template/server_linux/main.c +++ /dev/null @@ -1,75 +0,0 @@ -#include -#include - -#include -#include -#include - -#include - -int main(int argc, char *argv[]) { - int sockfd, newsockfd; - uint16_t portno; - unsigned int clilen; - char buffer[256]; - struct sockaddr_in serv_addr, cli_addr; - ssize_t n; - - /* First call to socket() function */ - sockfd = socket(AF_INET, SOCK_STREAM, 0); - - if (sockfd < 0) { - perror("ERROR opening socket"); - exit(1); - } - - /* Initialize socket structure */ - bzero((char *) &serv_addr, sizeof(serv_addr)); - portno = 5001; - - serv_addr.sin_family = AF_INET; - serv_addr.sin_addr.s_addr = INADDR_ANY; - serv_addr.sin_port = htons(portno); - - /* Now bind the host address using bind() call.*/ - if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { - perror("ERROR on binding"); - exit(1); - } - - /* Now start listening for the clients, here process will - * go in sleep mode and will wait for the incoming connection - */ - - listen(sockfd, 5); - clilen = sizeof(cli_addr); - - /* Accept actual connection from the client */ - newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen); - - if (newsockfd < 0) { - perror("ERROR on accept"); - exit(1); - } - - /* If connection is established then start communicating */ - bzero(buffer, 256); - n = read(newsockfd, buffer, 255); // recv on Windows - - if (n < 0) { - perror("ERROR reading from socket"); - exit(1); - } - - printf("Here is the message: %s\n", buffer); - - /* Write a response to the client */ - n = write(newsockfd, "I got your message", 18); // send on Windows - - if (n < 0) { - perror("ERROR writing to socket"); - exit(1); - } - - return 0; -} diff --git a/udp/client/.gitkeep b/udp/client/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/udp/server/.gitkeep b/udp/server/.gitkeep deleted file mode 100644 index e69de29..0000000 From 77b3742f207e5fac39e00a89c3b14d6c320d7d9f Mon Sep 17 00:00:00 2001 From: RamSaw Date: Fri, 15 Mar 2019 20:32:55 +0300 Subject: [PATCH 17/17] Bugfix. --- CMakeLists.txt | 7 - include/Client.h | 2 - include/catch.hpp | 14688 ------------------------------------------ src/Client.cpp | 10 +- test/ClientTest.cpp | 333 - 5 files changed, 1 insertion(+), 15039 deletions(-) delete mode 100644 include/catch.hpp delete mode 100644 test/ClientTest.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 974b6a5..ab79c8e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,11 +11,4 @@ add_executable(NetworksLab2019HSE src/Client.cpp src/CurrencyClientApplication.cpp src/Currency.cpp - ) - -add_executable(runTests - include/catch.hpp - test/ClientTest.cpp - src/Client.cpp - src/Currency.cpp ) \ No newline at end of file diff --git a/include/Client.h b/include/Client.h index 92618b1..2e874e3 100644 --- a/include/Client.h +++ b/include/Client.h @@ -50,8 +50,6 @@ class Client { const std::vector translate_list_message(std::vector &message) const; - void remove_ending_symbols(std::vector &message) const; - void write_string(std::vector &buffer, const std::string ¤cy_name) const; void write_int32(std::vector &buffer, int32_t rate) const; diff --git a/include/catch.hpp b/include/catch.hpp deleted file mode 100644 index 02d766e..0000000 --- a/include/catch.hpp +++ /dev/null @@ -1,14688 +0,0 @@ -/* - * Catch v2.6.0 - * Generated: 2019-01-31 22:25:55.560884 - * ---------------------------------------------------------- - * This file has been merged from multiple headers. Please don't edit it directly - * Copyright (c) 2019 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 6 -#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__ - // Because REQUIREs trigger GCC's -Wparentheses, and because still - // supported version of g++ have only buggy support for _Pragmas, - // Wparentheses have to be suppressed globally. -# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details - -# 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 -# 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_ 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() && defined(CATCH_CPP17_OR_GREATER) -# define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW -#endif -#endif - -//////////////////////////////////////////////////////////////////////////////// -// Check if optional is available and usable -#if defined(__has_include) -# if __has_include() && defined(CATCH_CPP17_OR_GREATER) -# define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL -# endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) -#endif // __has_include - -//////////////////////////////////////////////////////////////////////////////// -// Check if variant is available and usable -#if defined(__has_include) -# if __has_include() && defined(CATCH_CPP17_OR_GREATER) -# if defined(__clang__) && (__clang_major__ < 8) - // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 - // fix should be in clang 8, workaround in libstdc++ 8.2 -# include -# if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) -# define CATCH_CONFIG_NO_CPP17_VARIANT -# else -# define CATCH_INTERNAL_CONFIG_CPP17_VARIANT -# endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) -# else -# define CATCH_INTERNAL_CONFIG_CPP17_VARIANT -# endif // defined(__clang__) && (__clang_major__ < 8) -# endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) -#endif // __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_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) -# define CATCH_CONFIG_CPP17_OPTIONAL -#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 -#include -#include - -// 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& operator = ( SourceLineInfo const& ) = default; - SourceLineInfo( SourceLineInfo&& ) noexcept = default; - SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = 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 - T const& operator + ( T const& value, StreamEndStop ) { - return value; - } -} - -#define CATCH_INTERNAL_LINEINFO \ - ::Catch::SourceLineInfo( __FILE__, static_cast( __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 - -namespace Catch { - - class TestSpec; - - struct ITestInvoker { - virtual void invoke () const = 0; - virtual ~ITestInvoker(); - }; - - class TestCase; - struct IConfig; - - struct ITestCaseRegistry { - virtual ~ITestCaseRegistry(); - virtual std::vector const& getAllTests() const = 0; - virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; - }; - - bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); - std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); - std::vector const& getAllTestCasesSorted( IConfig const& config ); - -} - -// end catch_interfaces_testcase.h -// start catch_stringref.h - -#include -#include -#include - -namespace Catch { - - /// A non-owning string class (similar to the forthcoming std::string_view) - /// Note that, because a StringRef may be a substring of another string, - /// it may not be null terminated. 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 - - -#include - -namespace Catch{ - -#ifdef CATCH_CPP17_OR_GREATER - template - inline constexpr auto is_unique = std::true_type{}; - - template - inline constexpr auto is_unique = std::bool_constant< - (!std::is_same_v && ...) && is_unique - >{}; -#else - -template -struct is_unique : std::true_type{}; - -template -struct is_unique : std::integral_constant -::value - && is_unique::value - && is_unique::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 - -#define INTERNAL_CATCH_MAKE_TYPE_LIST(types) TypeList - -#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(types)\ - CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,INTERNAL_CATCH_REMOVE_PARENS(types)) - -// end catch_preprocessor.hpp -// start catch_meta.hpp - - -#include - -template< typename... > -struct TypeList{}; - -template< typename... > -struct append; - -template< template class L1 - , typename...E1 - , template class L2 - , typename...E2 - > -struct append< L1, L2 > -{ - using type = L1; -}; - -template< template class L1 - , typename...E1 - , template class L2 - , typename...E2 - , typename...Rest - > -struct append< L1, L2, Rest...> -{ - using type = typename append< L1, Rest... >::type; -}; - -template< template class - , typename... - > -struct rewrap; - -template< template class Container - , template class List - , typename...elems - > -struct rewrap> -{ - using type = TypeList< Container< elems... > >; -}; - -template< template class Container - , template class List - , class...Elems - , typename...Elements> -struct rewrap, Elements...> -{ - using type = typename append>, typename rewrap::type>::type; -}; - -template< template class...Containers > -struct combine -{ - template< typename...Types > - struct with_types - { - template< template class Final > - struct into - { - using type = typename append, typename rewrap::type...>::type; - }; - }; -}; - -template -struct always_false : std::false_type {}; - -// end catch_meta.hpp -namespace Catch { - -template -class TestInvokerAsMethod : public ITestInvoker { - void (C::*m_testAsMethod)(); -public: - TestInvokerAsMethod( void (C::*testAsMethod)() ) noexcept : m_testAsMethod( testAsMethod ) {} - - void invoke() const override { - C obj; - (obj.*m_testAsMethod)(); - } -}; - -auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker*; - -template -auto makeTestInvoker( void (C::*testAsMethod)() ) noexcept -> ITestInvoker* { - return new(std::nothrow) TestInvokerAsMethod( 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 \ - static void TestName() - #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \ - namespace{ \ - template \ - struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName ) { \ - void test(); \ - }; \ - } \ - template \ - 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 \ - static void TestFunc();\ - namespace {\ - template \ - struct TestName{\ - template \ - 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 ), 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 \ - 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_UD(INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME,Name, __VA_ARGS__));\ - return 0;\ - }(); - - #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(TestName, TestFuncName, Name, Tags, TmplTypes, TypesList) \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - template static void TestFuncName(); \ - namespace { \ - template \ - struct TestName { \ - TestName() { \ - CATCH_INTERNAL_CHECK_UNIQUE_TYPES(Types...) \ - int index = 0; \ - using expander = int[]; \ - (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFuncName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + Catch::StringMaker::convert(index++), Tags } ), 0)... };/* NOLINT */ \ - } \ - }; \ - static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \ - using TestInit = combine \ - ::with_types::into::type; \ - TestInit(); \ - return 0; \ - }(); \ - } \ - CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ - template \ - static void TestFuncName() - -#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR - #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\ - INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(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_PRODUCT_TEST_CASE(Name, Tags, ...)\ - INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( 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_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, ... ) \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - namespace{ \ - template \ - struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName ) { \ - void test();\ - };\ - template \ - struct TestNameClass{\ - template \ - 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::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 \ - void TestName::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 - - #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(TestNameClass, TestName, ClassName, Name, Tags, TmplTypes, TypesList)\ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - template \ - struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName ) { \ - void test();\ - };\ - namespace {\ - template\ - struct TestNameClass{\ - TestNameClass(){\ - CATCH_INTERNAL_CHECK_UNIQUE_TYPES(Types...)\ - int index = 0;\ - using expander = int[];\ - (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + Catch::StringMaker::convert(index++), Tags } ), 0)... };/* NOLINT */ \ - }\ - };\ - static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ - using TestInit = combine\ - ::with_types::into::type;\ - TestInit();\ - return 0;\ - }(); \ - }\ - CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ - template \ - void TestName::test() - -#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR - #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\ - INTERNAL_CATCH_TEMPLATE_PRODUCT_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____ ), 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____ ), ClassName, Name, Tags, __VA_ARGS__ ) -#else - #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\ - INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_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____ ), 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____ ), 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 -#include -#include -#include -// start catch_stream.h - -#include -#include -#include - -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 - 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 -#endif - -#ifdef __OBJC__ -// start catch_objc_arc.hpp - -#import - -#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 - std::string rawMemoryToString( const T& object ) { - return rawMemoryToString( &object, sizeof(object) ); - } - - template - class IsStreamInsertable { - template - static auto test(int) - -> decltype(std::declval() << std::declval(), std::true_type()); - - template - static auto test(...)->std::false_type; - - public: - static const bool value = decltype(test(0))::value; - }; - - template - std::string convertUnknownEnumToString( E e ); - - template - typename std::enable_if< - !std::is_enum::value && !std::is_base_of::value, - std::string>::type convertUnstreamable( T const& ) { - return Detail::unprintableString; - } - template - typename std::enable_if< - !std::is_enum::value && std::is_base_of::value, - std::string>::type convertUnstreamable(T const& ex) { - return ex.what(); - } - - template - typename std::enable_if< - std::is_enum::value - , std::string>::type convertUnstreamable( T const& value ) { - return convertUnknownEnumToString( value ); - } - -#if defined(_MANAGED) - //! Convert a CLR string to a utf8 std::string - template - std::string clrReferenceToString( T^ ref ) { - if (ref == nullptr) - return std::string("null"); - auto bytes = System::Text::Encoding::UTF8->GetBytes(ref->ToString()); - cli::pin_ptr p = &bytes[0]; - return std::string(reinterpret_cast(p), bytes->Length); - } -#endif - - } // namespace Detail - - // If we decide for C++14, change these to enable_if_ts - template - struct StringMaker { - template - static - typename std::enable_if<::Catch::Detail::IsStreamInsertable::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 - static - typename std::enable_if::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 - std::string stringify(const T& e) { - return ::Catch::StringMaker::type>::type>::convert(e); - } - - template - std::string convertUnknownEnumToString( E e ) { - return ::Catch::Detail::stringify(static_cast::type>(e)); - } - -#if defined(_MANAGED) - template - std::string stringify( T^ e ) { - return ::Catch::StringMaker::convert(e); - } -#endif - - } // namespace Detail - - // Some predefined specializations - - template<> - struct StringMaker { - static std::string convert(const std::string& str); - }; - -#ifdef CATCH_CONFIG_CPP17_STRING_VIEW - template<> - struct StringMaker { - static std::string convert(std::string_view str); - }; -#endif - - template<> - struct StringMaker { - static std::string convert(char const * str); - }; - template<> - struct StringMaker { - static std::string convert(char * str); - }; - -#ifdef CATCH_CONFIG_WCHAR - template<> - struct StringMaker { - static std::string convert(const std::wstring& wstr); - }; - -# ifdef CATCH_CONFIG_CPP17_STRING_VIEW - template<> - struct StringMaker { - static std::string convert(std::wstring_view str); - }; -# endif - - template<> - struct StringMaker { - static std::string convert(wchar_t const * str); - }; - template<> - struct StringMaker { - static std::string convert(wchar_t * str); - }; -#endif - - // TBD: Should we use `strnlen` to ensure that we don't go out of the buffer, - // while keeping string semantics? - template - struct StringMaker { - static std::string convert(char const* str) { - return ::Catch::Detail::stringify(std::string{ str }); - } - }; - template - struct StringMaker { - static std::string convert(signed char const* str) { - return ::Catch::Detail::stringify(std::string{ reinterpret_cast(str) }); - } - }; - template - struct StringMaker { - static std::string convert(unsigned char const* str) { - return ::Catch::Detail::stringify(std::string{ reinterpret_cast(str) }); - } - }; - - template<> - struct StringMaker { - static std::string convert(int value); - }; - template<> - struct StringMaker { - static std::string convert(long value); - }; - template<> - struct StringMaker { - static std::string convert(long long value); - }; - template<> - struct StringMaker { - static std::string convert(unsigned int value); - }; - template<> - struct StringMaker { - static std::string convert(unsigned long value); - }; - template<> - struct StringMaker { - static std::string convert(unsigned long long value); - }; - - template<> - struct StringMaker { - static std::string convert(bool b); - }; - - template<> - struct StringMaker { - static std::string convert(char c); - }; - template<> - struct StringMaker { - static std::string convert(signed char c); - }; - template<> - struct StringMaker { - static std::string convert(unsigned char c); - }; - - template<> - struct StringMaker { - static std::string convert(std::nullptr_t); - }; - - template<> - struct StringMaker { - static std::string convert(float value); - }; - template<> - struct StringMaker { - static std::string convert(double value); - }; - - template - struct StringMaker { - template - static std::string convert(U* p) { - if (p) { - return ::Catch::Detail::rawMemoryToString(p); - } else { - return "nullptr"; - } - } - }; - - template - struct StringMaker { - static std::string convert(R C::* p) { - if (p) { - return ::Catch::Detail::rawMemoryToString(p); - } else { - return "nullptr"; - } - } - }; - -#if defined(_MANAGED) - template - struct StringMaker { - static std::string convert( T^ ref ) { - return ::Catch::Detail::clrReferenceToString(ref); - } - }; -#endif - - namespace Detail { - template - std::string rangeToString(InputIterator first, 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 { - static std::string convert(NSString * nsstring) { - if (!nsstring) - return "nil"; - return std::string("@") + [nsstring UTF8String]; - } - }; - template<> - struct StringMaker { - static std::string convert(NSObject* nsObject) { - return ::Catch::Detail::stringify([nsObject description]); - } - - }; - namespace Detail { - inline std::string stringify( NSString* nsstring ) { - return StringMaker::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 -# define CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER -#endif - -// Separate std::pair specialization -#if defined(CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER) -#include -namespace Catch { - template - struct StringMaker > { - static std::string convert(const std::pair& pair) { - ReusableStringStream rss; - rss << "{ " - << ::Catch::Detail::stringify(pair.first) - << ", " - << ::Catch::Detail::stringify(pair.second) - << " }"; - return rss.str(); - } - }; -} -#endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER - -#if defined(CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_OPTIONAL) -#include -namespace Catch { - template - struct StringMaker > { - static std::string convert(const std::optional& optional) { - ReusableStringStream rss; - if (optional.has_value()) { - rss << ::Catch::Detail::stringify(*optional); - } else { - rss << "{ }"; - } - return rss.str(); - } - }; -} -#endif // CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER - -// Separate std::tuple specialization -#if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER) -#include -namespace Catch { - namespace Detail { - template< - typename Tuple, - std::size_t N = 0, - bool = (N < std::tuple_size::value) - > - struct TupleElementPrinter { - static void print(const Tuple& tuple, std::ostream& os) { - os << (N ? ", " : " ") - << ::Catch::Detail::stringify(std::get(tuple)); - TupleElementPrinter::print(tuple, os); - } - }; - - template< - typename Tuple, - std::size_t N - > - struct TupleElementPrinter { - static void print(const Tuple&, std::ostream&) {} - }; - - } - - template - struct StringMaker> { - static std::string convert(const std::tuple& tuple) { - ReusableStringStream rss; - rss << '{'; - Detail::TupleElementPrinter>::print(tuple, rss.get()); - rss << " }"; - return rss.str(); - } - }; -} -#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER - -#if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT) -#include -namespace Catch { - template<> - struct StringMaker { - static std::string convert(const std::monostate&) { - return "{ }"; - } - }; - - template - struct StringMaker> { - static std::string convert(const std::variant& variant) { - if (variant.valueless_by_exception()) { - return "{valueless variant}"; - } else { - return std::visit( - [](const auto& value) { - return ::Catch::Detail::stringify(value); - }, - variant - ); - } - } - }; -} -#endif // CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER - -namespace Catch { - 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 - struct is_range { - static const bool value = - !std::is_same())), not_this_one>::value && - !std::is_same())), not_this_one>::value; - }; - -#if defined(_MANAGED) // Managed types are never ranges - template - struct is_range { - static const bool value = false; - }; -#endif - - template - std::string rangeToString( Range const& range ) { - return ::Catch::Detail::rangeToString( begin( range ), end( range ) ); - } - - // Handle vector specially - template - std::string rangeToString( std::vector const& v ) { - ReusableStringStream rss; - rss << "{ "; - bool first = true; - for( bool b : v ) { - if( first ) - first = false; - else - rss << ", "; - rss << ::Catch::Detail::stringify( b ); - } - rss << " }"; - return rss.str(); - } - - template - struct StringMaker::value && !::Catch::Detail::IsStreamInsertable::value>::type> { - static std::string convert( R const& range ) { - return rangeToString( range ); - } - }; - - template - struct StringMaker { - static std::string convert(T const(&arr)[SZ]) { - return rangeToString(arr); - } - }; - -} // namespace Catch - -// Separate std::chrono::duration specialization -#if defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) -#include -#include -#include - -namespace Catch { - -template -struct ratio_string { - static std::string symbol(); -}; - -template -std::string ratio_string::symbol() { - Catch::ReusableStringStream rss; - rss << '[' << Ratio::num << '/' - << Ratio::den << ']'; - return rss.str(); -} -template <> -struct ratio_string { - static std::string symbol(); -}; -template <> -struct ratio_string { - static std::string symbol(); -}; -template <> -struct ratio_string { - static std::string symbol(); -}; -template <> -struct ratio_string { - static std::string symbol(); -}; -template <> -struct ratio_string { - static std::string symbol(); -}; -template <> -struct ratio_string { - static std::string symbol(); -}; - - //////////// - // std::chrono::duration specializations - template - struct StringMaker> { - static std::string convert(std::chrono::duration const& duration) { - ReusableStringStream rss; - rss << duration.count() << ' ' << ratio_string::symbol() << 's'; - return rss.str(); - } - }; - template - struct StringMaker>> { - static std::string convert(std::chrono::duration> const& duration) { - ReusableStringStream rss; - rss << duration.count() << " s"; - return rss.str(); - } - }; - template - struct StringMaker>> { - static std::string convert(std::chrono::duration> const& duration) { - ReusableStringStream rss; - rss << duration.count() << " m"; - return rss.str(); - } - }; - template - struct StringMaker>> { - static std::string convert(std::chrono::duration> const& duration) { - ReusableStringStream rss; - rss << duration.count() << " h"; - return rss.str(); - } - }; - - //////////// - // std::chrono::time_point specialization - // Generic time_point cannot be specialized, only std::chrono::time_point - template - struct StringMaker> { - static std::string convert(std::chrono::time_point const& time_point) { - return ::Catch::Detail::stringify(time_point.time_since_epoch()) + " since epoch"; - } - }; - // std::chrono::time_point specialization - template - struct StringMaker> { - static std::string convert(std::chrono::time_point const& time_point) { - auto converted = std::chrono::system_clock::to_time_t(time_point); - -#ifdef _MSC_VER - std::tm timeInfo = {}; - gmtime_s(&timeInfo, &converted); -#else - std::tm* timeInfo = std::gmtime(&converted); -#endif - - auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); - char timeStamp[timeStampSize]; - const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; - -#ifdef _MSC_VER - std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); -#else - std::strftime(timeStamp, timeStampSize, fmt, timeInfo); -#endif - return std::string(timeStamp); - } - }; -} -#endif // CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -// end catch_tostring.h -#include - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable:4389) // '==' : signed/unsigned mismatch -#pragma warning(disable:4018) // more "signed/unsigned mismatch" -#pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform) -#pragma warning(disable:4180) // qualifier applied to function type has no meaning -#pragma warning(disable:4800) // Forcing result to true or false -#endif - -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 - class BinaryExpr : public ITransientExpression { - LhsT m_lhs; - StringRef m_op; - RhsT m_rhs; - - void streamReconstructedExpression( std::ostream &os ) const override { - formatReconstructedExpression - ( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) ); - } - - public: - BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs ) - : ITransientExpression{ true, comparisonResult }, - m_lhs( lhs ), - m_op( op ), - m_rhs( rhs ) - {} - - template - auto operator && ( T ) const -> BinaryExpr const { - static_assert(always_false::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - - template - auto operator || ( T ) const -> BinaryExpr const { - static_assert(always_false::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - - template - auto operator == ( T ) const -> BinaryExpr const { - static_assert(always_false::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - - template - auto operator != ( T ) const -> BinaryExpr const { - static_assert(always_false::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - - template - auto operator > ( T ) const -> BinaryExpr const { - static_assert(always_false::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - - template - auto operator < ( T ) const -> BinaryExpr const { - static_assert(always_false::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - - template - auto operator >= ( T ) const -> BinaryExpr const { - static_assert(always_false::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - - template - auto operator <= ( T ) const -> BinaryExpr const { - static_assert(always_false::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - }; - - template - class UnaryExpr : public ITransientExpression { - LhsT m_lhs; - - void streamReconstructedExpression( std::ostream &os ) const override { - os << Catch::Detail::stringify( m_lhs ); - } - - public: - explicit UnaryExpr( LhsT lhs ) - : ITransientExpression{ false, static_cast(lhs) }, - m_lhs( lhs ) - {} - }; - - // Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int) - template - auto compareEqual( LhsT const& lhs, RhsT const& rhs ) -> bool { return static_cast(lhs == rhs); } - template - auto compareEqual( T* const& lhs, int rhs ) -> bool { return lhs == reinterpret_cast( rhs ); } - template - auto compareEqual( T* const& lhs, long rhs ) -> bool { return lhs == reinterpret_cast( rhs ); } - template - auto compareEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) == rhs; } - template - auto compareEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) == rhs; } - - template - auto compareNotEqual( LhsT const& lhs, RhsT&& rhs ) -> bool { return static_cast(lhs != rhs); } - template - auto compareNotEqual( T* const& lhs, int rhs ) -> bool { return lhs != reinterpret_cast( rhs ); } - template - auto compareNotEqual( T* const& lhs, long rhs ) -> bool { return lhs != reinterpret_cast( rhs ); } - template - auto compareNotEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) != rhs; } - template - auto compareNotEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) != rhs; } - - template - class ExprLhs { - LhsT m_lhs; - public: - explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {} - - template - auto operator == ( RhsT const& rhs ) -> BinaryExpr const { - return { compareEqual( m_lhs, rhs ), m_lhs, "==", rhs }; - } - auto operator == ( bool rhs ) -> BinaryExpr const { - return { m_lhs == rhs, m_lhs, "==", rhs }; - } - - template - auto operator != ( RhsT const& rhs ) -> BinaryExpr const { - return { compareNotEqual( m_lhs, rhs ), m_lhs, "!=", rhs }; - } - auto operator != ( bool rhs ) -> BinaryExpr const { - return { m_lhs != rhs, m_lhs, "!=", rhs }; - } - - template - auto operator > ( RhsT const& rhs ) -> BinaryExpr const { - return { static_cast(m_lhs > rhs), m_lhs, ">", rhs }; - } - template - auto operator < ( RhsT const& rhs ) -> BinaryExpr const { - return { static_cast(m_lhs < rhs), m_lhs, "<", rhs }; - } - template - auto operator >= ( RhsT const& rhs ) -> BinaryExpr const { - return { static_cast(m_lhs >= rhs), m_lhs, ">=", rhs }; - } - template - auto operator <= ( RhsT const& rhs ) -> BinaryExpr const { - return { static_cast(m_lhs <= rhs), m_lhs, "<=", rhs }; - } - - template - auto operator && ( RhsT const& ) -> BinaryExpr const { - static_assert(always_false::value, - "operator&& is not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - - template - auto operator || ( RhsT const& ) -> BinaryExpr const { - static_assert(always_false::value, - "operator|| is not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - - auto makeUnaryExpr() const -> UnaryExpr { - return UnaryExpr{ m_lhs }; - } - }; - - void handleExpression( ITransientExpression const& expr ); - - template - void handleExpression( ExprLhs const& expr ) { - handleExpression( expr.makeUnaryExpr() ); - } - - struct Decomposer { - template - auto operator <= ( T const& lhs ) -> ExprLhs { - return ExprLhs{ lhs }; - } - - auto operator <=( bool value ) -> ExprLhs { - return ExprLhs{ value }; - } - }; - -} // end namespace Catch - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -// end catch_decomposer.h -// start catch_interfaces_capture.h - -#include - -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 - void handleExpr( ExprLhs 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 -#include - -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 - 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 - 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 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 - void captureValues( size_t index, T const& value ) { - captureValue( index, Catch::Detail::stringify( value ) ); - } - - template - void captureValues( size_t index, T const& value, Ts const&... values ) { - captureValue( index, Catch::Detail::stringify(value) ); - captureValues( index+1, values... ); - } - }; - -} // end namespace Catch - -// 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( !!(__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(__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(__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(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(__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 - -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 - -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 - -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 - -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 -#include - -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 -#include - -namespace Catch { - - class TestCase; - struct ITestCaseRegistry; - struct IExceptionTranslatorRegistry; - struct IExceptionTranslator; - struct IReporterRegistry; - struct IReporterFactory; - struct ITagAliasRegistry; - class StartupExceptionRegistry; - - using IReporterFactoryPtr = std::shared_ptr; - - 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 -#include -#include - -namespace Catch { - using exceptionTranslateFunction = std::string(*)(); - - struct IExceptionTranslator; - using ExceptionTranslators = std::vector>; - - 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 - 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 - ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) { - getMutableRegistryHub().registerTranslator - ( new ExceptionTranslator( 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 - -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 ::value>::type> - Approx operator()( T const& value ) { - Approx approx( static_cast(value) ); - approx.m_epsilon = m_epsilon; - approx.m_margin = m_margin; - approx.m_scale = m_scale; - return approx; - } - - template ::value>::type> - explicit Approx( T const& value ): Approx(static_cast(value)) - {} - - template ::value>::type> - friend bool operator == ( const T& lhs, Approx const& rhs ) { - auto lhs_v = static_cast(lhs); - return rhs.equalityComparisonImpl(lhs_v); - } - - template ::value>::type> - friend bool operator == ( Approx const& lhs, const T& rhs ) { - return operator==( rhs, lhs ); - } - - template ::value>::type> - friend bool operator != ( T const& lhs, Approx const& rhs ) { - return !operator==( lhs, rhs ); - } - - template ::value>::type> - friend bool operator != ( Approx const& lhs, T const& rhs ) { - return !operator==( rhs, lhs ); - } - - template ::value>::type> - friend bool operator <= ( T const& lhs, Approx const& rhs ) { - return static_cast(lhs) < rhs.m_value || lhs == rhs; - } - - template ::value>::type> - friend bool operator <= ( Approx const& lhs, T const& rhs ) { - return lhs.m_value < static_cast(rhs) || lhs == rhs; - } - - template ::value>::type> - friend bool operator >= ( T const& lhs, Approx const& rhs ) { - return static_cast(lhs) > rhs.m_value || lhs == rhs; - } - - template ::value>::type> - friend bool operator >= ( Approx const& lhs, T const& rhs ) { - return lhs.m_value > static_cast(rhs) || lhs == rhs; - } - - template ::value>::type> - Approx& epsilon( T const& newEpsilon ) { - double epsilonAsDouble = static_cast(newEpsilon); - setEpsilon(epsilonAsDouble); - return *this; - } - - template ::value>::type> - Approx& margin( T const& newMargin ) { - double marginAsDouble = static_cast(newMargin); - setMargin(marginAsDouble); - return *this; - } - - template ::value>::type> - Approx& scale( T const& newScale ) { - m_scale = static_cast(newScale); - return *this; - } - - std::string toString() const; - - private: - double m_epsilon; - double m_margin; - double m_scale; - double m_value; - }; -} // 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 { - static std::string convert(Catch::Detail::Approx const& value); -}; - -} // end namespace Catch - -// end catch_approx.h -// start catch_string_manip.h - -#include -#include - -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 -#include - -namespace Catch { -namespace Matchers { - namespace Impl { - - template struct MatchAllOf; - template struct MatchAnyOf; - template 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 - struct MatcherMethod { - virtual bool match( ObjectT const& arg ) const = 0; - }; - -#ifdef __clang__ -# pragma clang diagnostic pop -#endif - - template - struct MatcherBase : MatcherUntypedBase, MatcherMethod { - - MatchAllOf operator && ( MatcherBase const& other ) const; - MatchAnyOf operator || ( MatcherBase const& other ) const; - MatchNotOf operator ! () const; - }; - - template - struct MatchAllOf : MatcherBase { - 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& operator && ( MatcherBase const& other ) { - m_matchers.push_back( &other ); - return *this; - } - - std::vector const*> m_matchers; - }; - template - struct MatchAnyOf : MatcherBase { - - 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& operator || ( MatcherBase const& other ) { - m_matchers.push_back( &other ); - return *this; - } - - std::vector const*> m_matchers; - }; - - template - struct MatchNotOf : MatcherBase { - - MatchNotOf( MatcherBase 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 const& m_underlyingMatcher; - }; - - template - MatchAllOf MatcherBase::operator && ( MatcherBase const& other ) const { - return MatchAllOf() && *this && other; - } - template - MatchAnyOf MatcherBase::operator || ( MatcherBase const& other ) const { - return MatchAnyOf() || *this || other; - } - template - MatchNotOf MatcherBase::operator ! () const { - return MatchNotOf( *this ); - } - - } // namespace Impl - -} // namespace Matchers - -using namespace Matchers; -using Matchers::Impl::MatcherBase; - -} // namespace Catch - -// end catch_matchers.h -// start catch_matchers_floating.h - -#include -#include - -namespace Catch { -namespace Matchers { - - namespace Floating { - - enum class FloatingPointKind : uint8_t; - - struct WithinAbsMatcher : MatcherBase { - 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 { - 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 -#include - -namespace Catch { -namespace Matchers { -namespace Generic { - -namespace Detail { - std::string finalizeDescription(const std::string& desc); -} - -template -class PredicateMatcher : public MatcherBase { - std::function m_predicate; - std::string m_description; -public: - - PredicateMatcher(std::function 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 is hard (but possible) and - // requires a lot of TMP. - template - Generic::PredicateMatcher Predicate(std::function const& predicate, std::string const& description = "") { - return Generic::PredicateMatcher(predicate, description); - } - -} // namespace Matchers -} // namespace Catch - -// end catch_matchers_generic.hpp -// start catch_matchers_string.h - -#include - -namespace Catch { -namespace Matchers { - - namespace StdString { - - struct CasedString - { - CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ); - std::string adjustString( std::string const& str ) const; - std::string caseSensitivitySuffix() const; - - CaseSensitive::Choice m_caseSensitivity; - std::string m_str; - }; - - struct StringMatcherBase : MatcherBase { - StringMatcherBase( std::string const& operation, CasedString const& comparator ); - 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 { - 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 - -namespace Catch { -namespace Matchers { - - namespace Vector { - namespace Detail { - template - 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 - bool contains(InputIterator first, InputIterator last, T const& item) { - for (; first != last; ++first) { - if (*first == item) { - return true; - } - } - return false; - } - } - - template - struct ContainsElementMatcher : MatcherBase> { - - ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {} - - bool match(std::vector 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 - struct ContainsMatcher : MatcherBase> { - - ContainsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} - - bool match(std::vector 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 const& m_comparator; - }; - - template - struct EqualsMatcher : MatcherBase> { - - EqualsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} - - bool match(std::vector 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 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 const& m_comparator; - }; - - template - struct UnorderedEqualsMatcher : MatcherBase> { - UnorderedEqualsMatcher(std::vector const& target) : m_target(target) {} - bool match(std::vector const& vec) const override { - // Note: This is a reimplementation of std::is_permutation, - // because I don't want to include 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 const& m_target; - }; - - } // namespace Vector - - // The following functions create the actual matcher objects. - // This allows the types to be inferred - - template - Vector::ContainsMatcher Contains( std::vector const& comparator ) { - return Vector::ContainsMatcher( comparator ); - } - - template - Vector::ContainsElementMatcher VectorContains( T const& comparator ) { - return Vector::ContainsElementMatcher( comparator ); - } - - template - Vector::EqualsMatcher Equals( std::vector const& comparator ) { - return Vector::EqualsMatcher( comparator ); - } - - template - Vector::UnorderedEqualsMatcher UnorderedEquals(std::vector const& target) { - return Vector::UnorderedEqualsMatcher(target); - } - -} // namespace Matchers -} // namespace Catch - -// end catch_matchers_vector.h -namespace Catch { - - template - 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; - - void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString ); - - template - auto makeMatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef const& matcherString ) -> MatchExpr { - return MatchExpr( 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(__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 - -namespace Catch { - - namespace Generators { - class GeneratorUntypedBase { - public: - GeneratorUntypedBase() = default; - virtual ~GeneratorUntypedBase(); - // Attempts to move the generator to the next element - // - // Returns true iff the move succeeded (and a valid element - // can be retrieved). - virtual bool next() = 0; - }; - using GeneratorBasePtr = std::unique_ptr; - - } // 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; - }; - -} // namespace Catch - -// end catch_interfaces_generatortracker.h -// start catch_enforce.h - -#include - -namespace Catch { -#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) - template - [[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 -#include -#include - -#include -#include - -namespace Catch { - -class GeneratorException : public std::exception { - const char* const m_msg = ""; - -public: - GeneratorException(const char* msg): - m_msg(msg) - {} - - const char* what() const noexcept override final; -}; - -namespace Generators { - - // !TBD move this into its own location? - namespace pf{ - template - std::unique_ptr make_unique( Args&&... args ) { - return std::unique_ptr(new T(std::forward(args)...)); - } - } - - template - struct IGenerator : GeneratorUntypedBase { - virtual ~IGenerator() = default; - - // Returns the current element of the generator - // - // \Precondition The generator is either freshly constructed, - // or the last call to `next()` returned true - virtual T const& get() const = 0; - using type = T; - }; - - template - class SingleValueGenerator final : public IGenerator { - T m_value; - public: - SingleValueGenerator(T const& value) : m_value( value ) {} - SingleValueGenerator(T&& value) : m_value(std::move(value)) {} - - T const& get() const override { - return m_value; - } - bool next() override { - return false; - } - }; - - template - class FixedValuesGenerator final : public IGenerator { - std::vector m_values; - size_t m_idx = 0; - public: - FixedValuesGenerator( std::initializer_list values ) : m_values( values ) {} - - T const& get() const override { - return m_values[m_idx]; - } - bool next() override { - ++m_idx; - return m_idx < m_values.size(); - } - }; - - template - class GeneratorWrapper final { - std::unique_ptr> m_generator; - public: - GeneratorWrapper(std::unique_ptr> generator): - m_generator(std::move(generator)) - {} - T const& get() const { - return m_generator->get(); - } - bool next() { - return m_generator->next(); - } - }; - - template - GeneratorWrapper value(T&& value) { - return GeneratorWrapper(pf::make_unique>(std::forward(value))); - } - template - GeneratorWrapper values(std::initializer_list values) { - return GeneratorWrapper(pf::make_unique>(values)); - } - - template - class Generators : public IGenerator { - std::vector> m_generators; - size_t m_current = 0; - - void populate(GeneratorWrapper&& generator) { - m_generators.emplace_back(std::move(generator)); - } - void populate(T&& val) { - m_generators.emplace_back(value(std::move(val))); - } - template - void populate(U&& val) { - populate(T(std::move(val))); - } - template - void populate(U&& valueOrGenerator, Gs... moreGenerators) { - populate(std::forward(valueOrGenerator)); - populate(std::forward(moreGenerators)...); - } - - public: - template - Generators(Gs... moreGenerators) { - m_generators.reserve(sizeof...(Gs)); - populate(std::forward(moreGenerators)...); - } - - T const& get() const override { - return m_generators[m_current].get(); - } - - bool next() override { - if (m_current >= m_generators.size()) { - return false; - } - const bool current_status = m_generators[m_current].next(); - if (!current_status) { - ++m_current; - } - return m_current < m_generators.size(); - } - }; - - template - GeneratorWrapper> table( std::initializer_list::type...>> tuples ) { - return values>( tuples ); - } - - // Tag type to signal that a generator sequence should convert arguments to a specific type - template - struct as {}; - - template - auto makeGenerators( GeneratorWrapper&& generator, Gs... moreGenerators ) -> Generators { - return Generators(std::move(generator), std::forward(moreGenerators)...); - } - template - auto makeGenerators( GeneratorWrapper&& generator ) -> Generators { - return Generators(std::move(generator)); - } - template - auto makeGenerators( T&& val, Gs... moreGenerators ) -> Generators { - return makeGenerators( value( std::forward( val ) ), std::forward( moreGenerators )... ); - } - template - auto makeGenerators( as, U&& val, Gs... moreGenerators ) -> Generators { - return makeGenerators( value( T( std::forward( val ) ) ), std::forward( moreGenerators )... ); - } - - template - class TakeGenerator : public IGenerator { - GeneratorWrapper m_generator; - size_t m_returned = 0; - size_t m_target; - public: - TakeGenerator(size_t target, GeneratorWrapper&& generator): - m_generator(std::move(generator)), - m_target(target) - { - assert(target != 0 && "Empty generators are not allowed"); - } - T const& get() const override { - return m_generator.get(); - } - bool next() override { - ++m_returned; - if (m_returned >= m_target) { - return false; - } - - const auto success = m_generator.next(); - // If the underlying generator does not contain enough values - // then we cut short as well - if (!success) { - m_returned = m_target; - } - return success; - } - }; - - template - GeneratorWrapper take(size_t target, GeneratorWrapper&& generator) { - return GeneratorWrapper(pf::make_unique>(target, std::move(generator))); - } - - template - class FilterGenerator : public IGenerator { - GeneratorWrapper m_generator; - Predicate m_predicate; - public: - template - FilterGenerator(P&& pred, GeneratorWrapper&& generator): - m_generator(std::move(generator)), - m_predicate(std::forward

(pred)) - { - if (!m_predicate(m_generator.get())) { - // It might happen that there are no values that pass the - // filter. In that case we throw an exception. - auto has_initial_value = next(); - if (!has_initial_value) { - Catch::throw_exception(GeneratorException("No valid value found in filtered generator")); - } - } - } - - T const& get() const override { - return m_generator.get(); - } - - bool next() override { - bool success = m_generator.next(); - if (!success) { - return false; - } - while (!m_predicate(m_generator.get()) && (success = m_generator.next()) == true); - return success; - } - }; - - template - GeneratorWrapper filter(Predicate&& pred, GeneratorWrapper&& generator) { - return GeneratorWrapper(std::unique_ptr>(pf::make_unique>(std::forward(pred), std::move(generator)))); - } - - template - class RepeatGenerator : public IGenerator { - GeneratorWrapper m_generator; - mutable std::vector m_returned; - size_t m_target_repeats; - size_t m_current_repeat = 0; - size_t m_repeat_index = 0; - public: - RepeatGenerator(size_t repeats, GeneratorWrapper&& generator): - m_generator(std::move(generator)), - m_target_repeats(repeats) - { - assert(m_target_repeats > 0 && "Repeat generator must repeat at least once"); - } - - T const& get() const override { - if (m_current_repeat == 0) { - m_returned.push_back(m_generator.get()); - return m_returned.back(); - } - return m_returned[m_repeat_index]; - } - - bool next() override { - // There are 2 basic cases: - // 1) We are still reading the generator - // 2) We are reading our own cache - - // In the first case, we need to poke the underlying generator. - // If it happily moves, we are left in that state, otherwise it is time to start reading from our cache - if (m_current_repeat == 0) { - const auto success = m_generator.next(); - if (!success) { - ++m_current_repeat; - } - return m_current_repeat < m_target_repeats; - } - - // In the second case, we need to move indices forward and check that we haven't run up against the end - ++m_repeat_index; - if (m_repeat_index == m_returned.size()) { - m_repeat_index = 0; - ++m_current_repeat; - } - return m_current_repeat < m_target_repeats; - } - }; - - template - GeneratorWrapper repeat(size_t repeats, GeneratorWrapper&& generator) { - return GeneratorWrapper(pf::make_unique>(repeats, std::move(generator))); - } - - template - class MapGenerator : public IGenerator { - // TBD: provide static assert for mapping function, for friendly error message - GeneratorWrapper m_generator; - Func m_function; - // To avoid returning dangling reference, we have to save the values - T m_cache; - public: - template - MapGenerator(F2&& function, GeneratorWrapper&& generator) : - m_generator(std::move(generator)), - m_function(std::forward(function)), - m_cache(m_function(m_generator.get())) - {} - - T const& get() const override { - return m_cache; - } - bool next() override { - const auto success = m_generator.next(); - if (success) { - m_cache = m_function(m_generator.get()); - } - return success; - } - }; - - template - GeneratorWrapper map(Func&& function, GeneratorWrapper&& generator) { - return GeneratorWrapper( - pf::make_unique>(std::forward(function), std::move(generator)) - ); - } - template - GeneratorWrapper map(Func&& function, GeneratorWrapper&& generator) { - return GeneratorWrapper( - pf::make_unique>(std::forward(function), std::move(generator)) - ); - } - - auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker&; - - template - // 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().get()) { - using UnderlyingType = typename decltype(generatorExpression())::type; - - IGeneratorTracker& tracker = acquireGeneratorTracker( lineInfo ); - if (!tracker.hasGenerator()) { - tracker.setGenerator(pf::make_unique>(generatorExpression())); - } - - auto const& generator = static_cast const&>( *tracker.getGenerator() ); - return generator.get(); - } - -} // 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 -#include -#include - -#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 const& _tags, - SourceLineInfo const& _lineInfo ); - - friend void setTags( TestCaseInfo& testCaseInfo, std::vector 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 tags; - std::vector 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 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 - -#include - -// 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{ - 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 -#include -#include - -namespace Catch { - - class TestSpec { - struct Pattern { - virtual ~Pattern(); - virtual bool matches( TestCaseInfo const& testCase ) const = 0; - }; - using PatternPtr = std::shared_ptr; - - 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 m_patterns; - - bool matches( TestCaseInfo const& testCase ) const; - }; - - public: - bool hasFilters() const; - bool matches( TestCaseInfo const& testCase ) const; - - private: - std::vector 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 - -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 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 - 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( token ); - if( m_exclusion ) - pattern = std::make_shared( 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 -#include -#include -#include - -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 const& getSectionsToRun() const = 0; - virtual Verbosity verbosity() const = 0; - }; - - using IConfigPtr = std::shared_ptr; -} - -// end catch_interfaces_config.h -// Libstdc++ doesn't like incomplete classes for unique_ptr - -#include -#include -#include - -#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 testsOrTags; - std::vector 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 const& getTestsOrTags() const; - std::vector 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 m_stream; - TestSpec m_testSpec; - bool m_hasTestFilters = false; - }; - -} // end namespace Catch - -// end catch_config.hpp -// start catch_assertionresult.h - -#include - -namespace Catch { - - struct AssertionResultData - { - AssertionResultData() = delete; - - AssertionResultData( ResultWas::OfType _resultType, LazyExpression const& _lazyExpression ); - - std::string message; - mutable std::string reconstructedExpression; - LazyExpression lazyExpression; - ResultWas::OfType resultType; - - std::string reconstructExpression() const; - }; - - class AssertionResult { - public: - AssertionResult() = delete; - AssertionResult( AssertionInfo const& info, AssertionResultData const& data ); - - bool isOk() const; - bool succeeded() const; - ResultWas::OfType getResultType() const; - bool hasExpression() const; - bool hasMessage() const; - std::string getExpression() const; - std::string getExpressionInMacro() const; - bool hasExpandedExpression() const; - std::string getExpandedExpression() const; - 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 - 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 -#include -#include -#include -#include - -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 - struct LazyStat : Option { - LazyStat& operator=( T const& _value ) { - Option::operator=( _value ); - used = false; - return *this; - } - void reset() { - Option::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 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 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 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; - - struct IReporterFactory { - virtual ~IReporterFactory(); - virtual IStreamingReporterPtr create( ReporterConfig const& config ) const = 0; - virtual std::string getDescription() const = 0; - }; - using IReporterFactoryPtr = std::shared_ptr; - - struct IReporterRegistry { - using FactoryMap = std::map; - using Listeners = std::vector; - - 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 -#include -#include -#include -#include -#include -#include - -namespace Catch { - void prepareExpandedExpression(AssertionResult& result); - - // Returns double formatted as %.3f (format expected on output) - std::string getFormattedDuration( double duration ); - - template - 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 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 currentTestRunInfo; - LazyStat currentGroupInfo; - LazyStat currentTestCaseInfo; - - std::vector m_sectionStack; - ReporterPreferences m_reporterPrefs; - }; - - template - struct CumulativeReporterBase : IStreamingReporter { - template - struct Node { - explicit Node( T const& _value ) : value( _value ) {} - virtual ~Node() {} - - using ChildNodes = std::vector>; - 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 const& other) const { - return operator==(*other); - } - - SectionStats stats; - using ChildSections = std::vector>; - using Assertions = std::vector; - 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 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; - using TestGroupNode = Node; - using TestRunNode = Node; - - 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 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 node; - if( m_sectionStack.empty() ) { - if( !m_rootSection ) - m_rootSection = std::make_shared( 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( 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( 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(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(testGroupStats); - node->children.swap(m_testCases); - m_testGroups.push_back(node); - } - void testRunEnded(TestRunStats const& testRunStats) override { - auto node = std::make_shared(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 m_assertions; - std::vector>> m_sections; - std::vector> m_testCases; - std::vector> m_testGroups; - - std::vector> m_testRuns; - - std::shared_ptr m_rootSection; - std::shared_ptr m_deepestSection; - std::vector> m_sectionStack; - ReporterPreferences m_reporterPrefs; - }; - - template - 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( ReporterConfig const& _config ); - - static std::set 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 - class ReporterRegistrar { - - class ReporterFactory : public IReporterFactory { - - virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override { - return std::unique_ptr( 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() ); - } - }; - - template - class ListenerRegistrar { - - class ListenerFactory : public IReporterFactory { - - virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override { - return std::unique_ptr( new T( config ) ); - } - virtual std::string getDescription() const override { - return std::string(); - } - }; - - public: - - ListenerRegistrar() { - getMutableRegistryHub().registerListener( std::make_shared() ); - } - }; -} - -#if !defined(CATCH_CONFIG_DISABLE) - -#define CATCH_REGISTER_REPORTER( name, reporterType ) \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - namespace{ Catch::ReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } \ - CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS - -#define CATCH_REGISTER_LISTENER( listenerType ) \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - namespace{ Catch::ListenerRegistrar 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 { - - 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 { - std::unique_ptr 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 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 - -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 - 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 - 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 m_tags; - std::string m_indent; - std::ostream& m_os; - }; - -} - -// end catch_xmlwriter.h -namespace Catch { - - class JunitReporter : public CumulativeReporterBase { - 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 { - 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 -#include -#include - -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; - - 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 isGeneratorTracker() 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; - 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 isGeneratorTracker() const override; - - void open(); - - void close() override; - void fail() override; - void markAsNeedingAnotherRun() override; - - private: - void moveToParent(); - void moveToThis(); - }; - - class SectionTracker : public TrackerBase { - std::vector m_filters; - public: - SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); - - bool isSectionTracker() const override; - - bool isComplete() const override; - - static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ); - - void tryOpen(); - - void addInitialFilters( std::vector const& filters ); - void addNextFilters( std::vector const& filters ); - }; - -} // namespace TestCaseTracking - -using TestCaseTracking::ITracker; -using TestCaseTracking::TrackerContext; -using TestCaseTracking::SectionTracker; - -} // 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 -#include - -namespace { - -// Performs equivalent check of std::fabs(lhs - rhs) <= margin -// But without the subtraction to allow for INFINITY in comparison -bool marginComparison(double lhs, double rhs, double margin) { - return (lhs + margin >= rhs) && (rhs + margin >= lhs); -} - -} - -namespace Catch { -namespace Detail { - - Approx::Approx ( double value ) - : m_epsilon( std::numeric_limits::epsilon()*100 ), - m_margin( 0.0 ), - m_scale( 0.0 ), - m_value( value ) - {} - - Approx Approx::custom() { - return Approx( 0 ); - } - - Approx Approx::operator-() const { - auto temp(*this); - temp.m_value = -temp.m_value; - return temp; - } - - std::string Approx::toString() const { - ReusableStringStream rss; - rss << "Approx( " << ::Catch::Detail::stringify( m_value ) << " )"; - return rss.str(); - } - - bool Approx::equalityComparisonImpl(const double other) const { - // First try with fixed margin, then compute margin based on epsilon, scale and Approx's value - // Thanks to Richard Harris for his help refining the scaled margin value - return marginComparison(m_value, other, m_margin) || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(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::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 - -namespace Catch { - - struct IResultCapture; - struct IRunner; - struct IConfig; - struct IMutableContext; - - using IConfigPtr = std::shared_ptr; - - 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 - - #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 - #define CATCH_BREAK_INTO_DEBUGGER() []{}() -#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 -#else -#include -#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 - -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 - -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 = nullptr; - Option m_lastResult; - - IConfigPtr m_config; - Totals m_totals; - IStreamingReporterPtr m_reporter; - std::vector m_messages; - AssertionInfo m_lastAssertionInfo; - std::vector m_unfinishedSections; - std::vector 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; - - // 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 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() && __cplusplus >= 201703L -#include -#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 -#include -#include -#include - -#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 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 m_columns; - -public: - - class iterator { - friend Columns; - struct EndTag {}; - - std::vector const& m_columns; - std::vector 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 -#include -#include -#include - -#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 - struct UnaryLambdaTraits : UnaryLambdaTraits {}; - - template - struct UnaryLambdaTraits { - static const bool isValid = false; - }; - - template - struct UnaryLambdaTraits { - static const bool isValid = true; - using ArgType = typename std::remove_const::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 m_args; - - public: - Args( int argc, char const* const* argv ) - : m_exeName(argv[0]), - m_args(argv + 1, argv + argc) {} - - Args( std::initializer_list 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::const_iterator; - Iterator it; - Iterator itEnd; - std::vector 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 - 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 : public ResultBase { - protected: - using ResultBase::ResultBase; - }; - - template - class BasicResult : public ResultValueBase { - public: - template - explicit BasicResult( BasicResult const &other ) - : ResultValueBase( other.type() ), - m_errorMessage( other.errorMessage() ) - { - assert( type() != ResultBase::Ok ); - } - - template - 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(type), - m_errorMessage(message) - { - assert( m_type != ResultBase::Ok ); - } - - using ResultValueBase::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; - using ParserResult = BasicResult; - using InternalParseResult = BasicResult; - - struct HelpColumns { - std::string left; - std::string right; - }; - - template - 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( ::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 - inline auto convertInto( std::string const &source, CLARA_CONFIG_OPTIONAL_TYPE& 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 - 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 - struct BoundValueRef> : BoundValueRefBase { - std::vector &m_ref; - - explicit BoundValueRef( std::vector &ref ) : m_ref( ref ) {} - - auto isContainer() const -> bool override { return true; } - - auto setValue( std::string const &arg ) -> ParserResult override { - T temp; - auto result = convertInto( arg, temp ); - if( result ) - m_ref.push_back( temp ); - return result; - } - }; - - struct BoundFlagRef : BoundFlagRefBase { - bool &m_ref; - - explicit BoundFlagRef( bool &ref ) : m_ref( ref ) {} - - auto setFlag( bool flag ) -> ParserResult override { - m_ref = flag; - return ParserResult::ok( ParseResultType::Matched ); - } - }; - - template - struct LambdaInvoker { - static_assert( std::is_same::value, "Lambda must return void or clara::ParserResult" ); - - template - static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { - return lambda( arg ); - } - }; - - template<> - struct LambdaInvoker { - template - static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { - lambda( arg ); - return ParserResult::ok( ParseResultType::Matched ); - } - }; - - template - inline auto invokeLambda( L const &lambda, std::string const &arg ) -> ParserResult { - ArgType temp{}; - auto result = convertInto( arg, temp ); - return !result - ? result - : LambdaInvoker::ReturnType>::invoke( lambda, temp ); - } - - template - struct BoundLambda : BoundValueRefBase { - L m_lambda; - - static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); - explicit BoundLambda( L const &lambda ) : m_lambda( lambda ) {} - - auto setValue( std::string const &arg ) -> ParserResult override { - return invokeLambda::ArgType>( m_lambda, arg ); - } - }; - - template - struct BoundFlagLambda : BoundFlagRefBase { - L m_lambda; - - static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); - static_assert( std::is_same::ArgType, bool>::value, "flags must be boolean" ); - - explicit BoundFlagLambda( L const &lambda ) : m_lambda( lambda ) {} - - auto setFlag( bool flag ) -> ParserResult override { - return LambdaInvoker::ReturnType>::invoke( m_lambda, flag ); - } - }; - - enum class Optionality { Optional, Required }; - - 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 - class ComposableParserImpl : public ParserBase { - public: - template - auto operator|( T const &other ) const -> Parser; - - template - auto operator+( T const &other ) const -> Parser; - }; - - // Common code and state for Args and Opts - template - class ParserRefImpl : public ComposableParserImpl { - protected: - Optionality m_optionality = Optionality::Optional; - std::shared_ptr m_ref; - std::string m_hint; - std::string m_description; - - explicit ParserRefImpl( std::shared_ptr const &ref ) : m_ref( ref ) {} - - public: - template - ParserRefImpl( T &ref, std::string const &hint ) - : m_ref( std::make_shared>( ref ) ), - m_hint( hint ) - {} - - template - ParserRefImpl( LambdaT const &ref, std::string const &hint ) - : m_ref( std::make_shared>( ref ) ), - m_hint(hint) - {} - - auto operator()( std::string const &description ) -> DerivedT & { - m_description = description; - return static_cast( *this ); - } - - auto optional() -> DerivedT & { - m_optionality = Optionality::Optional; - return static_cast( *this ); - }; - - auto required() -> DerivedT & { - m_optionality = Optionality::Required; - return static_cast( *this ); - }; - - auto isOptional() const -> bool { - return m_optionality == Optionality::Optional; - } - - auto cardinality() const -> size_t override { - if( m_ref->isContainer() ) - return 0; - else - return 1; - } - - auto hint() const -> std::string { return m_hint; } - }; - - class ExeName : public ComposableParserImpl { - std::shared_ptr m_name; - std::shared_ptr m_ref; - - template - static auto makeRef(LambdaT const &lambda) -> std::shared_ptr { - return std::make_shared>( lambda) ; - } - - public: - ExeName() : m_name( std::make_shared( "" ) ) {} - - explicit ExeName( std::string &ref ) : ExeName() { - m_ref = std::make_shared>( ref ); - } - - template - explicit ExeName( LambdaT const& lambda ) : ExeName() { - m_ref = std::make_shared>( lambda ); - } - - // The exe name is not parsed out of the normal tokens, but is handled specially - 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 { - 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( 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 { - protected: - std::vector m_optNames; - - public: - template - explicit Opt( LambdaT const &ref ) : ParserRefImpl( std::make_shared>( ref ) ) {} - - explicit Opt( bool &ref ) : ParserRefImpl( std::make_shared( ref ) ) {} - - template - Opt( LambdaT const &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} - - template - 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 { - 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( 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( 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( *this ) - ("display usage information") - ["-?"]["-h"]["--help"] - .optional(); - } - }; - - struct Parser : ParserBase { - - mutable ExeName m_exeName; - std::vector m_options; - std::vector 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 - auto operator|( T const &other ) const -> Parser { - return Parser( *this ) |= other; - } - - // Forward deprecated interface with '+' instead of '|' - template - auto operator+=( T const &other ) -> Parser & { return operator|=( other ); } - template - auto operator+( T const &other ) const -> Parser { return operator|( other ); } - - auto getHelpColumns() const -> std::vector { - std::vector 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 - template - auto ComposableParserImpl::operator|( T const &other ) const -> Parser { - return Parser() | static_cast( *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 -#include - -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( 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( 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 -#include - -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 const& Config::getTestsOrTags() const { return m_data.testsOrTags; } - std::vector 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 - -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 - -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 ) { - getCurrentContext().getConfig()->stream() - << '\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 - -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 -# include -# include -# include -# include -# include -# include - -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 - #include - - namespace Catch{ - // The standard POSIX way of detecting a debugger is to attempt to - // ptrace() the process, but this needs to be done from a child and not - // this process itself to still allow attaching to this process later - // if wanted, so is rather heavy. Under Linux we have the PID of the - // "debugger" (which doesn't need to be gdb, of course, it could also - // be strace, for example) in /proc/$PID/status, so just get it from - // there instead. - bool isDebuggerActive(){ - // Libstdc++ has a bug, where std::ifstream sets errno to 0 - // This way our users can properly assert over errno values - ErrnoGuard guard; - std::ifstream in("/proc/self/status"); - for( std::string line; std::getline(in, line); ) { - static const int PREFIX_LEN = 11; - if( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) { - // We're traced if the PID is not 0 and no other PID starts - // with 0 digit, so it's enough to check for just a single - // character. - return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; - } - } - - return false; - } - } // namespace Catch -#elif defined(_MSC_VER) - extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); - namespace Catch { - bool isDebuggerActive() { - return IsDebuggerPresent() != 0; - } - } -#elif defined(__MINGW32__) - extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); - namespace Catch { - bool isDebuggerActive() { - return IsDebuggerPresent() != 0; - } - } -#else - namespace Catch { - bool isDebuggerActive() { return false; } - } -#endif // Platform -// 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 - -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 -#include -#include - -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> 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( 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"; - } - } - - std::string ExceptionTranslatorRegistry::tryTranslators() const { - if (m_translators.empty()) { - std::rethrow_exception(std::current_exception()); - } else { - return m_translators[0]->translate(m_translators.begin() + 1, m_translators.end()); - } - } - -#else // ^^ Exceptions are enabled // Exceptions are disabled vv - std::string ExceptionTranslatorRegistry::translateActiveException() const { - CATCH_INTERNAL_ERROR("Attempted to translate active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); - } - - std::string ExceptionTranslatorRegistry::tryTranslators() const { - CATCH_INTERNAL_ERROR("Attempted to use exception translators under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); - } -#endif - -} -// 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 = ""; - 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 -#include - -namespace Catch { - - struct IConfig; - - std::mt19937& rng(); - void seedRng( IConfig const& config ); - unsigned int rngSeed(); - -} - -// end catch_random_number_generator.h -#include -#include - -namespace Catch { - -IGeneratorTracker::~IGeneratorTracker() {} - -const char* GeneratorException::what() const noexcept { - return m_msg; -} - -namespace Generators { - - GeneratorUntypedBase::~GeneratorUntypedBase() {} - - auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { - return getResultCapture().acquireGeneratorTracker( lineInfo ); - } - -} // 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; - 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 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 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 - -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 - -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 spellings; - std::size_t count = 0; - }; - - std::size_t listTags( Config const& config ); - - std::size_t listReporters(); - - Option 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 -#include -#include - -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 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 tagCounts; - - std::vector 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 list( Config const& config ) { - Option 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 - -namespace Catch { - template - 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 -#include -#include - -namespace Catch { -namespace Matchers { -namespace Floating { -enum class FloatingPointKind : uint8_t { - Float, - Double -}; -} -} -} - -namespace { - -template -struct Converter; - -template <> -struct Converter { - 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 { - 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 -auto convert(T t) -> Converter { - return Converter(t); -} - -template -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(static_cast(matchee), static_cast(m_target), m_ulps); - case FloatingPointKind::Double: - return almostEqualUlps(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 - -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 -#include - -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 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 -#include -#include - -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 -#include -#include -#include -#include - -#if defined(CATCH_CONFIG_NEW_CAPTURE) - #if defined(_MSC_VER) - #include //_dup and _dup2 - #define dup _dup - #define dup2 _dup2 - #define fileno _fileno - #else - #include // dup and dup2 - #endif -#endif - -namespace Catch { - - RedirectedStream::RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream ) - : m_originalStream( originalStream ), - m_redirectionStream( redirectionStream ), - m_prevBuf( m_originalStream.rdbuf() ) - { - m_originalStream.rdbuf( m_redirectionStream.rdbuf() ); - } - - RedirectedStream::~RedirectedStream() { - m_originalStream.rdbuf( m_prevBuf ); - } - - RedirectedStdOut::RedirectedStdOut() : m_cout( Catch::cout(), m_rss.get() ) {} - auto RedirectedStdOut::str() const -> std::string { return m_rss.str(); } - - RedirectedStdErr::RedirectedStdErr() - : m_cerr( Catch::cerr(), m_rss.get() ), - m_clog( Catch::clog(), m_rss.get() ) - {} - auto RedirectedStdErr::str() const -> std::string { return m_rss.str(); } - -#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 - -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 -#include -#include -#include - -namespace Catch { - - class TestCase; - struct IConfig; - - std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ); - bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); - - void enforceNoDuplicateTestCases( std::vector const& functions ); - - std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); - std::vector const& getAllTestCasesSorted( IConfig const& config ); - - class TestRegistry : public ITestCaseRegistry { - public: - virtual ~TestRegistry() = default; - - virtual void registerTest( TestCase const& testCase ); - - std::vector const& getAllTests() const override; - std::vector const& getAllTestsSorted( IConfig const& config ) const override; - - private: - std::vector m_functions; - mutable RunTests::InWhatOrder m_currentSortOrder = RunTests::InDeclarationOrder; - mutable std::vector 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 - -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 - -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 - -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 m_registry; - }; - -} // end namespace Catch - -// end catch_tag_alias_registry.h -// start catch_startup_exception_registry.h - -#include -#include - -namespace Catch { - - class StartupExceptionRegistry { - public: - void add(std::exception_ptr const& exception) noexcept; - std::vector const& getExceptions() const noexcept; - private: - std::vector 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 - 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; - - 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( static_cast( lhs ) | static_cast( rhs ) ); - } - - bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } - bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; } - -} // end namespace Catch -// end catch_result_type.cpp -// start catch_run_context.cpp - -#include -#include -#include - -namespace Catch { - - namespace Generators { - struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker { - GeneratorBasePtr m_generator; - - GeneratorTracker( TestCaseTracking::NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) - : TrackerBase( nameAndLocation, ctx, parent ) - {} - ~GeneratorTracker(); - - static GeneratorTracker& acquire( TrackerContext& ctx, TestCaseTracking::NameAndLocation const& nameAndLocation ) { - std::shared_ptr tracker; - - ITracker& currentTracker = ctx.currentTracker(); - if( TestCaseTracking::ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { - assert( childTracker ); - assert( childTracker->isGeneratorTracker() ); - tracker = std::static_pointer_cast( childTracker ); - } - else { - tracker = std::make_shared( nameAndLocation, ctx, ¤tTracker ); - currentTracker.addChild( tracker ); - } - - if( !ctx.completedCycle() && !tracker->isComplete() ) { - tracker->open(); - } - - return *tracker; - } - - // TrackerBase interface - bool isGeneratorTracker() const override { return true; } - auto hasGenerator() const -> bool override { - return !!m_generator; - } - void close() override { - TrackerBase::close(); - // Generator interface only finds out if it has another item on atual move - if (m_runState == CompletedSuccessfully && m_generator->next()) { - m_children.clear(); - m_runState = Executing; - } - } - - // IGeneratorTracker interface - auto getGenerator() const -> GeneratorBasePtr const& override { - return m_generator; - } - void setGenerator( GeneratorBasePtr&& generator ) override { - m_generator = std::move( generator ); - } - }; - 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(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(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(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 - -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 - 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 m_config; - bool m_startupExceptions = false; - }; - -} // end namespace Catch - -// end catch_session.h -// start catch_version.h - -#include - -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 -#include - -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 const& config) { - if (Catch::getRegistryHub().getReporterRegistry().getListeners().empty()) { - return createReporter(config->getReporterName(), config); - } - - // On older platforms, returning std::unique_ptr - // when the return type is std::unique_ptr - // doesn't compile without a std::move call. However, this causes - // a warning on newer platforms. Thus, we have to work around - // it a bit and downcast the pointer manually. - auto ret = std::unique_ptr(new ListeningReporter); - auto& multi = static_cast(*ret); - 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 ret; - } - - Catch::Totals runTests(std::shared_ptr 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&>(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(std::getchar()); - } - int exitCode = runInternal(); - if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) { - Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << std::endl; - static_cast(std::getchar()); - } - return exitCode; - } - - clara::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( 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 listed = list( config() ) ) - return static_cast( *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(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 - -namespace Catch { - - namespace { - static auto getSingletons() -> std::vector*& { - static std::vector* g_singletons = nullptr; - if( !g_singletons ) - g_singletons = new std::vector(); - return g_singletons; - } - } - - ISingleton::~ISingleton() {} - - 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 const& StartupExceptionRegistry::getExceptions() const noexcept { - return m_exceptions; - } - -} // end namespace Catch -// end catch_startup_exception_registry.cpp -// start catch_stream.cpp - -#include -#include -#include -#include -#include -#include - -namespace Catch { - - Catch::IStream::~IStream() = default; - - namespace detail { namespace { - template - class StreamBufImpl : public std::streambuf { - char data[bufferSize]; - WriterF m_writer; - - public: - StreamBufImpl() { - setp( data, data + sizeof(data) ); - } - - ~StreamBufImpl() noexcept { - StreamBufImpl::sync(); - } - - private: - int overflow( int c ) override { - sync(); - - if( c != EOF ) { - if( pbase() == epptr() ) - m_writer( std::string( 1, static_cast( c ) ) ); - else - sputc( static_cast( c ) ); - } - return 0; - } - - int sync() override { - if( pbase() != pptr() ) { - m_writer( std::string( pbase(), static_cast( pptr() - pbase() ) ) ); - setp( pbase(), epptr() ); - } - return 0; - } - }; - - /////////////////////////////////////////////////////////////////////////// - - struct OutputDebugWriter { - - void operator()( std::string const&str ) { - 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> m_streamBuf; - mutable std::ostream m_os; - public: - DebugOutStream() - : m_streamBuf( new StreamBufImpl() ), - 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> m_streams; - std::vector m_unused; - std::ostringstream m_referenceStream; // Used for copy state/ flags from - - auto add() -> std::size_t { - if( m_unused.empty() ) { - m_streams.push_back( std::unique_ptr( 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::getMutable().add() ), - m_oss( Singleton::getMutable().m_streams[m_index].get() ) - {} - - ReusableStringStream::~ReusableStringStream() { - static_cast( m_oss )->str(""); - m_oss->clear(); - Singleton::getMutable().release( m_index ); - } - - auto ReusableStringStream::str() const -> std::string { - return static_cast( 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 -#include -#include -#include - -namespace Catch { - - namespace { - char toLowerCh(char c) { - return static_cast( 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 -#include -#include - -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(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( 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 - -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 -#include -#include -#include - -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::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(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 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 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.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 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 - -namespace Catch { - - std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { - - std::vector 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 const& functions ) { - std::set 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 filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ) { - std::vector filtered; - filtered.reserve( testCases.size() ); - for( auto const& testCase : testCases ) - if( matchTest( testCase, testSpec, config ) ) - filtered.push_back( testCase ); - return filtered; - } - std::vector 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 const& TestRegistry::getAllTests() const { - return m_functions; - } - std::vector 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 -#include -#include -#include -#include - -#if defined(__clang__) -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wexit-time-destructors" -#endif - -namespace Catch { -namespace TestCaseTracking { - - NameAndLocation::NameAndLocation( std::string const& _name, SourceLineInfo const& _location ) - : name( _name ), - location( _location ) - {} - - ITracker::~ITracker() = default; - - TrackerContext& TrackerContext::instance() { - static TrackerContext s_instance; - return s_instance; - } - - ITracker& TrackerContext::startRun() { - m_rootTracker = std::make_shared( 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::isGeneratorTracker() 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( *parent ); - addNextFilters( parentSection.m_filters ); - } - } - - bool SectionTracker::isComplete() const { - bool complete = true; - - if ((m_filters.empty() || m_filters[0] == "") || - std::find(m_filters.begin(), m_filters.end(), - m_nameAndLocation.name) != m_filters.end()) - complete = TrackerBase::isComplete(); - return complete; - - } - - bool SectionTracker::isSectionTracker() const { return true; } - - SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { - std::shared_ptr section; - - ITracker& currentTracker = ctx.currentTracker(); - if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { - assert( childTracker ); - assert( childTracker->isSectionTracker() ); - section = std::static_pointer_cast( childTracker ); - } - else { - section = std::make_shared( 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 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 const& filters ) { - if( filters.size() > 1 ) - m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() ); - } - -} // namespace TestCaseTracking - -using TestCaseTracking::ITracker; -using TestCaseTracking::TrackerContext; -using TestCaseTracking::SectionTracker; - -} // 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 -#include -#include -#include - -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(); - 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(); - addFilter(); - } - else if( c == '[' ) { - if( subString() == "exclude:" ) - m_exclusion = true; - else - addPattern(); - startNewMode( Tag, ++m_pos ); - } - else if( c == '\\' ) - escape(); - } - else if( m_mode == EscapedName ) - m_mode = Name; - else if( m_mode == QuotedName && c == '"' ) - addPattern(); - else if( m_mode == Tag && c == ']' ) - addPattern(); - } - 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 - -static const uint64_t nanosecondsInSecond = 1000000000; - -namespace Catch { - - auto getCurrentNanosecondsSinceEpoch() -> uint64_t { - return std::chrono::duration_cast( 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 + 1u ); - } - } - - // 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(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 -#include - -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( size ), inc = 1; - if( Endianness::which() == Endianness::Little ) { - i = end-1; - end = inc = -1; - } - - unsigned char const *bytes = static_cast(object); - ReusableStringStream rss; - rss << "0x" << std::setfill('0') << std::hex; - for( ; i != end; i += inc ) - rss << std::setw(2) << static_cast(bytes[i]); - return rss.str(); - } -} - -template -std::string fpToString( T value, int precision ) { - if (Catch::isnan(value)) { - return "nan"; - } - - ReusableStringStream rss; - rss << std::setprecision( precision ) - << std::fixed - << value; - std::string d = rss.str(); - std::size_t i = d.find_last_not_of( '0' ); - if( i != std::string::npos && i != d.size()-1 ) { - if( d[i] == '.' ) - i++; - d = d.substr( 0, i+1 ); - } - return d; -} - -//// ======================================================= //// -// -// Out-of-line defs for full specialization of StringMaker -// -//// ======================================================= //// - -std::string StringMaker::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::convert(std::string_view str) { - return ::Catch::Detail::stringify(std::string{ str }); -} -#endif - -std::string StringMaker::convert(char const* str) { - if (str) { - return ::Catch::Detail::stringify(std::string{ str }); - } else { - return{ "{null string}" }; - } -} -std::string StringMaker::convert(char* str) { - if (str) { - return ::Catch::Detail::stringify(std::string{ str }); - } else { - return{ "{null string}" }; - } -} - -#ifdef CATCH_CONFIG_WCHAR -std::string StringMaker::convert(const std::wstring& wstr) { - std::string s; - s.reserve(wstr.size()); - for (auto c : wstr) { - s += (c <= 0xff) ? static_cast(c) : '?'; - } - return ::Catch::Detail::stringify(s); -} - -# ifdef CATCH_CONFIG_CPP17_STRING_VIEW -std::string StringMaker::convert(std::wstring_view str) { - return StringMaker::convert(std::wstring(str)); -} -# endif - -std::string StringMaker::convert(wchar_t const * str) { - if (str) { - return ::Catch::Detail::stringify(std::wstring{ str }); - } else { - return{ "{null string}" }; - } -} -std::string StringMaker::convert(wchar_t * str) { - if (str) { - return ::Catch::Detail::stringify(std::wstring{ str }); - } else { - return{ "{null string}" }; - } -} -#endif - -std::string StringMaker::convert(int value) { - return ::Catch::Detail::stringify(static_cast(value)); -} -std::string StringMaker::convert(long value) { - return ::Catch::Detail::stringify(static_cast(value)); -} -std::string StringMaker::convert(long long value) { - ReusableStringStream rss; - rss << value; - if (value > Detail::hexThreshold) { - rss << " (0x" << std::hex << value << ')'; - } - return rss.str(); -} - -std::string StringMaker::convert(unsigned int value) { - return ::Catch::Detail::stringify(static_cast(value)); -} -std::string StringMaker::convert(unsigned long value) { - return ::Catch::Detail::stringify(static_cast(value)); -} -std::string StringMaker::convert(unsigned long long value) { - ReusableStringStream rss; - rss << value; - if (value > Detail::hexThreshold) { - rss << " (0x" << std::hex << value << ')'; - } - return rss.str(); -} - -std::string StringMaker::convert(bool b) { - return b ? "true" : "false"; -} - -std::string StringMaker::convert(signed char value) { - if (value == '\r') { - return "'\\r'"; - } else if (value == '\f') { - return "'\\f'"; - } else if (value == '\n') { - return "'\\n'"; - } else if (value == '\t') { - return "'\\t'"; - } else if ('\0' <= value && value < ' ') { - return ::Catch::Detail::stringify(static_cast(value)); - } else { - char chstr[] = "' '"; - chstr[1] = value; - return chstr; - } -} -std::string StringMaker::convert(char c) { - return ::Catch::Detail::stringify(static_cast(c)); -} -std::string StringMaker::convert(unsigned char c) { - return ::Catch::Detail::stringify(static_cast(c)); -} - -std::string StringMaker::convert(std::nullptr_t) { - return "nullptr"; -} - -std::string StringMaker::convert(float value) { - return fpToString(value, 5) + 'f'; -} -std::string StringMaker::convert(double value) { - return fpToString(value, 10); -} - -std::string ratio_string::symbol() { return "a"; } -std::string ratio_string::symbol() { return "f"; } -std::string ratio_string::symbol() { return "p"; } -std::string ratio_string::symbol() { return "n"; } -std::string ratio_string::symbol() { return "u"; } -std::string ratio_string::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 - -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 - -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, 6, 0, "", 0 ); - return version; - } - -} -// end catch_version.cpp -// start catch_wildcard_pattern.cpp - -#include - -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( 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 - -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) { - std::ios_base::fmtflags f(os.flags()); - os << "\\x" - << std::uppercase << std::hex << std::setfill('0') << std::setw(2) - << static_cast(c); - os.flags(f); - } - -} // 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_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 << ""; - m_needsNewline = true; - return *this; - } - - void XmlWriter::writeStylesheetRef( std::string const& url ) { - m_os << "\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 << "\n"; - } - - void XmlWriter::newlineIfNecessary() { - if( m_needsNewline ) { - m_os << std::endl; - m_needsNewline = false; - } - } -} -// end catch_xmlwriter.cpp -// start catch_reporter_bases.cpp - -#include -#include -#include -#include -#include - -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 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::const_iterator itEnd = messages.end(); - const std::size_t N = static_cast(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 messages; - std::vector::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 -#include - -#if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch - // Note that 4062 (not all labels are handled - // and default is missing) is enabled -#endif - -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 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(s_nanosecondsInAMicrosecond); - case Unit::Milliseconds: - return m_inNanoseconds / static_cast(s_nanosecondsInAMillisecond); - case Unit::Seconds: - return m_inNanoseconds / static_cast(s_nanosecondsInASecond); - case Unit::Minutes: - return m_inNanoseconds / static_cast(s_nanosecondsInAMinute); - default: - return static_cast(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 m_columnInfos; - std::ostringstream m_oss; - int m_currentColumn = -1; - bool m_isOpen = false; - -public: - TablePrinter( std::ostream& os, std::vector columnInfos ) - : m_os( os ), - m_columnInfos( std::move( columnInfos ) ) {} - - auto columnInfos() const -> std::vector 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 - 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(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(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( 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 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 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 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 -#include -#include -#include - -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 &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 - -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 ListeningReporter::getSupportedVerbosities() { - return std::set{ }; - } - - 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( 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 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__ ) -#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) -#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_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__ ) ) -#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) ) -#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_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__ ) -#define TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) -#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_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__ ) ) -#define TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) ) -#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_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 ) -#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) -#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) -#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 ) ) -#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) -#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) -#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 ) -#define TEMPLATE_PRODUCT_TEST_CASE( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ ) -#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) -#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 ) ) -#define TEMPLATE_PRODUCT_TEST_CASE( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ ) -#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) -#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/src/Client.cpp b/src/Client.cpp index c867af7..02021a5 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -159,13 +159,11 @@ void Client::write_command(std::vector &buffer, int32_t command_no) cons } bool Client::translate_remove_message(std::vector &message) const { - remove_ending_symbols(message); auto it = message.begin(); return read_bool(it); } std::vector Client::translate_get_currency_history_message(std::vector &message) const { - remove_ending_symbols(message); std::vector history; for (auto it = message.begin(); it < message.end();) { history.push_back(read_int32(it)); @@ -174,13 +172,11 @@ std::vector Client::translate_get_currency_history_message(std::vector< } bool Client::translate_add_message(std::vector &message) const { - remove_ending_symbols(message); auto it = message.begin(); return read_bool(it); } const std::vector Client::translate_list_message(std::vector &message) const { - remove_ending_symbols(message); std::vector currencies; for (auto it = message.begin(); it < message.end();) { std::string currency_name = read_string(it, CURRENCY_NAME_SIZE_IN_LIST); @@ -197,10 +193,6 @@ const std::vector Client::translate_list_message(std::vector & return currencies; } -void Client::remove_ending_symbols(std::vector &message) const { - message.erase(message.end() - 2, message.end()); -} - void Client::write_string(std::vector &buffer, const std::string ¤cy_name) const { std::string string_to_write(currency_name); string_to_write.resize(CURRENCY_NAME_SIZE_IN_LIST, 0); @@ -259,7 +251,7 @@ std::vector Client::construct_message_from_packets(const std::map> &packets, int number_of_packets) const { for (int packet_id = 0; packet_id < number_of_packets; packet_id++) { - if (packets.count(packet_id) != 0) { + if (packets.count(packet_id) == 0) { return false; } } diff --git a/test/ClientTest.cpp b/test/ClientTest.cpp deleted file mode 100644 index 0f5f980..0000000 --- a/test/ClientTest.cpp +++ /dev/null @@ -1,333 +0,0 @@ -#define CATCH_CONFIG_MAIN - -#include -#include -#include -#include "catch.hpp" - -#include "Client.h" - -class ClientTest { -public: - uint16_t PORTNO = 0; - const std::string HOST = "localhost"; -}; - -uint16_t get_current_port(int sockfd); - -void setup_server_returning(const std::vector &expected_to_receive, - const std::vector &return_value, ClientTest &clientTest) { - int sockfd, newsockfd; - unsigned int clilen; - char buffer[256]; - struct sockaddr_in serv_addr{}, cli_addr{}; - sockfd = socket(AF_INET, SOCK_STREAM, 0); - - if (sockfd < 0) { - perror("ERROR opening socket"); - exit(1); - } - - bzero((char *) &serv_addr, sizeof(serv_addr)); - serv_addr.sin_family = AF_INET; - serv_addr.sin_addr.s_addr = INADDR_ANY; - serv_addr.sin_port = htons(0); - - if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { - perror("ERROR on binding"); - exit(1); - } - - clientTest.PORTNO = get_current_port(sockfd); - listen(sockfd, 1); - clilen = sizeof(cli_addr); - newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen); - - if (newsockfd < 0) { - perror("ERROR on accept"); - exit(1); - } - - bzero(buffer, 256); - ssize_t n_read = read(newsockfd, buffer, 255); - if (n_read < 0) { - perror("ERROR reading from socket"); - exit(1); - } - std::vector received(buffer, buffer + n_read); - REQUIRE(received == expected_to_receive); - if (write(newsockfd, return_value.data(), return_value.size()) < 0) { - perror("ERROR writing to socket"); - exit(1); - } - close(newsockfd); - close(sockfd); -} - -uint16_t get_current_port(int sockfd) { - struct sockaddr_in serv_addr{}; - unsigned int clilen = sizeof(serv_addr); - if (getsockname(sockfd, (struct sockaddr *) &serv_addr, &clilen) < 0) { - perror("ERROR getting current port"); - exit(1); - } - return ntohs(serv_addr.sin_port); -} - -TEST_CASE("list empty currencies") { - ClientTest clientTest; - std::vector expected_to_receive = {0, 0, 0, 0, '\\', 0}; - std::vector server_return = {'\\', 0}; - std::thread server_thread(setup_server_returning, expected_to_receive, server_return, std::ref(clientTest)); - while (clientTest.PORTNO == 0) { - std::this_thread::sleep_for(std::chrono::seconds(1)); - } - Client client = Client(clientTest.HOST, clientTest.PORTNO); - std::vector result = client.list(); - REQUIRE(result.empty()); - server_thread.join(); -} - -TEST_CASE("list one currency") { - ClientTest clientTest; - std::vector expected_to_receive = {0, 0, 0, 0, '\\', 0}; - std::vector server_return = {'c', 'u', 'r', 'r', 'e', 'n', 'c', 'y','N', 'a', 'm', 'e', '1', 0, 0, 0, - 1, 1, 1, 1, - 1, - 1, 0, 1, 0, - 12, 0, 0, 0, - '\\', 0}; - std::thread server_thread(setup_server_returning, expected_to_receive, server_return, std::ref(clientTest)); - while (clientTest.PORTNO == 0) { - std::this_thread::sleep_for(std::chrono::seconds(1)); - } - Client client = Client(clientTest.HOST, clientTest.PORTNO); - std::vector result = client.list(); - std::vector expected = {Currency("currencyName1", - (1 << 24) + (1 << 16) + (1 << 8) + (1 << 0), - (1 << 16) + (1 << 0), - 12)}; - REQUIRE(result == expected); - server_thread.join(); -} - - -TEST_CASE("list two currencies") { - ClientTest clientTest; - std::vector expected_to_receive = {0, 0, 0, 0, '\\', 0}; - std::vector server_return = {'c', 'u', 'r', 'r', 'e', 'n', 'c', 'y','N', 'a', 'm', 'e', '1', 0, 0, 0, - 1, 1, 1, 1, - 1, - 1, 0, 1, 0, - 12, 0, 0, 0, - 'c', 'u', 'r', 'r', 'e', 'n', 'c', 'y','N', 'a', 'm', 'e', '2', 'e', 'n', 'd', - 1, 0, 0, 0, - 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - '\\', 0}; - std::thread server_thread(setup_server_returning, expected_to_receive, server_return, std::ref(clientTest)); - while (clientTest.PORTNO == 0) { - std::this_thread::sleep_for(std::chrono::seconds(1)); - } - Client client = Client(clientTest.HOST, clientTest.PORTNO); - std::vector result = client.list(); - std::vector expected = {Currency("currencyName1", - (1 << 24) + (1 << 16) + (1 << 8) + (1 << 0), - (1 << 16) + (1 << 0), - 12), - Currency("currencyName2end", 1)}; - REQUIRE(result == expected); - server_thread.join(); -} - -TEST_CASE("list three currencies") { - ClientTest clientTest; - std::vector expected_to_receive = {0, 0, 0, 0, '\\', 0}; - std::vector server_return = {'c', 'u', 'r', 'r', 'e', 'n', 'c', 'y','N', 'a', 'm', 'e', '1', 0, 0, 0, - 1, 1, 1, 1, - 1, - 1, 0, 1, 0, - 12, 0, 0, 0, - 'c', 'u', 'r', 'r', 'e', 'n', 'c', 'y','N', 'a', 'm', 'e', '2', 'e', 'n', 'd', - 1, 0, 0, 0, - 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 'c', 'u', 'r', 'r', 'e', 'n', 'c', 'y','N', 'a', 'm', 'e', '3', 0, 0, 0, - 1, 0, 1, 0, - 1, - 1, 0, 0, 0, - 1, 0, 0, 0, - '\\', 0}; - std::thread server_thread(setup_server_returning, expected_to_receive, server_return, std::ref(clientTest)); - while (clientTest.PORTNO == 0) { - std::this_thread::sleep_for(std::chrono::seconds(1)); - } - Client client = Client(clientTest.HOST, clientTest.PORTNO); - std::vector result = client.list(); - std::vector expected = {Currency("currencyName1", - (1 << 0) + (1 << 8) + (1 << 16) + (1 << 24), - (1 << 0) + (1 << 16), - 12), - Currency("currencyName2end", 1), - Currency("currencyName3", - (1 << 0) + (1 << 16), - (1 << 0), - (1 << 0))}; - REQUIRE(result == expected); - server_thread.join(); -} - -TEST_CASE("add non-existing currency") { - ClientTest clientTest; - Currency currency_to_add("currencyName", (3 << 0) + (44 << 8) + (22 << 24)); - std::vector expected_to_receive = {1, 0, 0, 0, - 'c', 'u', 'r', 'r', 'e', 'n', 'c', 'y', 'N', 'a', 'm', 'e', 0, 0, 0, 0, - 3, 44, 0, 22, - '\\', 0}; - std::vector server_return = {1, '\\', 0}; - std::thread server_thread(setup_server_returning, expected_to_receive, server_return, std::ref(clientTest)); - while (clientTest.PORTNO == 0) { - std::this_thread::sleep_for(std::chrono::seconds(1)); - } - Client client = Client(clientTest.HOST, clientTest.PORTNO); - REQUIRE(client.addCurrency(currency_to_add)); - server_thread.join(); -} - -TEST_CASE("add existing currency") { - ClientTest clientTest; - Currency currency_to_add("currencyName", (3 << 0) + (44 << 8) + (22 << 24)); - std::vector expected_to_receive = {1, 0, 0, 0, - 'c', 'u', 'r', 'r', 'e', 'n', 'c', 'y', 'N', 'a', 'm', 'e', 0, 0, 0, 0, - 3, 44, 0, 22, - '\\', 0}; - std::vector server_return = {0, '\\', 0}; - std::thread server_thread(setup_server_returning, expected_to_receive, server_return, std::ref(clientTest)); - while (clientTest.PORTNO == 0) { - std::this_thread::sleep_for(std::chrono::seconds(1)); - } - Client client = Client(clientTest.HOST, clientTest.PORTNO); - REQUIRE(!client.addCurrency(currency_to_add)); - server_thread.join(); -} - -TEST_CASE("remove non-existing currency") { - ClientTest clientTest; - Currency currency_to_remove("currencyName"); - std::vector expected_to_receive = {2, 0, 0, 0, - 'c', 'u', 'r', 'r', 'e', 'n', 'c', 'y', 'N', 'a', 'm', 'e', 0, 0, 0, 0, - '\\', 0}; - std::vector server_return = {0, '\\', 0}; - std::thread server_thread(setup_server_returning, expected_to_receive, server_return, std::ref(clientTest)); - while (clientTest.PORTNO == 0) { - std::this_thread::sleep_for(std::chrono::seconds(1)); - } - Client client = Client(clientTest.HOST, clientTest.PORTNO); - REQUIRE(!client.remove(currency_to_remove)); - server_thread.join(); -} - -TEST_CASE("remove existing currency") { - ClientTest clientTest; - Currency currency_to_remove("currencyName"); - std::vector expected_to_receive = {2, 0, 0, 0, - 'c', 'u', 'r', 'r', 'e', 'n', 'c', 'y', 'N', 'a', 'm', 'e', 0, 0, 0, 0, - '\\', 0}; - std::vector server_return = {1, '\\', 0}; - std::thread server_thread(setup_server_returning, expected_to_receive, server_return, std::ref(clientTest)); - while (clientTest.PORTNO == 0) { - std::this_thread::sleep_for(std::chrono::seconds(1)); - } - Client client = Client(clientTest.HOST, clientTest.PORTNO); - REQUIRE(client.remove(currency_to_remove)); - server_thread.join(); -} - -TEST_CASE("add rate to non-existing currency") { - ClientTest clientTest; - Currency currency_to_add("currencyName"); - int32_t new_rate = (3 << 0) + (44 << 8) + (11 << 16) + (22 << 24); - std::vector expected_to_receive = {3, 0, 0, 0, - 'c', 'u', 'r', 'r', 'e', 'n', 'c', 'y', 'N', 'a', 'm', 'e', 0, 0, 0, 0, - 3, 44, 11, 22, - '\\', 0}; - std::vector server_return = {0, '\\', 0}; - std::thread server_thread(setup_server_returning, expected_to_receive, server_return, std::ref(clientTest)); - while (clientTest.PORTNO == 0) { - std::this_thread::sleep_for(std::chrono::seconds(1)); - } - Client client = Client(clientTest.HOST, clientTest.PORTNO); - REQUIRE(!client.addRate(currency_to_add, new_rate)); - server_thread.join(); -} - -TEST_CASE("add rate to existing currency") { - ClientTest clientTest; - Currency currency_to_add("currencyNameExit"); - int32_t new_rate = (3 << 0) + (44 << 8) + (11 << 16) + (22 << 24); - std::vector expected_to_receive = {3, 0, 0, 0, - 'c', 'u', 'r', 'r', 'e', 'n', 'c', 'y', 'N', 'a', 'm', 'e', 'E', 'x', 'i', 't', - 3, 44, 11, 22, - '\\', 0}; - std::vector server_return = {1, '\\', 0}; - std::thread server_thread(setup_server_returning, expected_to_receive, server_return, std::ref(clientTest)); - while (clientTest.PORTNO == 0) { - std::this_thread::sleep_for(std::chrono::seconds(1)); - } - Client client = Client(clientTest.HOST, clientTest.PORTNO); - REQUIRE(client.addRate(currency_to_add, new_rate)); - server_thread.join(); -} - -TEST_CASE("get empty history for non-existing currency") { - ClientTest clientTest; - Currency currency_to_get_history("currencyNameExit"); - std::vector expected_to_receive = {4, 0, 0, 0, - 'c', 'u', 'r', 'r', 'e', 'n', 'c', 'y', 'N', 'a', 'm', 'e', 'E', 'x', 'i', 't', - '\\', 0}; - std::vector server_return = {'\\', 0}; - std::thread server_thread(setup_server_returning, expected_to_receive, server_return, std::ref(clientTest)); - while (clientTest.PORTNO == 0) { - std::this_thread::sleep_for(std::chrono::seconds(1)); - } - Client client = Client(clientTest.HOST, clientTest.PORTNO); - auto result = client.getCurrencyWithHistory(currency_to_get_history); - REQUIRE(result == currency_to_get_history); - server_thread.join(); -} - -TEST_CASE("get one rate history for currency") { - ClientTest clientTest; - Currency currency_to_get_history("currencyName", (33 << 0) + (3 << 8)); - std::vector expected_to_receive = {4, 0, 0, 0, - 'c', 'u', 'r', 'r', 'e', 'n', 'c', 'y', 'N', 'a', 'm', 'e', 0, 0, 0, 0, - '\\', 0}; - std::vector server_return = {33, 3, 0, 0, '\\', 0}; - std::thread server_thread(setup_server_returning, expected_to_receive, server_return, std::ref(clientTest)); - while (clientTest.PORTNO == 0) { - std::this_thread::sleep_for(std::chrono::seconds(1)); - } - Client client = Client(clientTest.HOST, clientTest.PORTNO); - auto result = client.getCurrencyWithHistory(currency_to_get_history); - REQUIRE(result == currency_to_get_history); - server_thread.join(); -} - -TEST_CASE("get two rates history for currency") { - ClientTest clientTest; - Currency currency_to_get_history("currencyName", {(33 << 0) + (3 << 8), 0}); - std::vector expected_to_receive = {4, 0, 0, 0, - 'c', 'u', 'r', 'r', 'e', 'n', 'c', 'y', 'N', 'a', 'm', 'e', 0, 0, 0, 0, - '\\', 0}; - std::vector server_return = {33, 3, 0, 0, 0, 0, 0, 0, '\\', 0}; - std::thread server_thread(setup_server_returning, expected_to_receive, server_return, std::ref(clientTest)); - while (clientTest.PORTNO == 0) { - std::this_thread::sleep_for(std::chrono::seconds(1)); - } - Client client = Client(clientTest.HOST, clientTest.PORTNO); - auto result = client.getCurrencyWithHistory(currency_to_get_history); - REQUIRE(result == currency_to_get_history); - server_thread.join(); -} \ No newline at end of file