diff --git a/Master_Slave_Management/Master_Slave_Management/DynamicBuffer.cpp b/Master_Slave_Management/Master_Slave_Management/DynamicBuffer.cpp
new file mode 100644
index 0000000..57b444c
--- /dev/null
+++ b/Master_Slave_Management/Master_Slave_Management/DynamicBuffer.cpp
@@ -0,0 +1,84 @@
+#include "dynamicBuffer.h"
+
+void DynamicBuffer::writeToFile()
+{
+}
+
+DynamicBuffer::DynamicBuffer(int size)
+ : maxSize(size), bufferIndex(0), altBufferIndex(1), min_time(system_clock::now()), max_time(system_clock::now())
+{
+ buffers[0].reserve(maxSize);
+ buffers[1].reserve(maxSize);
+}
+
+DynamicBuffer::DynamicBuffer(const DynamicBuffer& other)
+ :maxSize(other.maxSize), bufferIndex(other.bufferIndex),
+ altBufferIndex(other.altBufferIndex),min_time(other.min_time),
+ max_time(other.max_time)
+{
+ buffers[0] = other.buffers[0];
+ buffers[1] = other.buffers[1];
+
+}
+
+DynamicBuffer& DynamicBuffer::operator=(const DynamicBuffer& other)
+{
+ if (this != &other)
+ {
+ buffers[0] = other.buffers[0];
+ buffers[1] = other.buffers[1];
+ maxSize = other.maxSize;
+ bufferIndex = other.bufferIndex;
+ altBufferIndex = other.altBufferIndex;
+ min_time = other.min_time;
+ max_time = other.max_time;
+ }
+ return *this;
+}
+
+
+
+int DynamicBuffer::getBufferIndex()
+{
+ return bufferIndex;
+}
+
+
+///
+/// get event and add it to the buffer
+///
+///
+/// true if the current buffer is full and false else
+bool DynamicBuffer::addEvent(const Event& event)
+{
+ bool isFull = false;
+ unique_lock lock(mutex_);
+
+ if (buffers[bufferIndex].size() == maxSize)
+ {
+ isFull = true;
+ swap(bufferIndex, altBufferIndex);
+ }
+
+ buffers[bufferIndex].push_back(event);
+ return isFull;
+}
+
+vector& DynamicBuffer::getBuffer()
+{
+ unique_lock lock(mutex_);
+ return buffers[bufferIndex];
+}
+
+vector& DynamicBuffer::getAltBuffer()
+{
+ return buffers[altBufferIndex];
+}
+
+
+
+void DynamicBuffer::clearAltBuffer()
+{
+ unique_lock lock(mutex_);
+ buffers[altBufferIndex].clear();
+}
diff --git a/Master_Slave_Management/Master_Slave_Management/Event.cpp b/Master_Slave_Management/Master_Slave_Management/Event.cpp
new file mode 100644
index 0000000..8ed8e29
--- /dev/null
+++ b/Master_Slave_Management/Master_Slave_Management/Event.cpp
@@ -0,0 +1,128 @@
+#include "event.h"
+using namespace std;
+
+//compare times
+bool Event::compareTimes(const tm& tm1, const tm& tm2)
+{
+ if (tm1.tm_year != tm2.tm_year) return tm1.tm_year < tm2.tm_year;
+ if (tm1.tm_mon != tm2.tm_mon) return tm1.tm_mon < tm2.tm_mon;
+ if (tm1.tm_mday != tm2.tm_mday) return tm1.tm_mday < tm2.tm_mday;
+ if (tm1.tm_hour != tm2.tm_hour) return tm1.tm_hour < tm2.tm_hour;
+ if (tm1.tm_min != tm2.tm_min) return tm1.tm_min < tm2.tm_min;
+ return tm1.tm_sec < tm2.tm_sec;
+}
+
+Event::Event()
+{
+}
+
+Event::Event(string message)
+ : message(message)
+{
+ time_point now = system_clock::now();
+ time_t now_time_t = system_clock::to_time_t(now);
+ time = *(localtime(&now_time_t));
+}
+
+Event::Event(tm time, string message)
+ :time(time),message(message)
+{
+}
+
+Event::Event(const Event& other)
+ : time(other.time), message(other.message)
+{
+}
+
+Event& Event::operator=(const Event& other)
+{
+ if (this != &other)
+ {
+ time = other.time;
+ message = other.message;
+ }
+ return *this;
+}
+
+bool Event::operator<(const Event& other) const
+{
+ return compareTimes(time, other.time);
+}
+
+bool Event::operator<(const tm& otherTime) const
+{
+ return compareTimes(time,otherTime);
+}
+
+bool Event::operator>(const Event& other) const
+{
+ return other < *this;
+}
+
+bool Event::operator>(const tm& otherTime) const
+{
+ return compareTimes(otherTime,time);
+}
+
+bool Event::operator==(const Event& other) const
+{
+ return !compareTimes(time, other.time) && !compareTimes(other.time, time);
+}
+
+bool Event::operator==(const tm& otherTime) const
+{
+ return !compareTimes(time,otherTime) && !compareTimes(otherTime,time);
+}
+
+bool Event::operator<=(const tm& otherTime) const
+{
+ return !(*this > otherTime);
+}
+
+bool Event::operator>=(const tm& otherTime) const
+{
+ return !(*this < otherTime);
+}
+
+
+tm Event::getTime()
+{
+ return time;
+}
+
+string Event::getMessage()
+{
+ return this->message;
+}
+
+string Event::getFormatTime()
+{
+ char buffer[80];
+ strftime(buffer, sizeof(buffer), "%d-%m-%Y_%H.%M.%S", &time);
+ return string(buffer);
+}
+
+size_t Event::getSize()
+{
+ return sizeof(*this);
+}
+
+string Event::toString()
+{
+ return "date: "+getFormatTime()+ ", message: "+getMessage();
+}
+
+bool operator<(const std::tm& tm1, const std::tm& tm2)
+{
+ return Event::compareTimes(tm1,tm2);
+}
+
+bool operator>=(const std::tm& tm1, const std::tm& tm2)
+{
+ return !Event::compareTimes(tm1,tm2);
+}
+
+bool operator<=(const std::tm& tm1, const std::tm& tm2)
+{
+ return !Event::compareTimes(tm2,tm1);
+}
diff --git a/Master_Slave_Management/Master_Slave_Management/FileHandler.cpp b/Master_Slave_Management/Master_Slave_Management/FileHandler.cpp
new file mode 100644
index 0000000..b51fb8e
--- /dev/null
+++ b/Master_Slave_Management/Master_Slave_Management/FileHandler.cpp
@@ -0,0 +1,256 @@
+#include "fileHandler.h"
+
+FileHandler::FileHandler(const string& baseFileName, size_t maxFiles)
+ :baseFileName(baseFileName), maxFiles(maxFiles)
+{
+ loadMetadata();
+}
+
+FileHandler::FileHandler(const FileHandler& other)
+ :baseFileName(other.baseFileName), maxFiles(other.maxFiles)
+{
+ fileNamesQueue = other.fileNamesQueue;
+}
+
+FileHandler& FileHandler::operator=(const FileHandler& other)
+{
+ if (this != &other)
+ {
+ baseFileName = other.baseFileName;
+ maxFiles = other.maxFiles;
+ fileNamesQueue = other.fileNamesQueue;
+ }
+ return *this;
+}
+
+string FileHandler::writeFileName(string startTimeFormatted, string endTimeFormatted,bool isSortedList)
+{
+ string fileName;
+
+ if (isSortedList)
+ fileName = "sorted_event_list_by_time_range";
+ else
+ fileName = to_string(fileNamesQueue.size()) + "_" + baseFileName;
+ fileName+= "_from_" + startTimeFormatted + "_to_" + endTimeFormatted + ".log";
+ return fileName;
+}
+
+
+void FileHandler::loadMetadata()
+{
+ ifstream metaFile(baseFileName + "_metadata.txt");
+ if (!metaFile.is_open())
+ return;
+
+ string line,fileName, startTimeStr, endTimeStr;
+ istringstream iss;
+ tm tmStart = {}, tmEnd = {};
+ time_point startTime, endTime;
+ while (getline(metaFile,line))
+ {
+ iss.clear();
+ iss.str(line);
+
+ if (!(iss >> fileName >> startTimeStr >> endTimeStr))
+ {
+ cerr << "Failed to read metadata line: " << line << endl;
+ continue;
+ }
+
+ if (!parseTimeString(startTimeStr, tmStart))
+ continue;
+
+ if (!parseTimeString(endTimeStr, tmEnd))
+ continue;
+
+ fileNamesQueue.emplace_back(fileName, tmStart, tmEnd);
+ }
+ metaFile.close();
+}
+
+void FileHandler::saveMetadata()
+{
+ char buffer1[80];
+ char buffer2[80];
+ ofstream metaFile(baseFileName + "_metadata.txt", ios::out | ios::trunc);
+ for (const auto& fileInfo : fileNamesQueue)
+ {
+ strftime(buffer1, sizeof(buffer1), "%d-%m-%Y_%H.%M.%S", &fileInfo.startTime);
+ strftime(buffer2, sizeof(buffer2), "%d-%m-%Y_%H.%M.%S", &fileInfo.endTime);
+ metaFile << fileInfo.fileName << " "
+ << string(buffer1) << " "
+ << string(buffer2) << "\n";
+ }
+}
+
+
+bool FileHandler::parseTimeString(const string& timeStr, tm& tm)
+{
+ istringstream ss(timeStr);
+ ss >> get_time(&tm, "%d-%m-%Y_%H.%M.%S");
+ if(ss.fail())
+ cerr << "Failed to parse time string: " << timeStr << endl;
+ return !ss.fail();
+}
+
+///
+/// Reads the contents of the file found in the received path and returns it as a string.
+///
+/// The file path to read.
+/// the file content.
+string FileHandler::readContentFromFile(string filePath)
+{
+ ifstream inFile;
+ inFile.open(filePath, ios::in | ios::ate);
+ if (inFile.is_open())
+ {
+ streampos fileSize = inFile.tellg();
+ inFile.seekg(0, ios::beg); //Returns the pointer to the beginning of the file
+
+ string fileContent((istreambuf_iterator(inFile)), istreambuf_iterator());
+ inFile.close();
+ return fileContent;
+ }
+ else
+ {
+ return "";
+ //throw runtime_error("Unable to open file: " + filePath);
+ }
+}
+
+///
+/// adding the relevant events to the vector events.
+///
+/// The contents of the file.
+/// The event vector to which it is added.
+/// The maximum size to adding events
+/// The beginning of the time range of the desired events
+/// The end of the time range of the desired events
+void FileHandler::addRelevantEvents(const string& fileContent, vector& events, size_t maxSize,const tm& startTime,const tm& endTime)
+{
+ //Creates a stream from the content of the file.
+ istringstream fileStream(fileContent), iss;
+ string line, timeStr, eventString;
+ size_t currentSize = 0, eventSize;
+ tm tm = {};
+
+
+ // Reads the lines and add them to vector
+ vector lines;
+
+ while (getline(fileStream, line))
+ {
+ lines.push_back(line);
+ }
+
+ //Go over the lines from the end to the beginning.
+ for (auto lineIt = lines.rbegin(); lineIt != lines.rend(); ++lineIt)
+ {
+ iss.clear();
+ iss.str(*lineIt);
+ iss >> timeStr; //read the time from thr line.
+ getline(iss, eventString);//read the string of the event.
+
+ //Checks if the read date is correct and is within the desired time range.
+ if (parseTimeString(timeStr, tm) && tm >= startTime && tm <= endTime)
+ {
+ Event event(tm, eventString);
+ eventSize = event.getSize();
+ if (currentSize + eventSize > maxSize)
+ {
+ return; //Exits the function if the event size has reached the maximum.
+ }
+ events.push_back(event); // Add the event to the temporary vector.
+ currentSize += eventSize;
+ }
+ }
+}
+
+///
+/// Reads the events in the range from the disk and adds them to the event vector.
+///
+/// The event vector to which it is added.
+/// The beginning of the time range of the desired events
+/// The end of the time range of the desired events
+/// The maximum size to adding events
+void FileHandler::readFromDiskAndAddToEvents(vector& events, const tm startTime, const tm endTime, size_t maxSize)
+{
+ string fileContent;
+ tm tm = {};
+ istringstream iss, ss, fileStream;
+
+ //Go through the queue of the list of file names.
+ for (auto it = fileNamesQueue.rbegin(); it != fileNamesQueue.rend(); ++it)
+ {
+ const auto& fileInfo = *it;
+
+ //If the time of the last event in the file is less than the start of the desired time range,
+ //we will terminate the function because there will be no more suitable files.
+ if (fileInfo.endTime< startTime)
+ return;
+ //If the events in the file are not within the time range,
+ //we will continue to the next file.
+ if (endTime < fileInfo.startTime)
+ continue;
+
+ //reads the contents of the file.
+ fileContent= readContentFromFile(fileInfo.fileName);
+
+ //If the content is read correctly,
+ //adds the relevant events to the events list.
+ if(fileContent!= "")
+ addRelevantEvents(fileContent, events, maxSize, startTime, endTime);
+ }
+}
+
+
+///
+/// Writes the list of received events to the file.
+/// If the list is a list accumulated in the buffer - adds the path to the queue that saves the file names.
+///
+/// The list of events to write to the file.
+/// True if it is a sorted list of events from several drones
+/// and false if it is a normal list of events accumulated in the buffer and written to memory.
+/// returns the path to the file
+string FileHandler::writeToFile(vector events, bool isSortedList)
+{
+ if (events.empty())
+ return "";
+
+ tm startTime = events.front().getTime();
+ tm endTime = events.back().getTime();
+
+ string startTimeFormatted = events.front().getFormatTime();
+ string endTimeFormatted = events.back().getFormatTime();
+
+ string fileName = writeFileName(startTimeFormatted, endTimeFormatted, isSortedList);
+
+ //If we have already written a maximum possible number of files, we will overwrite the oldest one
+ if (fileNamesQueue.size() >= maxFiles)
+ {
+ remove(fileNamesQueue.front().fileName.c_str());
+ fileNamesQueue.pop_front();
+ }
+
+ ofstream outFile(fileName);
+
+ if (!outFile) {
+ cerr << "Failed to open file for writing: " << fileName << endl;
+ return "";
+ }
+
+ for (auto& event : events)
+ {
+ outFile << event.getFormatTime() << " " << event.getMessage() << "\n";
+ }
+ outFile.close();
+
+ //adding the file name, start and end time to the fileNamesQueue that save it.
+ if(!isSortedList)
+ {
+ fileNamesQueue.emplace_back(fileName, startTime, endTime);
+ saveMetadata();
+ }
+ return fileName;
+}
+
diff --git a/Master_Slave_Management/Master_Slave_Management/FileInfo.cpp b/Master_Slave_Management/Master_Slave_Management/FileInfo.cpp
new file mode 100644
index 0000000..9e9834a
--- /dev/null
+++ b/Master_Slave_Management/Master_Slave_Management/FileInfo.cpp
@@ -0,0 +1,12 @@
+#include "fileInfo.h"
+
+FileInfo::FileInfo()
+ :fileName("baseName")/*, startTime(system_clock::now()),endTime(system_clock::now())*/
+{
+}
+
+FileInfo::FileInfo(const string& fileName, const tm& start, const tm& end)
+: fileName(fileName), startTime(start), endTime(end)
+{
+}
+
diff --git a/Master_Slave_Management/Master_Slave_Management/MasterEventManager.cpp b/Master_Slave_Management/Master_Slave_Management/MasterEventManager.cpp
new file mode 100644
index 0000000..51f223c
--- /dev/null
+++ b/Master_Slave_Management/Master_Slave_Management/MasterEventManager.cpp
@@ -0,0 +1,148 @@
+#include "masterEventManager.h"
+#include "mockDataManager.h"
+
+int MasterEventManager::reqCounter = 0;
+
+///
+/// Gets a list of event lists and merges them into one sorted event list.
+///
+/// list of event lists
+void MasterEventManager::mergeAndSortEvents(vector>& allEvents)
+{
+ auto compare = [](const pair& a, const pair& b) {
+ return a.first > b.first;
+ };
+ priority_queue, vector>, decltype(compare)> minHeap(compare);
+
+ for (size_t i = 0; i < allEvents.size(); i++)
+ {
+ if (!allEvents[i].empty())
+ {
+ minHeap.emplace(allEvents[i][0],i);
+ }
+ }
+
+ vector indices(allEvents.size(), 0);
+ vector sortedEvents;
+
+ while (!minHeap.empty())
+ {
+ auto topElement = minHeap.top();
+ Event event = topElement.first;
+ size_t listIndex = topElement.second;
+ //auto [event, listIndex] = minHeap.top();
+ minHeap.pop();
+
+ sortedEvents.push_back(event);
+
+ indices[listIndex]++;
+ if (indices[listIndex] < allEvents[listIndex].size())
+ {
+ minHeap.emplace(allEvents[listIndex][indices[listIndex]],listIndex);
+ }
+ }
+ string fileName = fileHandler.writeToFile(sortedEvents, true);
+ cout << "The Sorted List File saves in: " << fileName;
+
+ #ifdef TEST_MODE
+ mockDataManager.sortedListFileName = fileName;
+ #endif
+
+ //Emptying the event lists of the current request
+ currentEventsFromSlaves.allEvents.clear();
+}
+
+MasterEventManager::MasterEventManager()
+ :SlaveEventManager()
+{
+}
+
+MasterEventManager::MasterEventManager(const MasterEventManager& other)
+ :SlaveEventManager(other)
+{
+}
+
+MasterEventManager& MasterEventManager::operator=(const MasterEventManager& other)
+{
+ if (this != &other)
+ {
+ SlaveEventManager::operator=(other);
+ }
+ return *this;
+}
+
+int MasterEventManager::getNumberOfResFromSlaves()
+{
+ return currentEventsFromSlaves.numberOfResFromSlaves;
+}
+
+///
+/// Checks if all the drones have already responded,
+/// and if so sends the event lists for sorting.
+///
+void MasterEventManager::areAllSlavesResponded()
+{
+ if (currentEventsFromSlaves.numberOfReqFromSlaves == currentEventsFromSlaves.numberOfResFromSlaves)
+ mergeAndSortEvents(currentEventsFromSlaves.allEvents);
+}
+
+
+
+///
+/// Receives the response with the event list from the drone and adds it to the event lists only if it belongs to the current request
+/// and check if all the drones have already responded.
+///
+/// The response message received from the drone with the list of events according to the requested range.
+void MasterEventManager::receiveEventsFromSlave(Message* message)
+{
+ vector eventList = message->messageParams->STM_GET_EVENTS_PARAMS.events;
+ int messageReqId = message->reqId;
+ if (messageReqId == currentEventsFromSlaves.reqId)
+ {
+ currentEventsFromSlaves.numberOfResFromSlaves++;
+ currentEventsFromSlaves.allEvents.push_back(eventList);
+ areAllSlavesResponded();
+ }
+}
+
+
+
+///
+/// Sends requests for a list of events in a specified time range to multiple drones.
+///
+/// List of IDs of drones from which you want the sorted list
+/// The beginning of the time range of the desired events
+/// The end of the time range of the desired events
+/// The maximum size of the list of events that will be returned
+
+
+void MasterEventManager::sendReqToEventListFromSlaves(vector slaves, tm& startTime, tm& endTime, size_t maxSize)
+{
+ int reqId = reqCounter++;
+ currentEventsFromSlaves.reqId = reqId;
+ vector> allEvents;
+ size_t maxEventPerSlave = maxSize / slaves.size();
+
+
+ for (int slaveId : slaves)
+ {
+ currentEventsFromSlaves.numberOfReqFromSlaves++;
+ Message* message = new Message();
+ message->reqId = reqId;
+ message->droneId = slaveId;
+ message->messageType = MessageType::MTS_SEND_EVENTS;
+ message->messageParams = new MessageParams();
+ message->messageParams->MTS_SEND_EVENTS_PARAMS.startTime = startTime;
+ message->messageParams->MTS_SEND_EVENTS_PARAMS.endTime = endTime;
+ message->messageParams->MTS_SEND_EVENTS_PARAMS.maxSize = maxEventPerSlave;
+
+ sendMessage(message);
+
+ #ifdef TEST_MODE
+ mockDataManager.receivedMessages[slaveId].push_back(message);
+ #endif
+
+ }
+}
+
+
diff --git a/Master_Slave_Management/Master_Slave_Management/Master_Slave_Management.vcxproj b/Master_Slave_Management/Master_Slave_Management/Master_Slave_Management.vcxproj
index af34aad..89d8961 100644
--- a/Master_Slave_Management/Master_Slave_Management/Master_Slave_Management.vcxproj
+++ b/Master_Slave_Management/Master_Slave_Management/Master_Slave_Management.vcxproj
@@ -17,7 +17,6 @@
Release
x64
-
16.0
@@ -53,27 +52,24 @@
true
Unicode
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Level3
@@ -130,9 +126,32 @@
true
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
\ No newline at end of file
diff --git a/Master_Slave_Management/Master_Slave_Management/Master_Slave_Management.vcxproj.filters b/Master_Slave_Management/Master_Slave_Management/Master_Slave_Management.vcxproj.filters
index 814a456..45eb0d3 100644
--- a/Master_Slave_Management/Master_Slave_Management/Master_Slave_Management.vcxproj.filters
+++ b/Master_Slave_Management/Master_Slave_Management/Master_Slave_Management.vcxproj.filters
@@ -61,5 +61,75 @@
{2854cb75-ba29-441b-8dff-4947a76a6041}
+
+ {a2ad7442-9f50-4578-9d51-86428d0c1240}
+
+
+
+
+ Source Files\Events
+
+
+ Source Files\Events
+
+
+ Source Files\Events
+
+
+ Source Files\Events
+
+
+ Source Files\Events
+
+
+ Source Files\Events
+
+
+ Source Files\Events
+
+
+ Source Files\Events
+
+
+ Source Files\Events\tests
+
+
+
+
+ Header Files\Events
+
+
+ Header Files\Events
+
+
+ Header Files\Events
+
+
+ Header Files\Events
+
+
+ Header Files\Events
+
+
+ Header Files\Events
+
+
+ Header Files\Events
+
+
+ Header Files\Events
+
+
+ Header Files\Events
+
+
+ Header Files\Events
+
+
+ Header Files\Events
+
+
+ Header Files\Events
+
\ No newline at end of file
diff --git a/Master_Slave_Management/Master_Slave_Management/Master_Slave_Management.vcxproj.user b/Master_Slave_Management/Master_Slave_Management/Master_Slave_Management.vcxproj.user
index 88a5509..5df420f 100644
--- a/Master_Slave_Management/Master_Slave_Management/Master_Slave_Management.vcxproj.user
+++ b/Master_Slave_Management/Master_Slave_Management/Master_Slave_Management.vcxproj.user
@@ -1,4 +1,6 @@
ן»¿
-
+
+ false
+
\ No newline at end of file
diff --git a/Master_Slave_Management/Master_Slave_Management/Message.cpp b/Master_Slave_Management/Master_Slave_Management/Message.cpp
new file mode 100644
index 0000000..88c3c88
--- /dev/null
+++ b/Master_Slave_Management/Master_Slave_Management/Message.cpp
@@ -0,0 +1,8 @@
+#include "Message.h"
+
+int Message::counter = 0;
+
+
+
+
+
diff --git a/Master_Slave_Management/Master_Slave_Management/Message.h b/Master_Slave_Management/Master_Slave_Management/Message.h
new file mode 100644
index 0000000..d303320
--- /dev/null
+++ b/Master_Slave_Management/Master_Slave_Management/Message.h
@@ -0,0 +1,38 @@
+#pragma once
+#include "MessageParams.h"
+#include "MessageType.h"
+struct Message
+{
+ int messageId;
+ int reqId;
+ MessageType messageType;
+ int droneId;
+ MessageParams* messageParams;
+ Priority priority;
+
+ Message() :messageId(counter++), messageParams(nullptr) {}
+
+ // Copy constructor
+ /*Message(const Message& message)
+ : messageId(counter++),reqId(message.reqId),messageType(message.messageType),droneId(message.droneId),
+ messageParams(message.messageParams),priority(message.priority)
+ {}*/
+
+ Message& operator=(const Message& other)
+ {
+ if (this != &other)
+ {
+ // Note: messageId should remain unique, so we do not copy it.
+ reqId = other.reqId;
+ messageType = other.messageType;
+ droneId = other.droneId;
+ messageParams = other.messageParams; // Assuming proper copy behavior in MessageParams
+ priority = other.priority;
+ }
+ return *this;
+ }
+
+private:
+ int static counter;
+
+};
\ No newline at end of file
diff --git a/Master_Slave_Management/Master_Slave_Management/MessageParams.h b/Master_Slave_Management/Master_Slave_Management/MessageParams.h
new file mode 100644
index 0000000..59258f0
--- /dev/null
+++ b/Master_Slave_Management/Master_Slave_Management/MessageParams.h
@@ -0,0 +1,173 @@
+#pragma once
+#include
+#include
+#include "Message.h"
+#include "Priority.h"
+#include "event.h"
+#include "MessageType.h"
+
+using namespace std;
+
+typedef struct {
+
+}MTS_SEND_LOCATION_PARAMS;
+typedef struct {
+
+}MTS_MOVE_OUT_OF_THIS_EREA_PARAMS;
+typedef struct {}MTS_STOP_SEND_LOCATION_PARAMS;
+typedef struct {}MTS_STOP_AT_PLACE_PARAMS;
+typedef struct {}MTS_BACK_TO_STARTING_POINT_PARAMS;
+typedef struct {}STM_LOCATION_PARAMS;
+typedef struct MTS_SEND_EVENTS_PARAMS_{
+ tm startTime;
+ tm endTime;
+ size_t maxSize;
+
+}MTS_SEND_EVENTS_PARAMS;
+typedef struct STM_GET_EVENTS_PARAMS {
+ vector events;
+}STM_GET_EVENTS_PARAMS;
+
+//struct RequestToReceiveAMapFromOpenCV
+//{
+// vector realImages;
+// //MESSAGE_TYPE: MTIP_REQ_FOR_REAL_MAP,
+// //handling function:openCV API has to get the message and handle it.
+//};
+
+
+//struct MessageOfNoCommuincationBetwwenAllSlaves
+//{
+// string msg22;
+// //MESSAGE_TYPE:MTM_NO_COMMUNICATION_AT_ALL,
+// //handling function:__________________
+//};
+//struct GetTheSlavesThatDidntCommunicate
+//{
+// vector SlavesWithNoCommunication;
+// //MESSAGE_TYPE:MTM_SLAVES_NOT_COMMUNICATE
+// //handling function:SlavesManager::getTheSlavesThatDidntCommunicate
+//};
+//
+//
+//
+
+
+
+
+union MessageParams
+{
+ MTS_SEND_LOCATION_PARAMS MTS_SEND_LOCATION_PARAMS;
+ MTS_MOVE_OUT_OF_THIS_EREA_PARAMS MTS_MOVE_OUT_OF_THIS_EREA_PARAMS;
+ MTS_STOP_SEND_LOCATION_PARAMS MTS_STOP_SEND_LOCATION_PARAMS;
+ STM_LOCATION_PARAMS STM_LOCATION_PARAMS;
+ MTS_STOP_AT_PLACE_PARAMS MTS_STOP_AT_PLACE_PARAMS;
+ MTS_BACK_TO_STARTING_POINT_PARAMS MTS_BACK_TO_STARTING_POINT_PARAMS;
+ MTS_SEND_EVENTS_PARAMS MTS_SEND_EVENTS_PARAMS;
+ STM_GET_EVENTS_PARAMS STM_GET_EVENTS_PARAMS;
+
+ // בנאי ברירת מחדל
+ MessageParams() {}
+ ~MessageParams() {}
+ //MessageParams(const MessageParams& other)
+ //{
+ // //// Logic to determine which member is active in 'other' and copy it
+ // //if (/* condition for M_GOT_REQ_PARAMS being active */) {
+ // // new (&M_GOT_REQ_PARAMS)struct M_GOT_REQ_PARAMS(other.M_GOT_REQ_PARAMS);
+ // //}
+ // //else if (/* condition for MTS_ASK_FOR_IMAGES_PARAMS being active */) {
+ // // new (&MTS_ASK_FOR_IMAGES_PARAMS)struct MTS_ASK_FOR_IMAGES_PARAMS(other.MTS_ASK_FOR_IMAGES_PARAMS);
+ // //}
+ // // Add other conditions and member copies as needed
+ //}
+
+ // סימון השדה הפעל
+ //MessageType activeField;
+
+
+
+// // מיישם אופרטור השמה
+// MessageParams& operator=(const MessageParams& other)
+// {
+// if (this != &other) {
+// // לנקות את השדה הנוכחי לפני השמה חדשה
+// destroyCurrentActiveField();
+//
+// activeField = other.activeField;
+// switch (activeField) {
+// case MessageType::M_GOT_REQ:
+// new (&M_GOT_REQ_PARAMS)struct M_GOT_REQ_PARAMS(other.M_GOT_REQ_PARAMS);
+// break;
+// case MessageType::MTS_ASK_FOR_IMAGES:
+// new (&MTS_ASK_FOR_IMAGES_PARAMS)struct MTS_ASK_FOR_IMAGES_PARAMS(other.MTS_ASK_FOR_IMAGES_PARAMS);
+// break;
+// case MessageType::STM_SEND_IMAGES_PROP:
+// new (&STM_SEND_IMAGES_PROP_PARAMS)struct STM_SEND_IMAGES_PROP_PARAMS(other.STM_SEND_IMAGES_PROP_PARAMS);
+// break;
+// case MessageType::MTS_GIVE_THE_CHOSEN_IMAGE:
+// new (&MTS_GIVE_THE_CHOSEN_IMAGE_PARAMS)struct MTS_GIVE_THE_CHOSEN_IMAGE_PARAMS(other.MTS_GIVE_THE_CHOSEN_IMAGE_PARAMS);
+// break;
+// case MessageType::STM_RESPONSE_TO_THE_CHOSEN_IMAGE:
+// new (&STM_RESPONSE_TO_THE_CHOSEN_IMAGE_PARAMS) struct STM_RESPONSE_TO_THE_CHOSEN_IMAGE_PARAMS(other.STM_RESPONSE_TO_THE_CHOSEN_IMAGE_PARAMS);
+// break;
+// case MessageType::MTS_SEND_LOCATION:
+// new(&MTS_SEND_LOCATION_PARAMS)struct MTS_SEND_LOCATION_PARAMS(other.MTS_SEND_LOCATION_PARAMS);
+// break;
+// case MessageType::MTS_MOVE_OUT_OF_THIS_EREA:
+// new (&MTS_MOVE_OUT_OF_THIS_EREA_PARAMS) struct MTS_MOVE_OUT_OF_THIS_EREA_PARAMS(other.MTS_MOVE_OUT_OF_THIS_EREA_PARAMS);
+// break;
+// case MessageType::MTS_STOP_SEND_LOCATION:
+// new (&MTS_STOP_SEND_LOCATION_PARAMS)struct MTS_STOP_SEND_LOCATION_PARAMS(other.MTS_STOP_SEND_LOCATION_PARAMS);
+// break;
+// case MessageType::MTS_STOP_AT_PLACE:
+// new (&MTS_STOP_AT_PLACE_PARAMS) struct MTS_STOP_AT_PLACE_PARAMS(other.MTS_STOP_AT_PLACE_PARAMS);
+// break;
+// case MessageType::MTS_BACK_TO_STARTING_POINT:
+// new (&MTS_BACK_TO_STARTING_POINT_PARAMS)struct MTS_BACK_TO_STARTING_POINT_PARAMS(other.MTS_BACK_TO_STARTING_POINT_PARAMS);
+// break;
+// case MessageType::MTS_SEND_EVENTS:
+// new (&MTS_SEND_EVENTS_PARAMS) struct MTS_SEND_EVENTS_PARAMS(other.MTS_SEND_EVENTS_PARAMS);
+// break;
+// case MessageType::STM_GET_EVENTS:
+// new (&STM_GET_EVENTS_PARAMS)struct STM_GET_EVENTS_PARAMS(other.STM_GET_EVENTS_PARAMS);
+// break;
+// default:
+// // לא שדה פעיל
+// activeField = MessageType::size;
+// break;
+// }
+// }
+// return *this;
+// }
+//
+// // הרס אופרטור
+// ~MessageParams()
+// {
+// destroyCurrentActiveField();
+// }
+//
+//private:
+// void destroyCurrentActiveField()
+// {
+// switch (activeField) {
+// case 0:
+// M_GOT_REQ_PARAMS.~M_GOT_REQ_PARAMS();
+// break;
+// case 1:
+// MTS_ASK_FOR_IMAGES_PARAMS.~MTS_ASK_FOR_IMAGES_PARAMS();
+// break;
+// case 2:
+// STM_SEND_IMAGES_PROP_PARAMS.~STM_SEND_IMAGES_PROP_PARAMS();
+// break;
+// // הוסף עוד מקרים לכל שדה ב-union
+// // ...
+// default:
+// break;
+// }
+// }
+};
+
+
+
+
+
diff --git a/Master_Slave_Management/Master_Slave_Management/MessageType.h b/Master_Slave_Management/Master_Slave_Management/MessageType.h
new file mode 100644
index 0000000..58da91c
--- /dev/null
+++ b/Master_Slave_Management/Master_Slave_Management/MessageType.h
@@ -0,0 +1,18 @@
+#pragma once
+enum class MessageType
+{
+ MtoS,
+ StoM,
+ M_GOT_REQ,
+ STM_SEND_IMAGES_PROP,
+ MTS_SEND_LOCATION,
+ MTS_MOVE,
+ MTS_STOP_SEND_LOCATION,
+ MTS_STOP_AT_PLACE,
+ MTS_BACK,
+ MTS_SEND_EVENTS,
+ STM_GET_EVENTS,
+ OTM_SEND_SORTED_EVENT_LIST,
+ MTO_GET_SORTED_EVENT_LIST
+};
+
diff --git a/Master_Slave_Management/Master_Slave_Management/MockDataManager.cpp b/Master_Slave_Management/Master_Slave_Management/MockDataManager.cpp
new file mode 100644
index 0000000..54c6e52
--- /dev/null
+++ b/Master_Slave_Management/Master_Slave_Management/MockDataManager.cpp
@@ -0,0 +1,15 @@
+#include "mockDataManager.h"
+
+MockDataManager mockDataManager;
+
+void mockSendMessage(Message* message) {
+ auto it = mockDataManager.slaveManagers.find(message->droneId);
+ if (it != mockDataManager.slaveManagers.end()) {
+ SlaveEventManager* slave = it->second;
+ slave->getEvents(message);
+ }
+}
+
+void mockReceiveMessages(Message* message) {
+ mockDataManager.masterManager.receiveEventsFromSlave(message);
+}
diff --git a/Master_Slave_Management/Master_Slave_Management/Priority.h b/Master_Slave_Management/Master_Slave_Management/Priority.h
new file mode 100644
index 0000000..19c38f5
--- /dev/null
+++ b/Master_Slave_Management/Master_Slave_Management/Priority.h
@@ -0,0 +1,8 @@
+enum class Priority
+{
+ Low, // Low priority - tasks that are not urgent and can be addressed later.
+ MediumLow, // Medium-Low priority - tasks that are less urgent than medium priority tasks.
+ Medium, // Medium priority - tasks that are somewhat urgent but not the highest priority.
+ MediumHigh, // Medium-High priority - tasks that are more urgent and require timely attention.
+ High // High priority - tasks that are the most urgent and require immediate attention.
+};
\ No newline at end of file
diff --git a/Master_Slave_Management/Master_Slave_Management/SlaveEventManager.cpp b/Master_Slave_Management/Master_Slave_Management/SlaveEventManager.cpp
new file mode 100644
index 0000000..0a1e422
--- /dev/null
+++ b/Master_Slave_Management/Master_Slave_Management/SlaveEventManager.cpp
@@ -0,0 +1,183 @@
+#include "slaveEventManager.h"
+#include "mockDataManager.h"
+
+int SlaveEventManager::counter = 0;
+
+void SlaveEventManager::sendMessage(Message* message)
+{
+}
+
+int SlaveEventManager::getSlaveId()
+{
+ return id;
+}
+
+SlaveEventManager::SlaveEventManager()
+ :id(counter++),buffer(30),fileHandler("baseFile_",10)
+{
+}
+
+///
+/// create a new slave
+///
+/// The number of events that will be saved in the dynamic buffer before being written to the file
+/// The base of the name of the files where the slave's events will be saved
+/// The maximum number of files that will be saved.
+SlaveEventManager::SlaveEventManager(int bufferSize, string baseFilename, size_t maxFiles):
+ id(counter++), buffer(bufferSize), fileHandler(baseFilename, maxFiles)
+{
+}
+
+SlaveEventManager::SlaveEventManager(const SlaveEventManager& other)
+ :id(other.id), buffer(other.buffer), fileHandler(other.fileHandler)
+{
+}
+
+SlaveEventManager& SlaveEventManager::operator=(const SlaveEventManager& other)
+{
+ if (this != &other)
+ {
+ id = other.id;
+ buffer = other.buffer;
+ fileHandler = other.fileHandler;
+ }
+ return *this;
+}
+
+///
+/// adds a new event
+///
+/// message of event
+void SlaveEventManager::addEvent(string event)
+{
+ addEvent(Event(event));
+}
+
+///
+/// Adds a new event to the buffer.
+/// If the buffer is full, write it to a file.
+///
+/// event to add
+void SlaveEventManager::addEvent(const Event& event)
+{
+ if (buffer.addEvent(event))
+ {
+ fileHandler.writeToFile(buffer.getAltBuffer());
+ buffer.clearAltBuffer();
+ }
+}
+
+///
+/// API that receives the request to get a list of events from the master.
+///
+/// the message with the range time and maximum size for the event list
+void SlaveEventManager::getEvents(Message* message)
+{
+ int reqId = message->reqId;
+ tm startTime = message->messageParams->MTS_SEND_EVENTS_PARAMS.startTime;
+ tm endTime = message->messageParams->MTS_SEND_EVENTS_PARAMS.endTime;
+ size_t maxSize = message->messageParams->MTS_SEND_EVENTS_PARAMS.maxSize;
+
+ readEvents(reqId,startTime,endTime,maxSize);
+}
+
+
+
+///
+/// Adds to the 'events' vector the events in the buffer in the range
+///
+/// An empty list to which the events matching the condition are added
+/// The beginning of the time range of the desired events
+/// The end of the time range of the desired events
+/// The maximum size of the list of events that will be sended
+void SlaveEventManager::addBufferEventsToVector(vector& events, const tm startTime, const tm endTime, size_t& maxSize)
+{
+ Event event;
+ tm eventTime;
+ vector bufferEvents = buffer.getBuffer();
+ size_t currentSize = 0;
+
+ for (auto it = bufferEvents.rbegin(); it != bufferEvents.rend(); ++it) {
+ event = *it;
+ eventTime = event.getTime();
+ if(event >= startTime && event<=endTime)
+ {
+ size_t eventSize = event.getSize();
+ if (currentSize + eventSize > maxSize)
+ {
+ break;
+ }
+ events.push_back(event);
+ currentSize += eventSize;
+ }
+ }
+ maxSize -= currentSize;
+}
+
+
+///
+/// send a response with the events list to the master
+///
+/// the global request ID of the master received with the request.
+/// The list of events was read from the drone's memory according to the time range.
+
+void SlaveEventManager::sendResponseToMaster(int reqId, vector events)
+{
+ Message* message = new Message();
+ message->reqId = reqId;
+ message->droneId = this->id;
+ message->messageType = MessageType::STM_GET_EVENTS;
+
+ message->messageParams = new MessageParams();
+ new (&message->messageParams->STM_GET_EVENTS_PARAMS) STM_GET_EVENTS_PARAMS();
+
+ message->messageParams->STM_GET_EVENTS_PARAMS.events.insert(
+ message->messageParams->STM_GET_EVENTS_PARAMS.events.end(),
+ events.begin(),
+ events.end()
+ );
+ sendMessage(message);
+
+ #ifdef TEST_MODE
+ mockReceiveMessages(message);
+ #endif
+
+
+ message->messageParams->STM_GET_EVENTS_PARAMS.~STM_GET_EVENTS_PARAMS();
+
+ // Deleting the message
+ delete message;
+}
+
+
+
+///
+/// Reads and returns as a response a list of events according to the received range and size.
+/// First reads the events in the buffer (only if they are in the range)
+/// and then reads from the files according to the remaining size.
+///
+/// The master's general request ID
+/// The beginning of the time range of the desired events
+/// The end of the time range of the desired events
+/// The maximum size of the list of events that will be sended
+void SlaveEventManager::readEvents(int reqId, const tm startTime, const tm endTime, size_t maxSize)
+{
+ vector events;
+ Event firstEvent = buffer.getBuffer().front();
+ Event lastEvent = buffer.getBuffer().back();
+ if(lastEvent>=startTime && firstEvent<=endTime) //Checks if there are events in the buffer that match the time range
+ {
+ addBufferEventsToVector(events, startTime, endTime, maxSize);// Adds the events in the buffer and reduces maxSize accordingly.
+ }
+
+ //Only if we have not reached the maximum size and it is also possible that there will be events on the disk that fit the range -
+ //we will read them from the disk and add them to the vector
+ if(maxSize>0 && firstEvent>startTime)
+ fileHandler.readFromDiskAndAddToEvents(events, startTime, endTime, maxSize);
+
+ reverse(events.begin(), events.end());
+
+ //Sends the response to the master
+ sendResponseToMaster(reqId, events);
+
+}
diff --git a/Master_Slave_Management/Master_Slave_Management/doctest.h b/Master_Slave_Management/Master_Slave_Management/doctest.h
new file mode 100644
index 0000000..42bab0d
--- /dev/null
+++ b/Master_Slave_Management/Master_Slave_Management/doctest.h
@@ -0,0 +1,7165 @@
+#pragma once
+// ====================================================================== lgtm [cpp/missing-header-guard]
+// == DO NOT MODIFY THIS FILE BY HAND - IT IS AUTO GENERATED BY CMAKE! ==
+// ======================================================================
+//
+// doctest.h - the lightest feature-rich C++ single-header testing framework for unit tests and TDD
+//
+// Copyright (c) 2016-2023 Viktor Kirilov
+//
+// Distributed under the MIT Software License
+// See accompanying file LICENSE.txt or copy at
+// https://opensource.org/licenses/MIT
+//
+// The documentation can be found at the library's page:
+// https://github.com/doctest/doctest/blob/master/doc/markdown/readme.md
+//
+// =================================================================================================
+// =================================================================================================
+// =================================================================================================
+//
+// The library is heavily influenced by Catch - https://github.com/catchorg/Catch2
+// which uses the Boost Software License - Version 1.0
+// see here - https://github.com/catchorg/Catch2/blob/master/LICENSE.txt
+//
+// The concept of subcases (sections in Catch) and expression decomposition are from there.
+// Some parts of the code are taken directly:
+// - stringification - the detection of "ostream& operator<<(ostream&, const T&)" and StringMaker<>
+// - the Approx() helper class for floating point comparison
+// - colors in the console
+// - breaking into a debugger
+// - signal / SEH handling
+// - timer
+// - XmlWriter class - thanks to Phil Nash for allowing the direct reuse (AKA copy/paste)
+//
+// The expression decomposing templates are taken from lest - https://github.com/martinmoene/lest
+// which uses the Boost Software License - Version 1.0
+// see here - https://github.com/martinmoene/lest/blob/master/LICENSE.txt
+//
+// =================================================================================================
+// =================================================================================================
+// =================================================================================================
+
+#ifndef DOCTEST_LIBRARY_INCLUDED
+#define DOCTEST_LIBRARY_INCLUDED
+
+// =================================================================================================
+// == VERSION ======================================================================================
+// =================================================================================================
+
+#define DOCTEST_VERSION_MAJOR 2
+#define DOCTEST_VERSION_MINOR 4
+#define DOCTEST_VERSION_PATCH 11
+
+// util we need here
+#define DOCTEST_TOSTR_IMPL(x) #x
+#define DOCTEST_TOSTR(x) DOCTEST_TOSTR_IMPL(x)
+
+#define DOCTEST_VERSION_STR \
+ DOCTEST_TOSTR(DOCTEST_VERSION_MAJOR) "." \
+ DOCTEST_TOSTR(DOCTEST_VERSION_MINOR) "." \
+ DOCTEST_TOSTR(DOCTEST_VERSION_PATCH)
+
+#define DOCTEST_VERSION \
+ (DOCTEST_VERSION_MAJOR * 10000 + DOCTEST_VERSION_MINOR * 100 + DOCTEST_VERSION_PATCH)
+
+// =================================================================================================
+// == COMPILER VERSION =============================================================================
+// =================================================================================================
+
+// ideas for the version stuff are taken from here: https://github.com/cxxstuff/cxx_detect
+
+#ifdef _MSC_VER
+#define DOCTEST_CPLUSPLUS _MSVC_LANG
+#else
+#define DOCTEST_CPLUSPLUS __cplusplus
+#endif
+
+#define DOCTEST_COMPILER(MAJOR, MINOR, PATCH) ((MAJOR)*10000000 + (MINOR)*100000 + (PATCH))
+
+// GCC/Clang and GCC/MSVC are mutually exclusive, but Clang/MSVC are not because of clang-cl...
+#if defined(_MSC_VER) && defined(_MSC_FULL_VER)
+#if _MSC_VER == _MSC_FULL_VER / 10000
+#define DOCTEST_MSVC DOCTEST_COMPILER(_MSC_VER / 100, _MSC_VER % 100, _MSC_FULL_VER % 10000)
+#else // MSVC
+#define DOCTEST_MSVC \
+ DOCTEST_COMPILER(_MSC_VER / 100, (_MSC_FULL_VER / 100000) % 100, _MSC_FULL_VER % 100000)
+#endif // MSVC
+#endif // MSVC
+#if defined(__clang__) && defined(__clang_minor__) && defined(__clang_patchlevel__)
+#define DOCTEST_CLANG DOCTEST_COMPILER(__clang_major__, __clang_minor__, __clang_patchlevel__)
+#elif defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) && \
+ !defined(__INTEL_COMPILER)
+#define DOCTEST_GCC DOCTEST_COMPILER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
+#endif // GCC
+#if defined(__INTEL_COMPILER)
+#define DOCTEST_ICC DOCTEST_COMPILER(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0)
+#endif // ICC
+
+#ifndef DOCTEST_MSVC
+#define DOCTEST_MSVC 0
+#endif // DOCTEST_MSVC
+#ifndef DOCTEST_CLANG
+#define DOCTEST_CLANG 0
+#endif // DOCTEST_CLANG
+#ifndef DOCTEST_GCC
+#define DOCTEST_GCC 0
+#endif // DOCTEST_GCC
+#ifndef DOCTEST_ICC
+#define DOCTEST_ICC 0
+#endif // DOCTEST_ICC
+
+// =================================================================================================
+// == COMPILER WARNINGS HELPERS ====================================================================
+// =================================================================================================
+
+#if DOCTEST_CLANG && !DOCTEST_ICC
+#define DOCTEST_PRAGMA_TO_STR(x) _Pragma(#x)
+#define DOCTEST_CLANG_SUPPRESS_WARNING_PUSH _Pragma("clang diagnostic push")
+#define DOCTEST_CLANG_SUPPRESS_WARNING(w) DOCTEST_PRAGMA_TO_STR(clang diagnostic ignored w)
+#define DOCTEST_CLANG_SUPPRESS_WARNING_POP _Pragma("clang diagnostic pop")
+#define DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH(w) \
+ DOCTEST_CLANG_SUPPRESS_WARNING_PUSH DOCTEST_CLANG_SUPPRESS_WARNING(w)
+#else // DOCTEST_CLANG
+#define DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
+#define DOCTEST_CLANG_SUPPRESS_WARNING(w)
+#define DOCTEST_CLANG_SUPPRESS_WARNING_POP
+#define DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH(w)
+#endif // DOCTEST_CLANG
+
+#if DOCTEST_GCC
+#define DOCTEST_PRAGMA_TO_STR(x) _Pragma(#x)
+#define DOCTEST_GCC_SUPPRESS_WARNING_PUSH _Pragma("GCC diagnostic push")
+#define DOCTEST_GCC_SUPPRESS_WARNING(w) DOCTEST_PRAGMA_TO_STR(GCC diagnostic ignored w)
+#define DOCTEST_GCC_SUPPRESS_WARNING_POP _Pragma("GCC diagnostic pop")
+#define DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH(w) \
+ DOCTEST_GCC_SUPPRESS_WARNING_PUSH DOCTEST_GCC_SUPPRESS_WARNING(w)
+#else // DOCTEST_GCC
+#define DOCTEST_GCC_SUPPRESS_WARNING_PUSH
+#define DOCTEST_GCC_SUPPRESS_WARNING(w)
+#define DOCTEST_GCC_SUPPRESS_WARNING_POP
+#define DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH(w)
+#endif // DOCTEST_GCC
+
+#if DOCTEST_MSVC
+#define DOCTEST_MSVC_SUPPRESS_WARNING_PUSH __pragma(warning(push))
+#define DOCTEST_MSVC_SUPPRESS_WARNING(w) __pragma(warning(disable : w))
+#define DOCTEST_MSVC_SUPPRESS_WARNING_POP __pragma(warning(pop))
+#define DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(w) \
+ DOCTEST_MSVC_SUPPRESS_WARNING_PUSH DOCTEST_MSVC_SUPPRESS_WARNING(w)
+#else // DOCTEST_MSVC
+#define DOCTEST_MSVC_SUPPRESS_WARNING_PUSH
+#define DOCTEST_MSVC_SUPPRESS_WARNING(w)
+#define DOCTEST_MSVC_SUPPRESS_WARNING_POP
+#define DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(w)
+#endif // DOCTEST_MSVC
+
+// =================================================================================================
+// == COMPILER WARNINGS ============================================================================
+// =================================================================================================
+
+// both the header and the implementation suppress all of these,
+// so it only makes sense to aggregate them like so
+#define DOCTEST_SUPPRESS_COMMON_WARNINGS_PUSH \
+ DOCTEST_CLANG_SUPPRESS_WARNING_PUSH \
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wunknown-pragmas") \
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wweak-vtables") \
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wpadded") \
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-prototypes") \
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat") \
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic") \
+ \
+ DOCTEST_GCC_SUPPRESS_WARNING_PUSH \
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wunknown-pragmas") \
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wpragmas") \
+ DOCTEST_GCC_SUPPRESS_WARNING("-Weffc++") \
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-overflow") \
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-aliasing") \
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-declarations") \
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wuseless-cast") \
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wnoexcept") \
+ \
+ DOCTEST_MSVC_SUPPRESS_WARNING_PUSH \
+ /* these 4 also disabled globally via cmake: */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4514) /* unreferenced inline function has been removed */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4571) /* SEH related */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4710) /* function not inlined */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4711) /* function selected for inline expansion*/ \
+ /* common ones */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4616) /* invalid compiler warning */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4619) /* invalid compiler warning */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4996) /* The compiler encountered a deprecated declaration */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4706) /* assignment within conditional expression */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4512) /* 'class' : assignment operator could not be generated */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4127) /* conditional expression is constant */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4820) /* padding */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4625) /* copy constructor was implicitly deleted */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4626) /* assignment operator was implicitly deleted */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5027) /* move assignment operator implicitly deleted */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5026) /* move constructor was implicitly deleted */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4640) /* construction of local static object not thread-safe */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5045) /* Spectre mitigation for memory load */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5264) /* 'variable-name': 'const' variable is not used */ \
+ /* static analysis */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(26439) /* Function may not throw. Declare it 'noexcept' */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(26495) /* Always initialize a member variable */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(26451) /* Arithmetic overflow ... */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(26444) /* Avoid unnamed objects with custom ctor and dtor... */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(26812) /* Prefer 'enum class' over 'enum' */
+
+#define DOCTEST_SUPPRESS_COMMON_WARNINGS_POP \
+ DOCTEST_CLANG_SUPPRESS_WARNING_POP \
+ DOCTEST_GCC_SUPPRESS_WARNING_POP \
+ DOCTEST_MSVC_SUPPRESS_WARNING_POP
+
+DOCTEST_SUPPRESS_COMMON_WARNINGS_PUSH
+
+DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
+DOCTEST_CLANG_SUPPRESS_WARNING("-Wnon-virtual-dtor")
+DOCTEST_CLANG_SUPPRESS_WARNING("-Wdeprecated")
+
+DOCTEST_GCC_SUPPRESS_WARNING_PUSH
+DOCTEST_GCC_SUPPRESS_WARNING("-Wctor-dtor-privacy")
+DOCTEST_GCC_SUPPRESS_WARNING("-Wnon-virtual-dtor")
+DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-promo")
+
+DOCTEST_MSVC_SUPPRESS_WARNING_PUSH
+DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly defined as deleted
+
+#define DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN \
+ DOCTEST_MSVC_SUPPRESS_WARNING_PUSH \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4548) /* before comma no effect; expected side - effect */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4265) /* virtual functions, but destructor is not virtual */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4986) /* exception specification does not match previous */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4350) /* 'member1' called instead of 'member2' */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4668) /* not defined as a preprocessor macro */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4365) /* signed/unsigned mismatch */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4774) /* format string not a string literal */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4820) /* padding */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4625) /* copy constructor was implicitly deleted */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4626) /* assignment operator was implicitly deleted */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5027) /* move assignment operator implicitly deleted */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5026) /* move constructor was implicitly deleted */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4623) /* default constructor was implicitly deleted */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5039) /* pointer to pot. throwing function passed to extern C */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5045) /* Spectre mitigation for memory load */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5105) /* macro producing 'defined' has undefined behavior */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4738) /* storing float result in memory, loss of performance */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5262) /* implicit fall-through */
+
+#define DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END DOCTEST_MSVC_SUPPRESS_WARNING_POP
+
+// =================================================================================================
+// == FEATURE DETECTION ============================================================================
+// =================================================================================================
+
+// general compiler feature support table: https://en.cppreference.com/w/cpp/compiler_support
+// MSVC C++11 feature support table: https://msdn.microsoft.com/en-us/library/hh567368.aspx
+// GCC C++11 feature support table: https://gcc.gnu.org/projects/cxx-status.html
+// MSVC version table:
+// https://en.wikipedia.org/wiki/Microsoft_Visual_C%2B%2B#Internal_version_numbering
+// MSVC++ 14.3 (17) _MSC_VER == 1930 (Visual Studio 2022)
+// MSVC++ 14.2 (16) _MSC_VER == 1920 (Visual Studio 2019)
+// MSVC++ 14.1 (15) _MSC_VER == 1910 (Visual Studio 2017)
+// MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015)
+// MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013)
+// MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012)
+// MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010)
+// MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008)
+// MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005)
+
+// Universal Windows Platform support
+#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
+#define DOCTEST_CONFIG_NO_WINDOWS_SEH
+#endif // WINAPI_FAMILY
+#if DOCTEST_MSVC && !defined(DOCTEST_CONFIG_WINDOWS_SEH)
+#define DOCTEST_CONFIG_WINDOWS_SEH
+#endif // MSVC
+#if defined(DOCTEST_CONFIG_NO_WINDOWS_SEH) && defined(DOCTEST_CONFIG_WINDOWS_SEH)
+#undef DOCTEST_CONFIG_WINDOWS_SEH
+#endif // DOCTEST_CONFIG_NO_WINDOWS_SEH
+
+#if !defined(_WIN32) && !defined(__QNX__) && !defined(DOCTEST_CONFIG_POSIX_SIGNALS) && \
+ !defined(__EMSCRIPTEN__) && !defined(__wasi__)
+#define DOCTEST_CONFIG_POSIX_SIGNALS
+#endif // _WIN32
+#if defined(DOCTEST_CONFIG_NO_POSIX_SIGNALS) && defined(DOCTEST_CONFIG_POSIX_SIGNALS)
+#undef DOCTEST_CONFIG_POSIX_SIGNALS
+#endif // DOCTEST_CONFIG_NO_POSIX_SIGNALS
+
+#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
+#if !defined(__cpp_exceptions) && !defined(__EXCEPTIONS) && !defined(_CPPUNWIND) \
+ || defined(__wasi__)
+#define DOCTEST_CONFIG_NO_EXCEPTIONS
+#endif // no exceptions
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
+
+#ifdef DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS
+#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
+#define DOCTEST_CONFIG_NO_EXCEPTIONS
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS
+
+#if defined(DOCTEST_CONFIG_NO_EXCEPTIONS) && !defined(DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS)
+#define DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS && !DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS
+
+#ifdef __wasi__
+#define DOCTEST_CONFIG_NO_MULTITHREADING
+#endif
+
+#if defined(DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN) && !defined(DOCTEST_CONFIG_IMPLEMENT)
+#define DOCTEST_CONFIG_IMPLEMENT
+#endif // DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
+
+#if defined(_WIN32) || defined(__CYGWIN__)
+#if DOCTEST_MSVC
+#define DOCTEST_SYMBOL_EXPORT __declspec(dllexport)
+#define DOCTEST_SYMBOL_IMPORT __declspec(dllimport)
+#else // MSVC
+#define DOCTEST_SYMBOL_EXPORT __attribute__((dllexport))
+#define DOCTEST_SYMBOL_IMPORT __attribute__((dllimport))
+#endif // MSVC
+#else // _WIN32
+#define DOCTEST_SYMBOL_EXPORT __attribute__((visibility("default")))
+#define DOCTEST_SYMBOL_IMPORT
+#endif // _WIN32
+
+#ifdef DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL
+#ifdef DOCTEST_CONFIG_IMPLEMENT
+#define DOCTEST_INTERFACE DOCTEST_SYMBOL_EXPORT
+#else // DOCTEST_CONFIG_IMPLEMENT
+#define DOCTEST_INTERFACE DOCTEST_SYMBOL_IMPORT
+#endif // DOCTEST_CONFIG_IMPLEMENT
+#else // DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL
+#define DOCTEST_INTERFACE
+#endif // DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL
+
+// needed for extern template instantiations
+// see https://github.com/fmtlib/fmt/issues/2228
+#if DOCTEST_MSVC
+#define DOCTEST_INTERFACE_DECL
+#define DOCTEST_INTERFACE_DEF DOCTEST_INTERFACE
+#else // DOCTEST_MSVC
+#define DOCTEST_INTERFACE_DECL DOCTEST_INTERFACE
+#define DOCTEST_INTERFACE_DEF
+#endif // DOCTEST_MSVC
+
+#define DOCTEST_EMPTY
+
+#if DOCTEST_MSVC
+#define DOCTEST_NOINLINE __declspec(noinline)
+#define DOCTEST_UNUSED
+#define DOCTEST_ALIGNMENT(x)
+#elif DOCTEST_CLANG && DOCTEST_CLANG < DOCTEST_COMPILER(3, 5, 0)
+#define DOCTEST_NOINLINE
+#define DOCTEST_UNUSED
+#define DOCTEST_ALIGNMENT(x)
+#else
+#define DOCTEST_NOINLINE __attribute__((noinline))
+#define DOCTEST_UNUSED __attribute__((unused))
+#define DOCTEST_ALIGNMENT(x) __attribute__((aligned(x)))
+#endif
+
+#ifdef DOCTEST_CONFIG_NO_CONTRADICTING_INLINE
+#define DOCTEST_INLINE_NOINLINE inline
+#else
+#define DOCTEST_INLINE_NOINLINE inline DOCTEST_NOINLINE
+#endif
+
+#ifndef DOCTEST_NORETURN
+#if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0))
+#define DOCTEST_NORETURN
+#else // DOCTEST_MSVC
+#define DOCTEST_NORETURN [[noreturn]]
+#endif // DOCTEST_MSVC
+#endif // DOCTEST_NORETURN
+
+#ifndef DOCTEST_NOEXCEPT
+#if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0))
+#define DOCTEST_NOEXCEPT
+#else // DOCTEST_MSVC
+#define DOCTEST_NOEXCEPT noexcept
+#endif // DOCTEST_MSVC
+#endif // DOCTEST_NOEXCEPT
+
+#ifndef DOCTEST_CONSTEXPR
+#if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0))
+#define DOCTEST_CONSTEXPR const
+#define DOCTEST_CONSTEXPR_FUNC inline
+#else // DOCTEST_MSVC
+#define DOCTEST_CONSTEXPR constexpr
+#define DOCTEST_CONSTEXPR_FUNC constexpr
+#endif // DOCTEST_MSVC
+#endif // DOCTEST_CONSTEXPR
+
+#ifndef DOCTEST_NO_SANITIZE_INTEGER
+#if DOCTEST_CLANG >= DOCTEST_COMPILER(3, 7, 0)
+#define DOCTEST_NO_SANITIZE_INTEGER __attribute__((no_sanitize("integer")))
+#else
+#define DOCTEST_NO_SANITIZE_INTEGER
+#endif
+#endif // DOCTEST_NO_SANITIZE_INTEGER
+
+// =================================================================================================
+// == FEATURE DETECTION END ========================================================================
+// =================================================================================================
+
+#define DOCTEST_DECLARE_INTERFACE(name) \
+ virtual ~name(); \
+ name() = default; \
+ name(const name&) = delete; \
+ name(name&&) = delete; \
+ name& operator=(const name&) = delete; \
+ name& operator=(name&&) = delete;
+
+#define DOCTEST_DEFINE_INTERFACE(name) \
+ name::~name() = default;
+
+// internal macros for string concatenation and anonymous variable name generation
+#define DOCTEST_CAT_IMPL(s1, s2) s1##s2
+#define DOCTEST_CAT(s1, s2) DOCTEST_CAT_IMPL(s1, s2)
+#ifdef __COUNTER__ // not standard and may be missing for some compilers
+#define DOCTEST_ANONYMOUS(x) DOCTEST_CAT(x, __COUNTER__)
+#else // __COUNTER__
+#define DOCTEST_ANONYMOUS(x) DOCTEST_CAT(x, __LINE__)
+#endif // __COUNTER__
+
+#ifndef DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE
+#define DOCTEST_REF_WRAP(x) x&
+#else // DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE
+#define DOCTEST_REF_WRAP(x) x
+#endif // DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE
+
+// not using __APPLE__ because... this is how Catch does it
+#ifdef __MAC_OS_X_VERSION_MIN_REQUIRED
+#define DOCTEST_PLATFORM_MAC
+#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
+#define DOCTEST_PLATFORM_IPHONE
+#elif defined(_WIN32)
+#define DOCTEST_PLATFORM_WINDOWS
+#elif defined(__wasi__)
+#define DOCTEST_PLATFORM_WASI
+#else // DOCTEST_PLATFORM
+#define DOCTEST_PLATFORM_LINUX
+#endif // DOCTEST_PLATFORM
+
+namespace doctest {
+ namespace detail {
+ static DOCTEST_CONSTEXPR int consume(const int*, int) noexcept { return 0; }
+ }
+}
+
+#define DOCTEST_GLOBAL_NO_WARNINGS(var, ...) \
+ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wglobal-constructors") \
+ static const int var = doctest::detail::consume(&var, __VA_ARGS__); \
+ DOCTEST_CLANG_SUPPRESS_WARNING_POP
+
+#ifndef DOCTEST_BREAK_INTO_DEBUGGER
+// should probably take a look at https://github.com/scottt/debugbreak
+#ifdef DOCTEST_PLATFORM_LINUX
+#if defined(__GNUC__) && (defined(__i386) || defined(__x86_64))
+// Break at the location of the failing check if possible
+#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) // NOLINT(hicpp-no-assembler)
+#else
+#include
+#define DOCTEST_BREAK_INTO_DEBUGGER() raise(SIGTRAP)
+#endif
+#elif defined(DOCTEST_PLATFORM_MAC)
+#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(__i386)
+#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) // NOLINT(hicpp-no-assembler)
+#elif defined(__ppc__) || defined(__ppc64__)
+// https://www.cocoawithlove.com/2008/03/break-into-debugger.html
+#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n": : : "memory","r0","r3","r4") // NOLINT(hicpp-no-assembler)
+#else
+#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("brk #0"); // NOLINT(hicpp-no-assembler)
+#endif
+#elif DOCTEST_MSVC
+#define DOCTEST_BREAK_INTO_DEBUGGER() __debugbreak()
+#elif defined(__MINGW32__)
+DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wredundant-decls")
+extern "C" __declspec(dllimport) void __stdcall DebugBreak();
+DOCTEST_GCC_SUPPRESS_WARNING_POP
+#define DOCTEST_BREAK_INTO_DEBUGGER() ::DebugBreak()
+#else // linux
+#define DOCTEST_BREAK_INTO_DEBUGGER() (static_cast(0))
+#endif // linux
+#endif // DOCTEST_BREAK_INTO_DEBUGGER
+
+// this is kept here for backwards compatibility since the config option was changed
+#ifdef DOCTEST_CONFIG_USE_IOSFWD
+#ifndef DOCTEST_CONFIG_USE_STD_HEADERS
+#define DOCTEST_CONFIG_USE_STD_HEADERS
+#endif
+#endif // DOCTEST_CONFIG_USE_IOSFWD
+
+// for clang - always include ciso646 (which drags some std stuff) because
+// we want to check if we are using libc++ with the _LIBCPP_VERSION macro in
+// which case we don't want to forward declare stuff from std - for reference:
+// https://github.com/doctest/doctest/issues/126
+// https://github.com/doctest/doctest/issues/356
+#if DOCTEST_CLANG
+#include
+#endif // clang
+
+#ifdef _LIBCPP_VERSION
+#ifndef DOCTEST_CONFIG_USE_STD_HEADERS
+#define DOCTEST_CONFIG_USE_STD_HEADERS
+#endif
+#endif // _LIBCPP_VERSION
+
+#ifdef DOCTEST_CONFIG_USE_STD_HEADERS
+#ifndef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+#define DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN
+#include
+#include
+#include
+DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END
+#else // DOCTEST_CONFIG_USE_STD_HEADERS
+
+// Forward declaring 'X' in namespace std is not permitted by the C++ Standard.
+DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4643)
+
+namespace std { // NOLINT(cert-dcl58-cpp)
+ typedef decltype(nullptr) nullptr_t; // NOLINT(modernize-use-using)
+ typedef decltype(sizeof(void*)) size_t; // NOLINT(modernize-use-using)
+ template
+ struct char_traits;
+ template <>
+ struct char_traits;
+ template
+ class basic_ostream; // NOLINT(fuchsia-virtual-inheritance)
+ typedef basic_ostream> ostream; // NOLINT(modernize-use-using)
+ template
+ // NOLINTNEXTLINE
+ basic_ostream& operator<<(basic_ostream&, const char*);
+ template
+ class basic_istream;
+ typedef basic_istream> istream; // NOLINT(modernize-use-using)
+ template
+ class tuple;
+#if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0)
+ // see this issue on why this is needed: https://github.com/doctest/doctest/issues/183
+ template
+ class allocator;
+ template
+ class basic_string;
+ using string = basic_string, allocator>;
+#endif // VS 2019
+} // namespace std
+
+DOCTEST_MSVC_SUPPRESS_WARNING_POP
+
+#endif // DOCTEST_CONFIG_USE_STD_HEADERS
+
+#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+#include
+#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+
+namespace doctest {
+
+ using std::size_t;
+
+ DOCTEST_INTERFACE extern bool is_running_in_test;
+
+#ifndef DOCTEST_CONFIG_STRING_SIZE_TYPE
+#define DOCTEST_CONFIG_STRING_SIZE_TYPE unsigned
+#endif
+
+ // A 24 byte string class (can be as small as 17 for x64 and 13 for x86) that can hold strings with length
+ // of up to 23 chars on the stack before going on the heap - the last byte of the buffer is used for:
+ // - "is small" bit - the highest bit - if "0" then it is small - otherwise its "1" (128)
+ // - if small - capacity left before going on the heap - using the lowest 5 bits
+ // - if small - 2 bits are left unused - the second and third highest ones
+ // - if small - acts as a null terminator if strlen() is 23 (24 including the null terminator)
+ // and the "is small" bit remains "0" ("as well as the capacity left") so its OK
+ // Idea taken from this lecture about the string implementation of facebook/folly - fbstring
+ // https://www.youtube.com/watch?v=kPR8h4-qZdk
+ // TODO:
+ // - optimizations - like not deleting memory unnecessarily in operator= and etc.
+ // - resize/reserve/clear
+ // - replace
+ // - back/front
+ // - iterator stuff
+ // - find & friends
+ // - push_back/pop_back
+ // - assign/insert/erase
+ // - relational operators as free functions - taking const char* as one of the params
+ class DOCTEST_INTERFACE String
+ {
+ public:
+ using size_type = DOCTEST_CONFIG_STRING_SIZE_TYPE;
+
+ private:
+ static DOCTEST_CONSTEXPR size_type len = 24; //!OCLINT avoid private static members
+ static DOCTEST_CONSTEXPR size_type last = len - 1; //!OCLINT avoid private static members
+
+ struct view // len should be more than sizeof(view) - because of the final byte for flags
+ {
+ char* ptr;
+ size_type size;
+ size_type capacity;
+ };
+
+ union
+ {
+ char buf[len]; // NOLINT(*-avoid-c-arrays)
+ view data;
+ };
+
+ char* allocate(size_type sz);
+
+ bool isOnStack() const noexcept { return (buf[last] & 128) == 0; }
+ void setOnHeap() noexcept;
+ void setLast(size_type in = last) noexcept;
+ void setSize(size_type sz) noexcept;
+
+ void copy(const String& other);
+
+ public:
+ static DOCTEST_CONSTEXPR size_type npos = static_cast(-1);
+
+ String() noexcept;
+ ~String();
+
+ // cppcheck-suppress noExplicitConstructor
+ String(const char* in);
+ String(const char* in, size_type in_size);
+
+ String(std::istream& in, size_type in_size);
+
+ String(const String& other);
+ String& operator=(const String& other);
+
+ String& operator+=(const String& other);
+
+ String(String&& other) noexcept;
+ String& operator=(String&& other) noexcept;
+
+ char operator[](size_type i) const;
+ char& operator[](size_type i);
+
+ // the only functions I'm willing to leave in the interface - available for inlining
+ const char* c_str() const { return const_cast(this)->c_str(); } // NOLINT
+ char* c_str() {
+ if (isOnStack()) {
+ return reinterpret_cast(buf);
+ }
+ return data.ptr;
+ }
+
+ size_type size() const;
+ size_type capacity() const;
+
+ String substr(size_type pos, size_type cnt = npos)&&;
+ String substr(size_type pos, size_type cnt = npos) const&;
+
+ size_type find(char ch, size_type pos = 0) const;
+ size_type rfind(char ch, size_type pos = npos) const;
+
+ int compare(const char* other, bool no_case = false) const;
+ int compare(const String& other, bool no_case = false) const;
+
+ friend DOCTEST_INTERFACE std::ostream& operator<<(std::ostream& s, const String& in);
+ };
+
+ DOCTEST_INTERFACE String operator+(const String& lhs, const String& rhs);
+
+ DOCTEST_INTERFACE bool operator==(const String& lhs, const String& rhs);
+ DOCTEST_INTERFACE bool operator!=(const String& lhs, const String& rhs);
+ DOCTEST_INTERFACE bool operator<(const String& lhs, const String& rhs);
+ DOCTEST_INTERFACE bool operator>(const String& lhs, const String& rhs);
+ DOCTEST_INTERFACE bool operator<=(const String& lhs, const String& rhs);
+ DOCTEST_INTERFACE bool operator>=(const String& lhs, const String& rhs);
+
+ class DOCTEST_INTERFACE Contains {
+ public:
+ explicit Contains(const String& string);
+
+ bool checkWith(const String& other) const;
+
+ String string;
+ };
+
+ DOCTEST_INTERFACE String toString(const Contains& in);
+
+ DOCTEST_INTERFACE bool operator==(const String& lhs, const Contains& rhs);
+ DOCTEST_INTERFACE bool operator==(const Contains& lhs, const String& rhs);
+ DOCTEST_INTERFACE bool operator!=(const String& lhs, const Contains& rhs);
+ DOCTEST_INTERFACE bool operator!=(const Contains& lhs, const String& rhs);
+
+ namespace Color {
+ enum Enum
+ {
+ None = 0,
+ White,
+ Red,
+ Green,
+ Blue,
+ Cyan,
+ Yellow,
+ Grey,
+
+ Bright = 0x10,
+
+ BrightRed = Bright | Red,
+ BrightGreen = Bright | Green,
+ LightGrey = Bright | Grey,
+ BrightWhite = Bright | White
+ };
+
+ DOCTEST_INTERFACE std::ostream& operator<<(std::ostream& s, Color::Enum code);
+ } // namespace Color
+
+ namespace assertType {
+ enum Enum
+ {
+ // macro traits
+
+ is_warn = 1,
+ is_check = 2 * is_warn,
+ is_require = 2 * is_check,
+
+ is_normal = 2 * is_require,
+ is_throws = 2 * is_normal,
+ is_throws_as = 2 * is_throws,
+ is_throws_with = 2 * is_throws_as,
+ is_nothrow = 2 * is_throws_with,
+
+ is_false = 2 * is_nothrow,
+ is_unary = 2 * is_false, // not checked anywhere - used just to distinguish the types
+
+ is_eq = 2 * is_unary,
+ is_ne = 2 * is_eq,
+
+ is_lt = 2 * is_ne,
+ is_gt = 2 * is_lt,
+
+ is_ge = 2 * is_gt,
+ is_le = 2 * is_ge,
+
+ // macro types
+
+ DT_WARN = is_normal | is_warn,
+ DT_CHECK = is_normal | is_check,
+ DT_REQUIRE = is_normal | is_require,
+
+ DT_WARN_FALSE = is_normal | is_false | is_warn,
+ DT_CHECK_FALSE = is_normal | is_false | is_check,
+ DT_REQUIRE_FALSE = is_normal | is_false | is_require,
+
+ DT_WARN_THROWS = is_throws | is_warn,
+ DT_CHECK_THROWS = is_throws | is_check,
+ DT_REQUIRE_THROWS = is_throws | is_require,
+
+ DT_WARN_THROWS_AS = is_throws_as | is_warn,
+ DT_CHECK_THROWS_AS = is_throws_as | is_check,
+ DT_REQUIRE_THROWS_AS = is_throws_as | is_require,
+
+ DT_WARN_THROWS_WITH = is_throws_with | is_warn,
+ DT_CHECK_THROWS_WITH = is_throws_with | is_check,
+ DT_REQUIRE_THROWS_WITH = is_throws_with | is_require,
+
+ DT_WARN_THROWS_WITH_AS = is_throws_with | is_throws_as | is_warn,
+ DT_CHECK_THROWS_WITH_AS = is_throws_with | is_throws_as | is_check,
+ DT_REQUIRE_THROWS_WITH_AS = is_throws_with | is_throws_as | is_require,
+
+ DT_WARN_NOTHROW = is_nothrow | is_warn,
+ DT_CHECK_NOTHROW = is_nothrow | is_check,
+ DT_REQUIRE_NOTHROW = is_nothrow | is_require,
+
+ DT_WARN_EQ = is_normal | is_eq | is_warn,
+ DT_CHECK_EQ = is_normal | is_eq | is_check,
+ DT_REQUIRE_EQ = is_normal | is_eq | is_require,
+
+ DT_WARN_NE = is_normal | is_ne | is_warn,
+ DT_CHECK_NE = is_normal | is_ne | is_check,
+ DT_REQUIRE_NE = is_normal | is_ne | is_require,
+
+ DT_WARN_GT = is_normal | is_gt | is_warn,
+ DT_CHECK_GT = is_normal | is_gt | is_check,
+ DT_REQUIRE_GT = is_normal | is_gt | is_require,
+
+ DT_WARN_LT = is_normal | is_lt | is_warn,
+ DT_CHECK_LT = is_normal | is_lt | is_check,
+ DT_REQUIRE_LT = is_normal | is_lt | is_require,
+
+ DT_WARN_GE = is_normal | is_ge | is_warn,
+ DT_CHECK_GE = is_normal | is_ge | is_check,
+ DT_REQUIRE_GE = is_normal | is_ge | is_require,
+
+ DT_WARN_LE = is_normal | is_le | is_warn,
+ DT_CHECK_LE = is_normal | is_le | is_check,
+ DT_REQUIRE_LE = is_normal | is_le | is_require,
+
+ DT_WARN_UNARY = is_normal | is_unary | is_warn,
+ DT_CHECK_UNARY = is_normal | is_unary | is_check,
+ DT_REQUIRE_UNARY = is_normal | is_unary | is_require,
+
+ DT_WARN_UNARY_FALSE = is_normal | is_false | is_unary | is_warn,
+ DT_CHECK_UNARY_FALSE = is_normal | is_false | is_unary | is_check,
+ DT_REQUIRE_UNARY_FALSE = is_normal | is_false | is_unary | is_require,
+ };
+ } // namespace assertType
+
+ DOCTEST_INTERFACE const char* assertString(assertType::Enum at);
+ DOCTEST_INTERFACE const char* failureString(assertType::Enum at);
+ DOCTEST_INTERFACE const char* skipPathFromFilename(const char* file);
+
+ struct DOCTEST_INTERFACE TestCaseData
+ {
+ String m_file; // the file in which the test was registered (using String - see #350)
+ unsigned m_line; // the line where the test was registered
+ const char* m_name; // name of the test case
+ const char* m_test_suite; // the test suite in which the test was added
+ const char* m_description;
+ bool m_skip;
+ bool m_no_breaks;
+ bool m_no_output;
+ bool m_may_fail;
+ bool m_should_fail;
+ int m_expected_failures;
+ double m_timeout;
+ };
+
+ struct DOCTEST_INTERFACE AssertData
+ {
+ // common - for all asserts
+ const TestCaseData* m_test_case;
+ assertType::Enum m_at;
+ const char* m_file;
+ int m_line;
+ const char* m_expr;
+ bool m_failed;
+
+ // exception-related - for all asserts
+ bool m_threw;
+ String m_exception;
+
+ // for normal asserts
+ String m_decomp;
+
+ // for specific exception-related asserts
+ bool m_threw_as;
+ const char* m_exception_type;
+
+ class DOCTEST_INTERFACE StringContains {
+ private:
+ Contains content;
+ bool isContains;
+
+ public:
+ StringContains(const String& str) : content(str), isContains(false) { }
+ StringContains(Contains cntn) : content(static_cast(cntn)), isContains(true) { }
+
+ bool check(const String& str) { return isContains ? (content == str) : (content.string == str); }
+
+ operator const String& () const { return content.string; }
+
+ const char* c_str() const { return content.string.c_str(); }
+ } m_exception_string;
+
+ AssertData(assertType::Enum at, const char* file, int line, const char* expr,
+ const char* exception_type, const StringContains& exception_string);
+ };
+
+ struct DOCTEST_INTERFACE MessageData
+ {
+ String m_string;
+ const char* m_file;
+ int m_line;
+ assertType::Enum m_severity;
+ };
+
+ struct DOCTEST_INTERFACE SubcaseSignature
+ {
+ String m_name;
+ const char* m_file;
+ int m_line;
+
+ bool operator==(const SubcaseSignature& other) const;
+ bool operator<(const SubcaseSignature& other) const;
+ };
+
+ struct DOCTEST_INTERFACE IContextScope
+ {
+ DOCTEST_DECLARE_INTERFACE(IContextScope)
+ virtual void stringify(std::ostream*) const = 0;
+ };
+
+ namespace detail {
+ struct DOCTEST_INTERFACE TestCase;
+ } // namespace detail
+
+ struct ContextOptions //!OCLINT too many fields
+ {
+ std::ostream* cout = nullptr; // stdout stream
+ String binary_name; // the test binary name
+
+ const detail::TestCase* currentTest = nullptr;
+
+ // == parameters from the command line
+ String out; // output filename
+ String order_by; // how tests should be ordered
+ unsigned rand_seed; // the seed for rand ordering
+
+ unsigned first; // the first (matching) test to be executed
+ unsigned last; // the last (matching) test to be executed
+
+ int abort_after; // stop tests after this many failed assertions
+ int subcase_filter_levels; // apply the subcase filters for the first N levels
+
+ bool success; // include successful assertions in output
+ bool case_sensitive; // if filtering should be case sensitive
+ bool exit; // if the program should be exited after the tests are ran/whatever
+ bool duration; // print the time duration of each test case
+ bool minimal; // minimal console output (only test failures)
+ bool quiet; // no console output
+ bool no_throw; // to skip exceptions-related assertion macros
+ bool no_exitcode; // if the framework should return 0 as the exitcode
+ bool no_run; // to not run the tests at all (can be done with an "*" exclude)
+ bool no_intro; // to not print the intro of the framework
+ bool no_version; // to not print the version of the framework
+ bool no_colors; // if output to the console should be colorized
+ bool force_colors; // forces the use of colors even when a tty cannot be detected
+ bool no_breaks; // to not break into the debugger
+ bool no_skip; // don't skip test cases which are marked to be skipped
+ bool gnu_file_line; // if line numbers should be surrounded with :x: and not (x):
+ bool no_path_in_filenames; // if the path to files should be removed from the output
+ bool no_line_numbers; // if source code line numbers should be omitted from the output
+ bool no_debug_output; // no output in the debug console when a debugger is attached
+ bool no_skipped_summary; // don't print "skipped" in the summary !!! UNDOCUMENTED !!!
+ bool no_time_in_output; // omit any time/timestamps from output !!! UNDOCUMENTED !!!
+
+ bool help; // to print the help
+ bool version; // to print the version
+ bool count; // if only the count of matching tests is to be retrieved
+ bool list_test_cases; // to list all tests matching the filters
+ bool list_test_suites; // to list all suites matching the filters
+ bool list_reporters; // lists all registered reporters
+ };
+
+ namespace detail {
+ namespace types {
+#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+ using namespace std;
+#else
+ template
+ struct enable_if { };
+
+ template
+ struct enable_if { using type = T; };
+
+ struct true_type { static DOCTEST_CONSTEXPR bool value = true; };
+ struct false_type { static DOCTEST_CONSTEXPR bool value = false; };
+
+ template struct remove_reference { using type = T; };
+ template struct remove_reference { using type = T; };
+ template struct remove_reference { using type = T; };
+
+ template struct is_rvalue_reference : false_type { };
+ template struct is_rvalue_reference : true_type { };
+
+ template struct remove_const { using type = T; };
+ template struct remove_const { using type = T; };
+
+ // Compiler intrinsics
+ template struct is_enum { static DOCTEST_CONSTEXPR bool value = __is_enum(T); };
+ template struct underlying_type { using type = __underlying_type(T); };
+
+ template struct is_pointer : false_type { };
+ template struct is_pointer : true_type { };
+
+ template struct is_array : false_type { };
+ // NOLINTNEXTLINE(*-avoid-c-arrays)
+ template struct is_array : true_type { };
+#endif
+ }
+
+ //
+ template
+ T&& declval();
+
+ template
+ DOCTEST_CONSTEXPR_FUNC T&& forward(typename types::remove_reference::type& t) DOCTEST_NOEXCEPT {
+ return static_cast(t);
+ }
+
+ template
+ DOCTEST_CONSTEXPR_FUNC T&& forward(typename types::remove_reference::type&& t) DOCTEST_NOEXCEPT {
+ return static_cast(t);
+ }
+
+ template
+ struct deferred_false : types::false_type { };
+
+ // MSVS 2015 :(
+#if !DOCTEST_CLANG && defined(_MSC_VER) && _MSC_VER <= 1900
+ template
+ struct has_global_insertion_operator : types::false_type { };
+
+ template
+ struct has_global_insertion_operator(), declval()), void())> : types::true_type { };
+
+ template
+ struct has_insertion_operator { static DOCTEST_CONSTEXPR bool value = has_global_insertion_operator::value; };
+
+ template
+ struct insert_hack;
+
+ template
+ struct insert_hack {
+ static void insert(std::ostream& os, const T& t) { ::operator<<(os, t); }
+ };
+
+ template
+ struct insert_hack {
+ static void insert(std::ostream& os, const T& t) { operator<<(os, t); }
+ };
+
+ template
+ using insert_hack_t = insert_hack::value>;
+#else
+ template
+ struct has_insertion_operator : types::false_type { };
+#endif
+
+ template
+ struct has_insertion_operator(), declval()), void())> : types::true_type { };
+
+ template
+ struct should_stringify_as_underlying_type {
+ static DOCTEST_CONSTEXPR bool value = detail::types::is_enum::value && !doctest::detail::has_insertion_operator::value;
+ };
+
+ DOCTEST_INTERFACE std::ostream* tlssPush();
+ DOCTEST_INTERFACE String tlssPop();
+
+ template
+ struct StringMakerBase {
+ template
+ static String convert(const DOCTEST_REF_WRAP(T)) {
+#ifdef DOCTEST_CONFIG_REQUIRE_STRINGIFICATION_FOR_ALL_USED_TYPES
+ static_assert(deferred_false::value, "No stringification detected for type T. See string conversion manual");
+#endif
+ return "{?}";
+ }
+ };
+
+ template
+ struct filldata;
+
+ template
+ void filloss(std::ostream* stream, const T& in) {
+ filldata::fill(stream, in);
+ }
+
+ template
+ void filloss(std::ostream* stream, const T(&in)[N]) { // NOLINT(*-avoid-c-arrays)
+ // T[N], T(&)[N], T(&&)[N] have same behaviour.
+ // Hence remove reference.
+ filloss::type>(stream, in);
+ }
+
+ template
+ String toStream(const T& in) {
+ std::ostream* stream = tlssPush();
+ filloss(stream, in);
+ return tlssPop();
+ }
+
+ template <>
+ struct StringMakerBase {
+ template
+ static String convert(const DOCTEST_REF_WRAP(T) in) {
+ return toStream(in);
+ }
+ };
+ } // namespace detail
+
+ template
+ struct StringMaker : public detail::StringMakerBase<
+ detail::has_insertion_operator::value || detail::types::is_pointer::value || detail::types::is_array::value>
+ {};
+
+#ifndef DOCTEST_STRINGIFY
+#ifdef DOCTEST_CONFIG_DOUBLE_STRINGIFY
+#define DOCTEST_STRINGIFY(...) toString(toString(__VA_ARGS__))
+#else
+#define DOCTEST_STRINGIFY(...) toString(__VA_ARGS__)
+#endif
+#endif
+
+ template
+ String toString() {
+#if DOCTEST_CLANG == 0 && DOCTEST_GCC == 0 && DOCTEST_ICC == 0
+ String ret = __FUNCSIG__; // class doctest::String __cdecl doctest::toString(void)
+ String::size_type beginPos = ret.find('<');
+ return ret.substr(beginPos + 1, ret.size() - beginPos - static_cast(sizeof(">(void)")));
+#else
+ String ret = __PRETTY_FUNCTION__; // doctest::String toString() [with T = TYPE]
+ String::size_type begin = ret.find('=') + 2;
+ return ret.substr(begin, ret.size() - begin - 1);
+#endif
+ }
+
+ template ::value, bool>::type = true>
+ String toString(const DOCTEST_REF_WRAP(T) value) {
+ return StringMaker::convert(value);
+ }
+
+#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+ DOCTEST_INTERFACE String toString(const char* in);
+#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+
+#if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0)
+ // see this issue on why this is needed: https://github.com/doctest/doctest/issues/183
+ DOCTEST_INTERFACE String toString(const std::string& in);
+#endif // VS 2019
+
+ DOCTEST_INTERFACE String toString(String in);
+
+ DOCTEST_INTERFACE String toString(std::nullptr_t);
+
+ DOCTEST_INTERFACE String toString(bool in);
+
+ DOCTEST_INTERFACE String toString(float in);
+ DOCTEST_INTERFACE String toString(double in);
+ DOCTEST_INTERFACE String toString(double long in);
+
+ DOCTEST_INTERFACE String toString(char in);
+ DOCTEST_INTERFACE String toString(char signed in);
+ DOCTEST_INTERFACE String toString(char unsigned in);
+ DOCTEST_INTERFACE String toString(short in);
+ DOCTEST_INTERFACE String toString(short unsigned in);
+ DOCTEST_INTERFACE String toString(signed in);
+ DOCTEST_INTERFACE String toString(unsigned in);
+ DOCTEST_INTERFACE String toString(long in);
+ DOCTEST_INTERFACE String toString(long unsigned in);
+ DOCTEST_INTERFACE String toString(long long in);
+ DOCTEST_INTERFACE String toString(long long unsigned in);
+
+ template ::value, bool>::type = true>
+ String toString(const DOCTEST_REF_WRAP(T) value) {
+ using UT = typename detail::types::underlying_type::type;
+ return (DOCTEST_STRINGIFY(static_cast(value)));
+ }
+
+ namespace detail {
+ template
+ struct filldata
+ {
+ static void fill(std::ostream* stream, const T& in) {
+#if defined(_MSC_VER) && _MSC_VER <= 1900
+ insert_hack_t::insert(*stream, in);
+#else
+ operator<<(*stream, in);
+#endif
+ }
+ };
+
+ DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4866)
+ // NOLINTBEGIN(*-avoid-c-arrays)
+ template
+ struct filldata {
+ static void fill(std::ostream* stream, const T(&in)[N]) {
+ *stream << "[";
+ for (size_t i = 0; i < N; i++) {
+ if (i != 0) { *stream << ", "; }
+ *stream << (DOCTEST_STRINGIFY(in[i]));
+ }
+ *stream << "]";
+ }
+ };
+ // NOLINTEND(*-avoid-c-arrays)
+ DOCTEST_MSVC_SUPPRESS_WARNING_POP
+
+ // Specialized since we don't want the terminating null byte!
+ // NOLINTBEGIN(*-avoid-c-arrays)
+ template
+ struct filldata {
+ static void fill(std::ostream* stream, const char(&in)[N]) {
+ *stream << String(in, in[N - 1] ? N : N - 1);
+ } // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks)
+ };
+ // NOLINTEND(*-avoid-c-arrays)
+
+ template <>
+ struct filldata {
+ static void fill(std::ostream* stream, const void* in);
+ };
+
+ template
+ struct filldata {
+ DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4180)
+ static void fill(std::ostream* stream, const T* in) {
+ DOCTEST_MSVC_SUPPRESS_WARNING_POP
+ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wmicrosoft-cast")
+ filldata::fill(stream,
+#if DOCTEST_GCC == 0 || DOCTEST_GCC >= DOCTEST_COMPILER(4, 9, 0)
+ reinterpret_cast(in)
+#else
+ * reinterpret_cast(&in)
+#endif
+ );
+ DOCTEST_CLANG_SUPPRESS_WARNING_POP
+ }
+ };
+ }
+
+ struct DOCTEST_INTERFACE Approx
+ {
+ Approx(double value);
+
+ Approx operator()(double value) const;
+
+#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+ template
+ explicit Approx(const T& value,
+ typename detail::types::enable_if::value>::type* =
+ static_cast(nullptr)) {
+ *this = static_cast(value);
+ }
+#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+
+ Approx& epsilon(double newEpsilon);
+
+#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+ template
+ typename std::enable_if::value, Approx&>::type epsilon(
+ const T& newEpsilon) {
+ m_epsilon = static_cast(newEpsilon);
+ return *this;
+ }
+#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+
+ Approx& scale(double newScale);
+
+#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+ template
+ typename std::enable_if::value, Approx&>::type scale(
+ const T& newScale) {
+ m_scale = static_cast(newScale);
+ return *this;
+ }
+#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+
+ // clang-format off
+ DOCTEST_INTERFACE friend bool operator==(double lhs, const Approx& rhs);
+ DOCTEST_INTERFACE friend bool operator==(const Approx& lhs, double rhs);
+ DOCTEST_INTERFACE friend bool operator!=(double lhs, const Approx& rhs);
+ DOCTEST_INTERFACE friend bool operator!=(const Approx& lhs, double rhs);
+ DOCTEST_INTERFACE friend bool operator<=(double lhs, const Approx& rhs);
+ DOCTEST_INTERFACE friend bool operator<=(const Approx& lhs, double rhs);
+ DOCTEST_INTERFACE friend bool operator>=(double lhs, const Approx& rhs);
+ DOCTEST_INTERFACE friend bool operator>=(const Approx& lhs, double rhs);
+ DOCTEST_INTERFACE friend bool operator< (double lhs, const Approx& rhs);
+ DOCTEST_INTERFACE friend bool operator< (const Approx& lhs, double rhs);
+ DOCTEST_INTERFACE friend bool operator> (double lhs, const Approx& rhs);
+ DOCTEST_INTERFACE friend bool operator> (const Approx& lhs, double rhs);
+
+#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+#define DOCTEST_APPROX_PREFIX \
+ template friend typename std::enable_if::value, bool>::type
+
+ DOCTEST_APPROX_PREFIX operator==(const T& lhs, const Approx& rhs) { return operator==(static_cast(lhs), rhs); }
+ DOCTEST_APPROX_PREFIX operator==(const Approx& lhs, const T& rhs) { return operator==(rhs, lhs); }
+ DOCTEST_APPROX_PREFIX operator!=(const T& lhs, const Approx& rhs) { return !operator==(lhs, rhs); }
+ DOCTEST_APPROX_PREFIX operator!=(const Approx& lhs, const T& rhs) { return !operator==(rhs, lhs); }
+ DOCTEST_APPROX_PREFIX operator<=(const T& lhs, const Approx& rhs) { return static_cast(lhs) < rhs.m_value || lhs == rhs; }
+ DOCTEST_APPROX_PREFIX operator<=(const Approx& lhs, const T& rhs) { return lhs.m_value < static_cast(rhs) || lhs == rhs; }
+ DOCTEST_APPROX_PREFIX operator>=(const T& lhs, const Approx& rhs) { return static_cast(lhs) > rhs.m_value || lhs == rhs; }
+ DOCTEST_APPROX_PREFIX operator>=(const Approx& lhs, const T& rhs) { return lhs.m_value > static_cast(rhs) || lhs == rhs; }
+ DOCTEST_APPROX_PREFIX operator< (const T& lhs, const Approx& rhs) { return static_cast(lhs) < rhs.m_value && lhs != rhs; }
+ DOCTEST_APPROX_PREFIX operator< (const Approx& lhs, const T& rhs) { return lhs.m_value < static_cast(rhs) && lhs != rhs; }
+ DOCTEST_APPROX_PREFIX operator> (const T& lhs, const Approx& rhs) { return static_cast(lhs) > rhs.m_value && lhs != rhs; }
+ DOCTEST_APPROX_PREFIX operator> (const Approx& lhs, const T& rhs) { return lhs.m_value > static_cast(rhs) && lhs != rhs; }
+#undef DOCTEST_APPROX_PREFIX
+#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+
+ // clang-format on
+
+ double m_epsilon;
+ double m_scale;
+ double m_value;
+ };
+
+ DOCTEST_INTERFACE String toString(const Approx& in);
+
+ DOCTEST_INTERFACE const ContextOptions* getContextOptions();
+
+ template
+ struct DOCTEST_INTERFACE_DECL IsNaN
+ {
+ F value; bool flipped;
+ IsNaN(F f, bool flip = false) : value(f), flipped(flip) { }
+ IsNaN operator!() const { return { value, !flipped }; }
+ operator bool() const;
+ };
+#ifndef __MINGW32__
+ extern template struct DOCTEST_INTERFACE_DECL IsNaN;
+ extern template struct DOCTEST_INTERFACE_DECL IsNaN;
+ extern template struct DOCTEST_INTERFACE_DECL IsNaN;
+#endif
+ DOCTEST_INTERFACE String toString(IsNaN in);
+ DOCTEST_INTERFACE String toString(IsNaN in);
+ DOCTEST_INTERFACE String toString(IsNaN in);
+
+#ifndef DOCTEST_CONFIG_DISABLE
+
+ namespace detail {
+ // clang-format off
+#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+ template struct decay_array { using type = T; };
+ template struct decay_array { using type = T*; };
+ template struct decay_array { using type = T*; };
+
+ template struct not_char_pointer { static DOCTEST_CONSTEXPR int value = 1; };
+ template<> struct not_char_pointer { static DOCTEST_CONSTEXPR int value = 0; };
+ template<> struct not_char_pointer { static DOCTEST_CONSTEXPR int value = 0; };
+
+ template struct can_use_op : public not_char_pointer::type> {};
+#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+ // clang-format on
+
+ struct DOCTEST_INTERFACE TestFailureException
+ {
+ };
+
+ DOCTEST_INTERFACE bool checkIfShouldThrow(assertType::Enum at);
+
+#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
+ DOCTEST_NORETURN
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
+ DOCTEST_INTERFACE void throwException();
+
+ struct DOCTEST_INTERFACE Subcase
+ {
+ SubcaseSignature m_signature;
+ bool m_entered = false;
+
+ Subcase(const String& name, const char* file, int line);
+ Subcase(const Subcase&) = delete;
+ Subcase(Subcase&&) = delete;
+ Subcase& operator=(const Subcase&) = delete;
+ Subcase& operator=(Subcase&&) = delete;
+ ~Subcase();
+
+ operator bool() const;
+
+ private:
+ bool checkFilters();
+ };
+
+ template
+ String stringifyBinaryExpr(const DOCTEST_REF_WRAP(L) lhs, const char* op,
+ const DOCTEST_REF_WRAP(R) rhs) {
+ return (DOCTEST_STRINGIFY(lhs)) + op + (DOCTEST_STRINGIFY(rhs));
+ }
+
+#if DOCTEST_CLANG && DOCTEST_CLANG < DOCTEST_COMPILER(3, 6, 0)
+ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wunused-comparison")
+#endif
+
+ // This will check if there is any way it could find a operator like member or friend and uses it.
+ // If not it doesn't find the operator or if the operator at global scope is defined after
+ // this template, the template won't be instantiated due to SFINAE. Once the template is not
+ // instantiated it can look for global operator using normal conversions.
+#ifdef __NVCC__
+#define SFINAE_OP(ret,op) ret
+#else
+#define SFINAE_OP(ret,op) decltype((void)(doctest::detail::declval() op doctest::detail::declval()),ret{})
+#endif
+
+#define DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(op, op_str, op_macro) \
+ template \
+ DOCTEST_NOINLINE SFINAE_OP(Result,op) operator op(R&& rhs) { \
+ bool res = op_macro(doctest::detail::forward(lhs), doctest::detail::forward(rhs)); \
+ if(m_at & assertType::is_false) \
+ res = !res; \
+ if(!res || doctest::getContextOptions()->success) \
+ return Result(res, stringifyBinaryExpr(lhs, op_str, rhs)); \
+ return Result(res); \
+ }
+
+ // more checks could be added - like in Catch:
+ // https://github.com/catchorg/Catch2/pull/1480/files
+ // https://github.com/catchorg/Catch2/pull/1481/files
+#define DOCTEST_FORBIT_EXPRESSION(rt, op) \
+ template \
+ rt& operator op(const R&) { \
+ static_assert(deferred_false::value, \
+ "Expression Too Complex Please Rewrite As Binary Comparison!"); \
+ return *this; \
+ }
+
+ struct DOCTEST_INTERFACE Result // NOLINT(*-member-init)
+ {
+ bool m_passed;
+ String m_decomp;
+
+ Result() = default; // TODO: Why do we need this? (To remove NOLINT)
+ Result(bool passed, const String& decomposition = String());
+
+ // forbidding some expressions based on this table: https://en.cppreference.com/w/cpp/language/operator_precedence
+ DOCTEST_FORBIT_EXPRESSION(Result, &)
+ DOCTEST_FORBIT_EXPRESSION(Result, ^)
+ DOCTEST_FORBIT_EXPRESSION(Result, | )
+ DOCTEST_FORBIT_EXPRESSION(Result, &&)
+ DOCTEST_FORBIT_EXPRESSION(Result, || )
+ DOCTEST_FORBIT_EXPRESSION(Result, == )
+ DOCTEST_FORBIT_EXPRESSION(Result, != )
+ DOCTEST_FORBIT_EXPRESSION(Result, < )
+ DOCTEST_FORBIT_EXPRESSION(Result, > )
+ DOCTEST_FORBIT_EXPRESSION(Result, <= )
+ DOCTEST_FORBIT_EXPRESSION(Result, >= )
+ DOCTEST_FORBIT_EXPRESSION(Result, =)
+ DOCTEST_FORBIT_EXPRESSION(Result, +=)
+ DOCTEST_FORBIT_EXPRESSION(Result, -=)
+ DOCTEST_FORBIT_EXPRESSION(Result, *=)
+ DOCTEST_FORBIT_EXPRESSION(Result, /=)
+ DOCTEST_FORBIT_EXPRESSION(Result, %=)
+ DOCTEST_FORBIT_EXPRESSION(Result, <<=)
+ DOCTEST_FORBIT_EXPRESSION(Result, >>=)
+ DOCTEST_FORBIT_EXPRESSION(Result, &=)
+ DOCTEST_FORBIT_EXPRESSION(Result, ^=)
+ DOCTEST_FORBIT_EXPRESSION(Result, |=)
+ };
+
+#ifndef DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION
+
+ DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wsign-conversion")
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wsign-compare")
+ //DOCTEST_CLANG_SUPPRESS_WARNING("-Wdouble-promotion")
+ //DOCTEST_CLANG_SUPPRESS_WARNING("-Wconversion")
+ //DOCTEST_CLANG_SUPPRESS_WARNING("-Wfloat-equal")
+
+ DOCTEST_GCC_SUPPRESS_WARNING_PUSH
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-conversion")
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-compare")
+ //DOCTEST_GCC_SUPPRESS_WARNING("-Wdouble-promotion")
+ //DOCTEST_GCC_SUPPRESS_WARNING("-Wconversion")
+ //DOCTEST_GCC_SUPPRESS_WARNING("-Wfloat-equal")
+
+ DOCTEST_MSVC_SUPPRESS_WARNING_PUSH
+ // https://stackoverflow.com/questions/39479163 what's the difference between 4018 and 4389
+ DOCTEST_MSVC_SUPPRESS_WARNING(4388) // signed/unsigned mismatch
+ DOCTEST_MSVC_SUPPRESS_WARNING(4389) // 'operator' : signed/unsigned mismatch
+ DOCTEST_MSVC_SUPPRESS_WARNING(4018) // 'expression' : signed/unsigned mismatch
+ //DOCTEST_MSVC_SUPPRESS_WARNING(4805) // 'operation' : unsafe mix of type 'type' and type 'type' in operation
+
+#endif // DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION
+
+ // clang-format off
+#ifndef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+#define DOCTEST_COMPARISON_RETURN_TYPE bool
+#else // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+#define DOCTEST_COMPARISON_RETURN_TYPE typename types::enable_if::value || can_use_op::value, bool>::type
+ inline bool eq(const char* lhs, const char* rhs) { return String(lhs) == String(rhs); }
+ inline bool ne(const char* lhs, const char* rhs) { return String(lhs) != String(rhs); }
+ inline bool lt(const char* lhs, const char* rhs) { return String(lhs) < String(rhs); }
+ inline bool gt(const char* lhs, const char* rhs) { return String(lhs) > String(rhs); }
+ inline bool le(const char* lhs, const char* rhs) { return String(lhs) <= String(rhs); }
+ inline bool ge(const char* lhs, const char* rhs) { return String(lhs) >= String(rhs); }
+#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+ // clang-format on
+
+#define DOCTEST_RELATIONAL_OP(name, op) \
+ template \
+ DOCTEST_COMPARISON_RETURN_TYPE name(const DOCTEST_REF_WRAP(L) lhs, \
+ const DOCTEST_REF_WRAP(R) rhs) { \
+ return lhs op rhs; \
+ }
+
+ DOCTEST_RELATIONAL_OP(eq, == )
+ DOCTEST_RELATIONAL_OP(ne, != )
+ DOCTEST_RELATIONAL_OP(lt, < )
+ DOCTEST_RELATIONAL_OP(gt, > )
+ DOCTEST_RELATIONAL_OP(le, <= )
+ DOCTEST_RELATIONAL_OP(ge, >= )
+
+#ifndef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+#define DOCTEST_CMP_EQ(l, r) l == r
+#define DOCTEST_CMP_NE(l, r) l != r
+#define DOCTEST_CMP_GT(l, r) l > r
+#define DOCTEST_CMP_LT(l, r) l < r
+#define DOCTEST_CMP_GE(l, r) l >= r
+#define DOCTEST_CMP_LE(l, r) l <= r
+#else // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+#define DOCTEST_CMP_EQ(l, r) eq(l, r)
+#define DOCTEST_CMP_NE(l, r) ne(l, r)
+#define DOCTEST_CMP_GT(l, r) gt(l, r)
+#define DOCTEST_CMP_LT(l, r) lt(l, r)
+#define DOCTEST_CMP_GE(l, r) ge(l, r)
+#define DOCTEST_CMP_LE(l, r) le(l, r)
+#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+
+ template
+ // cppcheck-suppress copyCtorAndEqOperator
+ struct Expression_lhs
+ {
+ L lhs;
+ assertType::Enum m_at;
+
+ explicit Expression_lhs(L&& in, assertType::Enum at)
+ : lhs(static_cast(in))
+ , m_at(at) {}
+
+ DOCTEST_NOINLINE operator Result() {
+ // this is needed only for MSVC 2015
+ DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4800) // 'int': forcing value to bool
+ bool res = static_cast(lhs);
+ DOCTEST_MSVC_SUPPRESS_WARNING_POP
+ if (m_at & assertType::is_false) { //!OCLINT bitwise operator in conditional
+ res = !res;
+ }
+
+ if (!res || getContextOptions()->success) {
+ return { res, (DOCTEST_STRINGIFY(lhs)) };
+ }
+ return { res };
+ }
+
+ /* This is required for user-defined conversions from Expression_lhs to L */
+ operator L() const { return lhs; }
+
+ // clang-format off
+ DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(== , " == ", DOCTEST_CMP_EQ) //!OCLINT bitwise operator in conditional
+ DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(!= , " != ", DOCTEST_CMP_NE) //!OCLINT bitwise operator in conditional
+ DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(> , " > ", DOCTEST_CMP_GT) //!OCLINT bitwise operator in conditional
+ DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(< , " < ", DOCTEST_CMP_LT) //!OCLINT bitwise operator in conditional
+ DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(>= , " >= ", DOCTEST_CMP_GE) //!OCLINT bitwise operator in conditional
+ DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(<= , " <= ", DOCTEST_CMP_LE) //!OCLINT bitwise operator in conditional
+ // clang-format on
+
+ // forbidding some expressions based on this table: https://en.cppreference.com/w/cpp/language/operator_precedence
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, &)
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, ^)
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, | )
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, &&)
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, || )
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, =)
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, +=)
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, -=)
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, *=)
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, /=)
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, %=)
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, <<=)
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, >>=)
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, &=)
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, ^=)
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, |=)
+ // these 2 are unfortunate because they should be allowed - they have higher precedence over the comparisons, but the
+ // ExpressionDecomposer class uses the left shift operator to capture the left operand of the binary expression...
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, << )
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, >> )
+ };
+
+#ifndef DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION
+
+ DOCTEST_CLANG_SUPPRESS_WARNING_POP
+ DOCTEST_MSVC_SUPPRESS_WARNING_POP
+ DOCTEST_GCC_SUPPRESS_WARNING_POP
+
+#endif // DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION
+
+#if DOCTEST_CLANG && DOCTEST_CLANG < DOCTEST_COMPILER(3, 6, 0)
+ DOCTEST_CLANG_SUPPRESS_WARNING_POP
+#endif
+
+ struct DOCTEST_INTERFACE ExpressionDecomposer
+ {
+ assertType::Enum m_at;
+
+ ExpressionDecomposer(assertType::Enum at);
+
+ // The right operator for capturing expressions is "<=" instead of "<<" (based on the operator precedence table)
+ // but then there will be warnings from GCC about "-Wparentheses" and since "_Pragma()" is problematic this will stay for now...
+ // https://github.com/catchorg/Catch2/issues/870
+ // https://github.com/catchorg/Catch2/issues/565
+ template
+ Expression_lhs operator<<(L&& operand) {
+ return Expression_lhs(static_cast(operand), m_at);
+ }
+
+ template ::value, void >::type* = nullptr>
+ Expression_lhs operator<<(const L& operand) {
+ return Expression_lhs(operand, m_at);
+ }
+ };
+
+ struct DOCTEST_INTERFACE TestSuite
+ {
+ const char* m_test_suite = nullptr;
+ const char* m_description = nullptr;
+ bool m_skip = false;
+ bool m_no_breaks = false;
+ bool m_no_output = false;
+ bool m_may_fail = false;
+ bool m_should_fail = false;
+ int m_expected_failures = 0;
+ double m_timeout = 0;
+
+ TestSuite& operator*(const char* in);
+
+ template
+ TestSuite& operator*(const T& in) {
+ in.fill(*this);
+ return *this;
+ }
+ };
+
+ using funcType = void (*)();
+
+ struct DOCTEST_INTERFACE TestCase : public TestCaseData
+ {
+ funcType m_test; // a function pointer to the test case
+
+ String m_type; // for templated test cases - gets appended to the real name
+ int m_template_id; // an ID used to distinguish between the different versions of a templated test case
+ String m_full_name; // contains the name (only for templated test cases!) + the template type
+
+ TestCase(funcType test, const char* file, unsigned line, const TestSuite& test_suite,
+ const String& type = String(), int template_id = -1);
+
+ TestCase(const TestCase& other);
+ TestCase(TestCase&&) = delete;
+
+ DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(26434) // hides a non-virtual function
+ TestCase& operator=(const TestCase& other);
+ DOCTEST_MSVC_SUPPRESS_WARNING_POP
+
+ TestCase& operator=(TestCase&&) = delete;
+
+ TestCase& operator*(const char* in);
+
+ template
+ TestCase& operator*(const T& in) {
+ in.fill(*this);
+ return *this;
+ }
+
+ bool operator<(const TestCase& other) const;
+
+ ~TestCase() = default;
+ };
+
+ // forward declarations of functions used by the macros
+ DOCTEST_INTERFACE int regTest(const TestCase& tc);
+ DOCTEST_INTERFACE int setTestSuite(const TestSuite& ts);
+ DOCTEST_INTERFACE bool isDebuggerActive();
+
+ template
+ int instantiationHelper(const T&) { return 0; }
+
+ namespace binaryAssertComparison {
+ enum Enum
+ {
+ eq = 0,
+ ne,
+ gt,
+ lt,
+ ge,
+ le
+ };
+ } // namespace binaryAssertComparison
+
+ // clang-format off
+ template struct RelationalComparator { bool operator()(const DOCTEST_REF_WRAP(L), const DOCTEST_REF_WRAP(R)) const { return false; } };
+
+#define DOCTEST_BINARY_RELATIONAL_OP(n, op) \
+ template struct RelationalComparator { bool operator()(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) const { return op(lhs, rhs); } };
+ // clang-format on
+
+ DOCTEST_BINARY_RELATIONAL_OP(0, doctest::detail::eq)
+ DOCTEST_BINARY_RELATIONAL_OP(1, doctest::detail::ne)
+ DOCTEST_BINARY_RELATIONAL_OP(2, doctest::detail::gt)
+ DOCTEST_BINARY_RELATIONAL_OP(3, doctest::detail::lt)
+ DOCTEST_BINARY_RELATIONAL_OP(4, doctest::detail::ge)
+ DOCTEST_BINARY_RELATIONAL_OP(5, doctest::detail::le)
+
+ struct DOCTEST_INTERFACE ResultBuilder : public AssertData
+ {
+ ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr,
+ const char* exception_type = "", const String& exception_string = "");
+
+ ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr,
+ const char* exception_type, const Contains& exception_string);
+
+ void setResult(const Result& res);
+
+ template
+ DOCTEST_NOINLINE bool binary_assert(const DOCTEST_REF_WRAP(L) lhs,
+ const DOCTEST_REF_WRAP(R) rhs) {
+ m_failed = !RelationalComparator()(lhs, rhs);
+ if (m_failed || getContextOptions()->success) {
+ m_decomp = stringifyBinaryExpr(lhs, ", ", rhs);
+ }
+ return !m_failed;
+ }
+
+ template
+ DOCTEST_NOINLINE bool unary_assert(const DOCTEST_REF_WRAP(L) val) {
+ m_failed = !val;
+
+ if (m_at & assertType::is_false) { //!OCLINT bitwise operator in conditional
+ m_failed = !m_failed;
+ }
+
+ if (m_failed || getContextOptions()->success) {
+ m_decomp = (DOCTEST_STRINGIFY(val));
+ }
+
+ return !m_failed;
+ }
+
+ void translateException();
+
+ bool log();
+ void react() const;
+ };
+
+ namespace assertAction {
+ enum Enum
+ {
+ nothing = 0,
+ dbgbreak = 1,
+ shouldthrow = 2
+ };
+ } // namespace assertAction
+
+ DOCTEST_INTERFACE void failed_out_of_a_testing_context(const AssertData& ad);
+
+ DOCTEST_INTERFACE bool decomp_assert(assertType::Enum at, const char* file, int line,
+ const char* expr, const Result& result);
+
+#define DOCTEST_ASSERT_OUT_OF_TESTS(decomp) \
+ do { \
+ if(!is_running_in_test) { \
+ if(failed) { \
+ ResultBuilder rb(at, file, line, expr); \
+ rb.m_failed = failed; \
+ rb.m_decomp = decomp; \
+ failed_out_of_a_testing_context(rb); \
+ if(isDebuggerActive() && !getContextOptions()->no_breaks) \
+ DOCTEST_BREAK_INTO_DEBUGGER(); \
+ if(checkIfShouldThrow(at)) \
+ throwException(); \
+ } \
+ return !failed; \
+ } \
+ } while(false)
+
+#define DOCTEST_ASSERT_IN_TESTS(decomp) \
+ ResultBuilder rb(at, file, line, expr); \
+ rb.m_failed = failed; \
+ if(rb.m_failed || getContextOptions()->success) \
+ rb.m_decomp = decomp; \
+ if(rb.log()) \
+ DOCTEST_BREAK_INTO_DEBUGGER(); \
+ if(rb.m_failed && checkIfShouldThrow(at)) \
+ throwException()
+
+ template
+ DOCTEST_NOINLINE bool binary_assert(assertType::Enum at, const char* file, int line,
+ const char* expr, const DOCTEST_REF_WRAP(L) lhs,
+ const DOCTEST_REF_WRAP(R) rhs) {
+ bool failed = !RelationalComparator()(lhs, rhs);
+
+ // ###################################################################################
+ // IF THE DEBUGGER BREAKS HERE - GO 1 LEVEL UP IN THE CALLSTACK FOR THE FAILING ASSERT
+ // THIS IS THE EFFECT OF HAVING 'DOCTEST_CONFIG_SUPER_FAST_ASSERTS' DEFINED
+ // ###################################################################################
+ DOCTEST_ASSERT_OUT_OF_TESTS(stringifyBinaryExpr(lhs, ", ", rhs));
+ DOCTEST_ASSERT_IN_TESTS(stringifyBinaryExpr(lhs, ", ", rhs));
+ return !failed;
+ }
+
+ template
+ DOCTEST_NOINLINE bool unary_assert(assertType::Enum at, const char* file, int line,
+ const char* expr, const DOCTEST_REF_WRAP(L) val) {
+ bool failed = !val;
+
+ if (at & assertType::is_false) //!OCLINT bitwise operator in conditional
+ failed = !failed;
+
+ // ###################################################################################
+ // IF THE DEBUGGER BREAKS HERE - GO 1 LEVEL UP IN THE CALLSTACK FOR THE FAILING ASSERT
+ // THIS IS THE EFFECT OF HAVING 'DOCTEST_CONFIG_SUPER_FAST_ASSERTS' DEFINED
+ // ###################################################################################
+ DOCTEST_ASSERT_OUT_OF_TESTS((DOCTEST_STRINGIFY(val)));
+ DOCTEST_ASSERT_IN_TESTS((DOCTEST_STRINGIFY(val)));
+ return !failed;
+ }
+
+ struct DOCTEST_INTERFACE IExceptionTranslator
+ {
+ DOCTEST_DECLARE_INTERFACE(IExceptionTranslator)
+ virtual bool translate(String&) const = 0;
+ };
+
+ template
+ class ExceptionTranslator : public IExceptionTranslator //!OCLINT destructor of virtual class
+ {
+ public:
+ explicit ExceptionTranslator(String(*translateFunction)(T))
+ : m_translateFunction(translateFunction) {}
+
+ bool translate(String& res) const override {
+#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
+ try {
+ throw; // lgtm [cpp/rethrow-no-exception]
+ // cppcheck-suppress catchExceptionByValue
+ }
+ catch (const T& ex) {
+ res = m_translateFunction(ex); //!OCLINT parameter reassignment
+ return true;
+ }
+ catch (...) {} //!OCLINT - empty catch statement
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
+ static_cast(res); // to silence -Wunused-parameter
+ return false;
+ }
+
+ private:
+ String(*m_translateFunction)(T);
+ };
+
+ DOCTEST_INTERFACE void registerExceptionTranslatorImpl(const IExceptionTranslator* et);
+
+ // ContextScope base class used to allow implementing methods of ContextScope
+ // that don't depend on the template parameter in doctest.cpp.
+ struct DOCTEST_INTERFACE ContextScopeBase : public IContextScope {
+ ContextScopeBase(const ContextScopeBase&) = delete;
+
+ ContextScopeBase& operator=(const ContextScopeBase&) = delete;
+ ContextScopeBase& operator=(ContextScopeBase&&) = delete;
+
+ ~ContextScopeBase() override = default;
+
+ protected:
+ ContextScopeBase();
+ ContextScopeBase(ContextScopeBase&& other) noexcept;
+
+ void destroy();
+ bool need_to_destroy{ true };
+ };
+
+ template class ContextScope : public ContextScopeBase
+ {
+ L lambda_;
+
+ public:
+ explicit ContextScope(const L& lambda) : lambda_(lambda) {}
+ explicit ContextScope(L&& lambda) : lambda_(static_cast(lambda)) { }
+
+ ContextScope(const ContextScope&) = delete;
+ ContextScope(ContextScope&&) noexcept = default;
+
+ ContextScope& operator=(const ContextScope&) = delete;
+ ContextScope& operator=(ContextScope&&) = delete;
+
+ void stringify(std::ostream* s) const override { lambda_(s); }
+
+ ~ContextScope() override {
+ if (need_to_destroy) {
+ destroy();
+ }
+ }
+ };
+
+ struct DOCTEST_INTERFACE MessageBuilder : public MessageData
+ {
+ std::ostream* m_stream;
+ bool logged = false;
+
+ MessageBuilder(const char* file, int line, assertType::Enum severity);
+
+ MessageBuilder(const MessageBuilder&) = delete;
+ MessageBuilder(MessageBuilder&&) = delete;
+
+ MessageBuilder& operator=(const MessageBuilder&) = delete;
+ MessageBuilder& operator=(MessageBuilder&&) = delete;
+
+ ~MessageBuilder();
+
+ // the preferred way of chaining parameters for stringification
+ DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4866)
+ template
+ MessageBuilder& operator,(const T& in) {
+ *m_stream << (DOCTEST_STRINGIFY(in));
+ return *this;
+ }
+ DOCTEST_MSVC_SUPPRESS_WARNING_POP
+
+ // kept here just for backwards-compatibility - the comma operator should be preferred now
+ template
+ MessageBuilder& operator<<(const T& in) { return this->operator,(in); }
+
+ // the `,` operator has the lowest operator precedence - if `<<` is used by the user then
+ // the `,` operator will be called last which is not what we want and thus the `*` operator
+ // is used first (has higher operator precedence compared to `<<`) so that we guarantee that
+ // an operator of the MessageBuilder class is called first before the rest of the parameters
+ template
+ MessageBuilder& operator*(const T& in) { return this->operator,(in); }
+
+ bool log();
+ void react();
+ };
+
+ template
+ ContextScope MakeContextScope(const L& lambda) {
+ return ContextScope(lambda);
+ }
+ } // namespace detail
+
+#define DOCTEST_DEFINE_DECORATOR(name, type, def) \
+ struct name \
+ { \
+ type data; \
+ name(type in = def) \
+ : data(in) {} \
+ void fill(detail::TestCase& state) const { state.DOCTEST_CAT(m_, name) = data; } \
+ void fill(detail::TestSuite& state) const { state.DOCTEST_CAT(m_, name) = data; } \
+ }
+
+ DOCTEST_DEFINE_DECORATOR(test_suite, const char*, "");
+ DOCTEST_DEFINE_DECORATOR(description, const char*, "");
+ DOCTEST_DEFINE_DECORATOR(skip, bool, true);
+ DOCTEST_DEFINE_DECORATOR(no_breaks, bool, true);
+ DOCTEST_DEFINE_DECORATOR(no_output, bool, true);
+ DOCTEST_DEFINE_DECORATOR(timeout, double, 0);
+ DOCTEST_DEFINE_DECORATOR(may_fail, bool, true);
+ DOCTEST_DEFINE_DECORATOR(should_fail, bool, true);
+ DOCTEST_DEFINE_DECORATOR(expected_failures, int, 0);
+
+ template
+ int registerExceptionTranslator(String(*translateFunction)(T)) {
+ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wexit-time-destructors")
+ static detail::ExceptionTranslator exceptionTranslator(translateFunction);
+ DOCTEST_CLANG_SUPPRESS_WARNING_POP
+ detail::registerExceptionTranslatorImpl(&exceptionTranslator);
+ return 0;
+ }
+
+} // namespace doctest
+
+// in a separate namespace outside of doctest because the DOCTEST_TEST_SUITE macro
+// introduces an anonymous namespace in which getCurrentTestSuite gets overridden
+namespace doctest_detail_test_suite_ns {
+ DOCTEST_INTERFACE doctest::detail::TestSuite& getCurrentTestSuite();
+} // namespace doctest_detail_test_suite_ns
+
+namespace doctest {
+#else // DOCTEST_CONFIG_DISABLE
+ template
+ int registerExceptionTranslator(String(*)(T)) {
+ return 0;
+ }
+#endif // DOCTEST_CONFIG_DISABLE
+
+ namespace detail {
+ using assert_handler = void (*)(const AssertData&);
+ struct ContextState;
+ } // namespace detail
+
+ class DOCTEST_INTERFACE Context
+ {
+ detail::ContextState* p;
+
+ void parseArgs(int argc, const char* const* argv, bool withDefaults = false);
+
+ public:
+ explicit Context(int argc = 0, const char* const* argv = nullptr);
+
+ Context(const Context&) = delete;
+ Context(Context&&) = delete;
+
+ Context& operator=(const Context&) = delete;
+ Context& operator=(Context&&) = delete;
+
+ ~Context(); // NOLINT(performance-trivially-destructible)
+
+ void applyCommandLine(int argc, const char* const* argv);
+
+ void addFilter(const char* filter, const char* value);
+ void clearFilters();
+ void setOption(const char* option, bool value);
+ void setOption(const char* option, int value);
+ void setOption(const char* option, const char* value);
+
+ bool shouldExit();
+
+ void setAsDefaultForAssertsOutOfTestCases();
+
+ void setAssertHandler(detail::assert_handler ah);
+
+ void setCout(std::ostream* out);
+
+ int run();
+ };
+
+ namespace TestCaseFailureReason {
+ enum Enum
+ {
+ None = 0,
+ AssertFailure = 1, // an assertion has failed in the test case
+ Exception = 2, // test case threw an exception
+ Crash = 4, // a crash...
+ TooManyFailedAsserts = 8, // the abort-after option
+ Timeout = 16, // see the timeout decorator
+ ShouldHaveFailedButDidnt = 32, // see the should_fail decorator
+ ShouldHaveFailedAndDid = 64, // see the should_fail decorator
+ DidntFailExactlyNumTimes = 128, // see the expected_failures decorator
+ FailedExactlyNumTimes = 256, // see the expected_failures decorator
+ CouldHaveFailedAndDid = 512 // see the may_fail decorator
+ };
+ } // namespace TestCaseFailureReason
+
+ struct DOCTEST_INTERFACE CurrentTestCaseStats
+ {
+ int numAssertsCurrentTest;
+ int numAssertsFailedCurrentTest;
+ double seconds;
+ int failure_flags; // use TestCaseFailureReason::Enum
+ bool testCaseSuccess;
+ };
+
+ struct DOCTEST_INTERFACE TestCaseException
+ {
+ String error_string;
+ bool is_crash;
+ };
+
+ struct DOCTEST_INTERFACE TestRunStats
+ {
+ unsigned numTestCases;
+ unsigned numTestCasesPassingFilters;
+ unsigned numTestSuitesPassingFilters;
+ unsigned numTestCasesFailed;
+ int numAsserts;
+ int numAssertsFailed;
+ };
+
+ struct QueryData
+ {
+ const TestRunStats* run_stats = nullptr;
+ const TestCaseData** data = nullptr;
+ unsigned num_data = 0;
+ };
+
+ struct DOCTEST_INTERFACE IReporter
+ {
+ // The constructor has to accept "const ContextOptions&" as a single argument
+ // which has most of the options for the run + a pointer to the stdout stream
+ // Reporter(const ContextOptions& in)
+
+ // called when a query should be reported (listing test cases, printing the version, etc.)
+ virtual void report_query(const QueryData&) = 0;
+
+ // called when the whole test run starts
+ virtual void test_run_start() = 0;
+ // called when the whole test run ends (caching a pointer to the input doesn't make sense here)
+ virtual void test_run_end(const TestRunStats&) = 0;
+
+ // called when a test case is started (safe to cache a pointer to the input)
+ virtual void test_case_start(const TestCaseData&) = 0;
+ // called when a test case is reentered because of unfinished subcases (safe to cache a pointer to the input)
+ virtual void test_case_reenter(const TestCaseData&) = 0;
+ // called when a test case has ended
+ virtual void test_case_end(const CurrentTestCaseStats&) = 0;
+
+ // called when an exception is thrown from the test case (or it crashes)
+ virtual void test_case_exception(const TestCaseException&) = 0;
+
+ // called whenever a subcase is entered (don't cache pointers to the input)
+ virtual void subcase_start(const SubcaseSignature&) = 0;
+ // called whenever a subcase is exited (don't cache pointers to the input)
+ virtual void subcase_end() = 0;
+
+ // called for each assert (don't cache pointers to the input)
+ virtual void log_assert(const AssertData&) = 0;
+ // called for each message (don't cache pointers to the input)
+ virtual void log_message(const MessageData&) = 0;
+
+ // called when a test case is skipped either because it doesn't pass the filters, has a skip decorator
+ // or isn't in the execution range (between first and last) (safe to cache a pointer to the input)
+ virtual void test_case_skipped(const TestCaseData&) = 0;
+
+ DOCTEST_DECLARE_INTERFACE(IReporter)
+
+ // can obtain all currently active contexts and stringify them if one wishes to do so
+ static int get_num_active_contexts();
+ static const IContextScope* const* get_active_contexts();
+
+ // can iterate through contexts which have been stringified automatically in their destructors when an exception has been thrown
+ static int get_num_stringified_contexts();
+ static const String* get_stringified_contexts();
+ };
+
+ namespace detail {
+ using reporterCreatorFunc = IReporter * (*)(const ContextOptions&);
+
+ DOCTEST_INTERFACE void registerReporterImpl(const char* name, int prio, reporterCreatorFunc c, bool isReporter);
+
+ template
+ IReporter* reporterCreator(const ContextOptions& o) {
+ return new Reporter(o);
+ }
+ } // namespace detail
+
+ template
+ int registerReporter(const char* name, int priority, bool isReporter) {
+ detail::registerReporterImpl(name, priority, detail::reporterCreator, isReporter);
+ return 0;
+ }
+} // namespace doctest
+
+#ifdef DOCTEST_CONFIG_ASSERTS_RETURN_VALUES
+#define DOCTEST_FUNC_EMPTY [] { return false; }()
+#else
+#define DOCTEST_FUNC_EMPTY (void)0
+#endif
+
+// if registering is not disabled
+#ifndef DOCTEST_CONFIG_DISABLE
+
+#ifdef DOCTEST_CONFIG_ASSERTS_RETURN_VALUES
+#define DOCTEST_FUNC_SCOPE_BEGIN [&]
+#define DOCTEST_FUNC_SCOPE_END ()
+#define DOCTEST_FUNC_SCOPE_RET(v) return v
+#else
+#define DOCTEST_FUNC_SCOPE_BEGIN do
+#define DOCTEST_FUNC_SCOPE_END while(false)
+#define DOCTEST_FUNC_SCOPE_RET(v) (void)0
+#endif
+
+// common code in asserts - for convenience
+#define DOCTEST_ASSERT_LOG_REACT_RETURN(b) \
+ if(b.log()) DOCTEST_BREAK_INTO_DEBUGGER(); \
+ b.react(); \
+ DOCTEST_FUNC_SCOPE_RET(!b.m_failed)
+
+#ifdef DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS
+#define DOCTEST_WRAP_IN_TRY(x) x;
+#else // DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS
+#define DOCTEST_WRAP_IN_TRY(x) \
+ try { \
+ x; \
+ } catch(...) { DOCTEST_RB.translateException(); }
+#endif // DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS
+
+#ifdef DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS
+#define DOCTEST_CAST_TO_VOID(...) \
+ DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wuseless-cast") \
+ static_cast(__VA_ARGS__); \
+ DOCTEST_GCC_SUPPRESS_WARNING_POP
+#else // DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS
+#define DOCTEST_CAST_TO_VOID(...) __VA_ARGS__;
+#endif // DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS
+
+// registers the test by initializing a dummy var with a function
+#define DOCTEST_REGISTER_FUNCTION(global_prefix, f, decorators) \
+ global_prefix DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_VAR_), /* NOLINT */ \
+ doctest::detail::regTest( \
+ doctest::detail::TestCase( \
+ f, __FILE__, __LINE__, \
+ doctest_detail_test_suite_ns::getCurrentTestSuite()) * \
+ decorators))
+
+#define DOCTEST_IMPLEMENT_FIXTURE(der, base, func, decorators) \
+ namespace { /* NOLINT */ \
+ struct der : public base \
+ { \
+ void f(); \
+ }; \
+ static DOCTEST_INLINE_NOINLINE void func() { \
+ der v; \
+ v.f(); \
+ } \
+ DOCTEST_REGISTER_FUNCTION(DOCTEST_EMPTY, func, decorators) \
+ } \
+ DOCTEST_INLINE_NOINLINE void der::f() // NOLINT(misc-definitions-in-headers)
+
+#define DOCTEST_CREATE_AND_REGISTER_FUNCTION(f, decorators) \
+ static void f(); \
+ DOCTEST_REGISTER_FUNCTION(DOCTEST_EMPTY, f, decorators) \
+ static void f()
+
+#define DOCTEST_CREATE_AND_REGISTER_FUNCTION_IN_CLASS(f, proxy, decorators) \
+ static doctest::detail::funcType proxy() { return f; } \
+ DOCTEST_REGISTER_FUNCTION(inline, proxy(), decorators) \
+ static void f()
+
+// for registering tests
+#define DOCTEST_TEST_CASE(decorators) \
+ DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), decorators)
+
+// for registering tests in classes - requires C++17 for inline variables!
+#if DOCTEST_CPLUSPLUS >= 201703L
+#define DOCTEST_TEST_CASE_CLASS(decorators) \
+ DOCTEST_CREATE_AND_REGISTER_FUNCTION_IN_CLASS(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), \
+ DOCTEST_ANONYMOUS(DOCTEST_ANON_PROXY_), \
+ decorators)
+#else // DOCTEST_TEST_CASE_CLASS
+#define DOCTEST_TEST_CASE_CLASS(...) \
+ TEST_CASES_CAN_BE_REGISTERED_IN_CLASSES_ONLY_IN_CPP17_MODE_OR_WITH_VS_2017_OR_NEWER
+#endif // DOCTEST_TEST_CASE_CLASS
+
+// for registering tests with a fixture
+#define DOCTEST_TEST_CASE_FIXTURE(c, decorators) \
+ DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(DOCTEST_ANON_CLASS_), c, \
+ DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), decorators)
+
+// for converting types to strings without the header and demangling
+#define DOCTEST_TYPE_TO_STRING_AS(str, ...) \
+ namespace doctest { \
+ template <> \
+ inline String toString<__VA_ARGS__>() { \
+ return str; \
+ } \
+ } \
+ static_assert(true, "")
+
+#define DOCTEST_TYPE_TO_STRING(...) DOCTEST_TYPE_TO_STRING_AS(#__VA_ARGS__, __VA_ARGS__)
+
+#define DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(dec, T, iter, func) \
+ template \
+ static void func(); \
+ namespace { /* NOLINT */ \
+ template \
+ struct iter; \
+ template \
+ struct iter> \
+ { \
+ iter(const char* file, unsigned line, int index) { \
+ doctest::detail::regTest(doctest::detail::TestCase(func, file, line, \
+ doctest_detail_test_suite_ns::getCurrentTestSuite(), \
+ doctest::toString(), \
+ int(line) * 1000 + index) \
+ * dec); \
+ iter>(file, line, index + 1); \
+ } \
+ }; \
+ template <> \
+ struct iter> \
+ { \
+ iter(const char*, unsigned, int) {} \
+ }; \
+ } \
+ template \
+ static void func()
+
+#define DOCTEST_TEST_CASE_TEMPLATE_DEFINE(dec, T, id) \
+ DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(dec, T, DOCTEST_CAT(id, ITERATOR), \
+ DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_))
+
+#define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, anon, ...) \
+ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_CAT(anon, DUMMY), /* NOLINT(cert-err58-cpp, fuchsia-statically-constructed-objects) */ \
+ doctest::detail::instantiationHelper( \
+ DOCTEST_CAT(id, ITERATOR)<__VA_ARGS__>(__FILE__, __LINE__, 0)))
+
+#define DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id, ...) \
+ DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_), std::tuple<__VA_ARGS__>) \
+ static_assert(true, "")
+
+#define DOCTEST_TEST_CASE_TEMPLATE_APPLY(id, ...) \
+ DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_), __VA_ARGS__) \
+ static_assert(true, "")
+
+#define DOCTEST_TEST_CASE_TEMPLATE_IMPL(dec, T, anon, ...) \
+ DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(dec, T, DOCTEST_CAT(anon, ITERATOR), anon); \
+ DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(anon, anon, std::tuple<__VA_ARGS__>) \
+ template \
+ static void anon()
+
+#define DOCTEST_TEST_CASE_TEMPLATE(dec, T, ...) \
+ DOCTEST_TEST_CASE_TEMPLATE_IMPL(dec, T, DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_), __VA_ARGS__)
+
+// for subcases
+#define DOCTEST_SUBCASE(name) \
+ if(const doctest::detail::Subcase & DOCTEST_ANONYMOUS(DOCTEST_ANON_SUBCASE_) DOCTEST_UNUSED = \
+ doctest::detail::Subcase(name, __FILE__, __LINE__))
+
+// for grouping tests in test suites by using code blocks
+#define DOCTEST_TEST_SUITE_IMPL(decorators, ns_name) \
+ namespace ns_name { namespace doctest_detail_test_suite_ns { \
+ static DOCTEST_NOINLINE doctest::detail::TestSuite& getCurrentTestSuite() noexcept { \
+ DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4640) \
+ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wexit-time-destructors") \
+ DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wmissing-field-initializers") \
+ static doctest::detail::TestSuite data{}; \
+ static bool inited = false; \
+ DOCTEST_MSVC_SUPPRESS_WARNING_POP \
+ DOCTEST_CLANG_SUPPRESS_WARNING_POP \
+ DOCTEST_GCC_SUPPRESS_WARNING_POP \
+ if(!inited) { \
+ data* decorators; \
+ inited = true; \
+ } \
+ return data; \
+ } \
+ } \
+ } \
+ namespace ns_name
+
+#define DOCTEST_TEST_SUITE(decorators) \
+ DOCTEST_TEST_SUITE_IMPL(decorators, DOCTEST_ANONYMOUS(DOCTEST_ANON_SUITE_))
+
+// for starting a testsuite block
+#define DOCTEST_TEST_SUITE_BEGIN(decorators) \
+ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_VAR_), /* NOLINT(cert-err58-cpp) */ \
+ doctest::detail::setTestSuite(doctest::detail::TestSuite() * decorators)) \
+ static_assert(true, "")
+
+// for ending a testsuite block
+#define DOCTEST_TEST_SUITE_END \
+ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_VAR_), /* NOLINT(cert-err58-cpp) */ \
+ doctest::detail::setTestSuite(doctest::detail::TestSuite() * "")) \
+ using DOCTEST_ANONYMOUS(DOCTEST_ANON_FOR_SEMICOLON_) = int
+
+// for registering exception translators
+#define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR_IMPL(translatorName, signature) \
+ inline doctest::String translatorName(signature); \
+ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_TRANSLATOR_), /* NOLINT(cert-err58-cpp) */ \
+ doctest::registerExceptionTranslator(translatorName)) \
+ doctest::String translatorName(signature)
+
+#define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature) \
+ DOCTEST_REGISTER_EXCEPTION_TRANSLATOR_IMPL(DOCTEST_ANONYMOUS(DOCTEST_ANON_TRANSLATOR_), \
+ signature)
+
+// for registering reporters
+#define DOCTEST_REGISTER_REPORTER(name, priority, reporter) \
+ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_REPORTER_), /* NOLINT(cert-err58-cpp) */ \
+ doctest::registerReporter(name, priority, true)) \
+ static_assert(true, "")
+
+// for registering listeners
+#define DOCTEST_REGISTER_LISTENER(name, priority, reporter) \
+ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_REPORTER_), /* NOLINT(cert-err58-cpp) */ \
+ doctest::registerReporter(name, priority, false)) \
+ static_assert(true, "")
+
+// clang-format off
+// for logging - disabling formatting because it's important to have these on 2 separate lines - see PR #557
+#define DOCTEST_INFO(...) \
+ DOCTEST_INFO_IMPL(DOCTEST_ANONYMOUS(DOCTEST_CAPTURE_), \
+ DOCTEST_ANONYMOUS(DOCTEST_CAPTURE_OTHER_), \
+ __VA_ARGS__)
+// clang-format on
+
+#define DOCTEST_INFO_IMPL(mb_name, s_name, ...) \
+ auto DOCTEST_ANONYMOUS(DOCTEST_CAPTURE_) = doctest::detail::MakeContextScope( \
+ [&](std::ostream* s_name) { \
+ doctest::detail::MessageBuilder mb_name(__FILE__, __LINE__, doctest::assertType::is_warn); \
+ mb_name.m_stream = s_name; \
+ mb_name * __VA_ARGS__; \
+ })
+
+#define DOCTEST_CAPTURE(x) DOCTEST_INFO(#x " := ", x)
+
+#define DOCTEST_ADD_AT_IMPL(type, file, line, mb, ...) \
+ DOCTEST_FUNC_SCOPE_BEGIN { \
+ doctest::detail::MessageBuilder mb(file, line, doctest::assertType::type); \
+ mb * __VA_ARGS__; \
+ if(mb.log()) \
+ DOCTEST_BREAK_INTO_DEBUGGER(); \
+ mb.react(); \
+ } DOCTEST_FUNC_SCOPE_END
+
+// clang-format off
+#define DOCTEST_ADD_MESSAGE_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_warn, file, line, DOCTEST_ANONYMOUS(DOCTEST_MESSAGE_), __VA_ARGS__)
+#define DOCTEST_ADD_FAIL_CHECK_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_check, file, line, DOCTEST_ANONYMOUS(DOCTEST_MESSAGE_), __VA_ARGS__)
+#define DOCTEST_ADD_FAIL_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_require, file, line, DOCTEST_ANONYMOUS(DOCTEST_MESSAGE_), __VA_ARGS__)
+// clang-format on
+
+#define DOCTEST_MESSAGE(...) DOCTEST_ADD_MESSAGE_AT(__FILE__, __LINE__, __VA_ARGS__)
+#define DOCTEST_FAIL_CHECK(...) DOCTEST_ADD_FAIL_CHECK_AT(__FILE__, __LINE__, __VA_ARGS__)
+#define DOCTEST_FAIL(...) DOCTEST_ADD_FAIL_AT(__FILE__, __LINE__, __VA_ARGS__)
+
+#define DOCTEST_TO_LVALUE(...) __VA_ARGS__ // Not removed to keep backwards compatibility.
+
+#ifndef DOCTEST_CONFIG_SUPER_FAST_ASSERTS
+
+#define DOCTEST_ASSERT_IMPLEMENT_2(assert_type, ...) \
+ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Woverloaded-shift-op-parentheses") \
+ /* NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) */ \
+ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
+ __LINE__, #__VA_ARGS__); \
+ DOCTEST_WRAP_IN_TRY(DOCTEST_RB.setResult( \
+ doctest::detail::ExpressionDecomposer(doctest::assertType::assert_type) \
+ << __VA_ARGS__)) /* NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) */ \
+ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB) \
+ DOCTEST_CLANG_SUPPRESS_WARNING_POP
+
+#define DOCTEST_ASSERT_IMPLEMENT_1(assert_type, ...) \
+ DOCTEST_FUNC_SCOPE_BEGIN { \
+ DOCTEST_ASSERT_IMPLEMENT_2(assert_type, __VA_ARGS__); \
+ } DOCTEST_FUNC_SCOPE_END // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks)
+
+#define DOCTEST_BINARY_ASSERT(assert_type, comp, ...) \
+ DOCTEST_FUNC_SCOPE_BEGIN { \
+ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
+ __LINE__, #__VA_ARGS__); \
+ DOCTEST_WRAP_IN_TRY( \
+ DOCTEST_RB.binary_assert( \
+ __VA_ARGS__)) \
+ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \
+ } DOCTEST_FUNC_SCOPE_END
+
+#define DOCTEST_UNARY_ASSERT(assert_type, ...) \
+ DOCTEST_FUNC_SCOPE_BEGIN { \
+ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
+ __LINE__, #__VA_ARGS__); \
+ DOCTEST_WRAP_IN_TRY(DOCTEST_RB.unary_assert(__VA_ARGS__)) \
+ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \
+ } DOCTEST_FUNC_SCOPE_END
+
+#else // DOCTEST_CONFIG_SUPER_FAST_ASSERTS
+
+// necessary for _MESSAGE
+#define DOCTEST_ASSERT_IMPLEMENT_2 DOCTEST_ASSERT_IMPLEMENT_1
+
+#define DOCTEST_ASSERT_IMPLEMENT_1(assert_type, ...) \
+ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Woverloaded-shift-op-parentheses") \
+ doctest::detail::decomp_assert( \
+ doctest::assertType::assert_type, __FILE__, __LINE__, #__VA_ARGS__, \
+ doctest::detail::ExpressionDecomposer(doctest::assertType::assert_type) \
+ << __VA_ARGS__) DOCTEST_CLANG_SUPPRESS_WARNING_POP
+
+#define DOCTEST_BINARY_ASSERT(assert_type, comparison, ...) \
+ doctest::detail::binary_assert( \
+ doctest::assertType::assert_type, __FILE__, __LINE__, #__VA_ARGS__, __VA_ARGS__)
+
+#define DOCTEST_UNARY_ASSERT(assert_type, ...) \
+ doctest::detail::unary_assert(doctest::assertType::assert_type, __FILE__, __LINE__, \
+ #__VA_ARGS__, __VA_ARGS__)
+
+#endif // DOCTEST_CONFIG_SUPER_FAST_ASSERTS
+
+#define DOCTEST_WARN(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_WARN, __VA_ARGS__)
+#define DOCTEST_CHECK(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_CHECK, __VA_ARGS__)
+#define DOCTEST_REQUIRE(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_REQUIRE, __VA_ARGS__)
+#define DOCTEST_WARN_FALSE(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_WARN_FALSE, __VA_ARGS__)
+#define DOCTEST_CHECK_FALSE(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_CHECK_FALSE, __VA_ARGS__)
+#define DOCTEST_REQUIRE_FALSE(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_REQUIRE_FALSE, __VA_ARGS__)
+
+// clang-format off
+#define DOCTEST_WARN_MESSAGE(cond, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN, cond); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_CHECK_MESSAGE(cond, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK, cond); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_REQUIRE_MESSAGE(cond, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE, cond); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_WARN_FALSE_MESSAGE(cond, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN_FALSE, cond); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_CHECK_FALSE_MESSAGE(cond, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK_FALSE, cond); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE_FALSE, cond); } DOCTEST_FUNC_SCOPE_END
+// clang-format on
+
+#define DOCTEST_WARN_EQ(...) DOCTEST_BINARY_ASSERT(DT_WARN_EQ, eq, __VA_ARGS__)
+#define DOCTEST_CHECK_EQ(...) DOCTEST_BINARY_ASSERT(DT_CHECK_EQ, eq, __VA_ARGS__)
+#define DOCTEST_REQUIRE_EQ(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_EQ, eq, __VA_ARGS__)
+#define DOCTEST_WARN_NE(...) DOCTEST_BINARY_ASSERT(DT_WARN_NE, ne, __VA_ARGS__)
+#define DOCTEST_CHECK_NE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_NE, ne, __VA_ARGS__)
+#define DOCTEST_REQUIRE_NE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_NE, ne, __VA_ARGS__)
+#define DOCTEST_WARN_GT(...) DOCTEST_BINARY_ASSERT(DT_WARN_GT, gt, __VA_ARGS__)
+#define DOCTEST_CHECK_GT(...) DOCTEST_BINARY_ASSERT(DT_CHECK_GT, gt, __VA_ARGS__)
+#define DOCTEST_REQUIRE_GT(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_GT, gt, __VA_ARGS__)
+#define DOCTEST_WARN_LT(...) DOCTEST_BINARY_ASSERT(DT_WARN_LT, lt, __VA_ARGS__)
+#define DOCTEST_CHECK_LT(...) DOCTEST_BINARY_ASSERT(DT_CHECK_LT, lt, __VA_ARGS__)
+#define DOCTEST_REQUIRE_LT(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_LT, lt, __VA_ARGS__)
+#define DOCTEST_WARN_GE(...) DOCTEST_BINARY_ASSERT(DT_WARN_GE, ge, __VA_ARGS__)
+#define DOCTEST_CHECK_GE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_GE, ge, __VA_ARGS__)
+#define DOCTEST_REQUIRE_GE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_GE, ge, __VA_ARGS__)
+#define DOCTEST_WARN_LE(...) DOCTEST_BINARY_ASSERT(DT_WARN_LE, le, __VA_ARGS__)
+#define DOCTEST_CHECK_LE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_LE, le, __VA_ARGS__)
+#define DOCTEST_REQUIRE_LE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_LE, le, __VA_ARGS__)
+
+#define DOCTEST_WARN_UNARY(...) DOCTEST_UNARY_ASSERT(DT_WARN_UNARY, __VA_ARGS__)
+#define DOCTEST_CHECK_UNARY(...) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY, __VA_ARGS__)
+#define DOCTEST_REQUIRE_UNARY(...) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY, __VA_ARGS__)
+#define DOCTEST_WARN_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_WARN_UNARY_FALSE, __VA_ARGS__)
+#define DOCTEST_CHECK_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY_FALSE, __VA_ARGS__)
+#define DOCTEST_REQUIRE_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY_FALSE, __VA_ARGS__)
+
+#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
+
+#define DOCTEST_ASSERT_THROWS_AS(expr, assert_type, message, ...) \
+ DOCTEST_FUNC_SCOPE_BEGIN { \
+ if(!doctest::getContextOptions()->no_throw) { \
+ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
+ __LINE__, #expr, #__VA_ARGS__, message); \
+ try { \
+ DOCTEST_CAST_TO_VOID(expr) \
+ } catch(const typename doctest::detail::types::remove_const< \
+ typename doctest::detail::types::remove_reference<__VA_ARGS__>::type>::type&) {\
+ DOCTEST_RB.translateException(); \
+ DOCTEST_RB.m_threw_as = true; \
+ } catch(...) { DOCTEST_RB.translateException(); } \
+ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \
+ } else { /* NOLINT(*-else-after-return) */ \
+ DOCTEST_FUNC_SCOPE_RET(false); \
+ } \
+ } DOCTEST_FUNC_SCOPE_END
+
+#define DOCTEST_ASSERT_THROWS_WITH(expr, expr_str, assert_type, ...) \
+ DOCTEST_FUNC_SCOPE_BEGIN { \
+ if(!doctest::getContextOptions()->no_throw) { \
+ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
+ __LINE__, expr_str, "", __VA_ARGS__); \
+ try { \
+ DOCTEST_CAST_TO_VOID(expr) \
+ } catch(...) { DOCTEST_RB.translateException(); } \
+ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \
+ } else { /* NOLINT(*-else-after-return) */ \
+ DOCTEST_FUNC_SCOPE_RET(false); \
+ } \
+ } DOCTEST_FUNC_SCOPE_END
+
+#define DOCTEST_ASSERT_NOTHROW(assert_type, ...) \
+ DOCTEST_FUNC_SCOPE_BEGIN { \
+ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
+ __LINE__, #__VA_ARGS__); \
+ try { \
+ DOCTEST_CAST_TO_VOID(__VA_ARGS__) \
+ } catch(...) { DOCTEST_RB.translateException(); } \
+ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \
+ } DOCTEST_FUNC_SCOPE_END
+
+// clang-format off
+#define DOCTEST_WARN_THROWS(...) DOCTEST_ASSERT_THROWS_WITH((__VA_ARGS__), #__VA_ARGS__, DT_WARN_THROWS, "")
+#define DOCTEST_CHECK_THROWS(...) DOCTEST_ASSERT_THROWS_WITH((__VA_ARGS__), #__VA_ARGS__, DT_CHECK_THROWS, "")
+#define DOCTEST_REQUIRE_THROWS(...) DOCTEST_ASSERT_THROWS_WITH((__VA_ARGS__), #__VA_ARGS__, DT_REQUIRE_THROWS, "")
+
+#define DOCTEST_WARN_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_WARN_THROWS_AS, "", __VA_ARGS__)
+#define DOCTEST_CHECK_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_CHECK_THROWS_AS, "", __VA_ARGS__)
+#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_REQUIRE_THROWS_AS, "", __VA_ARGS__)
+
+#define DOCTEST_WARN_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, #expr, DT_WARN_THROWS_WITH, __VA_ARGS__)
+#define DOCTEST_CHECK_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, #expr, DT_CHECK_THROWS_WITH, __VA_ARGS__)
+#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, #expr, DT_REQUIRE_THROWS_WITH, __VA_ARGS__)
+
+#define DOCTEST_WARN_THROWS_WITH_AS(expr, message, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_WARN_THROWS_WITH_AS, message, __VA_ARGS__)
+#define DOCTEST_CHECK_THROWS_WITH_AS(expr, message, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_CHECK_THROWS_WITH_AS, message, __VA_ARGS__)
+#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, message, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_REQUIRE_THROWS_WITH_AS, message, __VA_ARGS__)
+
+#define DOCTEST_WARN_NOTHROW(...) DOCTEST_ASSERT_NOTHROW(DT_WARN_NOTHROW, __VA_ARGS__)
+#define DOCTEST_CHECK_NOTHROW(...) DOCTEST_ASSERT_NOTHROW(DT_CHECK_NOTHROW, __VA_ARGS__)
+#define DOCTEST_REQUIRE_NOTHROW(...) DOCTEST_ASSERT_NOTHROW(DT_REQUIRE_NOTHROW, __VA_ARGS__)
+
+#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS(expr); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS(expr); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS(expr); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_AS(expr, ex); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_AS(expr, ex); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_AS(expr, ex); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_WITH(expr, with); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_WITH(expr, with); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_WITH(expr, with); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_WITH_AS(expr, with, ex); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ex); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ex); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_NOTHROW(expr); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_NOTHROW(expr); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_NOTHROW(expr); } DOCTEST_FUNC_SCOPE_END
+// clang-format on
+
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
+
+// =================================================================================================
+// == WHAT FOLLOWS IS VERSIONS OF THE MACROS THAT DO NOT DO ANY REGISTERING! ==
+// == THIS CAN BE ENABLED BY DEFINING DOCTEST_CONFIG_DISABLE GLOBALLY! ==
+// =================================================================================================
+#else // DOCTEST_CONFIG_DISABLE
+
+#define DOCTEST_IMPLEMENT_FIXTURE(der, base, func, name) \
+ namespace /* NOLINT */ { \
+ template \
+ struct der : public base \
+ { void f(); }; \
+ } \
+ template \
+ inline void der::f()
+
+#define DOCTEST_CREATE_AND_REGISTER_FUNCTION(f, name) \
+ template \
+ static inline void f()
+
+// for registering tests
+#define DOCTEST_TEST_CASE(name) \
+ DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), name)
+
+// for registering tests in classes
+#define DOCTEST_TEST_CASE_CLASS(name) \
+ DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), name)
+
+// for registering tests with a fixture
+#define DOCTEST_TEST_CASE_FIXTURE(x, name) \
+ DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(DOCTEST_ANON_CLASS_), x, \
+ DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), name)
+
+// for converting types to strings without the header and demangling
+#define DOCTEST_TYPE_TO_STRING_AS(str, ...) static_assert(true, "")
+#define DOCTEST_TYPE_TO_STRING(...) static_assert(true, "")
+
+// for typed tests
+#define DOCTEST_TEST_CASE_TEMPLATE(name, type, ...) \
+ template