diff --git a/AudioFile.h b/AudioFile.h index 99e6923..fcd3fd5 100644 --- a/AudioFile.h +++ b/AudioFile.h @@ -201,19 +201,11 @@ class AudioFile int16_t twoBytesToInt (std::vector& source, int startIndex, Endianness endianness = Endianness::LittleEndian); int getIndexOfString (std::vector& source, std::string s); int getIndexOfChunk (std::vector& source, const std::string& chunkHeaderID, int startIndex, Endianness endianness = Endianness::LittleEndian); - - //============================================================= - T sixteenBitIntToSample (int16_t sample); - int16_t sampleToSixteenBitInt (T sample); - + //============================================================= - uint8_t sampleToSingleByte (T sample); - T singleByteToSample (uint8_t sample); - uint32_t getAiffSampleRate (std::vector& fileData, int sampleRateStartIndex); bool tenByteMatch (std::vector& v1, int startIndex1, std::vector& v2, int startIndex2); void addSampleRateToAiffData (std::vector& fileData, uint32_t sampleRate); - T clamp (T v1, T minValue, T maxValue); //============================================================= void addStringToFileData (std::vector& fileData, std::string s); @@ -233,6 +225,49 @@ class AudioFile bool logErrorsToConsole {true}; }; +//============================================================= +template +struct AudioSampleConverter +{ + //============================================================= + /** Convert a signed 8-bit integer to an audio sample */ + static T signedByteToSample (int8_t sample); + + /** Convert an audio sample to an signed 8-bit representation */ + static int8_t sampleToSignedByte (T sample); + + //============================================================= + /** Convert an unsigned 8-bit integer to an audio sample */ + static T unsignedByteToSample (uint8_t sample); + + /** Convert an audio sample to an unsigned 8-bit representation */ + static uint8_t sampleToUnsignedByte (T sample); + + //============================================================= + /** Convert a 16-bit integer to an audio sample */ + static T sixteenBitIntToSample (int16_t sample); + + /** Convert a an audio sample to a 16-bit integer */ + static int16_t sampleToSixteenBitInt (T sample); + + //============================================================= + /** Convert a 24-bit value (int a 32-bit int) to an audio sample */ + static T twentyFourBitIntToSample (int32_t sample); + + /** Convert a an audio sample to a 24-bit value (in a 32-bit integer) */ + static int32_t sampleToTwentyFourBitInt (T sample); + + //============================================================= + /** Convert a 32-bit signed integer to an audio sample */ + static T thirtyTwoBitIntToSample (int32_t sample); + + /** Convert a an audio sample to a 32-bit signed integer */ + static int32_t sampleToThirtyTwoBitInt (T sample); + + //============================================================= + /** Helper clamp function to enforce ranges */ + static T clamp (T v1, T minValue, T maxValue); +}; //============================================================= // Pre-defined 10-byte representations of common sample rates @@ -284,8 +319,6 @@ enum AIFFAudioFormat template AudioFile::AudioFile() { - static_assert(std::is_floating_point::value, "ERROR: This version of AudioFile only supports floating point sample formats"); - bitDepth = 16; sampleRate = 44100; samples.resize (1); @@ -374,7 +407,7 @@ bool AudioFile::setAudioBuffer (AudioBuffer& newBuffer) if (numChannels <= 0) { - assert (false && "The buffer your are trying to use has no channels"); + assert (false && "The buffer you are trying to use has no channels"); return false; } @@ -497,7 +530,17 @@ bool AudioFile::load (std::string filePath) return false; } - return loadFromMemory (fileData); + // Handle very small files that will break our attempt to read the + // first header info from them + if (fileData.size() < 12) + { + reportError ("ERROR: File is not a valid audio file\n" + filePath); + return false; + } + else + { + return loadFromMemory (fileData); + } } //============================================================= @@ -558,6 +601,17 @@ bool AudioFile::decodeWaveFile (std::vector& fileData) uint16_t numBytesPerBlock = twoBytesToInt (fileData, f + 20); bitDepth = (int) twoBytesToInt (fileData, f + 22); + if (bitDepth > sizeof (T) * 8) + { + std::string message = "ERROR: you are trying to read a "; + message += std::to_string (bitDepth); + message += "-bit file using a "; + message += std::to_string (sizeof (T) * 8); + message += "-bit sample type"; + reportError (message); + return false; + } + uint16_t numBytesPerSample = static_cast (bitDepth) / 8; // check that the audio format is PCM or Float or extensible @@ -614,13 +668,13 @@ bool AudioFile::decodeWaveFile (std::vector& fileData) if (bitDepth == 8) { - T sample = singleByteToSample (fileData[sampleIndex]); + T sample = AudioSampleConverter::unsignedByteToSample (fileData[sampleIndex]); samples[channel].push_back (sample); } else if (bitDepth == 16) { int16_t sampleAsInt = twoBytesToInt (fileData, sampleIndex); - T sample = sixteenBitIntToSample (sampleAsInt); + T sample = AudioSampleConverter::sixteenBitIntToSample (sampleAsInt); samples[channel].push_back (sample); } else if (bitDepth == 24) @@ -631,7 +685,7 @@ bool AudioFile::decodeWaveFile (std::vector& fileData) if (sampleAsInt & 0x800000) // if the 24th bit is set, this is a negative number in 24-bit world sampleAsInt = sampleAsInt | ~0xFFFFFF; // so make sure sign is extended to the 32 bit float - T sample = (T)sampleAsInt / (T)8388608.; + T sample = AudioSampleConverter::twentyFourBitIntToSample (sampleAsInt); samples[channel].push_back (sample); } else if (bitDepth == 32) @@ -639,10 +693,16 @@ bool AudioFile::decodeWaveFile (std::vector& fileData) int32_t sampleAsInt = fourBytesToInt (fileData, sampleIndex); T sample; - if (audioFormat == WavAudioFormat::IEEEFloat) - sample = (T)reinterpret_cast (sampleAsInt); + if (audioFormat == WavAudioFormat::IEEEFloat && std::is_floating_point_v) + { + float f; + memcpy (&f, &sampleAsInt, sizeof(int32_t)); + sample = (T)f; + } else // assume PCM - sample = (T) sampleAsInt / static_cast (std::numeric_limits::max()); + { + sample = AudioSampleConverter::thirtyTwoBitIntToSample (sampleAsInt); + } samples[channel].push_back (sample); } @@ -700,6 +760,17 @@ bool AudioFile::decodeAiffFile (std::vector& fileData) bitDepth = (int) twoBytesToInt (fileData, p + 14, Endianness::BigEndian); sampleRate = getAiffSampleRate (fileData, p + 16); + if (bitDepth > sizeof (T) * 8) + { + std::string message = "ERROR: you are trying to read a "; + message += std::to_string (bitDepth); + message += "-bit file using a "; + message += std::to_string (sizeof (T) * 8); + message += "-bit sample type"; + reportError (message); + return false; + } + // check the sample rate was properly decoded if (sampleRate == 0) { @@ -758,14 +829,13 @@ bool AudioFile::decodeAiffFile (std::vector& fileData) if (bitDepth == 8) { - int8_t sampleAsSigned8Bit = (int8_t)fileData[sampleIndex]; - T sample = (T)sampleAsSigned8Bit / (T)128.; + T sample = AudioSampleConverter::signedByteToSample (static_cast (fileData[sampleIndex])); samples[channel].push_back (sample); } else if (bitDepth == 16) { int16_t sampleAsInt = twoBytesToInt (fileData, sampleIndex, Endianness::BigEndian); - T sample = sixteenBitIntToSample (sampleAsInt); + T sample = AudioSampleConverter::sixteenBitIntToSample (sampleAsInt); samples[channel].push_back (sample); } else if (bitDepth == 24) @@ -775,8 +845,8 @@ bool AudioFile::decodeAiffFile (std::vector& fileData) if (sampleAsInt & 0x800000) // if the 24th bit is set, this is a negative number in 24-bit world sampleAsInt = sampleAsInt | ~0xFFFFFF; // so make sure sign is extended to the 32 bit float - - T sample = (T)sampleAsInt / (T)8388608.; + + T sample = AudioSampleConverter::twentyFourBitIntToSample (sampleAsInt); samples[channel].push_back (sample); } else if (bitDepth == 32) @@ -786,9 +856,9 @@ bool AudioFile::decodeAiffFile (std::vector& fileData) if (audioFormat == AIFFAudioFormat::Compressed) sample = (T)reinterpret_cast (sampleAsInt); - else // assume uncompressed - sample = (T) sampleAsInt / static_cast (std::numeric_limits::max()); - + else // assume PCM + sample = AudioSampleConverter::thirtyTwoBitIntToSample (sampleAsInt); + samples[channel].push_back (sample); } else @@ -869,7 +939,7 @@ bool AudioFile::saveToWaveFile (std::string filePath) std::vector fileData; int32_t dataChunkSize = getNumSamplesPerChannel() * (getNumChannels() * bitDepth / 8); - int16_t audioFormat = bitDepth == 32 ? WavAudioFormat::IEEEFloat : WavAudioFormat::PCM; + int16_t audioFormat = bitDepth == 32 && std::is_floating_point_v ? WavAudioFormat::IEEEFloat : WavAudioFormat::PCM; int32_t formatChunkSize = audioFormat == WavAudioFormat::PCM ? 16 : 18; int32_t iXMLChunkSize = static_cast (iXMLChunk.size()); @@ -919,17 +989,17 @@ bool AudioFile::saveToWaveFile (std::string filePath) { if (bitDepth == 8) { - uint8_t byte = sampleToSingleByte (samples[channel][i]); + uint8_t byte = AudioSampleConverter::sampleToUnsignedByte (samples[channel][i]); fileData.push_back (byte); } else if (bitDepth == 16) { - int16_t sampleAsInt = sampleToSixteenBitInt (samples[channel][i]); + int16_t sampleAsInt = AudioSampleConverter::sampleToSixteenBitInt (samples[channel][i]); addInt16ToFileData (fileData, sampleAsInt); } else if (bitDepth == 24) { - int32_t sampleAsIntAgain = (int32_t) (samples[channel][i] * (T)8388608.); + int32_t sampleAsIntAgain = AudioSampleConverter::sampleToTwentyFourBitInt (samples[channel][i]); uint8_t bytes[3]; bytes[2] = (uint8_t) (sampleAsIntAgain >> 16) & 0xFF; @@ -947,7 +1017,7 @@ bool AudioFile::saveToWaveFile (std::string filePath) if (audioFormat == WavAudioFormat::IEEEFloat) sampleAsInt = (int32_t) reinterpret_cast (samples[channel][i]); else // assume PCM - sampleAsInt = (int32_t) (samples[channel][i] * std::numeric_limits::max()); + sampleAsInt = AudioSampleConverter::sampleToThirtyTwoBitInt (samples[channel][i]); addInt32ToFileData (fileData, sampleAsInt, Endianness::LittleEndian); } @@ -1029,17 +1099,17 @@ bool AudioFile::saveToAiffFile (std::string filePath) { if (bitDepth == 8) { - uint8_t byte = sampleToSingleByte (samples[channel][i]); + uint8_t byte = static_cast (AudioSampleConverter::sampleToSignedByte (samples[channel][i])); fileData.push_back (byte); } else if (bitDepth == 16) { - int16_t sampleAsInt = sampleToSixteenBitInt (samples[channel][i]); + int16_t sampleAsInt = AudioSampleConverter::sampleToSixteenBitInt (samples[channel][i]); addInt16ToFileData (fileData, sampleAsInt, Endianness::BigEndian); } else if (bitDepth == 24) { - int32_t sampleAsIntAgain = (int32_t) (samples[channel][i] * (T)8388608.); + int32_t sampleAsIntAgain = AudioSampleConverter::sampleToTwentyFourBitInt (samples[channel][i]); uint8_t bytes[3]; bytes[0] = (uint8_t) (sampleAsIntAgain >> 16) & 0xFF; @@ -1053,7 +1123,7 @@ bool AudioFile::saveToAiffFile (std::string filePath) else if (bitDepth == 32) { // write samples as signed integers (no implementation yet for floating point, but looking at WAV implementation should help) - int32_t sampleAsInt = (int32_t) (samples[channel][i] * std::numeric_limits::max()); + int32_t sampleAsInt = AudioSampleConverter::sampleToThirtyTwoBitInt (samples[channel][i]); addInt32ToFileData (fileData, sampleAsInt, Endianness::BigEndian); } else @@ -1190,14 +1260,22 @@ AudioFileFormat AudioFile::determineAudioFileFormat (std::vector& fi template int32_t AudioFile::fourBytesToInt (std::vector& source, int startIndex, Endianness endianness) { - int32_t result; - - if (endianness == Endianness::LittleEndian) - result = (source[startIndex + 3] << 24) | (source[startIndex + 2] << 16) | (source[startIndex + 1] << 8) | source[startIndex]; + if (source.size() >= (startIndex + 4)) + { + int32_t result; + + if (endianness == Endianness::LittleEndian) + result = (source[startIndex + 3] << 24) | (source[startIndex + 2] << 16) | (source[startIndex + 1] << 8) | source[startIndex]; + else + result = (source[startIndex] << 24) | (source[startIndex + 1] << 16) | (source[startIndex + 2] << 8) | source[startIndex + 3]; + + return result; + } else - result = (source[startIndex] << 24) | (source[startIndex + 1] << 16) | (source[startIndex + 2] << 8) | source[startIndex + 3]; - - return result; + { + assert (false && "Attempted to read four bytes from vector at position where out of bounds access would occur"); + return 0; // this is a dummy value as we don't have one to return + } } //============================================================= @@ -1240,6 +1318,7 @@ template int AudioFile::getIndexOfChunk (std::vector& source, const std::string& chunkHeaderID, int startIndex, Endianness endianness) { constexpr int dataLen = 4; + if (chunkHeaderID.size() != dataLen) { assert (false && "Invalid chunk header ID string"); @@ -1255,6 +1334,11 @@ int AudioFile::getIndexOfChunk (std::vector& source, const std::stri } i += dataLen; + + // If somehow we don't have 4 bytes left to read, then exit with -1 + if ((i + 4) >= source.size()) + return -1; + auto chunkSize = fourBytesToInt (source, i, endianness); i += (dataLen + chunkSize); } @@ -1264,50 +1348,234 @@ int AudioFile::getIndexOfChunk (std::vector& source, const std::stri //============================================================= template -T AudioFile::sixteenBitIntToSample (int16_t sample) +void AudioFile::reportError (std::string errorMessage) +{ + if (logErrorsToConsole) + std::cout << errorMessage << std::endl; +} + +//============================================================= +template +typename std::make_unsigned::type convertSignedToUnsigned (SignedType signedValue) +{ + static_assert (std::is_signed::value, "The input value must be signed"); + + typename std::make_unsigned::type unsignedValue = static_cast::type> (1) + std::numeric_limits::max(); + + unsignedValue += signedValue; + return unsignedValue; +} + +//============================================================= +enum SampleLimit +{ + SignedInt16_Min = -32768, + SignedInt16_Max = 32767, + UnsignedInt16_Min = 0, + UnsignedInt16_Max = 65535, + SignedInt24_Min = -8388608, + SignedInt24_Max = 8388607, + UnsignedInt24_Min = 0, + UnsignedInt24_Max = 16777215 +}; + +//============================================================= +template +T AudioSampleConverter::thirtyTwoBitIntToSample (int32_t sample) +{ + if constexpr (std::is_floating_point::value) + { + return static_cast (sample) / static_cast (std::numeric_limits::max()); + } + else if (std::numeric_limits::is_integer) + { + if constexpr (std::is_signed_v) + return static_cast (sample); + else + return static_cast (clamp (static_cast (sample + 2147483648), 0, 4294967295)); + } +} + +//============================================================= +template +int32_t AudioSampleConverter::sampleToThirtyTwoBitInt (T sample) { - return static_cast (sample) / static_cast (32768.); + if constexpr (std::is_floating_point::value) + { + // multiplying a float by a the max int32_t is problematic because + // of roundng errors which can cause wrong values to come out, so + // we use a different implementation here compared to other types + if constexpr (std::is_same_v) + { + if (sample >= 1.f) + return std::numeric_limits::max(); + else if (sample <= -1.f) + return std::numeric_limits::lowest() + 1; // starting at 1 preserves symmetry + else + return static_cast (sample * std::numeric_limits::max()); + } + else + { + return static_cast (clamp (sample, -1., 1.) * std::numeric_limits::max()); + } + } + else + { + if constexpr (std::is_signed_v) + return static_cast (clamp (sample, -2147483648LL, 2147483647LL)); + else + return static_cast (clamp (sample, 0, 4294967295) - 2147483648); + } } //============================================================= template -int16_t AudioFile::sampleToSixteenBitInt (T sample) +T AudioSampleConverter::twentyFourBitIntToSample (int32_t sample) { - sample = clamp (sample, -1., 1.); - return static_cast (sample * 32767.); + if constexpr (std::is_floating_point::value) + { + return static_cast (sample) / static_cast (8388607.); + } + else if (std::numeric_limits::is_integer) + { + if constexpr (std::is_signed_v) + return static_cast (clamp (sample, SignedInt24_Min, SignedInt24_Max)); + else + return static_cast (clamp (sample + 8388608, UnsignedInt24_Min, UnsignedInt24_Max)); + } } //============================================================= template -uint8_t AudioFile::sampleToSingleByte (T sample) +int32_t AudioSampleConverter::sampleToTwentyFourBitInt (T sample) { - sample = clamp (sample, -1., 1.); - sample = (sample + 1.) / 2.; - return static_cast (sample * 255.); + if constexpr (std::is_floating_point::value) + { + sample = clamp (sample, -1., 1.); + return static_cast (sample * 8388607.); + } + else + { + if constexpr (std::is_signed_v) + return static_cast (clamp (sample, SignedInt24_Min, SignedInt24_Max)); + else + return static_cast (clamp (sample, UnsignedInt24_Min, UnsignedInt24_Max) + SignedInt24_Min); + } } //============================================================= template -T AudioFile::singleByteToSample (uint8_t sample) +T AudioSampleConverter::sixteenBitIntToSample (int16_t sample) { - return static_cast (sample - 128) / static_cast (128.); + if constexpr (std::is_floating_point::value) + { + return static_cast (sample) / static_cast (32767.); + } + else if constexpr (std::numeric_limits::is_integer) + { + if constexpr (std::is_signed_v) + return static_cast (sample); + else + return static_cast (convertSignedToUnsigned (sample)); + } } //============================================================= template -T AudioFile::clamp (T value, T minValue, T maxValue) +int16_t AudioSampleConverter::sampleToSixteenBitInt (T sample) { - value = std::min (value, maxValue); - value = std::max (value, minValue); - return value; + if constexpr (std::is_floating_point::value) + { + sample = clamp (sample, -1., 1.); + return static_cast (sample * 32767.); + } + else + { + if constexpr (std::is_signed_v) + return static_cast (clamp (sample, SignedInt16_Min, SignedInt16_Max)); + else + return static_cast (clamp (sample, UnsignedInt16_Min, UnsignedInt16_Max) + SignedInt16_Min); + } } //============================================================= template -void AudioFile::reportError (std::string errorMessage) +uint8_t AudioSampleConverter::sampleToUnsignedByte (T sample) { - if (logErrorsToConsole) - std::cout << errorMessage << std::endl; + if constexpr (std::is_floating_point::value) + { + sample = clamp (sample, -1., 1.); + sample = (sample + 1.) / 2.; + return static_cast (1 + (sample * 254)); + } + else + { + if constexpr (std::is_signed_v) + return static_cast (clamp (sample, -128, 127) + 128); + else + return static_cast (clamp (sample, 0, 255)); + } +} + +//============================================================= +template +int8_t AudioSampleConverter::sampleToSignedByte (T sample) +{ + if constexpr (std::is_floating_point::value) + { + sample = clamp (sample, -1., 1.); + return static_cast (sample * (T)0x7F); + } + else + { + if constexpr (std::is_signed_v) + return static_cast (clamp (sample, -128, 127)); + else + return static_cast (clamp (sample, 0, 255) - 128); + } +} + +//============================================================= +template +T AudioSampleConverter::unsignedByteToSample (uint8_t sample) +{ + if constexpr (std::is_floating_point::value) + { + return static_cast (sample - 128) / static_cast (127.); + } + else if (std::numeric_limits::is_integer) + { + if constexpr (std::is_unsigned_v) + return static_cast (sample); + else + return static_cast (sample - 128); + } +} + +//============================================================= +template +T AudioSampleConverter::signedByteToSample (int8_t sample) +{ + if constexpr (std::is_floating_point::value) + { + return static_cast (sample) / static_cast (127.); + } + else if constexpr (std::numeric_limits::is_integer) + { + if constexpr (std::is_signed_v) + return static_cast (sample); + else + return static_cast (convertSignedToUnsigned (sample)); + } +} + +//============================================================= +template +T AudioSampleConverter::clamp (T value, T minValue, T maxValue) +{ + value = std::min (value, maxValue); + value = std::max (value, minValue); + return value; } #if defined (_MSC_VER) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ca06d6..4003e08 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ #=============================================================================== cmake_minimum_required (VERSION 3.12) -project ("AudioFile" VERSION 1.1.0 +project ("AudioFile" VERSION 1.1.1 DESCRIPTION "A simple C++ library for reading and writing audio files." HOMEPAGE_URL "https://github.com/adamstark/AudioFile") @@ -28,7 +28,7 @@ target_include_directories ( $) #=============================================================================== -target_compile_features (${PROJECT_NAME} INTERFACE cxx_std_11) +target_compile_features (${PROJECT_NAME} INTERFACE cxx_std_17) #=============================================================================== if (BUILD_EXAMPLES) diff --git a/README.md b/README.md index 7776369..f6eec4e 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # AudioFile -![Version](https://img.shields.io/badge/version-1.1.0-green.svg?style=flat-square) +![Version](https://img.shields.io/badge/version-1.1.1-green.svg?style=flat-square) ![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square) ![Language](https://img.shields.io/badge/language-C++-yellow.svg?style=flat-square) @@ -103,7 +103,7 @@ Usage audioFile.setNumSamplesPerChannel (numSamples); // Set the number of channels - audioFile.setNumChannels (int numChannels); + audioFile.setNumChannels (numChannels); ### Set bit depth and sample rate @@ -133,15 +133,44 @@ A Note On Types AudioFile is a template class and so it can be instantiated using floating point precision: +For example + AudioFile audioFile; -...or double precision: +...or double precision... AudioFile audioFile; -This simply reflects the data type you would like to use to store the underlying audio samples. You can still read or write 8, 16 or 24-bit audio files, regardless of the type that you use (unless your system uses a precision for floats less than your desired bit depth). +...or an integer type: + + AudioFile audioFile; + +This simply reflects the data type you would like to use to store the underlying audio samples. + +When you use an integer type to store the samples (e.g. `int` or `int8_t` or `int16_t` or `uint32_t`), the library will read in the integer sample values directly from the audio file. A couple of notes on integer types: + +* The range of samples is designed to be symmetric. This means that for (e.g.) an signed 8-bit integer (`int8_t`) we will use the range `[-127, 127]` for storing samples representing the `[-1., 1.]` range. The value `-128` is possible here given the `int8_t` type, but this is interpreted as a value slightly lower than `-1` (specifically `-1.007874015748`). + +* In the case of unsigned types, we obviously can't store samples as negative values. Therefore, we used the equivalent range of the unsigned type in use. E.g. if with a 8-bit signed integer (`int8_t`) the range would be `[-127, 127]`, for an 8-bit unsigned integer we would use the range `[1, 255]`. Note that we don't use `-128` for `int8_t` or `0` in `uint8_t`. -I have heard of people using the library with other types, but I have not designed for those cases. Let me know if you are interested in this supporting a specific type more formally. +* If you try to read an audio file with a larger bit-depth than the type you are using to store samples, the attempt to read the file will fail. Put more simply, you can't read a 16-bit audio file into an 8-bit integer. + +* If you are writing audio samples in integer formats, you should use the correct sample range for both a) the type you are using to store samples; and b) the bit depth of the audio you want to write. + +The following table details the sample range for each bit-depth: + +| Type | 8-bit Audio | 16-bit Audio | 24-bit Audio | 32-bit Audio | +| ------------- | ------------- | ------------- | ------------- | ------------- | +| `float` | `[-1.0, 1.0]` | `[-1.0, 1.0]` | `[-1.0, 1.0]` | `[-1.0, 1.0]` | +| `double` | `[-1.0, 1.0]` | `[-1.0, 1.0]` | `[-1.0, 1.0]` | `[-1.0, 1.0]` | +| `int8_t` | `[-127, 127]` | :x: (type too small) | :x: (type too small) | :x: (type too small) | +| `uint8_t` | `[1, 255]` | :x: (type too small) | :x: (type too small) | :x: (type too small) | +| `int16_t` | `[-127, 127]` | `[-32767, 32767]` | :x: (type too small) | :x: (type too small) | +| `uint16_t` | `[1, 255]` | `[1, 65535]` | :x: (type too small) | :x: (type too small) | +| `int32_t` | `[-127, 127]` | `[-32767, 32767]` | [`-8388607, 8388607]` | `[-2147483647, 2147483647]` | +| `uint32_t` | `[1, 255]` | `[1, 65535]` | `[1, 16777215]` | `[1, 4294967295]` | +| `int64_t` | `[-127, 127]` | `[-32767, 32767]` | [`-8388607, 8388607]` | `[-2147483647, 2147483647]` | +| `uint64_t` | `[1, 255]` | `[1, 65535]` | `[1, 16777215]` | `[1, 4294967295]` | Error Messages ----------------- @@ -156,6 +185,12 @@ If you prefer not to see these messages, you can disable this error logging beha Versions ------- +##### 1.1.1 - 4th April 2023 + +- Support for integer formats +- Improved unit testing +- Many bug fixes + ##### 1.1.0 - 15th January 2022 - Moved project to MIT licence @@ -205,13 +240,20 @@ Versions Contributions ------- -* Multichannel (i.e. >2 channels) audio file support ([Sidelobe](https://github.com/Sidelobe)) -* Read/write of iXML data chunks ([mynameisjohn](https://github.com/mynameisjohn)) -* Remove warnings ([Abhinav1997](https://github.com/Abhinav1997)) -* Better support on Ubuntu ([BenjaminHinchliff](https://github.com/BenjaminHinchliff)) -* Faster loading of audio files ([helloimmatt](https://github.com/helloimmatt/)) -* Improvements to Github Actions workflow ([emiro85](https://github.com/emiro85)) -* Pull request review ([MatthieuHernandez](https://github.com/MatthieuHernandez)) +Many thanks to the following people for their contributions to this library: + +* [Abhinav1997](https://github.com/Abhinav1997) +* [alxarsenault](https://github.com/alxarsenault) +* [BenjaminHinchliff](https://github.com/BenjaminHinchliff) +* [emiro85](https://github.com/emiro85) +* [heartofrain](https://github.com/heartofrain) +* [helloimmatt](https://github.com/helloimmatt/) +* [MatthieuHernandez](https://github.com/MatthieuHernandez) +* [mrpossoms](https://github.com/mrpossoms) +* [mynameisjohn](https://github.com/mynameisjohn) +* [Sidelobe](https://github.com/Sidelobe) +* [sschaetz](https://github.com/sschaetz) +* [Yhcrown](https://github.com/Yhcrown) Want to Contribute? ------- @@ -221,6 +263,7 @@ If you would like to submit a pull request for this library, please do! But kind * Make the changes as concise as is possible for the change you are proposing * Avoid unnecessarily changing a large number of lines - e.g. commits changing the number of spaces in indentations on all lines (and so on) * Keep to the code style of this library which is the [JUCE Coding Standards](https://juce.com/discover/stories/coding-standards) +* Make the changes relative to the develop branch of the library (as this may have advanced beyond the master branch) License ------- @@ -233,4 +276,4 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/examples/examples.cpp b/examples/examples.cpp index 870bf6e..6662839 100644 --- a/examples/examples.cpp +++ b/examples/examples.cpp @@ -59,7 +59,7 @@ namespace examples { for (int channel = 0; channel < a.getNumChannels(); channel++) { - a.samples[channel][i] = sin ((static_cast (i) / sampleRate) * frequencyInHz * 2.f * M_PI); + a.samples[channel][i] = sin ((static_cast (i) / sampleRate) * frequencyInHz * 2.f * (float)M_PI); } } @@ -140,7 +140,7 @@ namespace examples //--------------------------------------------------------------- // 4. Write audio file to disk - std::string outputFilePath = "quieter-audio-filer.wav"; // change this to somewhere useful for you + std::string outputFilePath = "quieter-audio-file.wav"; // change this to somewhere useful for you a.save (outputFilePath, AudioFileFormat::Aiff); } } diff --git a/tests/AiffLoadingTests.cpp b/tests/AiffLoadingTests.cpp index 109408c..90f71c9 100644 --- a/tests/AiffLoadingTests.cpp +++ b/tests/AiffLoadingTests.cpp @@ -21,7 +21,7 @@ const std::string projectBuildDirectory = PROJECT_BINARY_DIR; //============================================================= -TEST_SUITE ("AiffLoadingTests") +TEST_SUITE ("AiffLoadingTests - Floating Point Types - 8-bit File") { //============================================================= TEST_CASE ("AiffLoadingTests_Stereo_8bit_44100") @@ -39,11 +39,36 @@ TEST_SUITE ("AiffLoadingTests") { for (int k = 0; k < audioFile.getNumChannels(); k++) { - CHECK (audioFile.samples[k][i] == doctest::Approx (aiff_stereo_8bit_44100::testBuffer[k][i]).epsilon (0.00001)); + CHECK (audioFile.samples[k][i] == doctest::Approx (aiff_stereo_8bit_44100::testBuffer[k][i]).epsilon (0.01)); } } } + + //============================================================= + TEST_CASE ("AiffLoadingTests_Stereo_8bit_48000") + { + AudioFile audioFile; + bool loadedOK = audioFile.load (projectBuildDirectory + "/test-audio/aiff_stereo_8bit_48000.aif"); + + CHECK (loadedOK); + CHECK_EQ (audioFile.getNumSamplesPerChannel(), aiff_stereo_8bit_48000::numSamplesPerChannel); + CHECK_EQ (audioFile.getBitDepth(), aiff_stereo_8bit_48000::bitDepth); + CHECK_EQ (audioFile.getSampleRate(), aiff_stereo_8bit_48000::sampleRate); + CHECK_EQ (audioFile.getNumChannels(), aiff_stereo_8bit_48000::numChannels); + + for (size_t i = 0; i < aiff_stereo_8bit_48000::testBuffer[0].size(); i++) + { + for (int k = 0; k < audioFile.getNumChannels(); k++) + { + CHECK (audioFile.samples[k][i] == doctest::Approx (aiff_stereo_8bit_48000::testBuffer[k][i]).epsilon (0.01)); + } + } + } +} +//============================================================= +TEST_SUITE ("AiffLoadingTests - Floating Point Types - 16-bit File") +{ //============================================================= TEST_CASE ("AiffLoadingTests_Stereo_16bit_44100") { @@ -64,7 +89,32 @@ TEST_SUITE ("AiffLoadingTests") } } } + + //============================================================= + TEST_CASE ("AiffLoadingTests_Stereo_16bit_48000") + { + AudioFile audioFile; + bool loadedOK = audioFile.load (projectBuildDirectory + "/test-audio/aiff_stereo_16bit_48000.aif"); + + CHECK (loadedOK); + CHECK_EQ (audioFile.getNumSamplesPerChannel(), aiff_stereo_16bit_48000::numSamplesPerChannel); + CHECK_EQ (audioFile.getBitDepth(), aiff_stereo_16bit_48000::bitDepth); + CHECK_EQ (audioFile.getSampleRate(), aiff_stereo_16bit_48000::sampleRate); + CHECK_EQ (audioFile.getNumChannels(), aiff_stereo_16bit_48000::numChannels); + + for (size_t i = 0; i < aiff_stereo_16bit_48000::testBuffer[0].size(); i++) + { + for (int k = 0; k < audioFile.getNumChannels(); k++) + { + CHECK (audioFile.samples[k][i] == doctest::Approx (aiff_stereo_16bit_48000::testBuffer[k][i]).epsilon (0.00001)); + } + } + } +} +//============================================================= +TEST_SUITE ("AiffLoadingTests - Floating Point Types - 24-bit File") +{ //============================================================= TEST_CASE ("AiffLoadingTests_Stereo_24bit_44100") { @@ -85,7 +135,32 @@ TEST_SUITE ("AiffLoadingTests") } } } + + //============================================================= + TEST_CASE ("AiffLoadingTests_Stereo_24bit_48000") + { + AudioFile audioFile; + bool loadedOK = audioFile.load (projectBuildDirectory + "/test-audio/aiff_stereo_24bit_48000.aif"); + + CHECK (loadedOK); + CHECK_EQ (audioFile.getNumSamplesPerChannel(), aiff_stereo_24bit_48000::numSamplesPerChannel); + CHECK_EQ (audioFile.getBitDepth(), aiff_stereo_24bit_48000::bitDepth); + CHECK_EQ (audioFile.getSampleRate(), aiff_stereo_24bit_48000::sampleRate); + CHECK_EQ (audioFile.getNumChannels(), aiff_stereo_24bit_48000::numChannels); + + for (size_t i = 0; i < aiff_stereo_24bit_48000::testBuffer[0].size(); i++) + { + for (int k = 0; k < audioFile.getNumChannels(); k++) + { + CHECK (audioFile.samples[k][i] == doctest::Approx (aiff_stereo_24bit_48000::testBuffer[k][i]).epsilon (0.00001)); + } + } + } +} +//============================================================= +TEST_SUITE ("AiffLoadingTests - Floating Point Types - 32-bit File") +{ //============================================================= TEST_CASE ("AiffLoadingTests_Stereo_32bit_44100") { @@ -108,86 +183,170 @@ TEST_SUITE ("AiffLoadingTests") } //============================================================= - TEST_CASE ("AiffLoadingTests_Stereo_8bit_48000") + TEST_CASE ("AiffLoadingTests_Stereo_32bit_48000") { AudioFile audioFile; - bool loadedOK = audioFile.load (projectBuildDirectory + "/test-audio/aiff_stereo_8bit_48000.aif"); + bool loadedOK = audioFile.load (projectBuildDirectory + "/test-audio/aiff_stereo_32bit_48000.aif"); CHECK (loadedOK); - CHECK_EQ (audioFile.getNumSamplesPerChannel(), aiff_stereo_8bit_48000::numSamplesPerChannel); - CHECK_EQ (audioFile.getBitDepth(), aiff_stereo_8bit_48000::bitDepth); - CHECK_EQ (audioFile.getSampleRate(), aiff_stereo_8bit_48000::sampleRate); - CHECK_EQ (audioFile.getNumChannels(), aiff_stereo_8bit_48000::numChannels); + CHECK_EQ (audioFile.getNumSamplesPerChannel(), aiff_stereo_32bit_48000::numSamplesPerChannel); + CHECK_EQ (audioFile.getBitDepth(), aiff_stereo_32bit_48000::bitDepth); + CHECK_EQ (audioFile.getSampleRate(), aiff_stereo_32bit_48000::sampleRate); + CHECK_EQ (audioFile.getNumChannels(), aiff_stereo_32bit_48000::numChannels); - for (size_t i = 0; i < aiff_stereo_8bit_48000::testBuffer[0].size(); i++) + for (size_t i = 0; i < aiff_stereo_32bit_48000::testBuffer[0].size(); i++) { for (int k = 0; k < audioFile.getNumChannels(); k++) { - CHECK (audioFile.samples[k][i] == doctest::Approx (aiff_stereo_8bit_48000::testBuffer[k][i]).epsilon (0.00001)); + CHECK (audioFile.samples[k][i] == doctest::Approx (aiff_stereo_32bit_48000::testBuffer[k][i]).epsilon (0.00001)); } } } +} +//============================================================= +TEST_SUITE ("AiffLoadingTests - Integer Types - 8-bit File") +{ //============================================================= - TEST_CASE ("AiffLoadingTests_Stereo_16bit_48000") + template + void test8Bit44100WithInteger (bool expectFailure = false) { - AudioFile audioFile; - bool loadedOK = audioFile.load (projectBuildDirectory + "/test-audio/aiff_stereo_16bit_48000.aif"); + AudioFile audioFile; + bool loadedOK = audioFile.load (projectBuildDirectory + "/test-audio/aiff_stereo_8bit_44100.aif"); CHECK (loadedOK); - CHECK_EQ (audioFile.getNumSamplesPerChannel(), aiff_stereo_16bit_48000::numSamplesPerChannel); - CHECK_EQ (audioFile.getBitDepth(), aiff_stereo_16bit_48000::bitDepth); - CHECK_EQ (audioFile.getSampleRate(), aiff_stereo_16bit_48000::sampleRate); - CHECK_EQ (audioFile.getNumChannels(), aiff_stereo_16bit_48000::numChannels); + CHECK_EQ (audioFile.getNumSamplesPerChannel(), aiff_stereo_8bit_44100::numSamplesPerChannel); + CHECK_EQ (audioFile.getBitDepth(), aiff_stereo_8bit_44100::bitDepth); + CHECK_EQ (audioFile.getSampleRate(), aiff_stereo_8bit_44100::sampleRate); + CHECK_EQ (audioFile.getNumChannels(), aiff_stereo_8bit_44100::numChannels); - for (size_t i = 0; i < aiff_stereo_16bit_48000::testBuffer[0].size(); i++) + int offset = std::is_signed_v ? 0 : 128; + + for (size_t i = 0; i < aiff_stereo_8bit_44100::testBuffer[0].size(); i++) { for (int k = 0; k < audioFile.getNumChannels(); k++) { - CHECK (audioFile.samples[k][i] == doctest::Approx (aiff_stereo_16bit_48000::testBuffer[k][i]).epsilon (0.00001)); + CHECK (audioFile.samples[k][i] == doctest::Approx (aiff_stereo_8bit_44100::testBuffer[k][i] * 127 + offset).epsilon (0.01)); } } } + + //============================================================= + TEST_CASE ("AiffLoadingTests_Stereo_8bit_44100_integers") + { + test8Bit44100WithInteger(); + test8Bit44100WithInteger(); + test8Bit44100WithInteger(); + test8Bit44100WithInteger(); + test8Bit44100WithInteger(); + test8Bit44100WithInteger(); + test8Bit44100WithInteger(); + test8Bit44100WithInteger(); + } +} +//============================================================= +TEST_SUITE ("AiffLoadingTests - Integer Types - 16-bit File") +{ //============================================================= - TEST_CASE ("AiffLoadingTests_Stereo_24bit_48000") + template + void test16Bit44100WithInteger (bool expectFailure = false) { - AudioFile audioFile; - bool loadedOK = audioFile.load (projectBuildDirectory + "/test-audio/aiff_stereo_24bit_48000.aif"); + AudioFile audioFile; + + if (expectFailure) + audioFile.shouldLogErrorsToConsole (false); + + bool loadedOK = audioFile.load (projectBuildDirectory + "/test-audio/aiff_stereo_16bit_44100.aif"); + + if (expectFailure) + { + CHECK_EQ (loadedOK, false); + return; + } CHECK (loadedOK); - CHECK_EQ (audioFile.getNumSamplesPerChannel(), aiff_stereo_24bit_48000::numSamplesPerChannel); - CHECK_EQ (audioFile.getBitDepth(), aiff_stereo_24bit_48000::bitDepth); - CHECK_EQ (audioFile.getSampleRate(), aiff_stereo_24bit_48000::sampleRate); - CHECK_EQ (audioFile.getNumChannels(), aiff_stereo_24bit_48000::numChannels); + CHECK_EQ (audioFile.getNumSamplesPerChannel(), aiff_stereo_16bit_44100::numSamplesPerChannel); + CHECK_EQ (audioFile.getBitDepth(), aiff_stereo_16bit_44100::bitDepth); + CHECK_EQ (audioFile.getSampleRate(), aiff_stereo_16bit_44100::sampleRate); + CHECK_EQ (audioFile.getNumChannels(), aiff_stereo_16bit_44100::numChannels); - for (size_t i = 0; i < aiff_stereo_24bit_48000::testBuffer[0].size(); i++) + int offset = std::is_signed_v ? 0 : 32768; + + for (size_t i = 0; i < aiff_stereo_16bit_44100::testBuffer[0].size(); i++) { for (int k = 0; k < audioFile.getNumChannels(); k++) { - CHECK (audioFile.samples[k][i] == doctest::Approx (aiff_stereo_24bit_48000::testBuffer[k][i]).epsilon (0.00001)); + CHECK (audioFile.samples[k][i] == doctest::Approx (aiff_stereo_16bit_44100::testBuffer[k][i] * 32767 + offset).epsilon (0.0001)); } } } + + //============================================================= + TEST_CASE ("AiffLoadingTests_Stereo_16bit_44100_integers") + { + test16Bit44100WithInteger(); + test16Bit44100WithInteger(); + test16Bit44100WithInteger(); + test16Bit44100WithInteger(); + test16Bit44100WithInteger(); + test16Bit44100WithInteger(); + + // check these fail... + test16Bit44100WithInteger (true); + test16Bit44100WithInteger (true); + } +} +//============================================================= +TEST_SUITE ("AiffLoadingTests - Integer Types - 24-bit File") +{ //============================================================= - TEST_CASE ("AiffLoadingTests_Stereo_32bit_48000") + template + void test24Bit44100WithInteger (bool expectFailure = false) { - AudioFile audioFile; - bool loadedOK = audioFile.load (projectBuildDirectory + "/test-audio/aiff_stereo_32bit_48000.aif"); + AudioFile audioFile; + + if (expectFailure) + audioFile.shouldLogErrorsToConsole (false); + + bool loadedOK = audioFile.load (projectBuildDirectory + "/test-audio/aiff_stereo_24bit_44100.aif"); + + if (expectFailure) + { + REQUIRE_EQ (loadedOK, false); + return; + } CHECK (loadedOK); - CHECK_EQ (audioFile.getNumSamplesPerChannel(), aiff_stereo_32bit_48000::numSamplesPerChannel); - CHECK_EQ (audioFile.getBitDepth(), aiff_stereo_32bit_48000::bitDepth); - CHECK_EQ (audioFile.getSampleRate(), aiff_stereo_32bit_48000::sampleRate); - CHECK_EQ (audioFile.getNumChannels(), aiff_stereo_32bit_48000::numChannels); + CHECK_EQ (audioFile.getNumSamplesPerChannel(), aiff_stereo_24bit_44100::numSamplesPerChannel); + CHECK_EQ (audioFile.getBitDepth(), aiff_stereo_24bit_44100::bitDepth); + CHECK_EQ (audioFile.getSampleRate(), aiff_stereo_24bit_44100::sampleRate); + CHECK_EQ (audioFile.getNumChannels(), aiff_stereo_24bit_44100::numChannels); - for (size_t i = 0; i < aiff_stereo_32bit_48000::testBuffer[0].size(); i++) + int offset = std::is_signed_v ? 0 : 8388608; + + for (size_t i = 0; i < aiff_stereo_24bit_44100::testBuffer[0].size(); i++) { for (int k = 0; k < audioFile.getNumChannels(); k++) { - CHECK (audioFile.samples[k][i] == doctest::Approx (aiff_stereo_32bit_48000::testBuffer[k][i]).epsilon (0.00001)); + CHECK (audioFile.samples[k][i] == doctest::Approx (aiff_stereo_24bit_44100::testBuffer[k][i] * 8388607 + offset).epsilon (0.00001)); } } } + + //============================================================= + TEST_CASE ("AiffLoadingTests_Stereo_24bit_44100_integers") + { + test24Bit44100WithInteger(); + test24Bit44100WithInteger(); + test24Bit44100WithInteger(); + test24Bit44100WithInteger(); + + // check these fail... + test24Bit44100WithInteger (true); + test24Bit44100WithInteger (true); + test24Bit44100WithInteger (true); + test24Bit44100WithInteger (true); + } } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c708cf4..9007d3b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -6,7 +6,7 @@ add_definitions(-DPROJECT_BINARY_DIR="${CMAKE_CURRENT_BINARY_DIR}") file (COPY test-audio DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) file (MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/audio-write-tests) -add_executable (Tests main.cpp GeneralTests.cpp WavLoadingTests.cpp AiffLoadingTests.cpp FileWritingTests.cpp) +add_executable (Tests main.cpp GeneralTests.cpp WavLoadingTests.cpp AiffLoadingTests.cpp FileWritingTests.cpp SampleConversionTests.cpp) target_compile_features (Tests PRIVATE cxx_std_17) add_test (NAME Tests COMMAND Tests) diff --git a/tests/FileWritingTests.cpp b/tests/FileWritingTests.cpp index 234a642..b419a46 100644 --- a/tests/FileWritingTests.cpp +++ b/tests/FileWritingTests.cpp @@ -8,26 +8,55 @@ const std::string projectBuildDirectory = PROJECT_BINARY_DIR; //============================================================= -// Writes an audio file with a given number of channels, sample rate, bit deoth and format +// Writes an audio file with a given number of channels, sample rate, bit depth and format // Returns true if it was successful -bool writeTestAudioFile (int numChannels, int sampleRate, int bitDepth, AudioFileFormat format) +template +void writeTestAudioFile (int numChannels, int sampleRate, int bitDepth, AudioFileFormat format) { + std::string sampleType; float sampleRateAsFloat = (float) sampleRate; - AudioFile audioFile; + AudioFile audioFileWriter; - audioFile.setAudioBufferSize (numChannels, sampleRate * 4); + audioFileWriter.setAudioBufferSize (numChannels, sampleRate * 4); - for (int i = 0; i < audioFile.getNumSamplesPerChannel(); i++) + // In the case of an integer representation, this value will be + T maxValue; + + if constexpr (std::is_floating_point::value) + { + maxValue = 1; + sampleType = "floating_point"; + } + else if constexpr (std::numeric_limits::is_integer) { - float sample = sinf (2. * M_PI * ((float) i / sampleRateAsFloat) * 440.) ; + if constexpr (std::is_signed_v) + { + sampleType = "integer (signed, " + std::to_string (sizeof (T) * 8) + "-bit)"; + maxValue = pow (2, bitDepth - 1) - 1; + } + else + { + sampleType = "integer (unsigned, " + std::to_string (sizeof (T) * 8) + "-bit)"; + maxValue = pow (2, bitDepth) - 1; + } + } + + for (int i = 0; i < audioFileWriter.getNumSamplesPerChannel(); i++) + { + T sample; - for (int k = 0; k < audioFile.getNumChannels(); k++) - audioFile.samples[k][i] = sample * 0.5; + if constexpr (std::numeric_limits::is_integer && std::is_unsigned_v) + sample = (T)(((sinf (2. * M_PI * ((double) i / sampleRateAsFloat) * 440.) + 1.) / 2.) * maxValue); + else + sample = (T)(sinf (2. * M_PI * ((double) i / sampleRateAsFloat) * 440.) * maxValue); + + for (int k = 0; k < audioFileWriter.getNumChannels(); k++) + audioFileWriter.samples[k][i] = sample * 0.5f; } - audioFile.setSampleRate (sampleRate); - audioFile.setBitDepth (bitDepth); + audioFileWriter.setSampleRate (sampleRate); + audioFileWriter.setBitDepth (bitDepth); std::string numChannelsAsString; if (numChannels == 1) @@ -40,33 +69,89 @@ bool writeTestAudioFile (int numChannels, int sampleRate, int bitDepth, AudioFil std::string bitDepthAsString = std::to_string (bitDepth); std::string sampleRateAsString = std::to_string (sampleRate); + std::string filePath; + if (format == AudioFileFormat::Wave) { - return audioFile.save (projectBuildDirectory + "/audio-write-tests/" + numChannelsAsString + "_" + sampleRateAsString + "_" + bitDepthAsString + "bit" + ".wav", format); + filePath = projectBuildDirectory + "/audio-write-tests/" + sampleType + "_" + numChannelsAsString + "_" + sampleRateAsString + "_" + bitDepthAsString + "bit" + ".wav"; } - else if (format == AudioFileFormat::Aiff) { - return audioFile.save (projectBuildDirectory + "/audio-write-tests/" + numChannelsAsString + "_" + sampleRateAsString + "_" + bitDepthAsString + "bit" + ".aif", format); + filePath = projectBuildDirectory + "/audio-write-tests/" + sampleType + "_" + numChannelsAsString + "_" + sampleRateAsString + "_" + bitDepthAsString + "bit" + ".aif"; + } - return false; + bool OK = audioFileWriter.save (filePath, format); + REQUIRE (OK); + + //----------------------------------------------------------------- + // for some key bit depths and mono/stereo files, read in the audio file + // we just wrote and do a sample-by-sample comparison to confirm we are + // writing good files + if ((bitDepth == 8 || bitDepth == 16 || bitDepth == 24) && numChannels <= 2) + { + AudioFile audioFileReader; + audioFileReader.load (filePath); + + // confirm num channels + CHECK (audioFileReader.getNumChannels() == audioFileWriter.getNumChannels()); + + // confirm bit depth + CHECK (audioFileReader.getBitDepth() == audioFileWriter.getBitDepth()); + + // confirm sample rate + CHECK (audioFileReader.getSampleRate() == audioFileWriter.getSampleRate()); + + // confirm the number of samples per channel + CHECK (audioFileReader.getNumSamplesPerChannel() == audioFileWriter.getNumSamplesPerChannel()); + + for (size_t i = 0; i < 5000; i++) + { + for (int k = 0; k < audioFileReader.getNumChannels(); k++) + { + // NOTE: We can expect audio files we read back in to differ a small amount from the original due to small rounding errors + REQUIRE (audioFileReader.samples[k][i] == doctest::Approx (audioFileWriter.samples[k][i]).epsilon (0.01)); + } + } + } } - //============================================================= TEST_SUITE ("Writing Tests") { - -#if PEFORM_TEST_WRITE_TO_ALL_FORMATS //============================================================= - TEST_CASE ("WritingTest::WriteSineToneToManyFormats") + TEST_CASE ("WritingTest::WriteSineToneToManyFormats::FloatingPoint") { std::vector sampleRates = {22050, 44100, 48000, 96000}; std::vector bitDepths = {8, 16, 24, 32}; std::vector numChannels = {1, 2, 8}; std::vector audioFormats = {AudioFileFormat::Wave, AudioFileFormat::Aiff}; - + + for (auto& sampleRate : sampleRates) + { + for (auto& bitDepth : bitDepths) + { + for (auto& channels : numChannels) + { + for (auto& format : audioFormats) + { + auto fmt_str = format == AudioFileFormat::Wave ? "wav" : "aiff"; + std::cerr << sampleRate << "Hz " << bitDepth << "-bit " << channels << " " << fmt_str << " (floating point)" << std::endl; + writeTestAudioFile (channels, sampleRate, bitDepth, format); + } + } + } + } + } + + //============================================================= + TEST_CASE ("WritingTest::WriteSineToneToManyFormats::Integer") + { + std::vector sampleRates = {22050, 44100, 48000, 96000}; + std::vector bitDepths = {8, 16, 24, 32}; + std::vector numChannels = {1, 2}; + std::vector audioFormats = {AudioFileFormat::Wave, AudioFileFormat::Aiff}; + for (auto& sampleRate : sampleRates) { for (auto& bitDepth : bitDepths) @@ -75,13 +160,22 @@ TEST_SUITE ("Writing Tests") { for (auto& format : audioFormats) { - CHECK (writeTestAudioFile (channels, sampleRate, bitDepth, format)); + auto fmt_str = format == AudioFileFormat::Wave ? "wav" : "aiff"; + std::cerr << sampleRate << "Hz " << bitDepth << "-bit " << channels << " " << fmt_str << " (integer)" << std::endl; + + if (bitDepth == 8) + writeTestAudioFile (channels, sampleRate, bitDepth, format); + else if (bitDepth == 16) + writeTestAudioFile (channels, sampleRate, bitDepth, format); + else if (bitDepth == 24) + writeTestAudioFile (channels, sampleRate, bitDepth, format); + else if (bitDepth == 32) + writeTestAudioFile (channels, sampleRate, bitDepth, format); } } } } } -#endif //============================================================= TEST_CASE ("WritingTest::WriteFromCopiedSampleBuffer") diff --git a/tests/GeneralTests.cpp b/tests/GeneralTests.cpp index 398e65c..cc0ff39 100644 --- a/tests/GeneralTests.cpp +++ b/tests/GeneralTests.cpp @@ -11,7 +11,8 @@ TEST_SUITE ("General Tests") const std::string projectBuildDirectory = PROJECT_BINARY_DIR; //============================================================= - void checkFilesAreExactlyTheSame (AudioFile& a, AudioFile& b) + template + void checkFilesAreExactlyTheSame (AudioFile& a, AudioFile& b) { CHECK (a.getSampleRate() == b.getSampleRate()); CHECK (a.getBitDepth() == b.getBitDepth()); @@ -38,7 +39,7 @@ TEST_SUITE ("General Tests") AudioFile b (a); - checkFilesAreExactlyTheSame (a, b); + checkFilesAreExactlyTheSame (a, b); } //============================================================= @@ -50,7 +51,7 @@ TEST_SUITE ("General Tests") AudioFile b; b = a; - checkFilesAreExactlyTheSame (a, b); + checkFilesAreExactlyTheSame (a, b); } //============================================================= @@ -64,7 +65,7 @@ TEST_SUITE ("General Tests") b = a; c = std::move (a); - checkFilesAreExactlyTheSame (b, c); + checkFilesAreExactlyTheSame (b, c); } //============================================================= @@ -78,7 +79,7 @@ TEST_SUITE ("General Tests") AudioFile c (std::move (a)); - checkFilesAreExactlyTheSame (b, c); + checkFilesAreExactlyTheSame (b, c); } //============================================================= @@ -91,6 +92,19 @@ TEST_SUITE ("General Tests") AudioFile b (filePath); - checkFilesAreExactlyTheSame (a, b); + checkFilesAreExactlyTheSame (a, b); + } + + //============================================================= + TEST_CASE ("GeneralTests::IntegerFormat") + { + std::string filePath = projectBuildDirectory + "/test-audio/aiff_stereo_16bit_44100.aif"; + + AudioFile a; + a.load (filePath); + + AudioFile b (filePath); + + checkFilesAreExactlyTheSame (a, b); } } diff --git a/tests/SampleConversionTests.cpp b/tests/SampleConversionTests.cpp new file mode 100644 index 0000000..752fca1 --- /dev/null +++ b/tests/SampleConversionTests.cpp @@ -0,0 +1,771 @@ +#include "doctest.h" +#include +#include +#include + +//============================================================= +TEST_SUITE ("SampleConversionTests::8-bit Conversions - signedByteToSample()") +{ + //============================================================= + TEST_CASE ("8-bit Signed Conversions::signedByteToSample (float & double)") + { + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (127), 1.f); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (63), doctest::Approx (0.4960629921f).epsilon (0.0001)); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (0), 0.f); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (-63), doctest::Approx (-0.4960629921f).epsilon (0.0001)); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (-127), -1.f); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (-128), doctest::Approx (-1.007874015748f).epsilon (0.0001)); + + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (127), 1.); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (63), doctest::Approx (0.4960629921).epsilon (0.0001)); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (0), 0.); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (-63), doctest::Approx (-0.4960629921).epsilon (0.0001)); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (-127), -1.); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (-128), doctest::Approx (-1.007874015748).epsilon (0.0001)); + } + + //============================================================= + TEST_CASE ("8-bit Signed Conversions::signedByteToSample (unsigned integer types)") + { + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (127), 255); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (63), 191); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (0), 128); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (-63), 65); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (-127), 1); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (-128), 0); + + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (127), 255); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (63), 191); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (0), 128); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (-63), 65); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (-127), 1); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (-128), 0); + + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (127), 255); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (63), 191); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (0), 128); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (-63), 65); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (-127), 1); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (-128), 0); + + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (127), 255); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (63), 191); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (0), 128); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (-63), 65); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (-127), 1); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (-128), 0); + } + + //============================================================= + TEST_CASE ("8-bit Signed Conversions::signedByteToSample (signed integer types)") + { + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (127), 127); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (63), 63); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (0), 0); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (-63), -63); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (-127), -127); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (-128), -128); + + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (127), 127); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (63), 63); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (0), 0); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (-63), -63); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (-127), -127); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (-128), -128); + + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (127), 127); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (63), 63); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (0), 0); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (-63), -63); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (-127), -127); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (-128), -128); + + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (127), 127); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (63), 63); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (0), 0); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (-63), -63); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (-127), -127); + REQUIRE_EQ (AudioSampleConverter::signedByteToSample (-128), -128); + } +} + +//============================================================= +TEST_SUITE ("SampleConversionTests::8-bit Conversions - sampleToSignedByte()") +{ + //============================================================= + TEST_CASE ("8-bit Signed Conversions::sampleToSignedByte (float and double)") + { + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (std::numeric_limits::max()), 127); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (1.f), 127); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (0.5f), 63); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (0.f), 0); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (-0.5f), -63); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (-1.f), -127); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (std::numeric_limits::lowest()), -127); + + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (std::numeric_limits::max()), 127); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (1.), 127); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (0.5), 63); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (0.), 0); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (-0.5), -63); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (-1.), -127); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (std::numeric_limits::lowest()), -127); + } + + //============================================================= + TEST_CASE ("8-bit Signed Conversions::sampleToSignedByte (unsigned integers)") + { + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (255), 127); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (191), 63); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (128), 0); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (65), -63); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (1), -127); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (0), -128); + + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (std::numeric_limits::max()), 127); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (255), 127); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (191), 63); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (128), 0); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (65), -63); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (1), -127); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (0), -128); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (std::numeric_limits::min()), -128); + + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (std::numeric_limits::max()), 127); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (255), 127); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (191), 63); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (128), 0); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (65), -63); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (1), -127); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (0), -128); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (std::numeric_limits::min()), -128); + + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (std::numeric_limits::max()), 127); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (255), 127); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (191), 63); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (128), 0); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (65), -63); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (1), -127); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (0), -128); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (std::numeric_limits::min()), -128); + } + + //============================================================= + TEST_CASE ("8-bit Signed Conversions::sampleToSignedByte (signed integers)") + { + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (127), 127); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (63), 63); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (0), 0); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (-63), -63); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (-127), -127); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (-128), -128); + + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (std::numeric_limits::max()), 127); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (127), 127); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (63), 63); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (0), 0); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (-63), -63); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (-127), -127); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (-128), -128); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (std::numeric_limits::min()), -128); + + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (std::numeric_limits::max()), 127); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (127), 127); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (63), 63); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (0), 0); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (-63), -63); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (-127), -127); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (-128), -128); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (std::numeric_limits::min()), -128); + + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (std::numeric_limits::max()), 127); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (127), 127); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (63), 63); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (0), 0); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (-63), -63); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (-127), -127); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (-128), -128); + REQUIRE_EQ (AudioSampleConverter::sampleToSignedByte (std::numeric_limits::min()), -128); + } +} + +//============================================================= +TEST_SUITE ("SampleConversionTests::8-bit Conversions - unsignedByteToSample()") +{ + //============================================================= + TEST_CASE ("8-bit Unsigned Conversions::unsignedByteToSample (float and double)") + { + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (255), 1.f); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (191), doctest::Approx (0.4960629921f).epsilon (0.0001)); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (128), 0.f); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (65), doctest::Approx (-0.4960629921f).epsilon (0.0001)); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (1), -1.f); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (0), doctest::Approx (-1.007874015748f).epsilon (0.0001)); + + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (255), 1.); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (191), doctest::Approx (0.4960629921).epsilon (0.0001)); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (128), 0.); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (65), doctest::Approx (-0.4960629921).epsilon (0.0001)); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (1), -1.); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (0), doctest::Approx (-1.007874015748).epsilon (0.0001)); + } + + //============================================================= + TEST_CASE ("8-bit Unsigned Conversions::unsignedByteToSample (unsigned integers)") + { + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (255), 255); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (191), 191); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (128), 128); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (65), 65); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (1), 1); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (0), 0); + + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (255), 255); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (191), 191); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (128), 128); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (65), 65); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (1), 1); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (0), 0); + + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (255), 255); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (191), 191); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (128), 128); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (65), 65); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (1), 1); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (0), 0); + + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (255), 255); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (191), 191); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (128), 128); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (65), 65); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (1), 1); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (0), 0); + } + + //============================================================= + TEST_CASE ("8-bit Unsigned Conversions::unsignedByteToSample (signed integers)") + { + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (255), 127); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (191), 63); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (128), 0); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (65), -63); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (1), -127); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (0), -128); + + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (255), 127); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (191), 63); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (128), 0); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (65), -63); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (1), -127); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (0), -128); + + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (255), 127); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (191), 63); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (128), 0); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (65), -63); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (1), -127); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (0), -128); + + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (255), 127); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (191), 63); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (128), 0); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (65), -63); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (1), -127); + REQUIRE_EQ (AudioSampleConverter::unsignedByteToSample (0), -128); + } +} + +//============================================================= +TEST_SUITE ("SampleConversionTests::8-bit Conversions - sampleToUnsignedByte()") +{ + //============================================================= + TEST_CASE ("8-bit Signed Conversions::sampleToUnsignedByte (float and double)") + { + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (std::numeric_limits::max()), 255); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (1.f), 255); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (0.5f), 191); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (0.f), 128); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (-0.5f), 64); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (-1.f), 1); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (std::numeric_limits::lowest()), 1); + + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (std::numeric_limits::max()), 255); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (1.), 255); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (0.5), 191); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (0.), 128); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (-0.5), 64); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (-1.), 1); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (std::numeric_limits::lowest()), 1); + } + + //============================================================= + TEST_CASE ("8-bit Signed Conversions::sampleToUnsignedByte (unsigned integers)") + { + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (255), 255); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (191), 191); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (128), 128); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (65), 65); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (1), 1); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (0), 0); + + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (std::numeric_limits::max()), 255); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (255), 255); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (191), 191); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (128), 128); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (65), 65); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (1), 1); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (0), 0); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (std::numeric_limits::min()), 0); + + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (std::numeric_limits::max()), 255); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (255), 255); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (191), 191); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (128), 128); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (65), 65); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (1), 1); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (0), 0); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (std::numeric_limits::min()), 0); + + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (std::numeric_limits::max()), 255); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (255), 255); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (191), 191); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (128), 128); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (65), 65); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (1), 1); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (0), 0); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (std::numeric_limits::min()), 0); + } + + //============================================================= + TEST_CASE ("8-bit Signed Conversions::sampleToUnsignedByte (signed integers)") + { + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (127), 255); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (63), 191); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (0), 128); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (-63), 65); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (-127), 1); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (-128), 0); + + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (std::numeric_limits::max()), 255); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (127), 255); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (63), 191); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (0), 128); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (-63), 65); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (-127), 1); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (-128), 0); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (std::numeric_limits::min()), 0); + + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (std::numeric_limits::max()), 255); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (127), 255); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (63), 191); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (0), 128); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (-63), 65); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (-127), 1); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (-128), 0); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (std::numeric_limits::min()), 0); + + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (std::numeric_limits::max()), 255); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (127), 255); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (63), 191); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (0), 128); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (-63), 65); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (-127), 1); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (-128), 0); + REQUIRE_EQ (AudioSampleConverter::sampleToUnsignedByte (std::numeric_limits::min()), 0); + } +} + +//============================================================= +TEST_SUITE ("SampleConversionTests::16-bit Conversions") +{ + //============================================================= + TEST_CASE ("16-bit Conversions::sixteenBitIntToSample (float & double)") + { + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (32767), 1.f); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (16383), doctest::Approx (0.4999847407f).epsilon (0.0001)); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (0), 0.f); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (-16383), doctest::Approx (-0.4999847407f).epsilon (0.0001)); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (-32767), -1.f); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (-32768), doctest::Approx (-1.00003f).epsilon (0.0001)); + + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (32767), 1.); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (16383), doctest::Approx (0.4999847407).epsilon (0.0001)); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (0), 0.); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (-16383), doctest::Approx (-0.4999847407).epsilon (0.0001)); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (-32767), -1.); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (-32768), doctest::Approx (-1.00003).epsilon (0.0001)); + } + + //============================================================= + TEST_CASE ("16-bit Conversions::sixteenBitIntToSample (unsigned integers)") + { + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (32767), 65535); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (16383), 49151); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (0), 32768); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (-16383), 16385); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (-32767), 1); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (-32768), 0); + + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (32767), 65535); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (16383), 49151); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (0), 32768); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (-16383), 16385); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (-32767), 1); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (-32768), 0); + + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (32767), 65535); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (16383), 49151); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (0), 32768); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (-16383), 16385); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (-32767), 1); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (-32768), 0); + } + + //============================================================= + TEST_CASE ("16-bit Conversions::sixteenBitIntToSample (signed integers)") + { + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (32767), 32767); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (16383), 16383); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (0), 0); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (-16383), -16383); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (-32767), -32767); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (-32768), -32768); + + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (32767), 32767); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (16383), 16383); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (0), 0); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (-16383), -16383); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (-32767), -32767); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (-32768), -32768); + + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (32767), 32767); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (16383), 16383); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (0), 0); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (-16383), -16383); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (-32767), -32767); + REQUIRE_EQ (AudioSampleConverter::sixteenBitIntToSample (-32768), -32768); + } + + + //============================================================= + TEST_CASE ("16-bit Conversions::sampleToSixteenBitInt (float)") + { + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (1.f), 32767); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (0.5f), 16383); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (0.f), 0); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (-0.5f), -16383); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (-1.f), -32767); + } + + //============================================================= + TEST_CASE ("16-bit Conversions::sampleToSixteenBitInt (double)") + { + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (1.), 32767); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (0.5), 16383); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (0.), 0); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (-0.5), -16383); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (-1.), -32767); + } + + //============================================================= + TEST_CASE ("16-bit Conversions::sampleToSixteenBitInt (unsigned int)") + { + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (std::numeric_limits::max()), 32767); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (65535), 32767); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (49151), 16383); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (32768), 0); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (16385), -16383); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (1), -32767); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (0), -32768); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (std::numeric_limits::min()), -32768); + + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (std::numeric_limits::max()), 32767); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (65535), 32767); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (49151), 16383); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (32768), 0); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (16385), -16383); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (1), -32767); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (0), -32768); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (std::numeric_limits::min()), -32768); + + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (std::numeric_limits::max()), 32767); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (65535), 32767); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (49151), 16383); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (32768), 0); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (16385), -16383); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (1), -32767); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (0), -32768); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (std::numeric_limits::min()), -32768); + } + + //============================================================= + TEST_CASE ("16-bit Conversions::sampleToSixteenBitInt (signed int)") + { + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (std::numeric_limits::max()), 32767); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (32767), 32767); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (16383), 16383); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (0), 0); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (-16383), -16383); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (-32767), -32767); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (-32768), -32768); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (std::numeric_limits::lowest()), -32768); + + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (std::numeric_limits::max()), 32767); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (32767), 32767); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (16383), 16383); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (0), 0); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (-16383), -16383); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (-32767), -32767); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (-32768), -32768); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (std::numeric_limits::lowest()), -32768); + + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (std::numeric_limits::max()), 32767); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (32767), 32767); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (16383), 16383); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (0), 0); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (-16383), -16383); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (-32767), -32767); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (-32768), -32768); + REQUIRE_EQ (AudioSampleConverter::sampleToSixteenBitInt (std::numeric_limits::lowest()), -32768); + } +} + +//============================================================= +TEST_SUITE ("SampleConversionTests::24-bit Conversions") +{ + //============================================================= + TEST_CASE ("24-bit Conversions::twentyFourBitIntToSample (float and double)") + { + REQUIRE_EQ (AudioSampleConverter::twentyFourBitIntToSample (8388607), 1.f); + REQUIRE_EQ (AudioSampleConverter::twentyFourBitIntToSample (4194303), doctest::Approx (0.5f).epsilon (0.0001)); + REQUIRE_EQ (AudioSampleConverter::twentyFourBitIntToSample (0), 0.f); + REQUIRE_EQ (AudioSampleConverter::twentyFourBitIntToSample (-4194303), doctest::Approx (-0.5f).epsilon (0.0001)); + REQUIRE_EQ (AudioSampleConverter::twentyFourBitIntToSample (-8388607), -1.f); + REQUIRE_EQ (AudioSampleConverter::twentyFourBitIntToSample (-8388608), doctest::Approx (-1.0000001192f).epsilon (0.0001)); + + REQUIRE_EQ (AudioSampleConverter::twentyFourBitIntToSample (8388607), 1.); + REQUIRE_EQ (AudioSampleConverter::twentyFourBitIntToSample (4194303), doctest::Approx (0.5).epsilon (0.0001)); + REQUIRE_EQ (AudioSampleConverter::twentyFourBitIntToSample (0), 0.); + REQUIRE_EQ (AudioSampleConverter::twentyFourBitIntToSample (-4194303), doctest::Approx (-0.5).epsilon (0.0001)); + REQUIRE_EQ (AudioSampleConverter::twentyFourBitIntToSample (-8388607), -1.); + REQUIRE_EQ (AudioSampleConverter::twentyFourBitIntToSample (-8388608), doctest::Approx (-1.0000001192).epsilon (0.0001)); + } + + //============================================================= + TEST_CASE ("24-bit Conversions::twentyFourBitIntToSample (unsigned integers)") + { + REQUIRE_EQ (AudioSampleConverter::twentyFourBitIntToSample (8388607), 16777215); + REQUIRE_EQ (AudioSampleConverter::twentyFourBitIntToSample (4194303), 12582911); + REQUIRE_EQ (AudioSampleConverter::twentyFourBitIntToSample (0), 8388608); + REQUIRE_EQ (AudioSampleConverter::twentyFourBitIntToSample (-4194303), 4194305); + REQUIRE_EQ (AudioSampleConverter::twentyFourBitIntToSample (-8388607), 1); + REQUIRE_EQ (AudioSampleConverter::twentyFourBitIntToSample (-8388608), 0); + + REQUIRE_EQ (AudioSampleConverter::twentyFourBitIntToSample (8388607), 16777215); + REQUIRE_EQ (AudioSampleConverter::twentyFourBitIntToSample (4194303), 12582911); + REQUIRE_EQ (AudioSampleConverter::twentyFourBitIntToSample (0), 8388608); + REQUIRE_EQ (AudioSampleConverter::twentyFourBitIntToSample (-4194303), 4194305); + REQUIRE_EQ (AudioSampleConverter::twentyFourBitIntToSample (-8388607), 1); + REQUIRE_EQ (AudioSampleConverter::twentyFourBitIntToSample (-8388608), 0); + } + + //============================================================= + TEST_CASE ("24-bit Conversions::twentyFourBitIntToSample (signed integers)") + { + REQUIRE_EQ (AudioSampleConverter::twentyFourBitIntToSample (8388607), 8388607); + REQUIRE_EQ (AudioSampleConverter::twentyFourBitIntToSample (4194303), 4194303); + REQUIRE_EQ (AudioSampleConverter::twentyFourBitIntToSample (0), 0); + REQUIRE_EQ (AudioSampleConverter::twentyFourBitIntToSample (-4194303), -4194303); + REQUIRE_EQ (AudioSampleConverter::twentyFourBitIntToSample (-8388607), -8388607); + REQUIRE_EQ (AudioSampleConverter::twentyFourBitIntToSample (-8388608), -8388608); + + REQUIRE_EQ (AudioSampleConverter::twentyFourBitIntToSample (8388607), 8388607); + REQUIRE_EQ (AudioSampleConverter::twentyFourBitIntToSample (4194303), 4194303); + REQUIRE_EQ (AudioSampleConverter::twentyFourBitIntToSample (0), 0); + REQUIRE_EQ (AudioSampleConverter::twentyFourBitIntToSample (-4194303), -4194303); + REQUIRE_EQ (AudioSampleConverter::twentyFourBitIntToSample (-8388607), -8388607); + REQUIRE_EQ (AudioSampleConverter::twentyFourBitIntToSample (-8388608), -8388608); + } + + //============================================================= + TEST_CASE ("24-bit Conversions::sampleToTwentyFourBitInt (float and double)") + { + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (1.f), 8388607); + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (0.5f), 4194303); + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (0.f), 0); + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (-0.5f), -4194303); + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (-1.f), -8388607); + + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (1.), 8388607); + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (0.5), 4194303); + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (0.), 0); + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (-0.5), -4194303); + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (-1.), -8388607); + } + + //============================================================= + TEST_CASE ("24-bit Conversions::sampleToTwentyFourBitInt (unsigned integers)") + { + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (std::numeric_limits::max()), 8388607); + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (16777215), 8388607); + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (12582911), 4194303); + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (8388608), 0); + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (4194305), -4194303); + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (1), -8388607); + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (0), -8388608); + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (std::numeric_limits::min()), -8388608); + + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (std::numeric_limits::max()), 8388607); + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (16777215), 8388607); + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (12582911), 4194303); + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (8388608), 0); + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (4194305), -4194303); + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (1), -8388607); + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (0), -8388608); + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (std::numeric_limits::min()), -8388608); + } + + //============================================================= + TEST_CASE ("24-bit Conversions::sampleToTwentyFourBitInt (signed integers)") + { + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (std::numeric_limits::max()), 8388607); + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (8388607), 8388607); + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (4194303), 4194303); + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (0), 0); + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (-4194303), -4194303); + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (-8388607), -8388607); + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (-8388608), -8388608); + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (std::numeric_limits::min()), -8388608); + + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (std::numeric_limits::max()), 8388607); + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (8388607), 8388607); + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (4194303), 4194303); + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (0), 0); + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (-4194303), -4194303); + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (-8388607), -8388607); + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (-8388608), -8388608); + REQUIRE_EQ (AudioSampleConverter::sampleToTwentyFourBitInt (std::numeric_limits::min()), -8388608); + } +} + +//============================================================= +TEST_SUITE ("SampleConversionTests::32-bit Conversions") +{ + //============================================================= + TEST_CASE ("32-bit Conversions::thirtyTwoBitIntToSample (float and double)") + { + REQUIRE_EQ (AudioSampleConverter::thirtyTwoBitIntToSample (2147483647), 1.f); + REQUIRE_EQ (AudioSampleConverter::thirtyTwoBitIntToSample (1073741824), doctest::Approx (0.5f).epsilon (0.0001)); + REQUIRE_EQ (AudioSampleConverter::thirtyTwoBitIntToSample (0), 0.f); + REQUIRE_EQ (AudioSampleConverter::thirtyTwoBitIntToSample (-1073741824), doctest::Approx (-0.5f).epsilon (0.0001)); + REQUIRE_EQ (AudioSampleConverter::thirtyTwoBitIntToSample (-2147483647), -1.f); + REQUIRE_EQ (AudioSampleConverter::thirtyTwoBitIntToSample (-2147483648), doctest::Approx (-1.00000000046566f).epsilon (0.0001)); + + REQUIRE_EQ (AudioSampleConverter::thirtyTwoBitIntToSample (2147483647), 1.); + REQUIRE_EQ (AudioSampleConverter::thirtyTwoBitIntToSample (1073741824), doctest::Approx (0.5).epsilon (0.0001)); + REQUIRE_EQ (AudioSampleConverter::thirtyTwoBitIntToSample (0), 0.f); + REQUIRE_EQ (AudioSampleConverter::thirtyTwoBitIntToSample (-1073741824), doctest::Approx (-0.5).epsilon (0.0001)); + REQUIRE_EQ (AudioSampleConverter::thirtyTwoBitIntToSample (-2147483647), -1.); + REQUIRE_EQ (AudioSampleConverter::thirtyTwoBitIntToSample (-2147483648), doctest::Approx (-1.00000000046566).epsilon (0.0001)); + } + + //============================================================= + TEST_CASE ("32-bit Conversions::thirtyTwoBitIntToSample (unsigned integer)") + { + REQUIRE_EQ (AudioSampleConverter::thirtyTwoBitIntToSample (2147483647), 4294967295); + REQUIRE_EQ (AudioSampleConverter::thirtyTwoBitIntToSample (1073741824), 3221225472); + REQUIRE_EQ (AudioSampleConverter::thirtyTwoBitIntToSample (0), 2147483648); + REQUIRE_EQ (AudioSampleConverter::thirtyTwoBitIntToSample (-1073741824), 1073741824); + REQUIRE_EQ (AudioSampleConverter::thirtyTwoBitIntToSample (-2147483647), 1); + REQUIRE_EQ (AudioSampleConverter::thirtyTwoBitIntToSample (-2147483648), 0); + + REQUIRE_EQ (AudioSampleConverter::thirtyTwoBitIntToSample (2147483647), 4294967295); + REQUIRE_EQ (AudioSampleConverter::thirtyTwoBitIntToSample (1073741824), 3221225472); + REQUIRE_EQ (AudioSampleConverter::thirtyTwoBitIntToSample (0), 2147483648); + REQUIRE_EQ (AudioSampleConverter::thirtyTwoBitIntToSample (-1073741824), 1073741824); + REQUIRE_EQ (AudioSampleConverter::thirtyTwoBitIntToSample (-2147483647), 1); + REQUIRE_EQ (AudioSampleConverter::thirtyTwoBitIntToSample (-2147483648), 0); + } + + //============================================================= + TEST_CASE ("32-bit Conversions::thirtyTwoBitIntToSample (signed integer)") + { + REQUIRE_EQ (AudioSampleConverter::thirtyTwoBitIntToSample (2147483647), 2147483647); + REQUIRE_EQ (AudioSampleConverter::thirtyTwoBitIntToSample (1073741824), 1073741824); + REQUIRE_EQ (AudioSampleConverter::thirtyTwoBitIntToSample (0), 0); + REQUIRE_EQ (AudioSampleConverter::thirtyTwoBitIntToSample (-1073741824), -1073741824); + REQUIRE_EQ (AudioSampleConverter::thirtyTwoBitIntToSample (-2147483647), -2147483647); + REQUIRE_EQ (AudioSampleConverter::thirtyTwoBitIntToSample (-2147483648), -2147483648); + + REQUIRE_EQ (AudioSampleConverter::thirtyTwoBitIntToSample (2147483647), 2147483647); + REQUIRE_EQ (AudioSampleConverter::thirtyTwoBitIntToSample (1073741824), 1073741824); + REQUIRE_EQ (AudioSampleConverter::thirtyTwoBitIntToSample (0), 0); + REQUIRE_EQ (AudioSampleConverter::thirtyTwoBitIntToSample (-1073741824), -1073741824); + REQUIRE_EQ (AudioSampleConverter::thirtyTwoBitIntToSample (-2147483647), -2147483647); + REQUIRE_EQ (AudioSampleConverter::thirtyTwoBitIntToSample (-2147483648), -2147483648LL); + } + + //============================================================= + TEST_CASE ("32-bit Conversions::sampleToThirtyTwoBitInt (float and double)") + { + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (std::numeric_limits::max()), 2147483647); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (1.f), 2147483647); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (0.5f), 1073741824); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (0.f), 0); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (-0.5f), -1073741824); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (-1.f), -2147483647); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (std::numeric_limits::lowest()), -2147483647); + + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (std::numeric_limits::max()), 2147483647); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (1.f), 2147483647); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (0.5f), 1073741823); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (0.f), 0); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (-0.5f), -1073741823); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (-1.), -2147483647); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (std::numeric_limits::lowest()), -2147483647); + } + + //============================================================= + TEST_CASE ("32-bit Conversions::sampleToThirtyTwoBitInt (unsigned integers)") + { + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (std::numeric_limits::max()), 2147483647); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (4294967295), 2147483647); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (3221225472), 1073741824); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (2147483648), 0); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (1073741824), -1073741824); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (1), -2147483647); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (0), -2147483648); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (std::numeric_limits::min()), -2147483648); + + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (std::numeric_limits::max()), 2147483647); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (4294967295), 2147483647); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (3221225472), 1073741824); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (2147483648), 0); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (1073741824), -1073741824); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (1), -2147483647); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (0), -2147483648); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (std::numeric_limits::min()), -2147483648); + } + + //============================================================= + TEST_CASE ("32-bit Conversions::sampleToThirtyTwoBitInt (signed integers)") + { + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (std::numeric_limits::max()), 2147483647); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (2147483647), 2147483647); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (1073741824), 1073741824); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (0), 0); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (-1073741824), -1073741824); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (-2147483647), -2147483647); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (-2147483648), -2147483648); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (std::numeric_limits::min()), -2147483648); + + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (std::numeric_limits::max()), 2147483647); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (2147483647), 2147483647); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (1073741824), 1073741824); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (0), 0); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (-1073741824), -1073741824); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (-2147483647), -2147483647); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (-2147483648LL), -2147483648LL); + REQUIRE_EQ (AudioSampleConverter::sampleToThirtyTwoBitInt (std::numeric_limits::min()), -2147483648); + } +} diff --git a/tests/WavLoadingTests.cpp b/tests/WavLoadingTests.cpp index 99dcfa9..2735e9c 100644 --- a/tests/WavLoadingTests.cpp +++ b/tests/WavLoadingTests.cpp @@ -2,6 +2,7 @@ #include #include #include +#include // -------------------------------------------- // Test audio files: 44.1kHz Stereo @@ -35,7 +36,7 @@ const std::string projectBuildDirectory = PROJECT_BINARY_DIR; //============================================================= -TEST_SUITE ("WavLoadingTests") +TEST_SUITE ("WavLoadingTests - Floating Point Types - 8-bit File") { //============================================================= TEST_CASE ("WavLoadingTests_Stereo_8bit_44100") @@ -53,80 +54,69 @@ TEST_SUITE ("WavLoadingTests") { for (int k = 0; k < audioFile.getNumChannels(); k++) { - CHECK (audioFile.samples[k][i] == doctest::Approx (wav_stereo_8bit_44100::testBuffer[k][i]).epsilon (0.00001)); + CHECK (audioFile.samples[k][i] == doctest::Approx (wav_stereo_8bit_44100::testBuffer[k][i]).epsilon (0.01)); } } } - + //============================================================= - TEST_CASE ("WavLoadingTests_Stereo_16bit_44100") + TEST_CASE ("WavLoadingTests_Mono_8bit_44100") { - AudioFile audioFile; - bool loadedOK = audioFile.load (projectBuildDirectory + "/test-audio/wav_stereo_16bit_44100.wav"); - - CHECK (loadedOK); - CHECK_EQ (audioFile.getNumSamplesPerChannel(), wav_stereo_16bit_44100::numSamplesPerChannel); - CHECK_EQ (audioFile.getBitDepth(), wav_stereo_16bit_44100::bitDepth); - CHECK_EQ (audioFile.getSampleRate(), wav_stereo_16bit_44100::sampleRate); - CHECK_EQ (audioFile.getNumChannels(), static_cast (wav_stereo_16bit_44100::testBuffer.size())); - - for (size_t i = 0; i < wav_stereo_16bit_44100::testBuffer[0].size(); i++) - { - for (int k = 0; k < audioFile.getNumChannels(); k++) - { - CHECK (audioFile.samples[k][i] == doctest::Approx (wav_stereo_16bit_44100::testBuffer[k][i]).epsilon (0.00001)); - } - } + //TODO } - + //============================================================= - TEST_CASE ("WavLoadingTests_Stereo_24bit_44100") + TEST_CASE ("WavLoadingTests_Stereo_8bit_48000") { AudioFile audioFile; - bool loadedOK = audioFile.load (projectBuildDirectory + "/test-audio/wav_stereo_24bit_44100.wav"); + bool loadedOK = audioFile.load (projectBuildDirectory + "/test-audio/wav_stereo_8bit_48000.wav"); CHECK (loadedOK); - CHECK_EQ (audioFile.getNumSamplesPerChannel(), wav_stereo_24bit_44100::numSamplesPerChannel); - CHECK_EQ (audioFile.getBitDepth(), wav_stereo_24bit_44100::bitDepth); - CHECK_EQ (audioFile.getSampleRate(), wav_stereo_24bit_44100::sampleRate); - CHECK_EQ (audioFile.getNumChannels(), static_cast (wav_stereo_24bit_44100::testBuffer.size())); + CHECK_EQ (audioFile.getNumSamplesPerChannel(), wav_stereo_8bit_48000::numSamplesPerChannel); + CHECK_EQ (audioFile.getBitDepth(), wav_stereo_8bit_48000::bitDepth); + CHECK_EQ (audioFile.getSampleRate(), wav_stereo_8bit_48000::sampleRate); + CHECK_EQ (audioFile.getNumChannels(), static_cast (wav_stereo_8bit_48000::testBuffer.size())); - for (size_t i = 0; i < wav_stereo_24bit_44100::testBuffer[0].size(); i++) + for (size_t i = 0; i < wav_stereo_8bit_48000::testBuffer[0].size(); i++) { for (int k = 0; k < audioFile.getNumChannels(); k++) { - CHECK (audioFile.samples[k][i] == doctest::Approx (wav_stereo_24bit_44100::testBuffer[k][i]).epsilon (0.00001)); + CHECK (audioFile.samples[k][i] == doctest::Approx (wav_stereo_8bit_48000::testBuffer[k][i]).epsilon (0.01)); } } } + + //============================================================= + TEST_CASE ("WavLoadingTests_Mono_8bit_48000") + { + //TODO + } +} +//============================================================= +TEST_SUITE ("WavLoadingTests - Floating Point Types - 16-bit File") +{ //============================================================= - TEST_CASE ("WavLoadingTests_Stereo_32bit_44100") + TEST_CASE ("WavLoadingTests_Stereo_16bit_44100") { AudioFile audioFile; - bool loadedOK = audioFile.load (projectBuildDirectory + "/test-audio/wav_stereo_32bit_44100.wav"); + bool loadedOK = audioFile.load (projectBuildDirectory + "/test-audio/wav_stereo_16bit_44100.wav"); CHECK (loadedOK); - CHECK_EQ (audioFile.getNumSamplesPerChannel(), wav_stereo_32bit_44100::numSamplesPerChannel); - CHECK_EQ (audioFile.getBitDepth(), wav_stereo_32bit_44100::bitDepth); - CHECK_EQ (audioFile.getSampleRate(), wav_stereo_32bit_44100::sampleRate); - CHECK_EQ (audioFile.getNumChannels(), static_cast (wav_stereo_32bit_44100::testBuffer.size())); + CHECK_EQ (audioFile.getNumSamplesPerChannel(), wav_stereo_16bit_44100::numSamplesPerChannel); + CHECK_EQ (audioFile.getBitDepth(), wav_stereo_16bit_44100::bitDepth); + CHECK_EQ (audioFile.getSampleRate(), wav_stereo_16bit_44100::sampleRate); + CHECK_EQ (audioFile.getNumChannels(), static_cast (wav_stereo_16bit_44100::testBuffer.size())); - for (size_t i = 0; i < wav_stereo_32bit_44100::testBuffer[0].size(); i++) + for (size_t i = 0; i < wav_stereo_16bit_44100::testBuffer[0].size(); i++) { for (int k = 0; k < audioFile.getNumChannels(); k++) { - CHECK (audioFile.samples[k][i] == doctest::Approx (wav_stereo_32bit_44100::testBuffer[k][i]).epsilon (0.00001)); + CHECK (audioFile.samples[k][i] == doctest::Approx (wav_stereo_16bit_44100::testBuffer[k][i]).epsilon (0.00001)); } } } - - //============================================================= - TEST_CASE ("WavLoadingTests_Mono_8bit_44100") - { - //TODO - } - + //============================================================= TEST_CASE ("WavLoadingTests_Mono_16bit_44100") { @@ -147,55 +137,80 @@ TEST_SUITE ("WavLoadingTests") } } } - + //============================================================= - TEST_CASE ("WavLoadingTests_Mono_24bit_44100") + TEST_CASE ("WavLoadingTests_Stereo_16bit_48000") { - //TODO + AudioFile audioFile; + bool loadedOK = audioFile.load (projectBuildDirectory + "/test-audio/wav_stereo_16bit_48000.wav"); + + CHECK (loadedOK); + CHECK_EQ (audioFile.getNumSamplesPerChannel(), wav_stereo_16bit_48000::numSamplesPerChannel); + CHECK_EQ (audioFile.getBitDepth(), wav_stereo_16bit_48000::bitDepth); + CHECK_EQ (audioFile.getSampleRate(), wav_stereo_16bit_48000::sampleRate); + CHECK_EQ (audioFile.getNumChannels(), static_cast (wav_stereo_16bit_48000::testBuffer.size())); + + for (size_t i = 0; i < wav_stereo_16bit_48000::testBuffer[0].size(); i++) + { + for (int k = 0; k < audioFile.getNumChannels(); k++) + { + CHECK (audioFile.samples[k][i] == doctest::Approx (wav_stereo_16bit_48000::testBuffer[k][i]).epsilon (0.001)); + } + } } - + //============================================================= - TEST_CASE ("WavLoadingTests_Stereo_8bit_48000") + TEST_CASE ("WavLoadingTests_Mono_16bit_48000") { AudioFile audioFile; - bool loadedOK = audioFile.load (projectBuildDirectory + "/test-audio/wav_stereo_8bit_48000.wav"); + bool loadedOK = audioFile.load (projectBuildDirectory + "/test-audio/wav_mono_16bit_48000.wav"); CHECK (loadedOK); - CHECK_EQ (audioFile.getNumSamplesPerChannel(), wav_stereo_8bit_48000::numSamplesPerChannel); - CHECK_EQ (audioFile.getBitDepth(), wav_stereo_8bit_48000::bitDepth); - CHECK_EQ (audioFile.getSampleRate(), wav_stereo_8bit_48000::sampleRate); - CHECK_EQ (audioFile.getNumChannels(), static_cast (wav_stereo_8bit_48000::testBuffer.size())); + CHECK_EQ (audioFile.getNumSamplesPerChannel(), wav_mono_16bit_48000::numSamplesPerChannel); + CHECK_EQ (audioFile.getBitDepth(), wav_mono_16bit_48000::bitDepth); + CHECK_EQ (audioFile.getSampleRate(), wav_mono_16bit_48000::sampleRate); + CHECK_EQ (audioFile.getNumChannels(), static_cast (wav_mono_16bit_48000::numChannels)); - for (size_t i = 0; i < wav_stereo_8bit_48000::testBuffer[0].size(); i++) + for (size_t i = 0; i < wav_mono_16bit_48000::testBuffer.size(); i++) { for (int k = 0; k < audioFile.getNumChannels(); k++) { - CHECK (audioFile.samples[k][i] == doctest::Approx (wav_stereo_8bit_48000::testBuffer[k][i]).epsilon (0.001)); + CHECK (audioFile.samples[k][i] == doctest::Approx (wav_mono_16bit_48000::testBuffer[i]).epsilon (0.001)); } } } +} +//============================================================= +TEST_SUITE ("WavLoadingTests - Floating Point Types - 24-bit File") +{ //============================================================= - TEST_CASE ("WavLoadingTests_Stereo_16bit_48000") + TEST_CASE ("WavLoadingTests_Stereo_24bit_44100") { AudioFile audioFile; - bool loadedOK = audioFile.load (projectBuildDirectory + "/test-audio/wav_stereo_16bit_48000.wav"); + bool loadedOK = audioFile.load (projectBuildDirectory + "/test-audio/wav_stereo_24bit_44100.wav"); CHECK (loadedOK); - CHECK_EQ (audioFile.getNumSamplesPerChannel(), wav_stereo_16bit_48000::numSamplesPerChannel); - CHECK_EQ (audioFile.getBitDepth(), wav_stereo_16bit_48000::bitDepth); - CHECK_EQ (audioFile.getSampleRate(), wav_stereo_16bit_48000::sampleRate); - CHECK_EQ (audioFile.getNumChannels(), static_cast (wav_stereo_16bit_48000::testBuffer.size())); + CHECK_EQ (audioFile.getNumSamplesPerChannel(), wav_stereo_24bit_44100::numSamplesPerChannel); + CHECK_EQ (audioFile.getBitDepth(), wav_stereo_24bit_44100::bitDepth); + CHECK_EQ (audioFile.getSampleRate(), wav_stereo_24bit_44100::sampleRate); + CHECK_EQ (audioFile.getNumChannels(), static_cast (wav_stereo_24bit_44100::testBuffer.size())); - for (size_t i = 0; i < wav_stereo_16bit_48000::testBuffer[0].size(); i++) + for (size_t i = 0; i < wav_stereo_24bit_44100::testBuffer[0].size(); i++) { for (int k = 0; k < audioFile.getNumChannels(); k++) { - CHECK (audioFile.samples[k][i] == doctest::Approx (wav_stereo_16bit_48000::testBuffer[k][i]).epsilon (0.001)); + CHECK (audioFile.samples[k][i] == doctest::Approx (wav_stereo_24bit_44100::testBuffer[k][i]).epsilon (0.00001)); } } } - + + //============================================================= + TEST_CASE ("WavLoadingTests_Mono_24bit_44100") + { + //TODO + } + //============================================================= TEST_CASE ("WavLoadingTests_Stereo_24bit_48000") { @@ -216,7 +231,38 @@ TEST_SUITE ("WavLoadingTests") } } } + + //============================================================= + TEST_CASE ("WavLoadingTests_Mono_24bit_48000") + { + //TODO + } +} +//============================================================= +TEST_SUITE ("WavLoadingTests - Floating Point Types - 32-bit File") +{ + //============================================================= + TEST_CASE ("WavLoadingTests_Stereo_32bit_44100") + { + AudioFile audioFile; + bool loadedOK = audioFile.load (projectBuildDirectory + "/test-audio/wav_stereo_32bit_44100.wav"); + + CHECK (loadedOK); + CHECK_EQ (audioFile.getNumSamplesPerChannel(), wav_stereo_32bit_44100::numSamplesPerChannel); + CHECK_EQ (audioFile.getBitDepth(), wav_stereo_32bit_44100::bitDepth); + CHECK_EQ (audioFile.getSampleRate(), wav_stereo_32bit_44100::sampleRate); + CHECK_EQ (audioFile.getNumChannels(), static_cast (wav_stereo_32bit_44100::testBuffer.size())); + + for (size_t i = 0; i < wav_stereo_32bit_44100::testBuffer[0].size(); i++) + { + for (int k = 0; k < audioFile.getNumChannels(); k++) + { + CHECK (audioFile.samples[k][i] == doctest::Approx (wav_stereo_32bit_44100::testBuffer[k][i]).epsilon (0.00001)); + } + } + } + //============================================================= TEST_CASE ("WavLoadingTests_Stereo_32bit_48000") { @@ -237,52 +283,169 @@ TEST_SUITE ("WavLoadingTests") } } } +} +//============================================================= +TEST_SUITE ("WavLoadingTests - Integer Types - 8-bit file") +{ //============================================================= - TEST_CASE ("WavLoadingTests_Mono_8bit_48000") + template + void test8Bit44100WithInteger() { - //TODO + AudioFile audioFile; + bool loadedOK = audioFile.load (projectBuildDirectory + "/test-audio/wav_stereo_8bit_44100.wav"); + + CHECK (loadedOK); + CHECK_EQ (audioFile.getNumSamplesPerChannel(), wav_stereo_8bit_44100::numSamplesPerChannel); + CHECK_EQ (audioFile.getBitDepth(), wav_stereo_8bit_44100::bitDepth); + CHECK_EQ (audioFile.getSampleRate(), wav_stereo_8bit_44100::sampleRate); + CHECK_EQ (audioFile.getNumChannels(), static_cast (wav_stereo_8bit_44100::testBuffer.size())); + + int offset = std::is_signed_v ? 0 : 128; + + for (size_t i = 0; i < wav_stereo_8bit_44100::testBuffer[0].size(); i++) + { + for (int k = 0; k < audioFile.getNumChannels(); k++) + { + REQUIRE_EQ (audioFile.samples[k][i], doctest::Approx (wav_stereo_8bit_44100::testBuffer[k][i] * 127 + offset).epsilon (0.01)); + } + } + } + //============================================================= + TEST_CASE ("WavLoadingTests_Stereo_8bit_44100_integers") + { + test8Bit44100WithInteger(); + test8Bit44100WithInteger(); + test8Bit44100WithInteger(); + test8Bit44100WithInteger(); + test8Bit44100WithInteger(); + test8Bit44100WithInteger(); + test8Bit44100WithInteger(); + test8Bit44100WithInteger(); } +} +//============================================================= +TEST_SUITE ("WavLoadingTests - Integer Types - 16-bit file") +{ //============================================================= - TEST_CASE ("WavLoadingTests_Mono_16bit_48000") + template + void test16Bit44100WithInteger (bool expectFailure = false) { - AudioFile audioFile; - bool loadedOK = audioFile.load (projectBuildDirectory + "/test-audio/wav_mono_16bit_48000.wav"); + AudioFile audioFile; + + if (expectFailure) + audioFile.shouldLogErrorsToConsole (false); + + bool loadedOK = audioFile.load (projectBuildDirectory + "/test-audio/wav_stereo_16bit_44100.wav"); + + if (expectFailure) + { + CHECK_EQ (loadedOK, false); + return; + } CHECK (loadedOK); - CHECK_EQ (audioFile.getNumSamplesPerChannel(), wav_mono_16bit_48000::numSamplesPerChannel); - CHECK_EQ (audioFile.getBitDepth(), wav_mono_16bit_48000::bitDepth); - CHECK_EQ (audioFile.getSampleRate(), wav_mono_16bit_48000::sampleRate); - CHECK_EQ (audioFile.getNumChannels(), static_cast (wav_mono_16bit_48000::numChannels)); + CHECK_EQ (audioFile.getNumSamplesPerChannel(), wav_stereo_16bit_44100::numSamplesPerChannel); + CHECK_EQ (audioFile.getBitDepth(), wav_stereo_16bit_44100::bitDepth); + CHECK_EQ (audioFile.getSampleRate(), wav_stereo_16bit_44100::sampleRate); + CHECK_EQ (audioFile.getNumChannels(), static_cast (wav_stereo_16bit_44100::testBuffer.size())); - for (size_t i = 0; i < wav_mono_16bit_48000::testBuffer.size(); i++) + int offset = std::is_signed_v ? 0 : 32768; + + for (size_t i = 0; i < wav_stereo_16bit_44100::testBuffer[0].size(); i++) { for (int k = 0; k < audioFile.getNumChannels(); k++) { - CHECK (audioFile.samples[k][i] == doctest::Approx (wav_mono_16bit_48000::testBuffer[i]).epsilon (0.00001)); + REQUIRE_EQ (audioFile.samples[k][i], std::round (wav_stereo_16bit_44100::testBuffer[k][i] * 32767 + offset)); } } } + + //============================================================= + TEST_CASE ("WavLoadingTests_Stereo_16bit_44100_integers") + { + test16Bit44100WithInteger(); + test16Bit44100WithInteger(); + test16Bit44100WithInteger(); + test16Bit44100WithInteger(); + test16Bit44100WithInteger(); + test16Bit44100WithInteger(); + + // check that the following ones fail... + test16Bit44100WithInteger (true); + test16Bit44100WithInteger (true); + } +} +//============================================================= +TEST_SUITE ("WavLoadingTests - Integer Types - 24-bit file") +{ //============================================================= - TEST_CASE ("WavLoadingTests_Mono_24bit_48000") + template + void test24Bit44100WithInteger (bool expectFailure = false) { - //TODO + AudioFile audioFile; + + if (expectFailure) + audioFile.shouldLogErrorsToConsole (false); + + bool loadedOK = audioFile.load (projectBuildDirectory + "/test-audio/wav_stereo_24bit_44100.wav"); + + if (expectFailure) + { + CHECK_EQ (loadedOK, false); + return; + } + + CHECK (loadedOK); + CHECK_EQ (audioFile.getNumSamplesPerChannel(), wav_stereo_24bit_44100::numSamplesPerChannel); + CHECK_EQ (audioFile.getBitDepth(), wav_stereo_24bit_44100::bitDepth); + CHECK_EQ (audioFile.getSampleRate(), wav_stereo_24bit_44100::sampleRate); + CHECK_EQ (audioFile.getNumChannels(), static_cast (wav_stereo_24bit_44100::testBuffer.size())); + + int offset = std::is_signed_v ? 0 : 8388608; + + for (size_t i = 0; i < wav_stereo_24bit_44100::testBuffer[0].size(); i++) + { + for (int k = 0; k < audioFile.getNumChannels(); k++) + { + CHECK (audioFile.samples[k][i] == doctest::Approx (wav_stereo_24bit_44100::testBuffer[k][i] * 8388607 + offset).epsilon (0.00001)); + } + } + } + + //============================================================= + TEST_CASE ("WavLoadingTests_Stereo_24bit_44100_integers") + { + test24Bit44100WithInteger(); + test24Bit44100WithInteger(); + test24Bit44100WithInteger(); + test24Bit44100WithInteger(); + + // check that the following ones fail + test24Bit44100WithInteger (true); + test24Bit44100WithInteger (true); + test24Bit44100WithInteger (true); + test24Bit44100WithInteger (true); } +} +//============================================================= +TEST_SUITE ("WavLoadingTests - Floating Point Types - 8 Channel File") +{ //============================================================= TEST_CASE ("WavLoadingTests_8chan_24bit_48000") { AudioFile audioFile; bool loadedOK = audioFile.load (projectBuildDirectory + "/test-audio/wav_8chan_24bit_48000.wav"); - + CHECK (loadedOK); CHECK_EQ (audioFile.getNumSamplesPerChannel(), wav_8chan_24bit_48000::numSamplesPerChannel); CHECK_EQ (audioFile.getBitDepth(), wav_8chan_24bit_48000::bitDepth); CHECK_EQ (audioFile.getSampleRate(), wav_8chan_24bit_48000::sampleRate); CHECK_EQ (audioFile.getNumChannels(), wav_8chan_24bit_48000::numChannels); - + for (size_t i = 0; i < wav_8chan_24bit_48000::testBuffer[0].size(); i++) { for (int k = 0; k < audioFile.getNumChannels(); k++) diff --git a/tests/doctest/doctest.h b/tests/doctest/doctest.h index 9444698..c158b5b 100644 --- a/tests/doctest/doctest.h +++ b/tests/doctest/doctest.h @@ -4,14 +4,14 @@ // // doctest.h - the lightest feature-rich C++ single-header testing framework for unit tests and TDD // -// Copyright (c) 2016-2019 Viktor Kirilov +// Copyright (c) 2016-2021 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/onqtam/doctest/blob/master/doc/markdown/readme.md +// https://github.com/doctest/doctest/blob/master/doc/markdown/readme.md // // ================================================================================================= // ================================================================================================= @@ -48,8 +48,16 @@ #define DOCTEST_VERSION_MAJOR 2 #define DOCTEST_VERSION_MINOR 4 -#define DOCTEST_VERSION_PATCH 0 -#define DOCTEST_VERSION_STR "2.4.0" +#define DOCTEST_VERSION_PATCH 10 + +// 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) @@ -60,6 +68,12 @@ // 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... @@ -71,12 +85,15 @@ DOCTEST_COMPILER(_MSC_VER / 100, (_MSC_FULL_VER / 100000) % 100, _MSC_FULL_VER % 100000) #endif // MSVC #endif // MSVC -#if defined(__clang__) && defined(__clang_minor__) +#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 @@ -87,12 +104,15 @@ #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 +#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) @@ -137,85 +157,94 @@ // == 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("-Wunknown-pragmas") DOCTEST_CLANG_SUPPRESS_WARNING("-Wnon-virtual-dtor") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wweak-vtables") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wpadded") DOCTEST_CLANG_SUPPRESS_WARNING("-Wdeprecated") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-prototypes") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-local-typedef") -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("-Wctor-dtor-privacy") -DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-declarations") DOCTEST_GCC_SUPPRESS_WARNING("-Wnon-virtual-dtor") -DOCTEST_GCC_SUPPRESS_WARNING("-Wunused-local-typedefs") -DOCTEST_GCC_SUPPRESS_WARNING("-Wuseless-cast") -DOCTEST_GCC_SUPPRESS_WARNING("-Wnoexcept") DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-promo") DOCTEST_MSVC_SUPPRESS_WARNING_PUSH -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 defined as deleted -DOCTEST_MSVC_SUPPRESS_WARNING(4626) // assignment operator was implicitly defined as deleted -DOCTEST_MSVC_SUPPRESS_WARNING(5027) // move assignment operator was implicitly defined as deleted -DOCTEST_MSVC_SUPPRESS_WARNING(5026) // move constructor was implicitly defined as deleted DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly defined as deleted -DOCTEST_MSVC_SUPPRESS_WARNING(4640) // construction of local static object is not thread-safe -// static analysis -DOCTEST_MSVC_SUPPRESS_WARNING(26439) // This kind of 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 construction and dtr... -DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum' - -// 4548 - expression before comma has no effect; expected expression with side - effect -// 4265 - class has virtual functions, but destructor is not virtual -// 4986 - exception specification does not match previous declaration -// 4350 - behavior change: 'member1' called instead of 'member2' -// 4668 - 'x' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif' -// 4365 - conversion from 'int' to 'unsigned long', signed/unsigned mismatch -// 4774 - format string expected in argument 'x' is not a string literal -// 4820 - padding in structs - -// only 4 should be disabled globally: -// - 4514 # unreferenced inline function has been removed -// - 4571 # SEH related -// - 4710 # function not inlined -// - 4711 # function 'x' selected for automatic inline expansion #define DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN \ DOCTEST_MSVC_SUPPRESS_WARNING_PUSH \ - DOCTEST_MSVC_SUPPRESS_WARNING(4548) \ - DOCTEST_MSVC_SUPPRESS_WARNING(4265) \ - DOCTEST_MSVC_SUPPRESS_WARNING(4986) \ - DOCTEST_MSVC_SUPPRESS_WARNING(4350) \ - DOCTEST_MSVC_SUPPRESS_WARNING(4668) \ - DOCTEST_MSVC_SUPPRESS_WARNING(4365) \ - DOCTEST_MSVC_SUPPRESS_WARNING(4774) \ - DOCTEST_MSVC_SUPPRESS_WARNING(4820) \ - DOCTEST_MSVC_SUPPRESS_WARNING(4625) \ - DOCTEST_MSVC_SUPPRESS_WARNING(4626) \ - DOCTEST_MSVC_SUPPRESS_WARNING(5027) \ - DOCTEST_MSVC_SUPPRESS_WARNING(5026) \ - DOCTEST_MSVC_SUPPRESS_WARNING(4623) \ - DOCTEST_MSVC_SUPPRESS_WARNING(5039) \ - DOCTEST_MSVC_SUPPRESS_WARNING(5045) \ - DOCTEST_MSVC_SUPPRESS_WARNING(5105) + 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 @@ -228,6 +257,7 @@ DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum' // 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) @@ -237,6 +267,10 @@ DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum' // 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 @@ -245,7 +279,7 @@ DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum' #endif // DOCTEST_CONFIG_NO_WINDOWS_SEH #if !defined(_WIN32) && !defined(__QNX__) && !defined(DOCTEST_CONFIG_POSIX_SIGNALS) && \ - !defined(__EMSCRIPTEN__) + !defined(__EMSCRIPTEN__) && !defined(__wasi__) #define DOCTEST_CONFIG_POSIX_SIGNALS #endif // _WIN32 #if defined(DOCTEST_CONFIG_NO_POSIX_SIGNALS) && defined(DOCTEST_CONFIG_POSIX_SIGNALS) @@ -253,7 +287,8 @@ DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum' #endif // DOCTEST_CONFIG_NO_POSIX_SIGNALS #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS -#if !defined(__cpp_exceptions) && !defined(__EXCEPTIONS) && !defined(_CPPUNWIND) +#if !defined(__cpp_exceptions) && !defined(__EXCEPTIONS) && !defined(_CPPUNWIND) \ + || defined(__wasi__) #define DOCTEST_CONFIG_NO_EXCEPTIONS #endif // no exceptions #endif // DOCTEST_CONFIG_NO_EXCEPTIONS @@ -268,6 +303,10 @@ DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum' #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 @@ -295,30 +334,87 @@ DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum' #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) -#else // MSVC +#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 // MSVC +#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) @@ -328,8 +424,6 @@ DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum' #define DOCTEST_ANONYMOUS(x) DOCTEST_CAT(x, __LINE__) #endif // __COUNTER__ -#define DOCTEST_TOSTR(x) #x - #ifndef DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE #define DOCTEST_REF_WRAP(x) x& #else // DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE @@ -343,20 +437,40 @@ DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum' #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 -#define DOCTEST_GLOBAL_NO_WARNINGS(var) \ - DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wglobal-constructors") \ - DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-variable") \ - static int var DOCTEST_UNUSED // NOLINT(fuchsia-statically-constructed-objects,cert-err58-cpp) -#define DOCTEST_GLOBAL_NO_WARNINGS_END() DOCTEST_CLANG_SUPPRESS_WARNING_POP +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_MAC -#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) +#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__) @@ -365,57 +479,73 @@ 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() ((void)0) +#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 -#ifdef DOCTEST_CONFIG_USE_STD_HEADERS -#include -#include -#include -#else // DOCTEST_CONFIG_USE_STD_HEADERS - +// 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 -// to detect if libc++ is being used with clang (the _LIBCPP_VERSION identifier) #include #endif // clang #ifdef _LIBCPP_VERSION -#define DOCTEST_STD_NAMESPACE_BEGIN _LIBCPP_BEGIN_NAMESPACE_STD -#define DOCTEST_STD_NAMESPACE_END _LIBCPP_END_NAMESPACE_STD -#else // _LIBCPP_VERSION -#define DOCTEST_STD_NAMESPACE_BEGIN namespace std { -#define DOCTEST_STD_NAMESPACE_END } +#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) -DOCTEST_STD_NAMESPACE_BEGIN // NOLINT (cert-dcl58-cpp) -typedef decltype(nullptr) nullptr_t; +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; -typedef basic_ostream> ostream; +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/onqtam/doctest/issues/183 -template +// see this issue on why this is needed: https://github.com/doctest/doctest/issues/183 +template class allocator; -template +template class basic_string; using string = basic_string, allocator>; #endif // VS 2019 -DOCTEST_STD_NAMESPACE_END +} // namespace std DOCTEST_MSVC_SUPPRESS_WARNING_POP @@ -427,8 +557,14 @@ DOCTEST_MSVC_SUPPRESS_WARNING_POP 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) @@ -441,7 +577,6 @@ DOCTEST_INTERFACE extern bool is_running_in_test; // TODO: // - optimizations - like not deleting memory unnecessarily in operator= and etc. // - resize/reserve/clear -// - substr // - replace // - back/front // - iterator stuff @@ -451,63 +586,84 @@ DOCTEST_INTERFACE extern bool is_running_in_test; // - relational operators as free functions - taking const char* as one of the params class DOCTEST_INTERFACE String { - static const unsigned len = 24; //!OCLINT avoid private static members - static const unsigned last = len - 1; //!OCLINT avoid private static members +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; - unsigned size; - unsigned capacity; + size_type size; + size_type capacity; }; union { - char buf[len]; + char buf[len]; // NOLINT(*-avoid-c-arrays) view data; }; - bool isOnStack() const { return (buf[last] & 128) == 0; } - void setOnHeap(); - void setLast(unsigned in = last); + 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: - String(); + static DOCTEST_CONSTEXPR size_type npos = static_cast(-1); + + String() noexcept; ~String(); // cppcheck-suppress noExplicitConstructor String(const char* in); - String(const char* in, unsigned in_size); + 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 operator+(const String& other) const; - String(String&& other); - String& operator=(String&& other); + String(String&& other) noexcept; + String& operator=(String&& other) noexcept; - char operator[](unsigned i) const; - char& operator[](unsigned i); + 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()) + if (isOnStack()) { return reinterpret_cast(buf); + } return data.ptr; } - unsigned size() const; - unsigned capacity() const; + 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); @@ -515,7 +671,21 @@ 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 std::ostream& operator<<(std::ostream& s, const String& in); +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 @@ -588,7 +758,7 @@ namespace assertType { 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, @@ -637,12 +807,14 @@ DOCTEST_INTERFACE const char* skipPathFromFilename(const char* file); struct DOCTEST_INTERFACE TestCaseData { - String m_file; // the file in which the test was registered + 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; @@ -667,9 +839,27 @@ struct DOCTEST_INTERFACE AssertData String m_decomp; // for specific exception-related asserts - bool m_threw_as; - const char* m_exception_type; - const char* m_exception_string; + 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 @@ -686,21 +876,26 @@ struct DOCTEST_INTERFACE SubcaseSignature const char* m_file; int m_line; + bool operator==(const SubcaseSignature& other) const; bool operator<(const SubcaseSignature& other) const; }; struct DOCTEST_INTERFACE IContextScope { - IContextScope(); - virtual ~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; // stdout stream - std::cout by default - std::ostream* cerr; // stderr stream - std::cerr by default - String binary_name; // the test binary name + 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 @@ -717,9 +912,12 @@ struct ContextOptions //!OCLINT too many fields 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 @@ -728,6 +926,7 @@ struct ContextOptions //!OCLINT too many fields 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 !!! @@ -740,121 +939,189 @@ struct ContextOptions //!OCLINT too many fields }; namespace detail { -#if defined(DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING) || defined(DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS) - template - struct enable_if - {}; + namespace types { +#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS + using namespace std; +#else + template + struct enable_if { }; - template - struct enable_if - { typedef TYPE type; }; -#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING) || DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS + template + struct enable_if { using type = T; }; - // clang-format off - template struct remove_reference { typedef T type; }; - template struct remove_reference { typedef T type; }; - template struct remove_reference { typedef T type; }; + struct true_type { static DOCTEST_CONSTEXPR bool value = true; }; + struct false_type { static DOCTEST_CONSTEXPR bool value = false; }; - template struct remove_const { typedef T type; }; - template struct remove_const { typedef T type; }; - // clang-format on + 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 - struct deferred_false - // cppcheck-suppress unusedStructMember - { static const bool value = false; }; - - namespace has_insertion_operator_impl { - std::ostream &os(); - template - DOCTEST_REF_WRAP(T) val(); - - template - struct check { - static constexpr auto value = false; - }; + T&& declval(); - template - struct check(), void())> { - static constexpr auto value = true; - }; - } // namespace has_insertion_operator_impl + 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 - using has_insertion_operator = has_insertion_operator_impl::check; + template + struct has_insertion_operator(), declval()), void())> : types::true_type { }; - DOCTEST_INTERFACE void my_memcpy(void* dest, const void* src, unsigned num); + 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* getTlsOss(); // returns a thread-local ostringstream - DOCTEST_INTERFACE String getTlsOssResult(); + DOCTEST_INTERFACE std::ostream* tlssPush(); + DOCTEST_INTERFACE String tlssPop(); template - struct StringMakerBase - { + 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 StringMakerBase - { - template - static String convert(const DOCTEST_REF_WRAP(T) in) { - *getTlsOss() << in; - return getTlsOssResult(); - } - }; - - DOCTEST_INTERFACE String rawMemoryToString(const void* object, unsigned size); + template + struct filldata; template - String rawMemoryToString(const DOCTEST_REF_WRAP(T) object) { - return rawMemoryToString(&object, sizeof(object)); + 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 - const char* type_to_string() { - return "<>"; + 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::value> +struct StringMaker : public detail::StringMakerBase< + detail::has_insertion_operator::value || detail::types::is_pointer::value || detail::types::is_array::value> {}; -template -struct StringMaker -{ - template - static String convert(U* p) { - if(p) - return detail::rawMemoryToString(p); - return "NULL"; - } -}; - -template -struct StringMaker -{ - static String convert(R C::*p) { - if(p) - return detail::rawMemoryToString(p); - return "NULL"; - } -}; +#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(char* in); 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); @@ -862,34 +1129,95 @@ 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(int short in); -DOCTEST_INTERFACE String toString(int short unsigned in); -DOCTEST_INTERFACE String toString(int in); -DOCTEST_INTERFACE String toString(int unsigned in); -DOCTEST_INTERFACE String toString(int long in); -DOCTEST_INTERFACE String toString(int long unsigned in); -DOCTEST_INTERFACE String toString(int long long in); -DOCTEST_INTERFACE String toString(int long long unsigned in); -DOCTEST_INTERFACE String toString(std::nullptr_t 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))); +} -#if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0) -// see this issue on why this is needed: https://github.com/onqtam/doctest/issues/183 -DOCTEST_INTERFACE String toString(const std::string& in); -#endif // VS 2019 +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 + } + }; +} -class DOCTEST_INTERFACE Approx +struct DOCTEST_INTERFACE Approx { -public: - explicit Approx(double value); + Approx(double value); Approx operator()(double value) const; #ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS template explicit Approx(const T& value, - typename detail::enable_if::value>::type* = + typename detail::types::enable_if::value>::type* = static_cast(nullptr)) { - *this = Approx(static_cast(value)); + *this = static_cast(value); } #endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS @@ -897,7 +1225,7 @@ class DOCTEST_INTERFACE Approx #ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS template - typename detail::enable_if::value, Approx&>::type epsilon( + typename std::enable_if::value, Approx&>::type epsilon( const T& newEpsilon) { m_epsilon = static_cast(newEpsilon); return *this; @@ -908,7 +1236,7 @@ class DOCTEST_INTERFACE Approx #ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS template - typename detail::enable_if::value, Approx&>::type scale( + typename std::enable_if::value, Approx&>::type scale( const T& newScale) { m_scale = static_cast(newScale); return *this; @@ -929,30 +1257,27 @@ class DOCTEST_INTERFACE Approx DOCTEST_INTERFACE friend bool operator> (double lhs, const Approx & rhs); DOCTEST_INTERFACE friend bool operator> (const Approx & lhs, double rhs); - DOCTEST_INTERFACE friend String toString(const Approx& in); - #ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS #define DOCTEST_APPROX_PREFIX \ - template friend typename detail::enable_if::value, bool>::type + template friend typename std::enable_if::value, bool>::type - DOCTEST_APPROX_PREFIX operator==(const T& lhs, const Approx& rhs) { return operator==(double(lhs), rhs); } + 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 double(lhs) < rhs.m_value || lhs == rhs; } - DOCTEST_APPROX_PREFIX operator<=(const Approx& lhs, const T& rhs) { return lhs.m_value < double(rhs) || lhs == rhs; } - DOCTEST_APPROX_PREFIX operator>=(const T& lhs, const Approx& rhs) { return double(lhs) > rhs.m_value || lhs == rhs; } - DOCTEST_APPROX_PREFIX operator>=(const Approx& lhs, const T& rhs) { return lhs.m_value > double(rhs) || lhs == rhs; } - DOCTEST_APPROX_PREFIX operator< (const T& lhs, const Approx& rhs) { return double(lhs) < rhs.m_value && lhs != rhs; } - DOCTEST_APPROX_PREFIX operator< (const Approx& lhs, const T& rhs) { return lhs.m_value < double(rhs) && lhs != rhs; } - DOCTEST_APPROX_PREFIX operator> (const T& lhs, const Approx& rhs) { return double(lhs) > rhs.m_value && lhs != rhs; } - DOCTEST_APPROX_PREFIX operator> (const Approx& lhs, const T& rhs) { return lhs.m_value > double(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; } + 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 -private: double m_epsilon; double m_scale; double m_value; @@ -962,18 +1287,35 @@ DOCTEST_INTERFACE String toString(const Approx& in); DOCTEST_INTERFACE const ContextOptions* getContextOptions(); -#if !defined(DOCTEST_CONFIG_DISABLE) +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 { typedef T type; }; - template struct decay_array { typedef T* type; }; - template struct decay_array { typedef T* type; }; + 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 { enum { value = 1 }; }; - template<> struct not_char_pointer { enum { value = 0 }; }; - template<> struct not_char_pointer { enum { value = 0 }; }; + template struct not_char_pointer { static DOCTEST_CONSTEXPR value = 1; }; + template<> struct not_char_pointer { static DOCTEST_CONSTEXPR value = 0; }; + template<> struct not_char_pointer { static DOCTEST_CONSTEXPR value = 0; }; template struct can_use_op : public not_char_pointer::type> {}; #endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING @@ -996,21 +1338,42 @@ namespace detail { 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 toString(lhs) + op + toString(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 Result operator op(const DOCTEST_REF_WRAP(R) rhs) { \ - bool res = op_macro(lhs, rhs); \ + 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) \ @@ -1029,11 +1392,12 @@ namespace detail { return *this; \ } - struct DOCTEST_INTERFACE Result + 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 @@ -1090,7 +1454,7 @@ namespace detail { #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 enable_if::value || can_use_op::value, bool>::type +#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); } @@ -1137,20 +1501,28 @@ namespace detail { L lhs; assertType::Enum m_at; - explicit Expression_lhs(L in, assertType::Enum at) - : lhs(in) + explicit Expression_lhs(L&& in, assertType::Enum at) + : lhs(static_cast(in)) , m_at(at) {} DOCTEST_NOINLINE operator Result() { - bool res = !!lhs; - if(m_at & assertType::is_false) //!OCLINT bitwise operator in conditional +// 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 Result(res, toString(lhs)); - return Result(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 @@ -1191,6 +1563,10 @@ namespace detail { #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; @@ -1202,20 +1578,27 @@ namespace detail { // https://github.com/catchorg/Catch2/issues/870 // https://github.com/catchorg/Catch2/issues/565 template - Expression_lhs operator<<(const DOCTEST_REF_WRAP(L) operand) { - return Expression_lhs(operand, m_at); + 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; - const char* m_description; - bool m_skip; - bool m_may_fail; - bool m_should_fail; - int m_expected_failures; - double m_timeout; + 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); @@ -1226,25 +1609,28 @@ namespace detail { } }; - typedef void (*funcType)(); + using funcType = void (*)(); struct DOCTEST_INTERFACE TestCase : public TestCaseData { funcType m_test; // a function pointer to the test case - const char* m_type; // for templated test cases - gets appended to the real name + 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 char* type = "", int template_id = -1); + 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 @@ -1254,6 +1640,8 @@ namespace detail { } bool operator<(const TestCase& other) const; + + ~TestCase() = default; }; // forward declarations of functions used by the macros @@ -1283,37 +1671,46 @@ namespace detail { 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, eq) - DOCTEST_BINARY_RELATIONAL_OP(1, ne) - DOCTEST_BINARY_RELATIONAL_OP(2, gt) - DOCTEST_BINARY_RELATIONAL_OP(3, lt) - DOCTEST_BINARY_RELATIONAL_OP(4, ge) - DOCTEST_BINARY_RELATIONAL_OP(5, le) + 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 char* exception_string = ""); + 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 void binary_assert(const DOCTEST_REF_WRAP(L) lhs, + 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) + if (m_failed || getContextOptions()->success) { m_decomp = stringifyBinaryExpr(lhs, ", ", rhs); + } + return !m_failed; } template - DOCTEST_NOINLINE void unary_assert(const DOCTEST_REF_WRAP(L) val) { + 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 + 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)); + } - if(m_failed || getContextOptions()->success) - m_decomp = toString(val); + return !m_failed; } void translateException(); @@ -1333,8 +1730,8 @@ namespace detail { DOCTEST_INTERFACE void failed_out_of_a_testing_context(const AssertData& ad); - DOCTEST_INTERFACE void decomp_assert(assertType::Enum at, const char* file, int line, - const char* expr, Result result); + 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 { \ @@ -1349,7 +1746,7 @@ namespace detail { if(checkIfShouldThrow(at)) \ throwException(); \ } \ - return; \ + return !failed; \ } \ } while(false) @@ -1364,7 +1761,7 @@ namespace detail { throwException() template - DOCTEST_NOINLINE void binary_assert(assertType::Enum at, const char* file, int line, + 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); @@ -1375,10 +1772,11 @@ namespace detail { // ################################################################################### DOCTEST_ASSERT_OUT_OF_TESTS(stringifyBinaryExpr(lhs, ", ", rhs)); DOCTEST_ASSERT_IN_TESTS(stringifyBinaryExpr(lhs, ", ", rhs)); + return !failed; } template - DOCTEST_NOINLINE void unary_assert(assertType::Enum at, const char* file, int line, + 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; @@ -1389,14 +1787,14 @@ namespace detail { // 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(toString(val)); - DOCTEST_ASSERT_IN_TESTS(toString(val)); + DOCTEST_ASSERT_OUT_OF_TESTS((DOCTEST_STRINGIFY(val))); + DOCTEST_ASSERT_IN_TESTS((DOCTEST_STRINGIFY(val))); + return !failed; } struct DOCTEST_INTERFACE IExceptionTranslator { - IExceptionTranslator(); - virtual ~IExceptionTranslator(); + DOCTEST_DECLARE_INTERFACE(IExceptionTranslator) virtual bool translate(String&) const = 0; }; @@ -1412,12 +1810,12 @@ namespace detail { try { throw; // lgtm [cpp/rethrow-no-exception] // cppcheck-suppress catchExceptionByValue - } catch(T ex) { // NOLINT + } catch(const T& ex) { res = m_translateFunction(ex); //!OCLINT parameter reassignment return true; - } catch(...) {} //!OCLINT - empty catch statement -#endif // DOCTEST_CONFIG_NO_EXCEPTIONS - ((void)res); // to silence -Wunused-parameter + } catch(...) {} //!OCLINT - empty catch statement +#endif // DOCTEST_CONFIG_NO_EXCEPTIONS + static_cast(res); // to silence -Wunused-parameter return false; } @@ -1427,99 +1825,86 @@ namespace detail { DOCTEST_INTERFACE void registerExceptionTranslatorImpl(const IExceptionTranslator* et); - template - struct StringStreamBase - { - template - static void convert(std::ostream* s, const T& in) { - *s << toString(in); - } - - // always treat char* as a string in this context - no matter - // if DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING is defined - static void convert(std::ostream* s, const char* in) { *s << String(in); } - }; - - template <> - struct StringStreamBase - { - template - static void convert(std::ostream* s, const T& in) { - *s << in; - } - }; + // 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; - template - struct StringStream : public StringStreamBase::value> - {}; + ContextScopeBase& operator=(const ContextScopeBase&) = delete; + ContextScopeBase& operator=(ContextScopeBase&&) = delete; - template - void toStream(std::ostream* s, const T& value) { - StringStream::convert(s, value); - } + ~ContextScopeBase() override = default; -#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING - DOCTEST_INTERFACE void toStream(std::ostream* s, char* in); - DOCTEST_INTERFACE void toStream(std::ostream* s, const char* in); -#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING - DOCTEST_INTERFACE void toStream(std::ostream* s, bool in); - DOCTEST_INTERFACE void toStream(std::ostream* s, float in); - DOCTEST_INTERFACE void toStream(std::ostream* s, double in); - DOCTEST_INTERFACE void toStream(std::ostream* s, double long in); - - DOCTEST_INTERFACE void toStream(std::ostream* s, char in); - DOCTEST_INTERFACE void toStream(std::ostream* s, char signed in); - DOCTEST_INTERFACE void toStream(std::ostream* s, char unsigned in); - DOCTEST_INTERFACE void toStream(std::ostream* s, int short in); - DOCTEST_INTERFACE void toStream(std::ostream* s, int short unsigned in); - DOCTEST_INTERFACE void toStream(std::ostream* s, int in); - DOCTEST_INTERFACE void toStream(std::ostream* s, int unsigned in); - DOCTEST_INTERFACE void toStream(std::ostream* s, int long in); - DOCTEST_INTERFACE void toStream(std::ostream* s, int long unsigned in); - DOCTEST_INTERFACE void toStream(std::ostream* s, int long long in); - DOCTEST_INTERFACE void toStream(std::ostream* s, int long long unsigned in); - - // ContextScope base class used to allow implementing methods of ContextScope - // that don't depend on the template parameter in doctest.cpp. - class DOCTEST_INTERFACE ContextScopeBase : public IContextScope { protected: ContextScopeBase(); + ContextScopeBase(ContextScopeBase&& other) noexcept; void destroy(); + bool need_to_destroy{true}; }; template class ContextScope : public ContextScopeBase { - const L &lambda_; + L lambda_; public: explicit ContextScope(const L &lambda) : lambda_(lambda) {} + explicit ContextScope(L&& lambda) : lambda_(static_cast(lambda)) { } - ContextScope(ContextScope &&other) : lambda_(other.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 { destroy(); } + ~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() = delete; + + 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) { - toStream(m_stream, in); + 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); @@ -1539,6 +1924,8 @@ namespace detail { 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); @@ -1570,7 +1957,7 @@ int registerExceptionTranslator(String (*)(T)) { #endif // DOCTEST_CONFIG_DISABLE namespace detail { - typedef void (*assert_handler)(const AssertData&); + using assert_handler = void (*)(const AssertData&); struct ContextState; } // namespace detail @@ -1583,12 +1970,19 @@ class DOCTEST_INTERFACE Context public: explicit Context(int argc = 0, const char* const* argv = nullptr); - ~Context(); + 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); @@ -1598,6 +1992,8 @@ class DOCTEST_INTERFACE Context void setAssertHandler(detail::assert_handler ah); + void setCout(std::ostream* out); + int run(); }; @@ -1624,6 +2020,7 @@ struct DOCTEST_INTERFACE CurrentTestCaseStats int numAssertsFailedCurrentTest; double seconds; int failure_flags; // use TestCaseFailureReason::Enum + bool testCaseSuccess; }; struct DOCTEST_INTERFACE TestCaseException @@ -1687,8 +2084,7 @@ struct DOCTEST_INTERFACE IReporter // 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 will not be managing the lifetimes of reporters given to it but this would still be nice to have - virtual ~IReporter(); + 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(); @@ -1700,7 +2096,7 @@ struct DOCTEST_INTERFACE IReporter }; namespace detail { - typedef IReporter* (*reporterCreatorFunc)(const ContextOptions&); + using reporterCreatorFunc = IReporter* (*)(const ContextOptions&); DOCTEST_INTERFACE void registerReporterImpl(const char* name, int prio, reporterCreatorFunc c, bool isReporter); @@ -1717,14 +2113,30 @@ int registerReporter(const char* name, int priority, bool isReporter) { } } // 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 -#if !defined(DOCTEST_CONFIG_DISABLE) +#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_AND_REACT(b) \ - if(b.log()) \ - DOCTEST_BREAK_INTO_DEBUGGER(); \ - b.react() +#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; @@ -1732,7 +2144,7 @@ int registerReporter(const char* name, int priority, bool isReporter) { #define DOCTEST_WRAP_IN_TRY(x) \ try { \ x; \ - } catch(...) { _DOCTEST_RB.translateException(); } + } catch(...) { DOCTEST_RB.translateException(); } #endif // DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS #ifdef DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS @@ -1746,27 +2158,26 @@ int registerReporter(const char* name, int priority, bool isReporter) { // 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_)) = \ + 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); \ - DOCTEST_GLOBAL_NO_WARNINGS_END() + decorators)) #define DOCTEST_IMPLEMENT_FIXTURE(der, base, func, decorators) \ - namespace { \ + namespace { /* NOLINT */ \ struct der : public base \ { \ void f(); \ }; \ - static void func() { \ + static DOCTEST_INLINE_NOINLINE void func() { \ der v; \ v.f(); \ } \ DOCTEST_REGISTER_FUNCTION(DOCTEST_EMPTY, func, decorators) \ } \ - inline DOCTEST_NOINLINE void der::f() + DOCTEST_INLINE_NOINLINE void der::f() // NOLINT(misc-definitions-in-headers) #define DOCTEST_CREATE_AND_REGISTER_FUNCTION(f, decorators) \ static void f(); \ @@ -1775,18 +2186,18 @@ int registerReporter(const char* name, int priority, bool isReporter) { #define DOCTEST_CREATE_AND_REGISTER_FUNCTION_IN_CLASS(f, proxy, decorators) \ static doctest::detail::funcType proxy() { return f; } \ - DOCTEST_REGISTER_FUNCTION(inline const, proxy(), decorators) \ + 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) + DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), decorators) // for registering tests in classes - requires C++17 for inline variables! -#if __cplusplus >= 201703L || (DOCTEST_MSVC >= DOCTEST_COMPILER(19, 12, 0) && _MSVC_LANG >= 201703L) +#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_), \ + 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(...) \ @@ -1795,26 +2206,25 @@ int registerReporter(const char* name, int priority, bool isReporter) { // 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) + 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_IMPL(...) \ - template <> \ - inline const char* type_to_string<__VA_ARGS__>() { \ - return "<" #__VA_ARGS__ ">"; \ - } -#define DOCTEST_TYPE_TO_STRING(...) \ - namespace doctest { namespace detail { \ - DOCTEST_TYPE_TO_STRING_IMPL(__VA_ARGS__) \ +#define DOCTEST_TYPE_TO_STRING_AS(str, ...) \ + namespace doctest { \ + template <> \ + inline String toString<__VA_ARGS__>() { \ + return str; \ } \ } \ - typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) + 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 { \ + namespace { /* NOLINT */ \ template \ struct iter; \ template \ @@ -1823,7 +2233,7 @@ int registerReporter(const char* name, int priority, bool isReporter) { iter(const char* file, unsigned line, int index) { \ doctest::detail::regTest(doctest::detail::TestCase(func, file, line, \ doctest_detail_test_suite_ns::getCurrentTestSuite(), \ - doctest::detail::type_to_string(), \ + doctest::toString(), \ int(line) * 1000 + index) \ * dec); \ iter>(file, line, index + 1); \ @@ -1840,20 +2250,20 @@ int registerReporter(const char* name, int priority, bool isReporter) { #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_)) + DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_)) #define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, anon, ...) \ - DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_CAT(anon, DUMMY)) = \ - doctest::detail::instantiationHelper(DOCTEST_CAT(id, ITERATOR)<__VA_ARGS__>(__FILE__, __LINE__, 0));\ - DOCTEST_GLOBAL_NO_WARNINGS_END() + 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__>) \ - typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) + 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__) \ - typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) + 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); \ @@ -1862,23 +2272,25 @@ int registerReporter(const char* name, int priority, bool isReporter) { static void anon() #define DOCTEST_TEST_CASE_TEMPLATE(dec, T, ...) \ - DOCTEST_TEST_CASE_TEMPLATE_IMPL(dec, T, DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_), __VA_ARGS__) + 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 = \ + 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() { \ + static DOCTEST_NOINLINE doctest::detail::TestSuite& getCurrentTestSuite() noexcept { \ DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4640) \ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wexit-time-destructors") \ - static doctest::detail::TestSuite data; \ + 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; \ @@ -1890,79 +2302,79 @@ int registerReporter(const char* name, int priority, bool isReporter) { namespace ns_name #define DOCTEST_TEST_SUITE(decorators) \ - DOCTEST_TEST_SUITE_IMPL(decorators, DOCTEST_ANONYMOUS(_DOCTEST_ANON_SUITE_)) + 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_)) = \ - doctest::detail::setTestSuite(doctest::detail::TestSuite() * decorators); \ - DOCTEST_GLOBAL_NO_WARNINGS_END() \ - typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) + 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_)) = \ - doctest::detail::setTestSuite(doctest::detail::TestSuite() * ""); \ - DOCTEST_GLOBAL_NO_WARNINGS_END() \ - typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) + 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_)) = \ - doctest::registerExceptionTranslator(translatorName); \ - DOCTEST_GLOBAL_NO_WARNINGS_END() \ + 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_), \ + 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_)) = \ - doctest::registerReporter(name, priority, true); \ - DOCTEST_GLOBAL_NO_WARNINGS_END() typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) + 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_)) = \ - doctest::registerReporter(name, priority, false); \ - DOCTEST_GLOBAL_NO_WARNINGS_END() typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) - -// for logging -#define DOCTEST_INFO(expression) \ - DOCTEST_INFO_IMPL(DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_), DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_), \ - DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_), expression) - -#define DOCTEST_INFO_IMPL(lambda_name, mb_name, s_name, expression) \ - DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4626) \ - auto lambda_name = [&](std::ostream* s_name) { \ + 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 << expression; \ - }; \ - DOCTEST_MSVC_SUPPRESS_WARNING_POP \ - auto DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_) = doctest::detail::MakeContextScope(lambda_name) + mb_name * __VA_ARGS__; \ + }) -#define DOCTEST_CAPTURE(x) DOCTEST_INFO(#x " := " << x) +#define DOCTEST_CAPTURE(x) DOCTEST_INFO(#x " := ", x) -#define DOCTEST_ADD_AT_IMPL(type, file, line, mb, x) \ - do { \ +#define DOCTEST_ADD_AT_IMPL(type, file, line, mb, ...) \ + DOCTEST_FUNC_SCOPE_BEGIN { \ doctest::detail::MessageBuilder mb(file, line, doctest::assertType::type); \ - mb << x; \ - DOCTEST_ASSERT_LOG_AND_REACT(mb); \ - } while(false) + 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, x) DOCTEST_ADD_AT_IMPL(is_warn, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), x) -#define DOCTEST_ADD_FAIL_CHECK_AT(file, line, x) DOCTEST_ADD_AT_IMPL(is_check, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), x) -#define DOCTEST_ADD_FAIL_AT(file, line, x) DOCTEST_ADD_AT_IMPL(is_require, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), x) +#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(x) DOCTEST_ADD_MESSAGE_AT(__FILE__, __LINE__, x) -#define DOCTEST_FAIL_CHECK(x) DOCTEST_ADD_FAIL_CHECK_AT(__FILE__, __LINE__, x) -#define DOCTEST_FAIL(x) DOCTEST_ADD_FAIL_AT(__FILE__, __LINE__, x) +#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. @@ -1970,18 +2382,37 @@ int registerReporter(const char* name, int priority, bool isReporter) { #define DOCTEST_ASSERT_IMPLEMENT_2(assert_type, ...) \ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Woverloaded-shift-op-parentheses") \ - doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ + /* 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_WRAP_IN_TRY(DOCTEST_RB.setResult( \ doctest::detail::ExpressionDecomposer(doctest::assertType::assert_type) \ - << __VA_ARGS__)) \ - DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB) \ + << __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, ...) \ - do { \ + DOCTEST_FUNC_SCOPE_BEGIN { \ DOCTEST_ASSERT_IMPLEMENT_2(assert_type, __VA_ARGS__); \ - } while(false) + } 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 @@ -1995,6 +2426,14 @@ int registerReporter(const char* name, int priority, bool isReporter) { 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__) @@ -2005,51 +2444,83 @@ int registerReporter(const char* name, int priority, bool isReporter) { #define DOCTEST_REQUIRE_FALSE(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_REQUIRE_FALSE, __VA_ARGS__) // clang-format off -#define DOCTEST_WARN_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN, cond); } while(false) -#define DOCTEST_CHECK_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK, cond); } while(false) -#define DOCTEST_REQUIRE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE, cond); } while(false) -#define DOCTEST_WARN_FALSE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN_FALSE, cond); } while(false) -#define DOCTEST_CHECK_FALSE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK_FALSE, cond); } while(false) -#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE_FALSE, cond); } while(false) +#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, ...) \ - do { \ + DOCTEST_FUNC_SCOPE_BEGIN { \ if(!doctest::getContextOptions()->no_throw) { \ - doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ + doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ __LINE__, #expr, #__VA_ARGS__, message); \ try { \ DOCTEST_CAST_TO_VOID(expr) \ - } catch(const doctest::detail::remove_const< \ - doctest::detail::remove_reference<__VA_ARGS__>::type>::type&) { \ - _DOCTEST_RB.translateException(); \ - _DOCTEST_RB.m_threw_as = true; \ - } catch(...) { _DOCTEST_RB.translateException(); } \ - DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ + } 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); \ } \ - } while(false) + } DOCTEST_FUNC_SCOPE_END #define DOCTEST_ASSERT_THROWS_WITH(expr, expr_str, assert_type, ...) \ - do { \ + DOCTEST_FUNC_SCOPE_BEGIN { \ if(!doctest::getContextOptions()->no_throw) { \ - doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ + 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_AND_REACT(_DOCTEST_RB); \ + } catch(...) { DOCTEST_RB.translateException(); } \ + DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \ + } else { /* NOLINT(*-else-after-return) */ \ + DOCTEST_FUNC_SCOPE_RET(false); \ } \ - } while(false) + } DOCTEST_FUNC_SCOPE_END #define DOCTEST_ASSERT_NOTHROW(assert_type, ...) \ - do { \ - doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ + 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_AND_REACT(_DOCTEST_RB); \ - } while(false) + } 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, "") @@ -2072,166 +2543,23 @@ int registerReporter(const char* name, int priority, bool isReporter) { #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, msg) do { DOCTEST_INFO(msg); DOCTEST_WARN_THROWS(expr); } while(false) -#define DOCTEST_CHECK_THROWS_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_CHECK_THROWS(expr); } while(false) -#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_REQUIRE_THROWS(expr); } while(false) -#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_WARN_THROWS_AS(expr, ex); } while(false) -#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_CHECK_THROWS_AS(expr, ex); } while(false) -#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_REQUIRE_THROWS_AS(expr, ex); } while(false) -#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, msg) do { DOCTEST_INFO(msg); DOCTEST_WARN_THROWS_WITH(expr, with); } while(false) -#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, msg) do { DOCTEST_INFO(msg); DOCTEST_CHECK_THROWS_WITH(expr, with); } while(false) -#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, msg) do { DOCTEST_INFO(msg); DOCTEST_REQUIRE_THROWS_WITH(expr, with); } while(false) -#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_WARN_THROWS_WITH_AS(expr, with, ex); } while(false) -#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ex); } while(false) -#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ex); } while(false) -#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_WARN_NOTHROW(expr); } while(false) -#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_CHECK_NOTHROW(expr); } while(false) -#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_REQUIRE_NOTHROW(expr); } while(false) +#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 -#ifndef DOCTEST_CONFIG_SUPER_FAST_ASSERTS - -#define DOCTEST_BINARY_ASSERT(assert_type, comp, ...) \ - do { \ - 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_AND_REACT(_DOCTEST_RB); \ - } while(false) - -#define DOCTEST_UNARY_ASSERT(assert_type, ...) \ - do { \ - 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_AND_REACT(_DOCTEST_RB); \ - } while(false) - -#else // DOCTEST_CONFIG_SUPER_FAST_ASSERTS - -#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_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__) - -#ifdef DOCTEST_CONFIG_NO_EXCEPTIONS - -#undef DOCTEST_WARN_THROWS -#undef DOCTEST_CHECK_THROWS -#undef DOCTEST_REQUIRE_THROWS -#undef DOCTEST_WARN_THROWS_AS -#undef DOCTEST_CHECK_THROWS_AS -#undef DOCTEST_REQUIRE_THROWS_AS -#undef DOCTEST_WARN_THROWS_WITH -#undef DOCTEST_CHECK_THROWS_WITH -#undef DOCTEST_REQUIRE_THROWS_WITH -#undef DOCTEST_WARN_THROWS_WITH_AS -#undef DOCTEST_CHECK_THROWS_WITH_AS -#undef DOCTEST_REQUIRE_THROWS_WITH_AS -#undef DOCTEST_WARN_NOTHROW -#undef DOCTEST_CHECK_NOTHROW -#undef DOCTEST_REQUIRE_NOTHROW - -#undef DOCTEST_WARN_THROWS_MESSAGE -#undef DOCTEST_CHECK_THROWS_MESSAGE -#undef DOCTEST_REQUIRE_THROWS_MESSAGE -#undef DOCTEST_WARN_THROWS_AS_MESSAGE -#undef DOCTEST_CHECK_THROWS_AS_MESSAGE -#undef DOCTEST_REQUIRE_THROWS_AS_MESSAGE -#undef DOCTEST_WARN_THROWS_WITH_MESSAGE -#undef DOCTEST_CHECK_THROWS_WITH_MESSAGE -#undef DOCTEST_REQUIRE_THROWS_WITH_MESSAGE -#undef DOCTEST_WARN_THROWS_WITH_AS_MESSAGE -#undef DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE -#undef DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE -#undef DOCTEST_WARN_NOTHROW_MESSAGE -#undef DOCTEST_CHECK_NOTHROW_MESSAGE -#undef DOCTEST_REQUIRE_NOTHROW_MESSAGE - -#ifdef DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS - -#define DOCTEST_WARN_THROWS(...) ((void)0) -#define DOCTEST_CHECK_THROWS(...) ((void)0) -#define DOCTEST_REQUIRE_THROWS(...) ((void)0) -#define DOCTEST_WARN_THROWS_AS(expr, ...) ((void)0) -#define DOCTEST_CHECK_THROWS_AS(expr, ...) ((void)0) -#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) ((void)0) -#define DOCTEST_WARN_THROWS_WITH(expr, ...) ((void)0) -#define DOCTEST_CHECK_THROWS_WITH(expr, ...) ((void)0) -#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) ((void)0) -#define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ...) ((void)0) -#define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ...) ((void)0) -#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ...) ((void)0) -#define DOCTEST_WARN_NOTHROW(...) ((void)0) -#define DOCTEST_CHECK_NOTHROW(...) ((void)0) -#define DOCTEST_REQUIRE_NOTHROW(...) ((void)0) - -#define DOCTEST_WARN_THROWS_MESSAGE(expr, msg) ((void)0) -#define DOCTEST_CHECK_THROWS_MESSAGE(expr, msg) ((void)0) -#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, msg) ((void)0) -#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0) -#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0) -#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0) -#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, msg) ((void)0) -#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, msg) ((void)0) -#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, msg) ((void)0) -#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, msg) ((void)0) -#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, msg) ((void)0) -#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, msg) ((void)0) -#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, msg) ((void)0) -#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, msg) ((void)0) -#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, msg) ((void)0) - -#else // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS - -#undef DOCTEST_REQUIRE -#undef DOCTEST_REQUIRE_FALSE -#undef DOCTEST_REQUIRE_MESSAGE -#undef DOCTEST_REQUIRE_FALSE_MESSAGE -#undef DOCTEST_REQUIRE_EQ -#undef DOCTEST_REQUIRE_NE -#undef DOCTEST_REQUIRE_GT -#undef DOCTEST_REQUIRE_LT -#undef DOCTEST_REQUIRE_GE -#undef DOCTEST_REQUIRE_LE -#undef DOCTEST_REQUIRE_UNARY -#undef DOCTEST_REQUIRE_UNARY_FALSE - -#endif // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS - #endif // DOCTEST_CONFIG_NO_EXCEPTIONS // ================================================================================================= @@ -2241,7 +2569,7 @@ int registerReporter(const char* name, int priority, bool isReporter) { #else // DOCTEST_CONFIG_DISABLE #define DOCTEST_IMPLEMENT_FIXTURE(der, base, func, name) \ - namespace { \ + namespace /* NOLINT */ { \ template \ struct der : public base \ { void f(); }; \ @@ -2255,138 +2583,308 @@ int registerReporter(const char* name, int priority, bool isReporter) { // for registering tests #define DOCTEST_TEST_CASE(name) \ - DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), 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) + 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) + 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(...) typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) -#define DOCTEST_TYPE_TO_STRING_IMPL(...) +#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 \ - inline void DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_)() + inline void DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_)() #define DOCTEST_TEST_CASE_TEMPLATE_DEFINE(name, type, id) \ template \ - inline void DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_)() - -#define DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id, ...) \ - typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) + inline void DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_)() -#define DOCTEST_TEST_CASE_TEMPLATE_APPLY(id, ...) \ - typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) +#define DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id, ...) static_assert(true, "") +#define DOCTEST_TEST_CASE_TEMPLATE_APPLY(id, ...) static_assert(true, "") // for subcases #define DOCTEST_SUBCASE(name) // for a testsuite block -#define DOCTEST_TEST_SUITE(name) namespace +#define DOCTEST_TEST_SUITE(name) namespace // NOLINT // for starting a testsuite block -#define DOCTEST_TEST_SUITE_BEGIN(name) typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) +#define DOCTEST_TEST_SUITE_BEGIN(name) static_assert(true, "") // for ending a testsuite block -#define DOCTEST_TEST_SUITE_END typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) +#define DOCTEST_TEST_SUITE_END using DOCTEST_ANONYMOUS(DOCTEST_ANON_FOR_SEMICOLON_) = int #define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature) \ template \ - static inline doctest::String DOCTEST_ANONYMOUS(_DOCTEST_ANON_TRANSLATOR_)(signature) + static inline doctest::String DOCTEST_ANONYMOUS(DOCTEST_ANON_TRANSLATOR_)(signature) #define DOCTEST_REGISTER_REPORTER(name, priority, reporter) #define DOCTEST_REGISTER_LISTENER(name, priority, reporter) -#define DOCTEST_INFO(x) ((void)0) -#define DOCTEST_CAPTURE(x) ((void)0) -#define DOCTEST_ADD_MESSAGE_AT(file, line, x) ((void)0) -#define DOCTEST_ADD_FAIL_CHECK_AT(file, line, x) ((void)0) -#define DOCTEST_ADD_FAIL_AT(file, line, x) ((void)0) -#define DOCTEST_MESSAGE(x) ((void)0) -#define DOCTEST_FAIL_CHECK(x) ((void)0) -#define DOCTEST_FAIL(x) ((void)0) - -#define DOCTEST_WARN(...) ((void)0) -#define DOCTEST_CHECK(...) ((void)0) -#define DOCTEST_REQUIRE(...) ((void)0) -#define DOCTEST_WARN_FALSE(...) ((void)0) -#define DOCTEST_CHECK_FALSE(...) ((void)0) -#define DOCTEST_REQUIRE_FALSE(...) ((void)0) - -#define DOCTEST_WARN_MESSAGE(cond, msg) ((void)0) -#define DOCTEST_CHECK_MESSAGE(cond, msg) ((void)0) -#define DOCTEST_REQUIRE_MESSAGE(cond, msg) ((void)0) -#define DOCTEST_WARN_FALSE_MESSAGE(cond, msg) ((void)0) -#define DOCTEST_CHECK_FALSE_MESSAGE(cond, msg) ((void)0) -#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, msg) ((void)0) - -#define DOCTEST_WARN_THROWS(...) ((void)0) -#define DOCTEST_CHECK_THROWS(...) ((void)0) -#define DOCTEST_REQUIRE_THROWS(...) ((void)0) -#define DOCTEST_WARN_THROWS_AS(expr, ...) ((void)0) -#define DOCTEST_CHECK_THROWS_AS(expr, ...) ((void)0) -#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) ((void)0) -#define DOCTEST_WARN_THROWS_WITH(expr, ...) ((void)0) -#define DOCTEST_CHECK_THROWS_WITH(expr, ...) ((void)0) -#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) ((void)0) -#define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ...) ((void)0) -#define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ...) ((void)0) -#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ...) ((void)0) -#define DOCTEST_WARN_NOTHROW(...) ((void)0) -#define DOCTEST_CHECK_NOTHROW(...) ((void)0) -#define DOCTEST_REQUIRE_NOTHROW(...) ((void)0) - -#define DOCTEST_WARN_THROWS_MESSAGE(expr, msg) ((void)0) -#define DOCTEST_CHECK_THROWS_MESSAGE(expr, msg) ((void)0) -#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, msg) ((void)0) -#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0) -#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0) -#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0) -#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, msg) ((void)0) -#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, msg) ((void)0) -#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, msg) ((void)0) -#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, msg) ((void)0) -#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, msg) ((void)0) -#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, msg) ((void)0) -#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, msg) ((void)0) -#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, msg) ((void)0) -#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, msg) ((void)0) - -#define DOCTEST_WARN_EQ(...) ((void)0) -#define DOCTEST_CHECK_EQ(...) ((void)0) -#define DOCTEST_REQUIRE_EQ(...) ((void)0) -#define DOCTEST_WARN_NE(...) ((void)0) -#define DOCTEST_CHECK_NE(...) ((void)0) -#define DOCTEST_REQUIRE_NE(...) ((void)0) -#define DOCTEST_WARN_GT(...) ((void)0) -#define DOCTEST_CHECK_GT(...) ((void)0) -#define DOCTEST_REQUIRE_GT(...) ((void)0) -#define DOCTEST_WARN_LT(...) ((void)0) -#define DOCTEST_CHECK_LT(...) ((void)0) -#define DOCTEST_REQUIRE_LT(...) ((void)0) -#define DOCTEST_WARN_GE(...) ((void)0) -#define DOCTEST_CHECK_GE(...) ((void)0) -#define DOCTEST_REQUIRE_GE(...) ((void)0) -#define DOCTEST_WARN_LE(...) ((void)0) -#define DOCTEST_CHECK_LE(...) ((void)0) -#define DOCTEST_REQUIRE_LE(...) ((void)0) - -#define DOCTEST_WARN_UNARY(...) ((void)0) -#define DOCTEST_CHECK_UNARY(...) ((void)0) -#define DOCTEST_REQUIRE_UNARY(...) ((void)0) -#define DOCTEST_WARN_UNARY_FALSE(...) ((void)0) -#define DOCTEST_CHECK_UNARY_FALSE(...) ((void)0) -#define DOCTEST_REQUIRE_UNARY_FALSE(...) ((void)0) +#define DOCTEST_INFO(...) (static_cast(0)) +#define DOCTEST_CAPTURE(x) (static_cast(0)) +#define DOCTEST_ADD_MESSAGE_AT(file, line, ...) (static_cast(0)) +#define DOCTEST_ADD_FAIL_CHECK_AT(file, line, ...) (static_cast(0)) +#define DOCTEST_ADD_FAIL_AT(file, line, ...) (static_cast(0)) +#define DOCTEST_MESSAGE(...) (static_cast(0)) +#define DOCTEST_FAIL_CHECK(...) (static_cast(0)) +#define DOCTEST_FAIL(...) (static_cast(0)) + +#if defined(DOCTEST_CONFIG_EVALUATE_ASSERTS_EVEN_WHEN_DISABLED) \ + && defined(DOCTEST_CONFIG_ASSERTS_RETURN_VALUES) + +#define DOCTEST_WARN(...) [&] { return __VA_ARGS__; }() +#define DOCTEST_CHECK(...) [&] { return __VA_ARGS__; }() +#define DOCTEST_REQUIRE(...) [&] { return __VA_ARGS__; }() +#define DOCTEST_WARN_FALSE(...) [&] { return !(__VA_ARGS__); }() +#define DOCTEST_CHECK_FALSE(...) [&] { return !(__VA_ARGS__); }() +#define DOCTEST_REQUIRE_FALSE(...) [&] { return !(__VA_ARGS__); }() + +#define DOCTEST_WARN_MESSAGE(cond, ...) [&] { return cond; }() +#define DOCTEST_CHECK_MESSAGE(cond, ...) [&] { return cond; }() +#define DOCTEST_REQUIRE_MESSAGE(cond, ...) [&] { return cond; }() +#define DOCTEST_WARN_FALSE_MESSAGE(cond, ...) [&] { return !(cond); }() +#define DOCTEST_CHECK_FALSE_MESSAGE(cond, ...) [&] { return !(cond); }() +#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, ...) [&] { return !(cond); }() + +namespace doctest { +namespace detail { +#define DOCTEST_RELATIONAL_OP(name, op) \ + template \ + bool 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, >=) +} // namespace detail +} // namespace doctest + +#define DOCTEST_WARN_EQ(...) [&] { return doctest::detail::eq(__VA_ARGS__); }() +#define DOCTEST_CHECK_EQ(...) [&] { return doctest::detail::eq(__VA_ARGS__); }() +#define DOCTEST_REQUIRE_EQ(...) [&] { return doctest::detail::eq(__VA_ARGS__); }() +#define DOCTEST_WARN_NE(...) [&] { return doctest::detail::ne(__VA_ARGS__); }() +#define DOCTEST_CHECK_NE(...) [&] { return doctest::detail::ne(__VA_ARGS__); }() +#define DOCTEST_REQUIRE_NE(...) [&] { return doctest::detail::ne(__VA_ARGS__); }() +#define DOCTEST_WARN_LT(...) [&] { return doctest::detail::lt(__VA_ARGS__); }() +#define DOCTEST_CHECK_LT(...) [&] { return doctest::detail::lt(__VA_ARGS__); }() +#define DOCTEST_REQUIRE_LT(...) [&] { return doctest::detail::lt(__VA_ARGS__); }() +#define DOCTEST_WARN_GT(...) [&] { return doctest::detail::gt(__VA_ARGS__); }() +#define DOCTEST_CHECK_GT(...) [&] { return doctest::detail::gt(__VA_ARGS__); }() +#define DOCTEST_REQUIRE_GT(...) [&] { return doctest::detail::gt(__VA_ARGS__); }() +#define DOCTEST_WARN_LE(...) [&] { return doctest::detail::le(__VA_ARGS__); }() +#define DOCTEST_CHECK_LE(...) [&] { return doctest::detail::le(__VA_ARGS__); }() +#define DOCTEST_REQUIRE_LE(...) [&] { return doctest::detail::le(__VA_ARGS__); }() +#define DOCTEST_WARN_GE(...) [&] { return doctest::detail::ge(__VA_ARGS__); }() +#define DOCTEST_CHECK_GE(...) [&] { return doctest::detail::ge(__VA_ARGS__); }() +#define DOCTEST_REQUIRE_GE(...) [&] { return doctest::detail::ge(__VA_ARGS__); }() +#define DOCTEST_WARN_UNARY(...) [&] { return __VA_ARGS__; }() +#define DOCTEST_CHECK_UNARY(...) [&] { return __VA_ARGS__; }() +#define DOCTEST_REQUIRE_UNARY(...) [&] { return __VA_ARGS__; }() +#define DOCTEST_WARN_UNARY_FALSE(...) [&] { return !(__VA_ARGS__); }() +#define DOCTEST_CHECK_UNARY_FALSE(...) [&] { return !(__VA_ARGS__); }() +#define DOCTEST_REQUIRE_UNARY_FALSE(...) [&] { return !(__VA_ARGS__); }() + +#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS + +#define DOCTEST_WARN_THROWS_WITH(expr, with, ...) [] { static_assert(false, "Exception translation is not available when doctest is disabled."); return false; }() +#define DOCTEST_CHECK_THROWS_WITH(expr, with, ...) DOCTEST_WARN_THROWS_WITH(,,) +#define DOCTEST_REQUIRE_THROWS_WITH(expr, with, ...) DOCTEST_WARN_THROWS_WITH(,,) +#define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH(,,) +#define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH(,,) +#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH(,,) + +#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_WARN_THROWS_WITH(,,) +#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_WARN_THROWS_WITH(,,) +#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_WARN_THROWS_WITH(,,) +#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH(,,) +#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH(,,) +#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH(,,) + +#define DOCTEST_WARN_THROWS(...) [&] { try { __VA_ARGS__; return false; } catch (...) { return true; } }() +#define DOCTEST_CHECK_THROWS(...) [&] { try { __VA_ARGS__; return false; } catch (...) { return true; } }() +#define DOCTEST_REQUIRE_THROWS(...) [&] { try { __VA_ARGS__; return false; } catch (...) { return true; } }() +#define DOCTEST_WARN_THROWS_AS(expr, ...) [&] { try { expr; } catch (__VA_ARGS__) { return true; } catch (...) { } return false; }() +#define DOCTEST_CHECK_THROWS_AS(expr, ...) [&] { try { expr; } catch (__VA_ARGS__) { return true; } catch (...) { } return false; }() +#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) [&] { try { expr; } catch (__VA_ARGS__) { return true; } catch (...) { } return false; }() +#define DOCTEST_WARN_NOTHROW(...) [&] { try { __VA_ARGS__; return true; } catch (...) { return false; } }() +#define DOCTEST_CHECK_NOTHROW(...) [&] { try { __VA_ARGS__; return true; } catch (...) { return false; } }() +#define DOCTEST_REQUIRE_NOTHROW(...) [&] { try { __VA_ARGS__; return true; } catch (...) { return false; } }() + +#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) [&] { try { __VA_ARGS__; return false; } catch (...) { return true; } }() +#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) [&] { try { __VA_ARGS__; return false; } catch (...) { return true; } }() +#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) [&] { try { __VA_ARGS__; return false; } catch (...) { return true; } }() +#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) [&] { try { expr; } catch (__VA_ARGS__) { return true; } catch (...) { } return false; }() +#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) [&] { try { expr; } catch (__VA_ARGS__) { return true; } catch (...) { } return false; }() +#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) [&] { try { expr; } catch (__VA_ARGS__) { return true; } catch (...) { } return false; }() +#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) [&] { try { __VA_ARGS__; return true; } catch (...) { return false; } }() +#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) [&] { try { __VA_ARGS__; return true; } catch (...) { return false; } }() +#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) [&] { try { __VA_ARGS__; return true; } catch (...) { return false; } }() + +#endif // DOCTEST_CONFIG_NO_EXCEPTIONS + +#else // DOCTEST_CONFIG_EVALUATE_ASSERTS_EVEN_WHEN_DISABLED + +#define DOCTEST_WARN(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_WARN_FALSE(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_FALSE(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_FALSE(...) DOCTEST_FUNC_EMPTY + +#define DOCTEST_WARN_MESSAGE(cond, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_MESSAGE(cond, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_MESSAGE(cond, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_WARN_FALSE_MESSAGE(cond, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_FALSE_MESSAGE(cond, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, ...) DOCTEST_FUNC_EMPTY + +#define DOCTEST_WARN_EQ(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_EQ(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_EQ(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_WARN_NE(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_NE(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_NE(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_WARN_GT(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_GT(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_GT(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_WARN_LT(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_LT(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_LT(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_WARN_GE(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_GE(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_GE(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_WARN_LE(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_LE(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_LE(...) DOCTEST_FUNC_EMPTY + +#define DOCTEST_WARN_UNARY(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_UNARY(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_UNARY(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_WARN_UNARY_FALSE(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_UNARY_FALSE(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_UNARY_FALSE(...) DOCTEST_FUNC_EMPTY + +#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS + +#define DOCTEST_WARN_THROWS(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_THROWS(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_THROWS(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_WARN_THROWS_AS(expr, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_THROWS_AS(expr, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_WARN_THROWS_WITH(expr, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_THROWS_WITH(expr, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_WARN_NOTHROW(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_NOTHROW(...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_NOTHROW(...) DOCTEST_FUNC_EMPTY + +#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) DOCTEST_FUNC_EMPTY +#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) DOCTEST_FUNC_EMPTY + +#endif // DOCTEST_CONFIG_NO_EXCEPTIONS + +#endif // DOCTEST_CONFIG_EVALUATE_ASSERTS_EVEN_WHEN_DISABLED #endif // DOCTEST_CONFIG_DISABLE +#ifdef DOCTEST_CONFIG_NO_EXCEPTIONS + +#ifdef DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS +#define DOCTEST_EXCEPTION_EMPTY_FUNC DOCTEST_FUNC_EMPTY +#else // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS +#define DOCTEST_EXCEPTION_EMPTY_FUNC [] { static_assert(false, "Exceptions are disabled! " \ + "Use DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS if you want to compile with exceptions disabled."); return false; }() + +#undef DOCTEST_REQUIRE +#undef DOCTEST_REQUIRE_FALSE +#undef DOCTEST_REQUIRE_MESSAGE +#undef DOCTEST_REQUIRE_FALSE_MESSAGE +#undef DOCTEST_REQUIRE_EQ +#undef DOCTEST_REQUIRE_NE +#undef DOCTEST_REQUIRE_GT +#undef DOCTEST_REQUIRE_LT +#undef DOCTEST_REQUIRE_GE +#undef DOCTEST_REQUIRE_LE +#undef DOCTEST_REQUIRE_UNARY +#undef DOCTEST_REQUIRE_UNARY_FALSE + +#define DOCTEST_REQUIRE DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_FALSE DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_MESSAGE DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_FALSE_MESSAGE DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_EQ DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_NE DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_GT DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_LT DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_GE DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_LE DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_UNARY DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_UNARY_FALSE DOCTEST_EXCEPTION_EMPTY_FUNC + +#endif // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS + +#define DOCTEST_WARN_THROWS(...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_CHECK_THROWS(...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_THROWS(...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_WARN_THROWS_AS(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_CHECK_THROWS_AS(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_WARN_THROWS_WITH(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_CHECK_THROWS_WITH(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_WARN_NOTHROW(...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_CHECK_NOTHROW(...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_NOTHROW(...) DOCTEST_EXCEPTION_EMPTY_FUNC + +#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC +#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) DOCTEST_EXCEPTION_EMPTY_FUNC + +#endif // DOCTEST_CONFIG_NO_EXCEPTIONS + // clang-format off // KEPT FOR BACKWARDS COMPATIBILITY - FORWARDING TO THE RIGHT MACROS #define DOCTEST_FAST_WARN_EQ DOCTEST_WARN_EQ @@ -2415,7 +2913,7 @@ int registerReporter(const char* name, int priority, bool isReporter) { #define DOCTEST_FAST_CHECK_UNARY_FALSE DOCTEST_CHECK_UNARY_FALSE #define DOCTEST_FAST_REQUIRE_UNARY_FALSE DOCTEST_REQUIRE_UNARY_FALSE -#define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE DOCTEST_TEST_CASE_TEMPLATE_INVOKE +#define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE(id, ...) DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id,__VA_ARGS__) // clang-format on // BDD style macros @@ -2433,176 +2931,157 @@ int registerReporter(const char* name, int priority, bool isReporter) { // clang-format on // == SHORT VERSIONS OF THE MACROS -#if !defined(DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES) - -#define TEST_CASE DOCTEST_TEST_CASE -#define TEST_CASE_CLASS DOCTEST_TEST_CASE_CLASS -#define TEST_CASE_FIXTURE DOCTEST_TEST_CASE_FIXTURE -#define TYPE_TO_STRING DOCTEST_TYPE_TO_STRING -#define TEST_CASE_TEMPLATE DOCTEST_TEST_CASE_TEMPLATE -#define TEST_CASE_TEMPLATE_DEFINE DOCTEST_TEST_CASE_TEMPLATE_DEFINE -#define TEST_CASE_TEMPLATE_INVOKE DOCTEST_TEST_CASE_TEMPLATE_INVOKE -#define TEST_CASE_TEMPLATE_APPLY DOCTEST_TEST_CASE_TEMPLATE_APPLY -#define SUBCASE DOCTEST_SUBCASE -#define TEST_SUITE DOCTEST_TEST_SUITE -#define TEST_SUITE_BEGIN DOCTEST_TEST_SUITE_BEGIN +#ifndef DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES + +#define TEST_CASE(name) DOCTEST_TEST_CASE(name) +#define TEST_CASE_CLASS(name) DOCTEST_TEST_CASE_CLASS(name) +#define TEST_CASE_FIXTURE(x, name) DOCTEST_TEST_CASE_FIXTURE(x, name) +#define TYPE_TO_STRING_AS(str, ...) DOCTEST_TYPE_TO_STRING_AS(str, __VA_ARGS__) +#define TYPE_TO_STRING(...) DOCTEST_TYPE_TO_STRING(__VA_ARGS__) +#define TEST_CASE_TEMPLATE(name, T, ...) DOCTEST_TEST_CASE_TEMPLATE(name, T, __VA_ARGS__) +#define TEST_CASE_TEMPLATE_DEFINE(name, T, id) DOCTEST_TEST_CASE_TEMPLATE_DEFINE(name, T, id) +#define TEST_CASE_TEMPLATE_INVOKE(id, ...) DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id, __VA_ARGS__) +#define TEST_CASE_TEMPLATE_APPLY(id, ...) DOCTEST_TEST_CASE_TEMPLATE_APPLY(id, __VA_ARGS__) +#define SUBCASE(name) DOCTEST_SUBCASE(name) +#define TEST_SUITE(decorators) DOCTEST_TEST_SUITE(decorators) +#define TEST_SUITE_BEGIN(name) DOCTEST_TEST_SUITE_BEGIN(name) #define TEST_SUITE_END DOCTEST_TEST_SUITE_END -#define REGISTER_EXCEPTION_TRANSLATOR DOCTEST_REGISTER_EXCEPTION_TRANSLATOR -#define REGISTER_REPORTER DOCTEST_REGISTER_REPORTER -#define REGISTER_LISTENER DOCTEST_REGISTER_LISTENER -#define INFO DOCTEST_INFO -#define CAPTURE DOCTEST_CAPTURE -#define ADD_MESSAGE_AT DOCTEST_ADD_MESSAGE_AT -#define ADD_FAIL_CHECK_AT DOCTEST_ADD_FAIL_CHECK_AT -#define ADD_FAIL_AT DOCTEST_ADD_FAIL_AT -#define MESSAGE DOCTEST_MESSAGE -#define FAIL_CHECK DOCTEST_FAIL_CHECK -#define FAIL DOCTEST_FAIL -#define TO_LVALUE DOCTEST_TO_LVALUE - -#define WARN DOCTEST_WARN -#define WARN_FALSE DOCTEST_WARN_FALSE -#define WARN_THROWS DOCTEST_WARN_THROWS -#define WARN_THROWS_AS DOCTEST_WARN_THROWS_AS -#define WARN_THROWS_WITH DOCTEST_WARN_THROWS_WITH -#define WARN_THROWS_WITH_AS DOCTEST_WARN_THROWS_WITH_AS -#define WARN_NOTHROW DOCTEST_WARN_NOTHROW -#define CHECK DOCTEST_CHECK -#define CHECK_FALSE DOCTEST_CHECK_FALSE -#define CHECK_THROWS DOCTEST_CHECK_THROWS -#define CHECK_THROWS_AS DOCTEST_CHECK_THROWS_AS -#define CHECK_THROWS_WITH DOCTEST_CHECK_THROWS_WITH -#define CHECK_THROWS_WITH_AS DOCTEST_CHECK_THROWS_WITH_AS -#define CHECK_NOTHROW DOCTEST_CHECK_NOTHROW -#define REQUIRE DOCTEST_REQUIRE -#define REQUIRE_FALSE DOCTEST_REQUIRE_FALSE -#define REQUIRE_THROWS DOCTEST_REQUIRE_THROWS -#define REQUIRE_THROWS_AS DOCTEST_REQUIRE_THROWS_AS -#define REQUIRE_THROWS_WITH DOCTEST_REQUIRE_THROWS_WITH -#define REQUIRE_THROWS_WITH_AS DOCTEST_REQUIRE_THROWS_WITH_AS -#define REQUIRE_NOTHROW DOCTEST_REQUIRE_NOTHROW - -#define WARN_MESSAGE DOCTEST_WARN_MESSAGE -#define WARN_FALSE_MESSAGE DOCTEST_WARN_FALSE_MESSAGE -#define WARN_THROWS_MESSAGE DOCTEST_WARN_THROWS_MESSAGE -#define WARN_THROWS_AS_MESSAGE DOCTEST_WARN_THROWS_AS_MESSAGE -#define WARN_THROWS_WITH_MESSAGE DOCTEST_WARN_THROWS_WITH_MESSAGE -#define WARN_THROWS_WITH_AS_MESSAGE DOCTEST_WARN_THROWS_WITH_AS_MESSAGE -#define WARN_NOTHROW_MESSAGE DOCTEST_WARN_NOTHROW_MESSAGE -#define CHECK_MESSAGE DOCTEST_CHECK_MESSAGE -#define CHECK_FALSE_MESSAGE DOCTEST_CHECK_FALSE_MESSAGE -#define CHECK_THROWS_MESSAGE DOCTEST_CHECK_THROWS_MESSAGE -#define CHECK_THROWS_AS_MESSAGE DOCTEST_CHECK_THROWS_AS_MESSAGE -#define CHECK_THROWS_WITH_MESSAGE DOCTEST_CHECK_THROWS_WITH_MESSAGE -#define CHECK_THROWS_WITH_AS_MESSAGE DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE -#define CHECK_NOTHROW_MESSAGE DOCTEST_CHECK_NOTHROW_MESSAGE -#define REQUIRE_MESSAGE DOCTEST_REQUIRE_MESSAGE -#define REQUIRE_FALSE_MESSAGE DOCTEST_REQUIRE_FALSE_MESSAGE -#define REQUIRE_THROWS_MESSAGE DOCTEST_REQUIRE_THROWS_MESSAGE -#define REQUIRE_THROWS_AS_MESSAGE DOCTEST_REQUIRE_THROWS_AS_MESSAGE -#define REQUIRE_THROWS_WITH_MESSAGE DOCTEST_REQUIRE_THROWS_WITH_MESSAGE -#define REQUIRE_THROWS_WITH_AS_MESSAGE DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE -#define REQUIRE_NOTHROW_MESSAGE DOCTEST_REQUIRE_NOTHROW_MESSAGE - -#define SCENARIO DOCTEST_SCENARIO -#define SCENARIO_CLASS DOCTEST_SCENARIO_CLASS -#define SCENARIO_TEMPLATE DOCTEST_SCENARIO_TEMPLATE -#define SCENARIO_TEMPLATE_DEFINE DOCTEST_SCENARIO_TEMPLATE_DEFINE -#define GIVEN DOCTEST_GIVEN -#define WHEN DOCTEST_WHEN -#define AND_WHEN DOCTEST_AND_WHEN -#define THEN DOCTEST_THEN -#define AND_THEN DOCTEST_AND_THEN - -#define WARN_EQ DOCTEST_WARN_EQ -#define CHECK_EQ DOCTEST_CHECK_EQ -#define REQUIRE_EQ DOCTEST_REQUIRE_EQ -#define WARN_NE DOCTEST_WARN_NE -#define CHECK_NE DOCTEST_CHECK_NE -#define REQUIRE_NE DOCTEST_REQUIRE_NE -#define WARN_GT DOCTEST_WARN_GT -#define CHECK_GT DOCTEST_CHECK_GT -#define REQUIRE_GT DOCTEST_REQUIRE_GT -#define WARN_LT DOCTEST_WARN_LT -#define CHECK_LT DOCTEST_CHECK_LT -#define REQUIRE_LT DOCTEST_REQUIRE_LT -#define WARN_GE DOCTEST_WARN_GE -#define CHECK_GE DOCTEST_CHECK_GE -#define REQUIRE_GE DOCTEST_REQUIRE_GE -#define WARN_LE DOCTEST_WARN_LE -#define CHECK_LE DOCTEST_CHECK_LE -#define REQUIRE_LE DOCTEST_REQUIRE_LE -#define WARN_UNARY DOCTEST_WARN_UNARY -#define CHECK_UNARY DOCTEST_CHECK_UNARY -#define REQUIRE_UNARY DOCTEST_REQUIRE_UNARY -#define WARN_UNARY_FALSE DOCTEST_WARN_UNARY_FALSE -#define CHECK_UNARY_FALSE DOCTEST_CHECK_UNARY_FALSE -#define REQUIRE_UNARY_FALSE DOCTEST_REQUIRE_UNARY_FALSE +#define REGISTER_EXCEPTION_TRANSLATOR(signature) DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature) +#define REGISTER_REPORTER(name, priority, reporter) DOCTEST_REGISTER_REPORTER(name, priority, reporter) +#define REGISTER_LISTENER(name, priority, reporter) DOCTEST_REGISTER_LISTENER(name, priority, reporter) +#define INFO(...) DOCTEST_INFO(__VA_ARGS__) +#define CAPTURE(x) DOCTEST_CAPTURE(x) +#define ADD_MESSAGE_AT(file, line, ...) DOCTEST_ADD_MESSAGE_AT(file, line, __VA_ARGS__) +#define ADD_FAIL_CHECK_AT(file, line, ...) DOCTEST_ADD_FAIL_CHECK_AT(file, line, __VA_ARGS__) +#define ADD_FAIL_AT(file, line, ...) DOCTEST_ADD_FAIL_AT(file, line, __VA_ARGS__) +#define MESSAGE(...) DOCTEST_MESSAGE(__VA_ARGS__) +#define FAIL_CHECK(...) DOCTEST_FAIL_CHECK(__VA_ARGS__) +#define FAIL(...) DOCTEST_FAIL(__VA_ARGS__) +#define TO_LVALUE(...) DOCTEST_TO_LVALUE(__VA_ARGS__) + +#define WARN(...) DOCTEST_WARN(__VA_ARGS__) +#define WARN_FALSE(...) DOCTEST_WARN_FALSE(__VA_ARGS__) +#define WARN_THROWS(...) DOCTEST_WARN_THROWS(__VA_ARGS__) +#define WARN_THROWS_AS(expr, ...) DOCTEST_WARN_THROWS_AS(expr, __VA_ARGS__) +#define WARN_THROWS_WITH(expr, ...) DOCTEST_WARN_THROWS_WITH(expr, __VA_ARGS__) +#define WARN_THROWS_WITH_AS(expr, with, ...) DOCTEST_WARN_THROWS_WITH_AS(expr, with, __VA_ARGS__) +#define WARN_NOTHROW(...) DOCTEST_WARN_NOTHROW(__VA_ARGS__) +#define CHECK(...) DOCTEST_CHECK(__VA_ARGS__) +#define CHECK_FALSE(...) DOCTEST_CHECK_FALSE(__VA_ARGS__) +#define CHECK_THROWS(...) DOCTEST_CHECK_THROWS(__VA_ARGS__) +#define CHECK_THROWS_AS(expr, ...) DOCTEST_CHECK_THROWS_AS(expr, __VA_ARGS__) +#define CHECK_THROWS_WITH(expr, ...) DOCTEST_CHECK_THROWS_WITH(expr, __VA_ARGS__) +#define CHECK_THROWS_WITH_AS(expr, with, ...) DOCTEST_CHECK_THROWS_WITH_AS(expr, with, __VA_ARGS__) +#define CHECK_NOTHROW(...) DOCTEST_CHECK_NOTHROW(__VA_ARGS__) +#define REQUIRE(...) DOCTEST_REQUIRE(__VA_ARGS__) +#define REQUIRE_FALSE(...) DOCTEST_REQUIRE_FALSE(__VA_ARGS__) +#define REQUIRE_THROWS(...) DOCTEST_REQUIRE_THROWS(__VA_ARGS__) +#define REQUIRE_THROWS_AS(expr, ...) DOCTEST_REQUIRE_THROWS_AS(expr, __VA_ARGS__) +#define REQUIRE_THROWS_WITH(expr, ...) DOCTEST_REQUIRE_THROWS_WITH(expr, __VA_ARGS__) +#define REQUIRE_THROWS_WITH_AS(expr, with, ...) DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, __VA_ARGS__) +#define REQUIRE_NOTHROW(...) DOCTEST_REQUIRE_NOTHROW(__VA_ARGS__) + +#define WARN_MESSAGE(cond, ...) DOCTEST_WARN_MESSAGE(cond, __VA_ARGS__) +#define WARN_FALSE_MESSAGE(cond, ...) DOCTEST_WARN_FALSE_MESSAGE(cond, __VA_ARGS__) +#define WARN_THROWS_MESSAGE(expr, ...) DOCTEST_WARN_THROWS_MESSAGE(expr, __VA_ARGS__) +#define WARN_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, __VA_ARGS__) +#define WARN_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, __VA_ARGS__) +#define WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, __VA_ARGS__) +#define WARN_NOTHROW_MESSAGE(expr, ...) DOCTEST_WARN_NOTHROW_MESSAGE(expr, __VA_ARGS__) +#define CHECK_MESSAGE(cond, ...) DOCTEST_CHECK_MESSAGE(cond, __VA_ARGS__) +#define CHECK_FALSE_MESSAGE(cond, ...) DOCTEST_CHECK_FALSE_MESSAGE(cond, __VA_ARGS__) +#define CHECK_THROWS_MESSAGE(expr, ...) DOCTEST_CHECK_THROWS_MESSAGE(expr, __VA_ARGS__) +#define CHECK_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, __VA_ARGS__) +#define CHECK_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, __VA_ARGS__) +#define CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, __VA_ARGS__) +#define CHECK_NOTHROW_MESSAGE(expr, ...) DOCTEST_CHECK_NOTHROW_MESSAGE(expr, __VA_ARGS__) +#define REQUIRE_MESSAGE(cond, ...) DOCTEST_REQUIRE_MESSAGE(cond, __VA_ARGS__) +#define REQUIRE_FALSE_MESSAGE(cond, ...) DOCTEST_REQUIRE_FALSE_MESSAGE(cond, __VA_ARGS__) +#define REQUIRE_THROWS_MESSAGE(expr, ...) DOCTEST_REQUIRE_THROWS_MESSAGE(expr, __VA_ARGS__) +#define REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, __VA_ARGS__) +#define REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, __VA_ARGS__) +#define REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, __VA_ARGS__) +#define REQUIRE_NOTHROW_MESSAGE(expr, ...) DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, __VA_ARGS__) + +#define SCENARIO(name) DOCTEST_SCENARIO(name) +#define SCENARIO_CLASS(name) DOCTEST_SCENARIO_CLASS(name) +#define SCENARIO_TEMPLATE(name, T, ...) DOCTEST_SCENARIO_TEMPLATE(name, T, __VA_ARGS__) +#define SCENARIO_TEMPLATE_DEFINE(name, T, id) DOCTEST_SCENARIO_TEMPLATE_DEFINE(name, T, id) +#define GIVEN(name) DOCTEST_GIVEN(name) +#define WHEN(name) DOCTEST_WHEN(name) +#define AND_WHEN(name) DOCTEST_AND_WHEN(name) +#define THEN(name) DOCTEST_THEN(name) +#define AND_THEN(name) DOCTEST_AND_THEN(name) + +#define WARN_EQ(...) DOCTEST_WARN_EQ(__VA_ARGS__) +#define CHECK_EQ(...) DOCTEST_CHECK_EQ(__VA_ARGS__) +#define REQUIRE_EQ(...) DOCTEST_REQUIRE_EQ(__VA_ARGS__) +#define WARN_NE(...) DOCTEST_WARN_NE(__VA_ARGS__) +#define CHECK_NE(...) DOCTEST_CHECK_NE(__VA_ARGS__) +#define REQUIRE_NE(...) DOCTEST_REQUIRE_NE(__VA_ARGS__) +#define WARN_GT(...) DOCTEST_WARN_GT(__VA_ARGS__) +#define CHECK_GT(...) DOCTEST_CHECK_GT(__VA_ARGS__) +#define REQUIRE_GT(...) DOCTEST_REQUIRE_GT(__VA_ARGS__) +#define WARN_LT(...) DOCTEST_WARN_LT(__VA_ARGS__) +#define CHECK_LT(...) DOCTEST_CHECK_LT(__VA_ARGS__) +#define REQUIRE_LT(...) DOCTEST_REQUIRE_LT(__VA_ARGS__) +#define WARN_GE(...) DOCTEST_WARN_GE(__VA_ARGS__) +#define CHECK_GE(...) DOCTEST_CHECK_GE(__VA_ARGS__) +#define REQUIRE_GE(...) DOCTEST_REQUIRE_GE(__VA_ARGS__) +#define WARN_LE(...) DOCTEST_WARN_LE(__VA_ARGS__) +#define CHECK_LE(...) DOCTEST_CHECK_LE(__VA_ARGS__) +#define REQUIRE_LE(...) DOCTEST_REQUIRE_LE(__VA_ARGS__) +#define WARN_UNARY(...) DOCTEST_WARN_UNARY(__VA_ARGS__) +#define CHECK_UNARY(...) DOCTEST_CHECK_UNARY(__VA_ARGS__) +#define REQUIRE_UNARY(...) DOCTEST_REQUIRE_UNARY(__VA_ARGS__) +#define WARN_UNARY_FALSE(...) DOCTEST_WARN_UNARY_FALSE(__VA_ARGS__) +#define CHECK_UNARY_FALSE(...) DOCTEST_CHECK_UNARY_FALSE(__VA_ARGS__) +#define REQUIRE_UNARY_FALSE(...) DOCTEST_REQUIRE_UNARY_FALSE(__VA_ARGS__) // KEPT FOR BACKWARDS COMPATIBILITY -#define FAST_WARN_EQ DOCTEST_FAST_WARN_EQ -#define FAST_CHECK_EQ DOCTEST_FAST_CHECK_EQ -#define FAST_REQUIRE_EQ DOCTEST_FAST_REQUIRE_EQ -#define FAST_WARN_NE DOCTEST_FAST_WARN_NE -#define FAST_CHECK_NE DOCTEST_FAST_CHECK_NE -#define FAST_REQUIRE_NE DOCTEST_FAST_REQUIRE_NE -#define FAST_WARN_GT DOCTEST_FAST_WARN_GT -#define FAST_CHECK_GT DOCTEST_FAST_CHECK_GT -#define FAST_REQUIRE_GT DOCTEST_FAST_REQUIRE_GT -#define FAST_WARN_LT DOCTEST_FAST_WARN_LT -#define FAST_CHECK_LT DOCTEST_FAST_CHECK_LT -#define FAST_REQUIRE_LT DOCTEST_FAST_REQUIRE_LT -#define FAST_WARN_GE DOCTEST_FAST_WARN_GE -#define FAST_CHECK_GE DOCTEST_FAST_CHECK_GE -#define FAST_REQUIRE_GE DOCTEST_FAST_REQUIRE_GE -#define FAST_WARN_LE DOCTEST_FAST_WARN_LE -#define FAST_CHECK_LE DOCTEST_FAST_CHECK_LE -#define FAST_REQUIRE_LE DOCTEST_FAST_REQUIRE_LE - -#define FAST_WARN_UNARY DOCTEST_FAST_WARN_UNARY -#define FAST_CHECK_UNARY DOCTEST_FAST_CHECK_UNARY -#define FAST_REQUIRE_UNARY DOCTEST_FAST_REQUIRE_UNARY -#define FAST_WARN_UNARY_FALSE DOCTEST_FAST_WARN_UNARY_FALSE -#define FAST_CHECK_UNARY_FALSE DOCTEST_FAST_CHECK_UNARY_FALSE -#define FAST_REQUIRE_UNARY_FALSE DOCTEST_FAST_REQUIRE_UNARY_FALSE - -#define TEST_CASE_TEMPLATE_INSTANTIATE DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE +#define FAST_WARN_EQ(...) DOCTEST_FAST_WARN_EQ(__VA_ARGS__) +#define FAST_CHECK_EQ(...) DOCTEST_FAST_CHECK_EQ(__VA_ARGS__) +#define FAST_REQUIRE_EQ(...) DOCTEST_FAST_REQUIRE_EQ(__VA_ARGS__) +#define FAST_WARN_NE(...) DOCTEST_FAST_WARN_NE(__VA_ARGS__) +#define FAST_CHECK_NE(...) DOCTEST_FAST_CHECK_NE(__VA_ARGS__) +#define FAST_REQUIRE_NE(...) DOCTEST_FAST_REQUIRE_NE(__VA_ARGS__) +#define FAST_WARN_GT(...) DOCTEST_FAST_WARN_GT(__VA_ARGS__) +#define FAST_CHECK_GT(...) DOCTEST_FAST_CHECK_GT(__VA_ARGS__) +#define FAST_REQUIRE_GT(...) DOCTEST_FAST_REQUIRE_GT(__VA_ARGS__) +#define FAST_WARN_LT(...) DOCTEST_FAST_WARN_LT(__VA_ARGS__) +#define FAST_CHECK_LT(...) DOCTEST_FAST_CHECK_LT(__VA_ARGS__) +#define FAST_REQUIRE_LT(...) DOCTEST_FAST_REQUIRE_LT(__VA_ARGS__) +#define FAST_WARN_GE(...) DOCTEST_FAST_WARN_GE(__VA_ARGS__) +#define FAST_CHECK_GE(...) DOCTEST_FAST_CHECK_GE(__VA_ARGS__) +#define FAST_REQUIRE_GE(...) DOCTEST_FAST_REQUIRE_GE(__VA_ARGS__) +#define FAST_WARN_LE(...) DOCTEST_FAST_WARN_LE(__VA_ARGS__) +#define FAST_CHECK_LE(...) DOCTEST_FAST_CHECK_LE(__VA_ARGS__) +#define FAST_REQUIRE_LE(...) DOCTEST_FAST_REQUIRE_LE(__VA_ARGS__) + +#define FAST_WARN_UNARY(...) DOCTEST_FAST_WARN_UNARY(__VA_ARGS__) +#define FAST_CHECK_UNARY(...) DOCTEST_FAST_CHECK_UNARY(__VA_ARGS__) +#define FAST_REQUIRE_UNARY(...) DOCTEST_FAST_REQUIRE_UNARY(__VA_ARGS__) +#define FAST_WARN_UNARY_FALSE(...) DOCTEST_FAST_WARN_UNARY_FALSE(__VA_ARGS__) +#define FAST_CHECK_UNARY_FALSE(...) DOCTEST_FAST_CHECK_UNARY_FALSE(__VA_ARGS__) +#define FAST_REQUIRE_UNARY_FALSE(...) DOCTEST_FAST_REQUIRE_UNARY_FALSE(__VA_ARGS__) + +#define TEST_CASE_TEMPLATE_INSTANTIATE(id, ...) DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE(id, __VA_ARGS__) #endif // DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES -#if !defined(DOCTEST_CONFIG_DISABLE) +#ifndef DOCTEST_CONFIG_DISABLE // this is here to clear the 'current test suite' for the current translation unit - at the top DOCTEST_TEST_SUITE_END(); -// add stringification for primitive/fundamental types -namespace doctest { namespace detail { - DOCTEST_TYPE_TO_STRING_IMPL(bool) - DOCTEST_TYPE_TO_STRING_IMPL(float) - DOCTEST_TYPE_TO_STRING_IMPL(double) - DOCTEST_TYPE_TO_STRING_IMPL(long double) - DOCTEST_TYPE_TO_STRING_IMPL(char) - DOCTEST_TYPE_TO_STRING_IMPL(signed char) - DOCTEST_TYPE_TO_STRING_IMPL(unsigned char) -#if !DOCTEST_MSVC || defined(_NATIVE_WCHAR_T_DEFINED) - DOCTEST_TYPE_TO_STRING_IMPL(wchar_t) -#endif // not MSVC or wchar_t support enabled - DOCTEST_TYPE_TO_STRING_IMPL(short int) - DOCTEST_TYPE_TO_STRING_IMPL(unsigned short int) - DOCTEST_TYPE_TO_STRING_IMPL(int) - DOCTEST_TYPE_TO_STRING_IMPL(unsigned int) - DOCTEST_TYPE_TO_STRING_IMPL(long int) - DOCTEST_TYPE_TO_STRING_IMPL(unsigned long int) - DOCTEST_TYPE_TO_STRING_IMPL(long long int) - DOCTEST_TYPE_TO_STRING_IMPL(unsigned long long int) -}} // namespace doctest::detail - #endif // DOCTEST_CONFIG_DISABLE DOCTEST_CLANG_SUPPRESS_WARNING_POP DOCTEST_MSVC_SUPPRESS_WARNING_POP DOCTEST_GCC_SUPPRESS_WARNING_POP +DOCTEST_SUPPRESS_COMMON_WARNINGS_POP + #endif // DOCTEST_LIBRARY_INCLUDED #ifndef DOCTEST_SINGLE_HEADER @@ -2622,13 +3101,11 @@ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wunused-macros") DOCTEST_CLANG_SUPPRESS_WARNING_POP +DOCTEST_SUPPRESS_COMMON_WARNINGS_PUSH + DOCTEST_CLANG_SUPPRESS_WARNING_PUSH -DOCTEST_CLANG_SUPPRESS_WARNING("-Wunknown-pragmas") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wpadded") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wweak-vtables") DOCTEST_CLANG_SUPPRESS_WARNING("-Wglobal-constructors") DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-prototypes") DOCTEST_CLANG_SUPPRESS_WARNING("-Wsign-conversion") DOCTEST_CLANG_SUPPRESS_WARNING("-Wshorten-64-to-32") DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-variable-declarations") @@ -2636,64 +3113,35 @@ DOCTEST_CLANG_SUPPRESS_WARNING("-Wswitch") DOCTEST_CLANG_SUPPRESS_WARNING("-Wswitch-enum") DOCTEST_CLANG_SUPPRESS_WARNING("-Wcovered-switch-default") DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-noreturn") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-local-typedef") DOCTEST_CLANG_SUPPRESS_WARNING("-Wdisabled-macro-expansion") DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-braces") DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-field-initializers") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat") -DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic") DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-member-function") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wnonportable-system-include-path") DOCTEST_GCC_SUPPRESS_WARNING_PUSH -DOCTEST_GCC_SUPPRESS_WARNING("-Wunknown-pragmas") -DOCTEST_GCC_SUPPRESS_WARNING("-Wpragmas") DOCTEST_GCC_SUPPRESS_WARNING("-Wconversion") -DOCTEST_GCC_SUPPRESS_WARNING("-Weffc++") DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-conversion") -DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-overflow") -DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-aliasing") DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-field-initializers") DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-braces") -DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-declarations") DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch") DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch-enum") DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch-default") DOCTEST_GCC_SUPPRESS_WARNING("-Wunsafe-loop-optimizations") DOCTEST_GCC_SUPPRESS_WARNING("-Wold-style-cast") -DOCTEST_GCC_SUPPRESS_WARNING("-Wunused-local-typedefs") -DOCTEST_GCC_SUPPRESS_WARNING("-Wuseless-cast") DOCTEST_GCC_SUPPRESS_WARNING("-Wunused-function") DOCTEST_GCC_SUPPRESS_WARNING("-Wmultiple-inheritance") -DOCTEST_GCC_SUPPRESS_WARNING("-Wnoexcept") DOCTEST_GCC_SUPPRESS_WARNING("-Wsuggest-attribute") DOCTEST_MSVC_SUPPRESS_WARNING_PUSH -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(4267) // 'var' : conversion from 'x' to 'y', possible loss of data -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(4530) // C++ exception handler used, but unwind semantics not enabled DOCTEST_MSVC_SUPPRESS_WARNING(4577) // 'noexcept' used with no exception handling mode specified DOCTEST_MSVC_SUPPRESS_WARNING(4774) // format string expected in argument is not a string literal DOCTEST_MSVC_SUPPRESS_WARNING(4365) // conversion from 'int' to 'unsigned', signed/unsigned mismatch -DOCTEST_MSVC_SUPPRESS_WARNING(4820) // padding in structs -DOCTEST_MSVC_SUPPRESS_WARNING(4640) // construction of local static object is not thread-safe DOCTEST_MSVC_SUPPRESS_WARNING(5039) // pointer to potentially throwing function passed to extern C -DOCTEST_MSVC_SUPPRESS_WARNING(5045) // Spectre mitigation stuff -DOCTEST_MSVC_SUPPRESS_WARNING(4626) // assignment operator was implicitly defined as deleted -DOCTEST_MSVC_SUPPRESS_WARNING(5027) // move assignment operator was implicitly defined as deleted -DOCTEST_MSVC_SUPPRESS_WARNING(5026) // move constructor was implicitly defined as deleted -DOCTEST_MSVC_SUPPRESS_WARNING(4625) // copy constructor was implicitly defined as deleted DOCTEST_MSVC_SUPPRESS_WARNING(4800) // forcing value to bool 'true' or 'false' (performance warning) -// static analysis -DOCTEST_MSVC_SUPPRESS_WARNING(26439) // This kind of 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 construction and dtor... -DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum' +DOCTEST_MSVC_SUPPRESS_WARNING(5245) // unreferenced function with internal linkage has been removed DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN @@ -2701,7 +3149,7 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN #include #include #include -// borland (Embarcadero) compiler requires math.h and not cmath - https://github.com/onqtam/doctest/pull/37 +// borland (Embarcadero) compiler requires math.h and not cmath - https://github.com/doctest/doctest/pull/37 #ifdef __BORLANDC__ #include #endif // __BORLANDC__ @@ -2713,22 +3161,33 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN #include #include #include +#ifndef DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM #include +#endif // DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM #include #include #include +#ifndef DOCTEST_CONFIG_NO_MULTITHREADING #include #include +#define DOCTEST_DECLARE_MUTEX(name) std::mutex name; +#define DOCTEST_DECLARE_STATIC_MUTEX(name) static DOCTEST_DECLARE_MUTEX(name) +#define DOCTEST_LOCK_MUTEX(name) std::lock_guard DOCTEST_ANONYMOUS(DOCTEST_ANON_LOCK_)(name); +#else // DOCTEST_CONFIG_NO_MULTITHREADING +#define DOCTEST_DECLARE_MUTEX(name) +#define DOCTEST_DECLARE_STATIC_MUTEX(name) +#define DOCTEST_LOCK_MUTEX(name) +#endif // DOCTEST_CONFIG_NO_MULTITHREADING #include #include +#include #include #include -#ifdef DOCTEST_CONFIG_POSIX_SIGNALS #include -#endif // DOCTEST_CONFIG_POSIX_SIGNALS #include #include #include +#include #ifdef DOCTEST_PLATFORM_MAC #include @@ -2741,20 +3200,18 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN // defines for a leaner windows.h #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN +#define DOCTEST_UNDEF_WIN32_LEAN_AND_MEAN #endif // WIN32_LEAN_AND_MEAN #ifndef NOMINMAX #define NOMINMAX +#define DOCTEST_UNDEF_NOMINMAX #endif // NOMINMAX // not sure what AfxWin.h is for - here I do what Catch does #ifdef __AFXDLL #include #else -#if defined(__MINGW32__) || defined(__MINGW64__) #include -#else // MINGW -#include -#endif // MINGW #endif #include @@ -2765,7 +3222,7 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN #endif // DOCTEST_PLATFORM_WINDOWS -// this is a fix for https://github.com/onqtam/doctest/issues/348 +// this is a fix for https://github.com/doctest/doctest/issues/348 // https://mail.gnome.org/archives/xml/2012-January/msg00000.html #if !defined(HAVE_UNISTD_H) && !defined(STDOUT_FILENO) #define STDOUT_FILENO fileno(stdout) @@ -2787,7 +3244,19 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END #endif #ifndef DOCTEST_THREAD_LOCAL +#if defined(DOCTEST_CONFIG_NO_MULTITHREADING) || DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0)) +#define DOCTEST_THREAD_LOCAL +#else // DOCTEST_MSVC #define DOCTEST_THREAD_LOCAL thread_local +#endif // DOCTEST_MSVC +#endif // DOCTEST_THREAD_LOCAL + +#ifndef DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES +#define DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES 32 +#endif + +#ifndef DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE +#define DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE 64 #endif #ifdef DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS @@ -2796,12 +3265,44 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END #define DOCTEST_OPTIONS_PREFIX_DISPLAY "" #endif +#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +#define DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS +#endif + +#ifndef DOCTEST_CDECL +#define DOCTEST_CDECL __cdecl +#endif + namespace doctest { bool is_running_in_test = false; namespace { using namespace detail; + + template + DOCTEST_NORETURN void throw_exception(Ex const& e) { +#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS + throw e; +#else // DOCTEST_CONFIG_NO_EXCEPTIONS +#ifdef DOCTEST_CONFIG_HANDLE_EXCEPTION + DOCTEST_CONFIG_HANDLE_EXCEPTION(e); +#else // DOCTEST_CONFIG_HANDLE_EXCEPTION +#ifndef DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM + std::cerr << "doctest will terminate because it needed to throw an exception.\n" + << "The message was: " << e.what() << '\n'; +#endif // DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM +#endif // DOCTEST_CONFIG_HANDLE_EXCEPTION + std::terminate(); +#endif // DOCTEST_CONFIG_NO_EXCEPTIONS + } + +#ifndef DOCTEST_INTERNAL_ERROR +#define DOCTEST_INTERNAL_ERROR(msg) \ + throw_exception(std::logic_error( \ + __FILE__ ":" DOCTEST_TOSTR(__LINE__) ": Internal doctest error: " msg)) +#endif // DOCTEST_INTERNAL_ERROR + // case insensitive strcmp int stricmp(const char* a, const char* b) { for(;; a++, b++) { @@ -2811,20 +3312,6 @@ namespace { } } - template - String fpToString(T value, int precision) { - std::ostringstream oss; - oss << std::setprecision(precision) << std::fixed << value; - std::string d = oss.str(); - size_t i = d.find_last_not_of('0'); - if(i != std::string::npos && i != d.size() - 1) { - if(d[i] == '.') - i++; - d = d.substr(0, i + 1); - } - return d.c_str(); - } - struct Endianness { enum Arch @@ -2845,58 +3332,56 @@ namespace { } // namespace namespace detail { - void my_memcpy(void* dest, const void* src, unsigned num) { memcpy(dest, src, num); } - - String rawMemoryToString(const void* object, unsigned size) { - // Reverse order for little endian architectures - int i = 0, end = static_cast(size), inc = 1; - if(Endianness::which() == Endianness::Little) { - i = end - 1; - end = inc = -1; - } - - unsigned const char* bytes = static_cast(object); - std::ostringstream oss; - oss << "0x" << std::setfill('0') << std::hex; - for(; i != end; i += inc) - oss << std::setw(2) << static_cast(bytes[i]); - return oss.str().c_str(); - } + DOCTEST_THREAD_LOCAL class + { + std::vector stack; + std::stringstream ss; + + public: + std::ostream* push() { + stack.push_back(ss.tellp()); + return &ss; + } - DOCTEST_THREAD_LOCAL std::ostringstream g_oss; // NOLINT(cert-err58-cpp) + String pop() { + if (stack.empty()) + DOCTEST_INTERNAL_ERROR("TLSS was empty when trying to pop!"); - std::ostream* getTlsOss() { - g_oss.clear(); // there shouldn't be anything worth clearing in the flags - g_oss.str(""); // the slow way of resetting a string stream - //g_oss.seekp(0); // optimal reset - as seen here: https://stackoverflow.com/a/624291/3162383 - return &g_oss; + std::streampos pos = stack.back(); + stack.pop_back(); + unsigned sz = static_cast(ss.tellp() - pos); + ss.rdbuf()->pubseekpos(pos, std::ios::in | std::ios::out); + return String(ss, sz); + } + } g_oss; + + std::ostream* tlssPush() { + return g_oss.push(); } - String getTlsOssResult() { - //g_oss << std::ends; // needed - as shown here: https://stackoverflow.com/a/624291/3162383 - return g_oss.str().c_str(); + String tlssPop() { + return g_oss.pop(); } #ifndef DOCTEST_CONFIG_DISABLE namespace timer_large_integer { - + #if defined(DOCTEST_PLATFORM_WINDOWS) - typedef ULONGLONG type; + using type = ULONGLONG; #else // DOCTEST_PLATFORM_WINDOWS - using namespace std; - typedef uint64_t type; + using type = std::uint64_t; #endif // DOCTEST_PLATFORM_WINDOWS } -typedef timer_large_integer::type ticks_t; +using ticks_t = timer_large_integer::type; #ifdef DOCTEST_CONFIG_GETCURRENTTICKS ticks_t getCurrentTicks() { return DOCTEST_CONFIG_GETCURRENTTICKS(); } #elif defined(DOCTEST_PLATFORM_WINDOWS) ticks_t getCurrentTicks() { - static LARGE_INTEGER hz = {0}, hzo = {0}; + static LARGE_INTEGER hz = { {0} }, hzo = { {0} }; if(!hz.QuadPart) { QueryPerformanceFrequency(&hz); QueryPerformanceCounter(&hzo); @@ -2928,18 +3413,110 @@ typedef timer_large_integer::type ticks_t; ticks_t m_ticks = 0; }; +#ifdef DOCTEST_CONFIG_NO_MULTITHREADING + template + using Atomic = T; +#else // DOCTEST_CONFIG_NO_MULTITHREADING + template + using Atomic = std::atomic; +#endif // DOCTEST_CONFIG_NO_MULTITHREADING + +#if defined(DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS) || defined(DOCTEST_CONFIG_NO_MULTITHREADING) + template + using MultiLaneAtomic = Atomic; +#else // DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS + // Provides a multilane implementation of an atomic variable that supports add, sub, load, + // store. Instead of using a single atomic variable, this splits up into multiple ones, + // each sitting on a separate cache line. The goal is to provide a speedup when most + // operations are modifying. It achieves this with two properties: + // + // * Multiple atomics are used, so chance of congestion from the same atomic is reduced. + // * Each atomic sits on a separate cache line, so false sharing is reduced. + // + // The disadvantage is that there is a small overhead due to the use of TLS, and load/store + // is slower because all atomics have to be accessed. + template + class MultiLaneAtomic + { + struct CacheLineAlignedAtomic + { + Atomic atomic{}; + char padding[DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE - sizeof(Atomic)]; + }; + CacheLineAlignedAtomic m_atomics[DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES]; + + static_assert(sizeof(CacheLineAlignedAtomic) == DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE, + "guarantee one atomic takes exactly one cache line"); + + public: + T operator++() DOCTEST_NOEXCEPT { return fetch_add(1) + 1; } + + T operator++(int) DOCTEST_NOEXCEPT { return fetch_add(1); } + + T fetch_add(T arg, std::memory_order order = std::memory_order_seq_cst) DOCTEST_NOEXCEPT { + return myAtomic().fetch_add(arg, order); + } + + T fetch_sub(T arg, std::memory_order order = std::memory_order_seq_cst) DOCTEST_NOEXCEPT { + return myAtomic().fetch_sub(arg, order); + } + + operator T() const DOCTEST_NOEXCEPT { return load(); } + + T load(std::memory_order order = std::memory_order_seq_cst) const DOCTEST_NOEXCEPT { + auto result = T(); + for(auto const& c : m_atomics) { + result += c.atomic.load(order); + } + return result; + } + + T operator=(T desired) DOCTEST_NOEXCEPT { // lgtm [cpp/assignment-does-not-return-this] + store(desired); + return desired; + } + + void store(T desired, std::memory_order order = std::memory_order_seq_cst) DOCTEST_NOEXCEPT { + // first value becomes desired", all others become 0. + for(auto& c : m_atomics) { + c.atomic.store(desired, order); + desired = {}; + } + } + + private: + // Each thread has a different atomic that it operates on. If more than NumLanes threads + // use this, some will use the same atomic. So performance will degrade a bit, but still + // everything will work. + // + // The logic here is a bit tricky. The call should be as fast as possible, so that there + // is minimal to no overhead in determining the correct atomic for the current thread. + // + // 1. A global static counter laneCounter counts continuously up. + // 2. Each successive thread will use modulo operation of that counter so it gets an atomic + // assigned in a round-robin fashion. + // 3. This tlsLaneIdx is stored in the thread local data, so it is directly available with + // little overhead. + Atomic& myAtomic() DOCTEST_NOEXCEPT { + static Atomic laneCounter; + DOCTEST_THREAD_LOCAL size_t tlsLaneIdx = + laneCounter++ % DOCTEST_MULTI_LANE_ATOMICS_THREAD_LANES; + + return m_atomics[tlsLaneIdx].atomic; + } + }; +#endif // DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS + // this holds both parameters from the command line and runtime data for tests struct ContextState : ContextOptions, TestRunStats, CurrentTestCaseStats { - std::atomic numAssertsCurrentTest_atomic; - std::atomic numAssertsFailedCurrentTest_atomic; + MultiLaneAtomic numAssertsCurrentTest_atomic; + MultiLaneAtomic numAssertsFailedCurrentTest_atomic; std::vector> filters = decltype(filters)(9); // 9 different filters std::vector reporters_currently_used; - const TestCase* currentTest = nullptr; - assert_handler ah = nullptr; Timer timer; @@ -2947,11 +3524,12 @@ typedef timer_large_integer::type ticks_t; std::vector stringifiedContexts; // logging from INFO() due to an exception // stuff for subcases - std::vector subcasesStack; - std::set subcasesPassed; - int subcasesCurrentMaxLevel; - bool should_reenter; - std::atomic shouldLogCurrentException; + bool reachedLeaf; + std::vector subcaseStack; + std::vector nextSubcaseStack; + std::unordered_set fullyTraversedSubcases; + size_t currentSubcaseDepth; + Atomic shouldLogCurrentException; void resetRunData() { numTestCases = 0; @@ -3001,7 +3579,8 @@ typedef timer_large_integer::type ticks_t; (TestCaseFailureReason::FailedExactlyNumTimes & failure_flags); // if any subcase has failed - the whole test case has failed - if(failure_flags && !ok_to_fail) + testCaseSuccess = !(failure_flags && !ok_to_fail); + if(!testCaseSuccess) numTestCasesFailed++; } }; @@ -3016,23 +3595,37 @@ typedef timer_large_integer::type ticks_t; #endif // DOCTEST_CONFIG_DISABLE } // namespace detail -void String::setOnHeap() { *reinterpret_cast(&buf[last]) = 128; } -void String::setLast(unsigned in) { buf[last] = char(in); } +char* String::allocate(size_type sz) { + if (sz <= last) { + buf[sz] = '\0'; + setLast(last - sz); + return buf; + } else { + setOnHeap(); + data.size = sz; + data.capacity = data.size + 1; + data.ptr = new char[data.capacity]; + data.ptr[sz] = '\0'; + return data.ptr; + } +} + +void String::setOnHeap() noexcept { *reinterpret_cast(&buf[last]) = 128; } +void String::setLast(size_type in) noexcept { buf[last] = char(in); } +void String::setSize(size_type sz) noexcept { + if (isOnStack()) { buf[sz] = '\0'; setLast(last - sz); } + else { data.ptr[sz] = '\0'; data.size = sz; } +} void String::copy(const String& other) { - using namespace std; if(other.isOnStack()) { memcpy(buf, other.buf, len); } else { - setOnHeap(); - data.size = other.data.size; - data.capacity = data.size + 1; - data.ptr = new char[data.capacity]; - memcpy(data.ptr, other.data.ptr, data.size + 1); + memcpy(allocate(other.data.size), other.data.ptr, other.data.size); } } -String::String() { +String::String() noexcept { buf[0] = '\0'; setLast(); } @@ -3040,23 +3633,17 @@ String::String() { String::~String() { if(!isOnStack()) delete[] data.ptr; -} +} // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) String::String(const char* in) : String(in, strlen(in)) {} -String::String(const char* in, unsigned in_size) { - using namespace std; - if(in_size <= last) { - memcpy(buf, in, in_size + 1); - setLast(last - in_size); - } else { - setOnHeap(); - data.size = in_size; - data.capacity = data.size + 1; - data.ptr = new char[data.capacity]; - memcpy(data.ptr, in, in_size + 1); - } +String::String(const char* in, size_type in_size) { + memcpy(allocate(in_size), in, in_size); +} + +String::String(std::istream& in, size_type in_size) { + in.read(allocate(in_size), in_size); } String::String(const String& other) { copy(other); } @@ -3073,14 +3660,14 @@ String& String::operator=(const String& other) { } String& String::operator+=(const String& other) { - const unsigned my_old_size = size(); - const unsigned other_size = other.size(); - const unsigned total_size = my_old_size + other_size; - using namespace std; + const size_type my_old_size = size(); + const size_type other_size = other.size(); + const size_type total_size = my_old_size + other_size; if(isOnStack()) { if(total_size < len) { // append to the current stack space memcpy(buf + my_old_size, other.c_str(), other_size + 1); + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) setLast(last - total_size); } else { // alloc new chunk @@ -3122,17 +3709,13 @@ String& String::operator+=(const String& other) { return *this; } -String String::operator+(const String& other) const { return String(*this) += other; } - -String::String(String&& other) { - using namespace std; +String::String(String&& other) noexcept { memcpy(buf, other.buf, len); other.buf[0] = '\0'; other.setLast(); } -String& String::operator=(String&& other) { - using namespace std; +String& String::operator=(String&& other) noexcept { if(this != &other) { if(!isOnStack()) delete[] data.ptr; @@ -3143,30 +3726,60 @@ String& String::operator=(String&& other) { return *this; } -char String::operator[](unsigned i) const { - return const_cast(this)->operator[](i); // NOLINT +char String::operator[](size_type i) const { + return const_cast(this)->operator[](i); } -char& String::operator[](unsigned i) { +char& String::operator[](size_type i) { if(isOnStack()) return reinterpret_cast(buf)[i]; return data.ptr[i]; } DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wmaybe-uninitialized") -unsigned String::size() const { +String::size_type String::size() const { if(isOnStack()) - return last - (unsigned(buf[last]) & 31); // using "last" would work only if "len" is 32 + return last - (size_type(buf[last]) & 31); // using "last" would work only if "len" is 32 return data.size; } DOCTEST_GCC_SUPPRESS_WARNING_POP -unsigned String::capacity() const { +String::size_type String::capacity() const { if(isOnStack()) return len; return data.capacity; } +String String::substr(size_type pos, size_type cnt) && { + cnt = std::min(cnt, size() - 1 - pos); + char* cptr = c_str(); + memmove(cptr, cptr + pos, cnt); + setSize(cnt); + return std::move(*this); +} + +String String::substr(size_type pos, size_type cnt) const & { + cnt = std::min(cnt, size() - 1 - pos); + return String{ c_str() + pos, cnt }; +} + +String::size_type String::find(char ch, size_type pos) const { + const char* begin = c_str(); + const char* end = begin + size(); + const char* it = begin + pos; + for (; it < end && *it != ch; it++); + if (it < end) { return static_cast(it - begin); } + else { return npos; } +} + +String::size_type String::rfind(char ch, size_type pos) const { + const char* begin = c_str(); + const char* it = begin + std::min(pos, size() - 1); + for (; it >= begin && *it != ch; it--); + if (it >= begin) { return static_cast(it - begin); } + else { return npos; } +} + int String::compare(const char* other, bool no_case) const { if(no_case) return doctest::stricmp(c_str(), other); @@ -3177,17 +3790,32 @@ int String::compare(const String& other, bool no_case) const { return compare(other.c_str(), no_case); } -// clang-format off +String operator+(const String& lhs, const String& rhs) { return String(lhs) += rhs; } + bool operator==(const String& lhs, const String& rhs) { return lhs.compare(rhs) == 0; } bool operator!=(const String& lhs, const String& rhs) { return lhs.compare(rhs) != 0; } bool operator< (const String& lhs, const String& rhs) { return lhs.compare(rhs) < 0; } bool operator> (const String& lhs, const String& rhs) { return lhs.compare(rhs) > 0; } bool operator<=(const String& lhs, const String& rhs) { return (lhs != rhs) ? lhs.compare(rhs) < 0 : true; } bool operator>=(const String& lhs, const String& rhs) { return (lhs != rhs) ? lhs.compare(rhs) > 0 : true; } -// clang-format on std::ostream& operator<<(std::ostream& s, const String& in) { return s << in.c_str(); } +Contains::Contains(const String& str) : string(str) { } + +bool Contains::checkWith(const String& other) const { + return strstr(other.c_str(), string.c_str()) != nullptr; +} + +String toString(const Contains& in) { + return "Contains( " + in.string + " )"; +} + +bool operator==(const String& lhs, const Contains& rhs) { return rhs.checkWith(lhs); } +bool operator==(const Contains& lhs, const String& rhs) { return lhs.checkWith(rhs); } +bool operator!=(const String& lhs, const Contains& rhs) { return !rhs.checkWith(lhs); } +bool operator!=(const Contains& lhs, const String& rhs) { return !lhs.checkWith(rhs); } + namespace { void color_to_stream(std::ostream&, Color::Enum) DOCTEST_BRANCH_ON_DISABLED({}, ;) } // namespace @@ -3199,66 +3827,44 @@ namespace Color { } } // namespace Color -// clang-format off -const char* assertString(assertType::Enum at) { - DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4062) // enum 'x' in switch of enum 'y' is not handled - switch(at) { //!OCLINT missing default in switch statements - case assertType::DT_WARN : return "WARN"; - case assertType::DT_CHECK : return "CHECK"; - case assertType::DT_REQUIRE : return "REQUIRE"; - - case assertType::DT_WARN_FALSE : return "WARN_FALSE"; - case assertType::DT_CHECK_FALSE : return "CHECK_FALSE"; - case assertType::DT_REQUIRE_FALSE : return "REQUIRE_FALSE"; - - case assertType::DT_WARN_THROWS : return "WARN_THROWS"; - case assertType::DT_CHECK_THROWS : return "CHECK_THROWS"; - case assertType::DT_REQUIRE_THROWS : return "REQUIRE_THROWS"; - - case assertType::DT_WARN_THROWS_AS : return "WARN_THROWS_AS"; - case assertType::DT_CHECK_THROWS_AS : return "CHECK_THROWS_AS"; - case assertType::DT_REQUIRE_THROWS_AS : return "REQUIRE_THROWS_AS"; - - case assertType::DT_WARN_THROWS_WITH : return "WARN_THROWS_WITH"; - case assertType::DT_CHECK_THROWS_WITH : return "CHECK_THROWS_WITH"; - case assertType::DT_REQUIRE_THROWS_WITH : return "REQUIRE_THROWS_WITH"; - - case assertType::DT_WARN_THROWS_WITH_AS : return "WARN_THROWS_WITH_AS"; - case assertType::DT_CHECK_THROWS_WITH_AS : return "CHECK_THROWS_WITH_AS"; - case assertType::DT_REQUIRE_THROWS_WITH_AS : return "REQUIRE_THROWS_WITH_AS"; - - case assertType::DT_WARN_NOTHROW : return "WARN_NOTHROW"; - case assertType::DT_CHECK_NOTHROW : return "CHECK_NOTHROW"; - case assertType::DT_REQUIRE_NOTHROW : return "REQUIRE_NOTHROW"; - - case assertType::DT_WARN_EQ : return "WARN_EQ"; - case assertType::DT_CHECK_EQ : return "CHECK_EQ"; - case assertType::DT_REQUIRE_EQ : return "REQUIRE_EQ"; - case assertType::DT_WARN_NE : return "WARN_NE"; - case assertType::DT_CHECK_NE : return "CHECK_NE"; - case assertType::DT_REQUIRE_NE : return "REQUIRE_NE"; - case assertType::DT_WARN_GT : return "WARN_GT"; - case assertType::DT_CHECK_GT : return "CHECK_GT"; - case assertType::DT_REQUIRE_GT : return "REQUIRE_GT"; - case assertType::DT_WARN_LT : return "WARN_LT"; - case assertType::DT_CHECK_LT : return "CHECK_LT"; - case assertType::DT_REQUIRE_LT : return "REQUIRE_LT"; - case assertType::DT_WARN_GE : return "WARN_GE"; - case assertType::DT_CHECK_GE : return "CHECK_GE"; - case assertType::DT_REQUIRE_GE : return "REQUIRE_GE"; - case assertType::DT_WARN_LE : return "WARN_LE"; - case assertType::DT_CHECK_LE : return "CHECK_LE"; - case assertType::DT_REQUIRE_LE : return "REQUIRE_LE"; - - case assertType::DT_WARN_UNARY : return "WARN_UNARY"; - case assertType::DT_CHECK_UNARY : return "CHECK_UNARY"; - case assertType::DT_REQUIRE_UNARY : return "REQUIRE_UNARY"; - case assertType::DT_WARN_UNARY_FALSE : return "WARN_UNARY_FALSE"; - case assertType::DT_CHECK_UNARY_FALSE : return "CHECK_UNARY_FALSE"; - case assertType::DT_REQUIRE_UNARY_FALSE : return "REQUIRE_UNARY_FALSE"; +// clang-format off +const char* assertString(assertType::Enum at) { + DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4061) // enum 'x' in switch of enum 'y' is not explicitly handled + #define DOCTEST_GENERATE_ASSERT_TYPE_CASE(assert_type) case assertType::DT_ ## assert_type: return #assert_type + #define DOCTEST_GENERATE_ASSERT_TYPE_CASES(assert_type) \ + DOCTEST_GENERATE_ASSERT_TYPE_CASE(WARN_ ## assert_type); \ + DOCTEST_GENERATE_ASSERT_TYPE_CASE(CHECK_ ## assert_type); \ + DOCTEST_GENERATE_ASSERT_TYPE_CASE(REQUIRE_ ## assert_type) + switch(at) { + DOCTEST_GENERATE_ASSERT_TYPE_CASE(WARN); + DOCTEST_GENERATE_ASSERT_TYPE_CASE(CHECK); + DOCTEST_GENERATE_ASSERT_TYPE_CASE(REQUIRE); + + DOCTEST_GENERATE_ASSERT_TYPE_CASES(FALSE); + + DOCTEST_GENERATE_ASSERT_TYPE_CASES(THROWS); + + DOCTEST_GENERATE_ASSERT_TYPE_CASES(THROWS_AS); + + DOCTEST_GENERATE_ASSERT_TYPE_CASES(THROWS_WITH); + + DOCTEST_GENERATE_ASSERT_TYPE_CASES(THROWS_WITH_AS); + + DOCTEST_GENERATE_ASSERT_TYPE_CASES(NOTHROW); + + DOCTEST_GENERATE_ASSERT_TYPE_CASES(EQ); + DOCTEST_GENERATE_ASSERT_TYPE_CASES(NE); + DOCTEST_GENERATE_ASSERT_TYPE_CASES(GT); + DOCTEST_GENERATE_ASSERT_TYPE_CASES(LT); + DOCTEST_GENERATE_ASSERT_TYPE_CASES(GE); + DOCTEST_GENERATE_ASSERT_TYPE_CASES(LE); + + DOCTEST_GENERATE_ASSERT_TYPE_CASES(UNARY); + DOCTEST_GENERATE_ASSERT_TYPE_CASES(UNARY_FALSE); + + default: DOCTEST_INTERNAL_ERROR("Tried stringifying invalid assert type!"); } DOCTEST_MSVC_SUPPRESS_WARNING_POP - return ""; } // clang-format on @@ -3276,6 +3882,7 @@ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wnull-dereference") DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wnull-dereference") // depending on the current options this will remove the path of filenames const char* skipPathFromFilename(const char* file) { +#ifndef DOCTEST_CONFIG_DISABLE if(getContextOptions()->no_path_in_filenames) { auto back = std::strrchr(file, '\\'); auto forward = std::strrchr(file, '/'); @@ -3285,11 +3892,18 @@ const char* skipPathFromFilename(const char* file) { return forward + 1; } } +#endif // DOCTEST_CONFIG_DISABLE return file; } DOCTEST_CLANG_SUPPRESS_WARNING_POP DOCTEST_GCC_SUPPRESS_WARNING_POP +bool SubcaseSignature::operator==(const SubcaseSignature& other) const { + return m_line == other.m_line + && std::strcmp(m_file, other.m_file) == 0 + && m_name == other.m_name; +} + bool SubcaseSignature::operator<(const SubcaseSignature& other) const { if(m_line != other.m_line) return m_line < other.m_line; @@ -3298,44 +3912,53 @@ bool SubcaseSignature::operator<(const SubcaseSignature& other) const { return m_name.compare(other.m_name) < 0; } -IContextScope::IContextScope() = default; -IContextScope::~IContextScope() = default; +DOCTEST_DEFINE_INTERFACE(IContextScope) -#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING -String toString(char* in) { return toString(static_cast(in)); } -String toString(const char* in) { return String("\"") + (in ? in : "{null string}") + "\""; } -#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING -String toString(bool in) { return in ? "true" : "false"; } -String toString(float in) { return fpToString(in, 5) + "f"; } -String toString(double in) { return fpToString(in, 10); } -String toString(double long in) { return fpToString(in, 15); } - -#define DOCTEST_TO_STRING_OVERLOAD(type, fmt) \ - String toString(type in) { \ - char buf[64]; \ - std::sprintf(buf, fmt, in); \ - return buf; \ +namespace detail { + void filldata::fill(std::ostream* stream, const void* in) { + if (in) { *stream << in; } + else { *stream << "nullptr"; } } -DOCTEST_TO_STRING_OVERLOAD(char, "%d") -DOCTEST_TO_STRING_OVERLOAD(char signed, "%d") -DOCTEST_TO_STRING_OVERLOAD(char unsigned, "%u") -DOCTEST_TO_STRING_OVERLOAD(int short, "%d") -DOCTEST_TO_STRING_OVERLOAD(int short unsigned, "%u") -DOCTEST_TO_STRING_OVERLOAD(int, "%d") -DOCTEST_TO_STRING_OVERLOAD(unsigned, "%u") -DOCTEST_TO_STRING_OVERLOAD(int long, "%ld") -DOCTEST_TO_STRING_OVERLOAD(int long unsigned, "%lu") -DOCTEST_TO_STRING_OVERLOAD(int long long, "%lld") -DOCTEST_TO_STRING_OVERLOAD(int long long unsigned, "%llu") + template + String toStreamLit(T t) { + std::ostream* os = tlssPush(); + os->operator<<(t); + return tlssPop(); + } +} -String toString(std::nullptr_t) { return "NULL"; } +#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING +String toString(const char* in) { return String("\"") + (in ? in : "{null string}") + "\""; } +#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/onqtam/doctest/issues/183 +// see this issue on why this is needed: https://github.com/doctest/doctest/issues/183 String toString(const std::string& in) { return in.c_str(); } #endif // VS 2019 +String toString(String in) { return in; } + +String toString(std::nullptr_t) { return "nullptr"; } + +String toString(bool in) { return in ? "true" : "false"; } + +String toString(float in) { return toStreamLit(in); } +String toString(double in) { return toStreamLit(in); } +String toString(double long in) { return toStreamLit(in); } + +String toString(char in) { return toStreamLit(static_cast(in)); } +String toString(char signed in) { return toStreamLit(static_cast(in)); } +String toString(char unsigned in) { return toStreamLit(static_cast(in)); } +String toString(short in) { return toStreamLit(in); } +String toString(short unsigned in) { return toStreamLit(in); } +String toString(signed in) { return toStreamLit(in); } +String toString(unsigned in) { return toStreamLit(in); } +String toString(long in) { return toStreamLit(in); } +String toString(long unsigned in) { return toStreamLit(in); } +String toString(long long in) { return toStreamLit(in); } +String toString(long long unsigned in) { return toStreamLit(in); } + Approx::Approx(double value) : m_epsilon(static_cast(std::numeric_limits::epsilon()) * 100) , m_scale(1.0) @@ -3375,10 +3998,25 @@ bool operator>(double lhs, const Approx& rhs) { return lhs > rhs.m_value && lhs bool operator>(const Approx& lhs, double rhs) { return lhs.m_value > rhs && lhs != rhs; } String toString(const Approx& in) { - return String("Approx( ") + doctest::toString(in.m_value) + " )"; + return "Approx( " + doctest::toString(in.m_value) + " )"; } const ContextOptions* getContextOptions() { return DOCTEST_BRANCH_ON_DISABLED(nullptr, g_cs); } +DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4738) +template +IsNaN::operator bool() const { + return std::isnan(value) ^ flipped; +} +DOCTEST_MSVC_SUPPRESS_WARNING_POP +template struct DOCTEST_INTERFACE_DEF IsNaN; +template struct DOCTEST_INTERFACE_DEF IsNaN; +template struct DOCTEST_INTERFACE_DEF IsNaN; +template +String toString(IsNaN in) { return String(in.flipped ? "! " : "") + "IsNaN( " + doctest::toString(in.value) + " )"; } +String toString(IsNaN in) { return toString(in); } +String toString(IsNaN in) { return toString(in); } +String toString(IsNaN in) { return toString(in); } + } // namespace doctest #ifdef DOCTEST_CONFIG_DISABLE @@ -3388,15 +4026,15 @@ Context::~Context() = default; void Context::applyCommandLine(int, const char* const*) {} void Context::addFilter(const char*, const char*) {} void Context::clearFilters() {} +void Context::setOption(const char*, bool) {} void Context::setOption(const char*, int) {} void Context::setOption(const char*, const char*) {} bool Context::shouldExit() { return false; } void Context::setAsDefaultForAssertsOutOfTestCases() {} void Context::setAssertHandler(detail::assert_handler) {} +void Context::setCout(std::ostream*) {} int Context::run() { return 0; } -IReporter::~IReporter() = default; - int IReporter::get_num_active_contexts() { return 0; } const IContextScope* const* IReporter::get_active_contexts() { return nullptr; } int IReporter::get_num_stringified_contexts() { return 0; } @@ -3420,7 +4058,7 @@ int registerReporter(const char*, int, IReporter*) { return 0; } namespace doctest_detail_test_suite_ns { // holds the current test suite doctest::detail::TestSuite& getCurrentTestSuite() { - static doctest::detail::TestSuite data; + static doctest::detail::TestSuite data{}; return data; } } // namespace doctest_detail_test_suite_ns @@ -3429,7 +4067,7 @@ namespace doctest { namespace { // the int (priority) is part of the key for automatic sorting - sadly one can register a // reporter with a duplicate name and a different priority but hopefully that won't happen often :| - typedef std::map, reporterCreatorFunc> reporterMap; + using reporterMap = std::map, reporterCreatorFunc>; reporterMap& getReporters() { static reporterMap data; @@ -3461,8 +4099,8 @@ namespace detail { #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS DOCTEST_NORETURN void throwException() { g_cs->shouldLogCurrentException = false; - throw TestFailureException(); - } // NOLINT(cert-err60-cpp) + throw TestFailureException(); // NOLINT(hicpp-exception-baseclass) + } #else // DOCTEST_CONFIG_NO_EXCEPTIONS void throwException() {} #endif // DOCTEST_CONFIG_NO_EXCEPTIONS @@ -3508,91 +4146,134 @@ namespace { return !*wild; } - //// C string hash function (djb2) - taken from http://www.cse.yorku.ca/~oz/hash.html - //unsigned hashStr(unsigned const char* str) { - // unsigned long hash = 5381; - // char c; - // while((c = *str++)) - // hash = ((hash << 5) + hash) + c; // hash * 33 + c - // return hash; - //} - // checks if the name matches any of the filters (and can be configured what to do when empty) bool matchesAny(const char* name, const std::vector& filters, bool matchEmpty, - bool caseSensitive) { - if(filters.empty() && matchEmpty) + bool caseSensitive) { + if (filters.empty() && matchEmpty) return true; - for(auto& curr : filters) - if(wildcmp(name, curr.c_str(), caseSensitive)) + for (auto& curr : filters) + if (wildcmp(name, curr.c_str(), caseSensitive)) return true; return false; } -} // namespace -namespace detail { - Subcase::Subcase(const String& name, const char* file, int line) - : m_signature({name, file, line}) { - ContextState* s = g_cs; + DOCTEST_NO_SANITIZE_INTEGER + unsigned long long hash(unsigned long long a, unsigned long long b) { + return (a << 5) + b; + } - // check subcase filters - if(s->subcasesStack.size() < size_t(s->subcase_filter_levels)) { - if(!matchesAny(m_signature.m_name.c_str(), s->filters[6], true, s->case_sensitive)) - return; - if(matchesAny(m_signature.m_name.c_str(), s->filters[7], false, s->case_sensitive)) - return; - } - - // if a Subcase on the same level has already been entered - if(s->subcasesStack.size() < size_t(s->subcasesCurrentMaxLevel)) { - s->should_reenter = true; - return; - } + // C string hash function (djb2) - taken from http://www.cse.yorku.ca/~oz/hash.html + DOCTEST_NO_SANITIZE_INTEGER + unsigned long long hash(const char* str) { + unsigned long long hash = 5381; + char c; + while ((c = *str++)) + hash = ((hash << 5) + hash) + c; // hash * 33 + c + return hash; + } - // push the current signature to the stack so we can check if the - // current stack + the current new subcase have been traversed - s->subcasesStack.push_back(m_signature); - if(s->subcasesPassed.count(s->subcasesStack) != 0) { - // pop - revert to previous stack since we've already passed this - s->subcasesStack.pop_back(); - return; + unsigned long long hash(const SubcaseSignature& sig) { + return hash(hash(hash(sig.m_file), hash(sig.m_name.c_str())), sig.m_line); + } + + unsigned long long hash(const std::vector& sigs, size_t count) { + unsigned long long running = 0; + auto end = sigs.begin() + count; + for (auto it = sigs.begin(); it != end; it++) { + running = hash(running, hash(*it)); } + return running; + } - s->subcasesCurrentMaxLevel = s->subcasesStack.size(); - m_entered = true; + unsigned long long hash(const std::vector& sigs) { + unsigned long long running = 0; + for (const SubcaseSignature& sig : sigs) { + running = hash(running, hash(sig)); + } + return running; + } +} // namespace +namespace detail { + bool Subcase::checkFilters() { + if (g_cs->subcaseStack.size() < size_t(g_cs->subcase_filter_levels)) { + if (!matchesAny(m_signature.m_name.c_str(), g_cs->filters[6], true, g_cs->case_sensitive)) + return true; + if (matchesAny(m_signature.m_name.c_str(), g_cs->filters[7], false, g_cs->case_sensitive)) + return true; + } + return false; + } - DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_start, m_signature); + Subcase::Subcase(const String& name, const char* file, int line) + : m_signature({name, file, line}) { + if (!g_cs->reachedLeaf) { + if (g_cs->nextSubcaseStack.size() <= g_cs->subcaseStack.size() + || g_cs->nextSubcaseStack[g_cs->subcaseStack.size()] == m_signature) { + // Going down. + if (checkFilters()) { return; } + + g_cs->subcaseStack.push_back(m_signature); + g_cs->currentSubcaseDepth++; + m_entered = true; + DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_start, m_signature); + } + } else { + if (g_cs->subcaseStack[g_cs->currentSubcaseDepth] == m_signature) { + // This subcase is reentered via control flow. + g_cs->currentSubcaseDepth++; + m_entered = true; + DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_start, m_signature); + } else if (g_cs->nextSubcaseStack.size() <= g_cs->currentSubcaseDepth + && g_cs->fullyTraversedSubcases.find(hash(hash(g_cs->subcaseStack, g_cs->currentSubcaseDepth), hash(m_signature))) + == g_cs->fullyTraversedSubcases.end()) { + if (checkFilters()) { return; } + // This subcase is part of the one to be executed next. + g_cs->nextSubcaseStack.clear(); + g_cs->nextSubcaseStack.insert(g_cs->nextSubcaseStack.end(), + g_cs->subcaseStack.begin(), g_cs->subcaseStack.begin() + g_cs->currentSubcaseDepth); + g_cs->nextSubcaseStack.push_back(m_signature); + } + } } - DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4996) // std::uncaught_exception is deprecated in C++17 - DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") + DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4996) // std::uncaught_exception is deprecated in C++17 + DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") Subcase::~Subcase() { - if(m_entered) { - // only mark the subcase stack as passed if no subcases have been skipped - if(g_cs->should_reenter == false) - g_cs->subcasesPassed.insert(g_cs->subcasesStack); - g_cs->subcasesStack.pop_back(); + if (m_entered) { + g_cs->currentSubcaseDepth--; + + if (!g_cs->reachedLeaf) { + // Leaf. + g_cs->fullyTraversedSubcases.insert(hash(g_cs->subcaseStack)); + g_cs->nextSubcaseStack.clear(); + g_cs->reachedLeaf = true; + } else if (g_cs->nextSubcaseStack.empty()) { + // All children are finished. + g_cs->fullyTraversedSubcases.insert(hash(g_cs->subcaseStack)); + } -#if defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411L +#if defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411L && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200) if(std::uncaught_exceptions() > 0 #else if(std::uncaught_exception() #endif - && g_cs->shouldLogCurrentException) { + && g_cs->shouldLogCurrentException) { DOCTEST_ITERATE_THROUGH_REPORTERS( test_case_exception, {"exception thrown in subcase - will translate later " - "when the whole test case has been exited (cannot " - "translate while there is an active exception)", - false}); + "when the whole test case has been exited (cannot " + "translate while there is an active exception)", + false}); g_cs->shouldLogCurrentException = false; } + DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_end, DOCTEST_EMPTY); } } - DOCTEST_CLANG_SUPPRESS_WARNING_POP - DOCTEST_GCC_SUPPRESS_WARNING_POP + DOCTEST_CLANG_SUPPRESS_WARNING_POP + DOCTEST_GCC_SUPPRESS_WARNING_POP DOCTEST_MSVC_SUPPRESS_WARNING_POP Subcase::operator bool() const { return m_entered; } @@ -3606,24 +4287,19 @@ namespace detail { TestSuite& TestSuite::operator*(const char* in) { m_test_suite = in; - // clear state - m_description = nullptr; - m_skip = false; - m_may_fail = false; - m_should_fail = false; - m_expected_failures = 0; - m_timeout = 0; return *this; } TestCase::TestCase(funcType test, const char* file, unsigned line, const TestSuite& test_suite, - const char* type, int template_id) { + const String& type, int template_id) { m_file = file; m_line = line; m_name = nullptr; // will be later overridden in operator* m_test_suite = test_suite.m_test_suite; m_description = test_suite.m_description; m_skip = test_suite.m_skip; + m_no_breaks = test_suite.m_no_breaks; + m_no_output = test_suite.m_no_output; m_may_fail = test_suite.m_may_fail; m_should_fail = test_suite.m_should_fail; m_expected_failures = test_suite.m_expected_failures; @@ -3640,10 +4316,8 @@ namespace detail { } DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(26434) // hides a non-virtual function - DOCTEST_MSVC_SUPPRESS_WARNING(26437) // Do not slice TestCase& TestCase::operator=(const TestCase& other) { - static_cast(*this) = static_cast(other); - + TestCaseData::operator=(other); m_test = other.m_test; m_type = other.m_type; m_template_id = other.m_template_id; @@ -3659,7 +4333,7 @@ namespace detail { m_name = in; // make a new name with an appended type for templated test case if(m_template_id != -1) { - m_full_name = String(m_name) + m_type; + m_full_name = String(m_name) + "<" + m_type + ">"; // redirect the name to point to the newly constructed full name m_name = m_full_name.c_str(); } @@ -3667,13 +4341,23 @@ namespace detail { } bool TestCase::operator<(const TestCase& other) const { + // this will be used only to differentiate between test cases - not relevant for sorting if(m_line != other.m_line) return m_line < other.m_line; + const int name_cmp = strcmp(m_name, other.m_name); + if(name_cmp != 0) + return name_cmp < 0; const int file_cmp = m_file.compare(other.m_file); if(file_cmp != 0) return file_cmp < 0; return m_template_id < other.m_template_id; } + + // all the registered tests + std::set& getRegisteredTests() { + static std::set data; + return data; + } } // namespace detail namespace { using namespace detail; @@ -3705,39 +4389,10 @@ namespace { return suiteOrderComparator(lhs, rhs); } - // all the registered tests - std::set& getRegisteredTests() { - static std::set data; - return data; - } - -#ifdef DOCTEST_CONFIG_COLORS_WINDOWS - HANDLE g_stdoutHandle; - WORD g_origFgAttrs; - WORD g_origBgAttrs; - bool g_attrsInitted = false; - - int colors_init() { - if(!g_attrsInitted) { - g_stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE); - g_attrsInitted = true; - CONSOLE_SCREEN_BUFFER_INFO csbiInfo; - GetConsoleScreenBufferInfo(g_stdoutHandle, &csbiInfo); - g_origFgAttrs = csbiInfo.wAttributes & ~(BACKGROUND_GREEN | BACKGROUND_RED | - BACKGROUND_BLUE | BACKGROUND_INTENSITY); - g_origBgAttrs = csbiInfo.wAttributes & ~(FOREGROUND_GREEN | FOREGROUND_RED | - FOREGROUND_BLUE | FOREGROUND_INTENSITY); - } - return 0; - } - - int dumy_init_console_colors = colors_init(); -#endif // DOCTEST_CONFIG_COLORS_WINDOWS - DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") void color_to_stream(std::ostream& s, Color::Enum code) { - ((void)s); // for DOCTEST_CONFIG_COLORS_NONE or DOCTEST_CONFIG_COLORS_WINDOWS - ((void)code); // for DOCTEST_CONFIG_COLORS_NONE + static_cast(s); // for DOCTEST_CONFIG_COLORS_NONE or DOCTEST_CONFIG_COLORS_WINDOWS + static_cast(code); // for DOCTEST_CONFIG_COLORS_NONE #ifdef DOCTEST_CONFIG_COLORS_ANSI if(g_no_colors || (isatty(STDOUT_FILENO) == false && getContextOptions()->force_colors == false)) @@ -3767,10 +4422,26 @@ namespace { #ifdef DOCTEST_CONFIG_COLORS_WINDOWS if(g_no_colors || - (isatty(fileno(stdout)) == false && getContextOptions()->force_colors == false)) + (_isatty(_fileno(stdout)) == false && getContextOptions()->force_colors == false)) return; -#define DOCTEST_SET_ATTR(x) SetConsoleTextAttribute(g_stdoutHandle, x | g_origBgAttrs) + static struct ConsoleHelper { + HANDLE stdoutHandle; + WORD origFgAttrs; + WORD origBgAttrs; + + ConsoleHelper() { + stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE); + CONSOLE_SCREEN_BUFFER_INFO csbiInfo; + GetConsoleScreenBufferInfo(stdoutHandle, &csbiInfo); + origFgAttrs = csbiInfo.wAttributes & ~(BACKGROUND_GREEN | BACKGROUND_RED | + BACKGROUND_BLUE | BACKGROUND_INTENSITY); + origBgAttrs = csbiInfo.wAttributes & ~(FOREGROUND_GREEN | FOREGROUND_RED | + FOREGROUND_BLUE | FOREGROUND_INTENSITY); + } + } ch; + +#define DOCTEST_SET_ATTR(x) SetConsoleTextAttribute(ch.stdoutHandle, x | ch.origBgAttrs) // clang-format off switch (code) { @@ -3787,7 +4458,7 @@ namespace { case Color::BrightWhite: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); break; case Color::None: case Color::Bright: // invalid - default: DOCTEST_SET_ATTR(g_origFgAttrs); + default: DOCTEST_SET_ATTR(ch.origFgAttrs); } // clang-format on #endif // DOCTEST_CONFIG_COLORS_WINDOWS @@ -3843,7 +4514,28 @@ namespace detail { #ifdef DOCTEST_IS_DEBUGGER_ACTIVE bool isDebuggerActive() { return DOCTEST_IS_DEBUGGER_ACTIVE(); } #else // DOCTEST_IS_DEBUGGER_ACTIVE -#ifdef DOCTEST_PLATFORM_MAC +#ifdef DOCTEST_PLATFORM_LINUX + class ErrnoGuard { + public: + ErrnoGuard() : m_oldErrno(errno) {} + ~ErrnoGuard() { errno = m_oldErrno; } + private: + int m_oldErrno; + }; + // See the comments in Catch2 for the reasoning behind this implementation: + // https://github.com/catchorg/Catch2/blob/v2.13.1/include/internal/catch_debugger.cpp#L79-L102 + bool isDebuggerActive() { + ErrnoGuard guard; + std::ifstream in("/proc/self/status"); + for(std::string line; std::getline(in, line);) { + static const int PREFIX_LEN = 11; + if(line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0) { + return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; + } + } + return false; + } +#elif defined(DOCTEST_PLATFORM_MAC) // The following function is taken directly from the following technical note: // https://developer.apple.com/library/archive/qa/qa1361/_index.html // Returns true if the current process is being debugged (either @@ -3883,42 +4575,29 @@ namespace detail { getExceptionTranslators().push_back(et); } -#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING - void toStream(std::ostream* s, char* in) { *s << in; } - void toStream(std::ostream* s, const char* in) { *s << in; } -#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING - void toStream(std::ostream* s, bool in) { *s << std::boolalpha << in << std::noboolalpha; } - void toStream(std::ostream* s, float in) { *s << in; } - void toStream(std::ostream* s, double in) { *s << in; } - void toStream(std::ostream* s, double long in) { *s << in; } - - void toStream(std::ostream* s, char in) { *s << in; } - void toStream(std::ostream* s, char signed in) { *s << in; } - void toStream(std::ostream* s, char unsigned in) { *s << in; } - void toStream(std::ostream* s, int short in) { *s << in; } - void toStream(std::ostream* s, int short unsigned in) { *s << in; } - void toStream(std::ostream* s, int in) { *s << in; } - void toStream(std::ostream* s, int unsigned in) { *s << in; } - void toStream(std::ostream* s, int long in) { *s << in; } - void toStream(std::ostream* s, int long unsigned in) { *s << in; } - void toStream(std::ostream* s, int long long in) { *s << in; } - void toStream(std::ostream* s, int long long unsigned in) { *s << in; } - DOCTEST_THREAD_LOCAL std::vector g_infoContexts; // for logging with INFO() ContextScopeBase::ContextScopeBase() { g_infoContexts.push_back(this); } - DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4996) // std::uncaught_exception is deprecated in C++17 - DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") + ContextScopeBase::ContextScopeBase(ContextScopeBase&& other) noexcept { + if (other.need_to_destroy) { + other.destroy(); + } + other.need_to_destroy = false; + g_infoContexts.push_back(this); + } + + DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4996) // std::uncaught_exception is deprecated in C++17 + DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") // destroy cannot be inlined into the destructor because that would mean calling stringify after // ContextScope has been destroyed (base class destructors run after derived class destructors). // Instead, ContextScope calls this method directly from its destructor. void ContextScopeBase::destroy() { -#if defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411L +#if defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411L && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200) if(std::uncaught_exceptions() > 0) { #else if(std::uncaught_exception()) { @@ -3930,8 +4609,8 @@ namespace detail { g_infoContexts.pop_back(); } - DOCTEST_CLANG_SUPPRESS_WARNING_POP - DOCTEST_GCC_SUPPRESS_WARNING_POP + DOCTEST_CLANG_SUPPRESS_WARNING_POP + DOCTEST_GCC_SUPPRESS_WARNING_POP DOCTEST_MSVC_SUPPRESS_WARNING_POP } // namespace detail namespace { @@ -3940,7 +4619,9 @@ namespace { #if !defined(DOCTEST_CONFIG_POSIX_SIGNALS) && !defined(DOCTEST_CONFIG_WINDOWS_SEH) struct FatalConditionHandler { - void reset() {} + static void reset() {} + static void allocateAltStackMem() {} + static void freeAltStackMem() {} }; #else // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH @@ -3957,26 +4638,45 @@ namespace { // Windows can easily distinguish between SO and SigSegV, // but SigInt, SigTerm, etc are handled differently. SignalDefs signalDefs[] = { - {EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal"}, - {EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow"}, - {EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal"}, - {EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error"}, + {static_cast(EXCEPTION_ILLEGAL_INSTRUCTION), + "SIGILL - Illegal instruction signal"}, + {static_cast(EXCEPTION_STACK_OVERFLOW), "SIGSEGV - Stack overflow"}, + {static_cast(EXCEPTION_ACCESS_VIOLATION), + "SIGSEGV - Segmentation violation signal"}, + {static_cast(EXCEPTION_INT_DIVIDE_BY_ZERO), "Divide by zero error"}, }; struct FatalConditionHandler { static LONG CALLBACK handleException(PEXCEPTION_POINTERS ExceptionInfo) { - for(size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) { - if(ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) { - reportFatal(signalDefs[i].name); - break; + // Multiple threads may enter this filter/handler at once. We want the error message to be printed on the + // console just once no matter how many threads have crashed. + DOCTEST_DECLARE_STATIC_MUTEX(mutex) + static bool execute = true; + { + DOCTEST_LOCK_MUTEX(mutex) + if(execute) { + bool reported = false; + for(size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) { + if(ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) { + reportFatal(signalDefs[i].name); + reported = true; + break; + } + } + if(reported == false) + reportFatal("Unhandled SEH exception caught"); + if(isDebuggerActive() && !g_cs->no_breaks) + DOCTEST_BREAK_INTO_DEBUGGER(); } + execute = false; } - // If its not an exception we care about, pass it along. - // This stops us from eating debugger breaks etc. - return EXCEPTION_CONTINUE_SEARCH; + std::exit(EXIT_FAILURE); } + static void allocateAltStackMem() {} + static void freeAltStackMem() {} + FatalConditionHandler() { isSet = true; // 32k seems enough for doctest to handle stack overflow, @@ -3986,6 +4686,51 @@ namespace { previousTop = SetUnhandledExceptionFilter(handleException); // Pass in guarantee size to be filled SetThreadStackGuarantee(&guaranteeSize); + + // On Windows uncaught exceptions from another thread, exceptions from + // destructors, or calls to std::terminate are not a SEH exception + + // The terminal handler gets called when: + // - std::terminate is called FROM THE TEST RUNNER THREAD + // - an exception is thrown from a destructor FROM THE TEST RUNNER THREAD + original_terminate_handler = std::get_terminate(); + std::set_terminate([]() DOCTEST_NOEXCEPT { + reportFatal("Terminate handler called"); + if(isDebuggerActive() && !g_cs->no_breaks) + DOCTEST_BREAK_INTO_DEBUGGER(); + std::exit(EXIT_FAILURE); // explicitly exit - otherwise the SIGABRT handler may be called as well + }); + + // SIGABRT is raised when: + // - std::terminate is called FROM A DIFFERENT THREAD + // - an exception is thrown from a destructor FROM A DIFFERENT THREAD + // - an uncaught exception is thrown FROM A DIFFERENT THREAD + prev_sigabrt_handler = std::signal(SIGABRT, [](int signal) DOCTEST_NOEXCEPT { + if(signal == SIGABRT) { + reportFatal("SIGABRT - Abort (abnormal termination) signal"); + if(isDebuggerActive() && !g_cs->no_breaks) + DOCTEST_BREAK_INTO_DEBUGGER(); + std::exit(EXIT_FAILURE); + } + }); + + // The following settings are taken from google test, and more + // specifically from UnitTest::Run() inside of gtest.cc + + // the user does not want to see pop-up dialogs about crashes + prev_error_mode_1 = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT | + SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); + // This forces the abort message to go to stderr in all circumstances. + prev_error_mode_2 = _set_error_mode(_OUT_TO_STDERR); + // In the debug version, Visual Studio pops up a separate dialog + // offering a choice to debug the aborted program - we want to disable that. + prev_abort_behavior = _set_abort_behavior(0x0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); + // In debug mode, the Windows CRT can crash with an assertion over invalid + // input (e.g. passing an invalid file descriptor). The default handling + // for these assertions is to pop up a dialog and wait for user input. + // Instead ask the CRT to dump such assertions to stderr non-interactively. + prev_report_mode = _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + prev_report_file = _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); } static void reset() { @@ -3993,7 +4738,13 @@ namespace { // Unregister handler and restore the old guarantee SetUnhandledExceptionFilter(previousTop); SetThreadStackGuarantee(&guaranteeSize); - previousTop = nullptr; + std::set_terminate(original_terminate_handler); + std::signal(SIGABRT, prev_sigabrt_handler); + SetErrorMode(prev_error_mode_1); + _set_error_mode(prev_error_mode_2); + _set_abort_behavior(prev_abort_behavior, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); + static_cast(_CrtSetReportMode(_CRT_ASSERT, prev_report_mode)); + static_cast(_CrtSetReportFile(_CRT_ASSERT, prev_report_file)); isSet = false; } } @@ -4001,11 +4752,25 @@ namespace { ~FatalConditionHandler() { reset(); } private: + static UINT prev_error_mode_1; + static int prev_error_mode_2; + static unsigned int prev_abort_behavior; + static int prev_report_mode; + static _HFILE prev_report_file; + static void (DOCTEST_CDECL *prev_sigabrt_handler)(int); + static std::terminate_handler original_terminate_handler; static bool isSet; static ULONG guaranteeSize; static LPTOP_LEVEL_EXCEPTION_FILTER previousTop; }; + UINT FatalConditionHandler::prev_error_mode_1; + int FatalConditionHandler::prev_error_mode_2; + unsigned int FatalConditionHandler::prev_abort_behavior; + int FatalConditionHandler::prev_report_mode; + _HFILE FatalConditionHandler::prev_report_file; + void (DOCTEST_CDECL *FatalConditionHandler::prev_sigabrt_handler)(int); + std::terminate_handler FatalConditionHandler::original_terminate_handler; bool FatalConditionHandler::isSet = false; ULONG FatalConditionHandler::guaranteeSize = 0; LPTOP_LEVEL_EXCEPTION_FILTER FatalConditionHandler::previousTop = nullptr; @@ -4029,7 +4794,8 @@ namespace { static bool isSet; static struct sigaction oldSigActions[DOCTEST_COUNTOF(signalDefs)]; static stack_t oldSigStack; - static char altStackMem[4 * SIGSTKSZ]; + static size_t altStackSize; + static char* altStackMem; static void handleSignal(int sig) { const char* name = ""; @@ -4045,15 +4811,23 @@ namespace { raise(sig); } + static void allocateAltStackMem() { + altStackMem = new char[altStackSize]; + } + + static void freeAltStackMem() { + delete[] altStackMem; + } + FatalConditionHandler() { isSet = true; stack_t sigStack; sigStack.ss_sp = altStackMem; - sigStack.ss_size = sizeof(altStackMem); + sigStack.ss_size = altStackSize; sigStack.ss_flags = 0; sigaltstack(&sigStack, &oldSigStack); struct sigaction sa = {}; - sa.sa_handler = handleSignal; // NOLINT + sa.sa_handler = handleSignal; sa.sa_flags = SA_ONSTACK; for(std::size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) { sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); @@ -4074,10 +4848,11 @@ namespace { } }; - bool FatalConditionHandler::isSet = false; + bool FatalConditionHandler::isSet = false; struct sigaction FatalConditionHandler::oldSigActions[DOCTEST_COUNTOF(signalDefs)] = {}; - stack_t FatalConditionHandler::oldSigStack = {}; - char FatalConditionHandler::altStackMem[] = {}; + stack_t FatalConditionHandler::oldSigStack = {}; + size_t FatalConditionHandler::altStackSize = 4 * SIGSTKSZ; + char* FatalConditionHandler::altStackMem = nullptr; #endif // DOCTEST_PLATFORM_WINDOWS #endif // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH @@ -4091,7 +4866,7 @@ namespace { #define DOCTEST_OUTPUT_DEBUG_STRING(text) ::OutputDebugStringA(text) #else // TODO: integration with XCode and other IDEs -#define DOCTEST_OUTPUT_DEBUG_STRING(text) // NOLINT(clang-diagnostic-unused-macros) +#define DOCTEST_OUTPUT_DEBUG_STRING(text) #endif // Platform void addAssert(assertType::Enum at) { @@ -4110,8 +4885,8 @@ namespace { DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_exception, {message.c_str(), true}); - while(g_cs->subcasesStack.size()) { - g_cs->subcasesStack.pop_back(); + while (g_cs->subcaseStack.size()) { + g_cs->subcaseStack.pop_back(); DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_end, DOCTEST_EMPTY); } @@ -4123,25 +4898,26 @@ namespace { } #endif // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH } // namespace -namespace detail { - ResultBuilder::ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr, - const char* exception_type, const char* exception_string) { - m_test_case = g_cs->currentTest; - m_at = at; - m_file = file; - m_line = line; - m_expr = expr; - m_failed = true; - m_threw = false; - m_threw_as = false; - m_exception_type = exception_type; - m_exception_string = exception_string; +AssertData::AssertData(assertType::Enum at, const char* file, int line, const char* expr, + const char* exception_type, const StringContains& exception_string) + : m_test_case(g_cs->currentTest), m_at(at), m_file(file), m_line(line), m_expr(expr), + m_failed(true), m_threw(false), m_threw_as(false), m_exception_type(exception_type), + m_exception_string(exception_string) { #if DOCTEST_MSVC - if(m_expr[0] == ' ') // this happens when variadic macros are disabled under MSVC - ++m_expr; + if (m_expr[0] == ' ') // this happens when variadic macros are disabled under MSVC + ++m_expr; #endif // MSVC - } +} + +namespace detail { + ResultBuilder::ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr, + const char* exception_type, const String& exception_string) + : AssertData(at, file, line, expr, exception_type, exception_string) { } + + ResultBuilder::ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr, + const char* exception_type, const Contains& exception_string) + : AssertData(at, file, line, expr, exception_type, exception_string) { } void ResultBuilder::setResult(const Result& res) { m_decomp = res.m_decomp; @@ -4157,17 +4933,17 @@ namespace detail { if(m_at & assertType::is_throws) { //!OCLINT bitwise operator in conditional m_failed = !m_threw; } else if((m_at & assertType::is_throws_as) && (m_at & assertType::is_throws_with)) { //!OCLINT - m_failed = !m_threw_as || (m_exception != m_exception_string); + m_failed = !m_threw_as || !m_exception_string.check(m_exception); } else if(m_at & assertType::is_throws_as) { //!OCLINT bitwise operator in conditional m_failed = !m_threw_as; } else if(m_at & assertType::is_throws_with) { //!OCLINT bitwise operator in conditional - m_failed = m_exception != m_exception_string; + m_failed = !m_exception_string.check(m_exception); } else if(m_at & assertType::is_nothrow) { //!OCLINT bitwise operator in conditional m_failed = m_threw; } if(m_exception.size()) - m_exception = String("\"") + m_exception + "\""; + m_exception = "\"" + m_exception + "\""; if(is_running_in_test) { addAssert(m_at); @@ -4179,8 +4955,8 @@ namespace detail { failed_out_of_a_testing_context(*this); } - return m_failed && isDebuggerActive() && - !getContextOptions()->no_breaks; // break into debugger + return m_failed && isDebuggerActive() && !getContextOptions()->no_breaks && + (g_cs->currentTest == nullptr || !g_cs->currentTest->m_no_breaks); // break into debugger } void ResultBuilder::react() const { @@ -4195,8 +4971,8 @@ namespace detail { std::abort(); } - void decomp_assert(assertType::Enum at, const char* file, int line, const char* expr, - Result result) { + bool decomp_assert(assertType::Enum at, const char* file, int line, const char* expr, + const Result& result) { bool failed = !result.m_passed; // ################################################################################### @@ -4205,20 +4981,29 @@ namespace detail { // ################################################################################### DOCTEST_ASSERT_OUT_OF_TESTS(result.m_decomp); DOCTEST_ASSERT_IN_TESTS(result.m_decomp); + return !failed; } MessageBuilder::MessageBuilder(const char* file, int line, assertType::Enum severity) { - m_stream = getTlsOss(); + m_stream = tlssPush(); m_file = file; m_line = line; m_severity = severity; } - IExceptionTranslator::IExceptionTranslator() = default; - IExceptionTranslator::~IExceptionTranslator() = default; + MessageBuilder::~MessageBuilder() { + if (!logged) + tlssPop(); + } + + DOCTEST_DEFINE_INTERFACE(IExceptionTranslator) bool MessageBuilder::log() { - m_string = getTlsOssResult(); + if (!logged) { + m_string = tlssPop(); + logged = true; + } + DOCTEST_ITERATE_THROUGH_REPORTERS(log_message, *this); const bool isWarn = m_severity & assertType::is_warn; @@ -4229,36 +5014,18 @@ namespace detail { addFailedAssert(m_severity); } - return isDebuggerActive() && !getContextOptions()->no_breaks && !isWarn; // break + return isDebuggerActive() && !getContextOptions()->no_breaks && !isWarn && + (g_cs->currentTest == nullptr || !g_cs->currentTest->m_no_breaks); // break into debugger } void MessageBuilder::react() { if(m_severity & assertType::is_require) //!OCLINT bitwise operator in conditional throwException(); } - - MessageBuilder::~MessageBuilder() = default; } // namespace detail namespace { using namespace detail; - template - DOCTEST_NORETURN void throw_exception(Ex const& e) { -#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS - throw e; -#else // DOCTEST_CONFIG_NO_EXCEPTIONS - std::cerr << "doctest will terminate because it needed to throw an exception.\n" - << "The message was: " << e.what() << '\n'; - std::terminate(); -#endif // DOCTEST_CONFIG_NO_EXCEPTIONS - } - -#ifndef DOCTEST_INTERNAL_ERROR -#define DOCTEST_INTERNAL_ERROR(msg) \ - throw_exception(std::logic_error( \ - __FILE__ ":" DOCTEST_TOSTR(__LINE__) ": Internal doctest error: " msg)) -#endif // DOCTEST_INTERNAL_ERROR - // clang-format off // ================================================================================================= @@ -4305,7 +5072,11 @@ namespace { mutable XmlWriter* m_writer = nullptr; }; +#ifndef DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM XmlWriter( std::ostream& os = std::cout ); +#else // DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM + XmlWriter( std::ostream& os ); +#endif // DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM ~XmlWriter(); XmlWriter( XmlWriter const& ) = delete; @@ -4340,10 +5111,10 @@ namespace { void ensureTagClosed(); - private: - void writeDeclaration(); + private: + void newlineIfNecessary(); bool m_tagIsOpen = false; @@ -4532,7 +5303,7 @@ namespace { XmlWriter::XmlWriter( std::ostream& os ) : m_os( os ) { - writeDeclaration(); + // writeDeclaration(); // called explicitly by the reporters that use the writer class - see issue #627 } XmlWriter::~XmlWriter() { @@ -4643,8 +5414,8 @@ namespace { struct XmlReporter : public IReporter { - XmlWriter xml; - std::mutex mutex; + XmlWriter xml; + DOCTEST_DECLARE_MUTEX(mutex) // caching pointers/references to objects of these types - safe to do const ContextOptions& opt; @@ -4721,7 +5492,8 @@ namespace { xml.scopedElement("TestCase").writeAttribute("name", in.data[i]->m_name) .writeAttribute("testsuite", in.data[i]->m_test_suite) .writeAttribute("filename", skipPathFromFilename(in.data[i]->m_file.c_str())) - .writeAttribute("line", line(in.data[i]->m_line)); + .writeAttribute("line", line(in.data[i]->m_line)) + .writeAttribute("skipped", in.data[i]->m_skip); } xml.scopedElement("OverallResultsTestCases") .writeAttribute("unskipped", in.run_stats->numTestCasesPassingFilters); @@ -4737,6 +5509,8 @@ namespace { } void test_run_start() override { + xml.writeDeclaration(); + // remove .exe extension - mainly to have the same output on UNIX and Windows std::string binary_name = skipPathFromFilename(opt.binary_name.c_str()); #ifdef DOCTEST_PLATFORM_WINDOWS @@ -4784,14 +5558,15 @@ namespace { test_case_start_impl(in); xml.ensureTagClosed(); } - + void test_case_reenter(const TestCaseData&) override {} void test_case_end(const CurrentTestCaseStats& st) override { xml.startElement("OverallResultsAsserts") .writeAttribute("successes", st.numAssertsCurrentTest - st.numAssertsFailedCurrentTest) - .writeAttribute("failures", st.numAssertsFailedCurrentTest); + .writeAttribute("failures", st.numAssertsFailedCurrentTest) + .writeAttribute("test_case_success", st.testCaseSuccess); if(opt.duration) xml.writeAttribute("duration", st.seconds); if(tc->m_expected_failures) @@ -4802,7 +5577,7 @@ namespace { } void test_case_exception(const TestCaseException& e) override { - std::lock_guard lock(mutex); + DOCTEST_LOCK_MUTEX(mutex) xml.scopedElement("Exception") .writeAttribute("crash", e.is_crash) @@ -4810,8 +5585,6 @@ namespace { } void subcase_start(const SubcaseSignature& in) override { - std::lock_guard lock(mutex); - xml.startElement("SubCase") .writeAttribute("name", in.m_name) .writeAttribute("filename", skipPathFromFilename(in.m_file)) @@ -4825,7 +5598,7 @@ namespace { if(!rb.m_failed && !opt.success) return; - std::lock_guard lock(mutex); + DOCTEST_LOCK_MUTEX(mutex) xml.startElement("Expression") .writeAttribute("success", !rb.m_failed) @@ -4841,7 +5614,7 @@ namespace { if(rb.m_at & assertType::is_throws_as) xml.scopedElement("ExpectedException").writeText(rb.m_exception_type); if(rb.m_at & assertType::is_throws_with) - xml.scopedElement("ExpectedExceptionString").writeText(rb.m_exception_string); + xml.scopedElement("ExpectedExceptionString").writeText(rb.m_exception_string.c_str()); if((rb.m_at & assertType::is_normal) && !rb.m_threw) xml.scopedElement("Expanded").writeText(rb.m_decomp.c_str()); @@ -4851,7 +5624,7 @@ namespace { } void log_message(const MessageData& mb) override { - std::lock_guard lock(mutex); + DOCTEST_LOCK_MUTEX(mutex) xml.startElement("Message") .writeAttribute("type", failureString(mb.m_severity)) @@ -4887,7 +5660,8 @@ namespace { } else if((rb.m_at & assertType::is_throws_as) && (rb.m_at & assertType::is_throws_with)) { //!OCLINT s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", \"" - << rb.m_exception_string << "\", " << rb.m_exception_type << " ) " << Color::None; + << rb.m_exception_string.c_str() + << "\", " << rb.m_exception_type << " ) " << Color::None; if(rb.m_threw) { if(!rb.m_failed) { s << "threw as expected!\n"; @@ -4908,7 +5682,8 @@ namespace { } else if(rb.m_at & assertType::is_throws_with) { //!OCLINT bitwise operator in conditional s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", \"" - << rb.m_exception_string << "\" ) " << Color::None + << rb.m_exception_string.c_str() + << "\" ) " << Color::None << (rb.m_threw ? (!rb.m_failed ? "threw as expected!" : "threw a DIFFERENT exception: ") : "did NOT throw at all!") @@ -4927,21 +5702,19 @@ namespace { } // TODO: - // - log_contexts() // - log_message() // - respond to queries // - honor remaining options // - more attributes in tags struct JUnitReporter : public IReporter { - XmlWriter xml; - std::mutex mutex; + XmlWriter xml; + DOCTEST_DECLARE_MUTEX(mutex) Timer timer; std::vector deepestSubcaseStackNames; struct JUnitTestCaseData { -DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") // gmtime static std::string getCurrentTimestamp() { // Beware, this is not reentrant because of backward compatibility issues // Also, UTC only, again because of backward compatibility (%z is C++11) @@ -4949,16 +5722,19 @@ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") // gmtime std::time(&rawtime); auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); - std::tm* timeInfo; - timeInfo = std::gmtime(&rawtime); + std::tm timeInfo; +#ifdef DOCTEST_PLATFORM_WINDOWS + gmtime_s(&timeInfo, &rawtime); +#else // DOCTEST_PLATFORM_WINDOWS + gmtime_r(&rawtime, &timeInfo); +#endif // DOCTEST_PLATFORM_WINDOWS char timeStamp[timeStampSize]; const char* const fmt = "%Y-%m-%dT%H:%M:%SZ"; - std::strftime(timeStamp, timeStampSize, fmt, timeInfo); + std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); return std::string(timeStamp); } -DOCTEST_CLANG_SUPPRESS_WARNING_POP struct JUnitTestMessage { @@ -5029,9 +5805,13 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP // WHAT FOLLOWS ARE OVERRIDES OF THE VIRTUAL METHODS OF THE REPORTER INTERFACE // ========================================================================================= - void report_query(const QueryData&) override {} + void report_query(const QueryData&) override { + xml.writeDeclaration(); + } - void test_run_start() override {} + void test_run_start() override { + xml.writeDeclaration(); + } void test_run_end(const TestRunStats& p) override { // remove .exe extension - mainly to have the same output on UNIX and Windows @@ -5101,12 +5881,11 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP } void test_case_exception(const TestCaseException& e) override { - std::lock_guard lock(mutex); + DOCTEST_LOCK_MUTEX(mutex) testCaseData.addError("exception", e.error_string.c_str()); } void subcase_start(const SubcaseSignature& in) override { - std::lock_guard lock(mutex); deepestSubcaseStackNames.push_back(in.m_name); } @@ -5116,19 +5895,34 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP if(!rb.m_failed) // report only failures & ignore the `success` option return; - std::lock_guard lock(mutex); + DOCTEST_LOCK_MUTEX(mutex) std::ostringstream os; os << skipPathFromFilename(rb.m_file) << (opt.gnu_file_line ? ":" : "(") << line(rb.m_line) << (opt.gnu_file_line ? ":" : "):") << std::endl; fulltext_log_assert_to_stream(os, rb); + log_contexts(os); testCaseData.addFailure(rb.m_decomp.c_str(), assertString(rb.m_at), os.str()); } void log_message(const MessageData&) override {} void test_case_skipped(const TestCaseData&) override {} + + void log_contexts(std::ostringstream& s) { + int num_contexts = get_num_active_contexts(); + if(num_contexts) { + auto contexts = get_active_contexts(); + + s << " logged: "; + for(int i = 0; i < num_contexts; ++i) { + s << (i == 0 ? "" : " "); + contexts[i]->stringify(&s); + s << std::endl; + } + } + } }; DOCTEST_REGISTER_REPORTER("junit", 0, JUnitReporter); @@ -5152,7 +5946,7 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP bool hasLoggedCurrentTestStart; std::vector subcasesStack; size_t currentSubcaseLevel; - std::mutex mutex; + DOCTEST_DECLARE_MUTEX(mutex) // caching pointers/references to objects of these types - safe to do const ContextOptions& opt; @@ -5257,9 +6051,11 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP } void printIntro() { - printVersion(); - s << Color::Cyan << "[doctest] " << Color::None - << "run with \"--" DOCTEST_OPTIONS_PREFIX_DISPLAY "help\" for options\n"; + if(opt.no_intro == false) { + printVersion(); + s << Color::Cyan << "[doctest] " << Color::None + << "run with \"--" DOCTEST_OPTIONS_PREFIX_DISPLAY "help\" for options\n"; + } } void printHelp() { @@ -5321,7 +6117,7 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP << Whitespace(sizePrefixDisplay*1) << "output filename\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ob, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "order-by= " << Whitespace(sizePrefixDisplay*1) << "how the tests should be ordered\n"; - s << Whitespace(sizePrefixDisplay*3) << " - by [file/suite/name/rand]\n"; + s << Whitespace(sizePrefixDisplay*3) << " - [file/suite/name/rand/none]\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "rs, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "rand-seed= " << Whitespace(sizePrefixDisplay*1) << "seed for random ordering\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "f, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "first= " @@ -5344,12 +6140,18 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP << Whitespace(sizePrefixDisplay*1) << "exits after the tests finish\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "d, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "duration= " << Whitespace(sizePrefixDisplay*1) << "prints the time duration of each test\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "m, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "minimal= " + << Whitespace(sizePrefixDisplay*1) << "minimal console output (only failures)\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "q, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "quiet= " + << Whitespace(sizePrefixDisplay*1) << "no console output\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nt, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-throw= " << Whitespace(sizePrefixDisplay*1) << "skips exceptions-related assert checks\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ne, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-exitcode= " << Whitespace(sizePrefixDisplay*1) << "returns (or exits) always with success\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nr, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-run= " << Whitespace(sizePrefixDisplay*1) << "skips all runtime doctest operations\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ni, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-intro= " + << Whitespace(sizePrefixDisplay*1) << "omit the framework intro in the output\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nv, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-version= " << Whitespace(sizePrefixDisplay*1) << "omit the framework version in the output\n"; s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-colors= " @@ -5387,22 +6189,6 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP printReporters(getReporters(), "reporters"); } - void list_query_results() { - separator_to_stream(); - if(opt.count || opt.list_test_cases) { - s << Color::Cyan << "[doctest] " << Color::None - << "unskipped test cases passing the current filters: " - << g_cs->numTestCasesPassingFilters << "\n"; - } else if(opt.list_test_suites) { - s << Color::Cyan << "[doctest] " << Color::None - << "unskipped test cases passing the current filters: " - << g_cs->numTestCasesPassingFilters << "\n"; - s << Color::Cyan << "[doctest] " << Color::None - << "test suites with unskipped test cases passing the current filters: " - << g_cs->numTestSuitesPassingFilters << "\n"; - } - } - // ========================================================================================= // WHAT FOLLOWS ARE OVERRIDES OF THE VIRTUAL METHODS OF THE REPORTER INTERFACE // ========================================================================================= @@ -5448,31 +6234,40 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP } } - void test_run_start() override { printIntro(); } + void test_run_start() override { + if(!opt.minimal) + printIntro(); + } void test_run_end(const TestRunStats& p) override { + if(opt.minimal && p.numTestCasesFailed == 0) + return; + separator_to_stream(); s << std::dec; + auto totwidth = int(std::ceil(log10((std::max(p.numTestCasesPassingFilters, static_cast(p.numAsserts))) + 1))); + auto passwidth = int(std::ceil(log10((std::max(p.numTestCasesPassingFilters - p.numTestCasesFailed, static_cast(p.numAsserts - p.numAssertsFailed))) + 1))); + auto failwidth = int(std::ceil(log10((std::max(p.numTestCasesFailed, static_cast(p.numAssertsFailed))) + 1))); const bool anythingFailed = p.numTestCasesFailed > 0 || p.numAssertsFailed > 0; - s << Color::Cyan << "[doctest] " << Color::None << "test cases: " << std::setw(6) + s << Color::Cyan << "[doctest] " << Color::None << "test cases: " << std::setw(totwidth) << p.numTestCasesPassingFilters << " | " << ((p.numTestCasesPassingFilters == 0 || anythingFailed) ? Color::None : Color::Green) - << std::setw(6) << p.numTestCasesPassingFilters - p.numTestCasesFailed << " passed" + << std::setw(passwidth) << p.numTestCasesPassingFilters - p.numTestCasesFailed << " passed" << Color::None << " | " << (p.numTestCasesFailed > 0 ? Color::Red : Color::None) - << std::setw(6) << p.numTestCasesFailed << " failed" << Color::None << " | "; + << std::setw(failwidth) << p.numTestCasesFailed << " failed" << Color::None << " |"; if(opt.no_skipped_summary == false) { const int numSkipped = p.numTestCases - p.numTestCasesPassingFilters; - s << (numSkipped == 0 ? Color::None : Color::Yellow) << std::setw(6) << numSkipped + s << " " << (numSkipped == 0 ? Color::None : Color::Yellow) << numSkipped << " skipped" << Color::None; } s << "\n"; - s << Color::Cyan << "[doctest] " << Color::None << "assertions: " << std::setw(6) + s << Color::Cyan << "[doctest] " << Color::None << "assertions: " << std::setw(totwidth) << p.numAsserts << " | " << ((p.numAsserts == 0 || anythingFailed) ? Color::None : Color::Green) - << std::setw(6) << (p.numAsserts - p.numAssertsFailed) << " passed" << Color::None - << " | " << (p.numAssertsFailed > 0 ? Color::Red : Color::None) << std::setw(6) + << std::setw(passwidth) << (p.numAsserts - p.numAssertsFailed) << " passed" << Color::None + << " | " << (p.numAssertsFailed > 0 ? Color::Red : Color::None) << std::setw(failwidth) << p.numAssertsFailed << " failed" << Color::None << " |\n"; s << Color::Cyan << "[doctest] " << Color::None << "Status: " << (p.numTestCasesFailed > 0 ? Color::Red : Color::Green) @@ -5485,16 +6280,19 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP subcasesStack.clear(); currentSubcaseLevel = 0; } - + void test_case_reenter(const TestCaseData&) override { subcasesStack.clear(); } void test_case_end(const CurrentTestCaseStats& st) override { + if(tc->m_no_output) + return; + // log the preamble of the test case only if there is something // else to print - something other than that an assert has failed if(opt.duration || - (st.failure_flags && st.failure_flags != TestCaseFailureReason::AssertFailure)) + (st.failure_flags && st.failure_flags != static_cast(TestCaseFailureReason::AssertFailure))) logTestStart(); if(opt.duration) @@ -5525,6 +6323,10 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP } void test_case_exception(const TestCaseException& e) override { + DOCTEST_LOCK_MUTEX(mutex) + if(tc->m_no_output) + return; + logTestStart(); file_line_to_stream(tc->m_file.c_str(), tc->m_line, " "); @@ -5546,23 +6348,21 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP } void subcase_start(const SubcaseSignature& subc) override { - std::lock_guard lock(mutex); subcasesStack.push_back(subc); ++currentSubcaseLevel; hasLoggedCurrentTestStart = false; } void subcase_end() override { - std::lock_guard lock(mutex); --currentSubcaseLevel; hasLoggedCurrentTestStart = false; } void log_assert(const AssertData& rb) override { - if(!rb.m_failed && !opt.success) + if((!rb.m_failed && !opt.success) || tc->m_no_output) return; - std::lock_guard lock(mutex); + DOCTEST_LOCK_MUTEX(mutex) logTestStart(); @@ -5575,7 +6375,10 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP } void log_message(const MessageData& mb) override { - std::lock_guard lock(mutex); + if(tc->m_no_output) + return; + + DOCTEST_LOCK_MUTEX(mutex) logTestStart(); @@ -5605,8 +6408,10 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP bool with_col = g_no_colors; \ g_no_colors = false; \ ConsoleReporter::func(arg); \ - DOCTEST_OUTPUT_DEBUG_STRING(oss.str().c_str()); \ - oss.str(""); \ + if(oss.tellp() != std::streampos{}) { \ + DOCTEST_OUTPUT_DEBUG_STRING(oss.str().c_str()); \ + oss.str(""); \ + } \ g_no_colors = with_col; \ } @@ -5684,18 +6489,42 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP std::vector& res) { String filtersString; if(parseOption(argc, argv, pattern, &filtersString)) { - // tokenize with "," as a separator - // cppcheck-suppress strtokCalled - DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") - auto pch = std::strtok(filtersString.c_str(), ","); // modifies the string - while(pch != nullptr) { - if(strlen(pch)) - res.push_back(pch); - // uses the strtok() internal state to go to the next token - // cppcheck-suppress strtokCalled - pch = std::strtok(nullptr, ","); + // tokenize with "," as a separator, unless escaped with backslash + std::ostringstream s; + auto flush = [&s, &res]() { + auto string = s.str(); + if(string.size() > 0) { + res.push_back(string.c_str()); + } + s.str(""); + }; + + bool seenBackslash = false; + const char* current = filtersString.c_str(); + const char* end = current + strlen(current); + while(current != end) { + char character = *current++; + if(seenBackslash) { + seenBackslash = false; + if(character == ',' || character == '\\') { + s.put(character); + continue; + } + s.put('\\'); + } + if(character == '\\') { + seenBackslash = true; + } else if(character == ',') { + flush(); + } else { + s.put(character); + } + } + + if(seenBackslash) { + s.put('\\'); } - DOCTEST_CLANG_SUPPRESS_WARNING_POP + flush(); return true; } return false; @@ -5714,30 +6543,30 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP if(!parseOption(argc, argv, pattern, &parsedValue)) return false; - if(type == 0) { + if(type) { + // integer + // TODO: change this to use std::stoi or something else! currently it uses undefined behavior - assumes '0' on failed parse... + int theInt = std::atoi(parsedValue.c_str()); + if (theInt != 0) { + res = theInt; //!OCLINT parameter reassignment + return true; + } + } else { // boolean - const char positive[][5] = {"1", "true", "on", "yes"}; // 5 - strlen("true") + 1 - const char negative[][6] = {"0", "false", "off", "no"}; // 6 - strlen("false") + 1 + const char positive[][5] = { "1", "true", "on", "yes" }; // 5 - strlen("true") + 1 + const char negative[][6] = { "0", "false", "off", "no" }; // 6 - strlen("false") + 1 // if the value matches any of the positive/negative possibilities - for(unsigned i = 0; i < 4; i++) { - if(parsedValue.compare(positive[i], true) == 0) { + for (unsigned i = 0; i < 4; i++) { + if (parsedValue.compare(positive[i], true) == 0) { res = 1; //!OCLINT parameter reassignment return true; } - if(parsedValue.compare(negative[i], true) == 0) { + if (parsedValue.compare(negative[i], true) == 0) { res = 0; //!OCLINT parameter reassignment return true; } } - } else { - // integer - // TODO: change this to use std::stoi or something else! currently it uses undefined behavior - assumes '0' on failed parse... - int theInt = std::atoi(parsedValue.c_str()); // NOLINT - if(theInt != 0) { - res = theInt; //!OCLINT parameter reassignment - return true; - } } return false; } @@ -5793,7 +6622,7 @@ void Context::parseArgs(int argc, const char* const* argv, bool withDefaults) { #define DOCTEST_PARSE_AS_BOOL_OR_FLAG(name, sname, var, default) \ if(parseIntOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX name "=", option_bool, intRes) || \ parseIntOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX sname "=", option_bool, intRes)) \ - p->var = !!intRes; \ + p->var = static_cast(intRes); \ else if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX name) || \ parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX sname)) \ p->var = true; \ @@ -5828,9 +6657,12 @@ void Context::parseArgs(int argc, const char* const* argv, bool withDefaults) { DOCTEST_PARSE_AS_BOOL_OR_FLAG("case-sensitive", "cs", case_sensitive, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("exit", "e", exit, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("duration", "d", duration, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("minimal", "m", minimal, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("quiet", "q", quiet, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-throw", "nt", no_throw, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-exitcode", "ne", no_exitcode, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-run", "nr", no_run, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-intro", "ni", no_intro, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-version", "nv", no_version, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-colors", "nc", no_colors, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("force-colors", "fc", force_colors, false); @@ -5839,6 +6671,7 @@ void Context::parseArgs(int argc, const char* const* argv, bool withDefaults) { DOCTEST_PARSE_AS_BOOL_OR_FLAG("gnu-file-line", "gfl", gnu_file_line, !bool(DOCTEST_MSVC)); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-path-filenames", "npf", no_path_in_filenames, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-line-numbers", "nln", no_line_numbers, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-debug-output", "ndo", no_debug_output, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-skipped-summary", "nss", no_skipped_summary, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-time-in-output", "ntio", no_time_in_output, false); // clang-format on @@ -5893,7 +6726,12 @@ void Context::clearFilters() { curr.clear(); } -// allows the user to override procedurally the int/bool options from the command line +// allows the user to override procedurally the bool options from the command line +void Context::setOption(const char* option, bool value) { + setOption(option, value ? "true" : "false"); +} + +// allows the user to override procedurally the int options from the command line void Context::setOption(const char* option, int value) { setOption(option, toString(value).c_str()); } @@ -5912,6 +6750,31 @@ void Context::setAsDefaultForAssertsOutOfTestCases() { g_cs = p; } void Context::setAssertHandler(detail::assert_handler ah) { p->ah = ah; } +void Context::setCout(std::ostream* out) { p->cout = out; } + +static class DiscardOStream : public std::ostream +{ +private: + class : public std::streambuf + { + private: + // allowing some buffering decreases the amount of calls to overflow + char buf[1024]; + + protected: + std::streamsize xsputn(const char_type*, std::streamsize count) override { return count; } + + int_type overflow(int_type ch) override { + setp(std::begin(buf), std::end(buf)); + return traits_type::not_eof(ch); + } + } discardBuf; + +public: + DiscardOStream() + : std::ostream(&discardBuf) {} +} discardOut; + // the main function that does all the filtering and test running int Context::run() { using namespace detail; @@ -5925,18 +6788,29 @@ int Context::run() { g_no_colors = p->no_colors; p->resetRunData(); - // stdout by default - p->cout = &std::cout; - p->cerr = &std::cerr; - - // or to a file if specified std::fstream fstr; - if(p->out.size()) { - fstr.open(p->out.c_str(), std::fstream::out); - p->cout = &fstr; + if(p->cout == nullptr) { + if(p->quiet) { + p->cout = &discardOut; + } else if(p->out.size()) { + // to a file if specified + fstr.open(p->out.c_str(), std::fstream::out); + p->cout = &fstr; + } else { +#ifndef DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM + // stdout by default + p->cout = &std::cout; +#else // DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM + return EXIT_FAILURE; +#endif // DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM + } } + FatalConditionHandler::allocateAltStackMem(); + auto cleanup_and_return = [&]() { + FatalConditionHandler::freeAltStackMem(); + if(fstr.is_open()) fstr.close(); @@ -5971,7 +6845,7 @@ int Context::run() { p->reporters_currently_used.insert(p->reporters_currently_used.begin(), curr.second(*g_cs)); #ifdef DOCTEST_PLATFORM_WINDOWS - if(isDebuggerActive()) + if(isDebuggerActive() && p->no_debug_output == false) p->reporters_currently_used.push_back(new DebugOutputWindowReporter(*g_cs)); #endif // DOCTEST_PLATFORM_WINDOWS @@ -6001,13 +6875,16 @@ int Context::run() { // random_shuffle implementation const auto first = &testArray[0]; for(size_t i = testArray.size() - 1; i > 0; --i) { - int idxToSwap = std::rand() % (i + 1); // NOLINT + int idxToSwap = std::rand() % (i + 1); const auto temp = first[i]; first[i] = first[idxToSwap]; first[idxToSwap] = temp; } + } else if(p->order_by.compare("none", true) == 0) { + // means no sorting - beneficial for death tests which call into the executable + // with a specific test case in mind - we don't want to slow down the startup times } } @@ -6085,19 +6962,20 @@ int Context::run() { p->numAssertsFailedCurrentTest_atomic = 0; p->numAssertsCurrentTest_atomic = 0; - p->subcasesPassed.clear(); + p->fullyTraversedSubcases.clear(); DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_start, tc); p->timer.start(); - + bool run_test = true; do { // reset some of the fields for subcases (except for the set of fully passed ones) - p->should_reenter = false; - p->subcasesCurrentMaxLevel = 0; - p->subcasesStack.clear(); + p->reachedLeaf = false; + // May not be empty if previous subcase exited via exception. + p->subcaseStack.clear(); + p->currentSubcaseDepth = 0; p->shouldLogCurrentException = true; @@ -6107,10 +6985,13 @@ int Context::run() { #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS try { #endif // DOCTEST_CONFIG_NO_EXCEPTIONS +// MSVC 2015 diagnoses fatalConditionHandler as unused (because reset() is a static method) +DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4101) // unreferenced local variable FatalConditionHandler fatalConditionHandler; // Handle signals // execute the test tc.m_test(); fatalConditionHandler.reset(); +DOCTEST_MSVC_SUPPRESS_WARNING_POP #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS } catch(const TestFailureException&) { p->failure_flags |= TestCaseFailureReason::AssertFailure; @@ -6127,10 +7008,10 @@ int Context::run() { run_test = false; p->failure_flags |= TestCaseFailureReason::TooManyFailedAsserts; } - - if(p->should_reenter && run_test) + + if(!p->nextSubcaseStack.empty() && run_test) DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_reenter, tc); - if(!p->should_reenter) + if(p->nextSubcaseStack.empty()) run_test = false; } while(run_test); @@ -6156,17 +7037,10 @@ int Context::run() { DOCTEST_ITERATE_THROUGH_REPORTERS(report_query, qdata); } - // see these issues on the reasoning for this: - // - https://github.com/onqtam/doctest/issues/143#issuecomment-414418903 - // - https://github.com/onqtam/doctest/issues/126 - auto DOCTEST_FIX_FOR_MACOS_LIBCPP_IOSFWD_STRING_LINK_ERRORS = []() DOCTEST_NOINLINE - { std::cout << std::string(); }; - DOCTEST_FIX_FOR_MACOS_LIBCPP_IOSFWD_STRING_LINK_ERRORS(); - return cleanup_and_return(); } -IReporter::~IReporter() = default; +DOCTEST_DEFINE_INTERFACE(IReporter) int IReporter::get_num_active_contexts() { return detail::g_infoContexts.size(); } const IContextScope* const* IReporter::get_active_contexts() { @@ -6201,5 +7075,17 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP DOCTEST_MSVC_SUPPRESS_WARNING_POP DOCTEST_GCC_SUPPRESS_WARNING_POP +DOCTEST_SUPPRESS_COMMON_WARNINGS_POP + #endif // DOCTEST_LIBRARY_IMPLEMENTATION #endif // DOCTEST_CONFIG_IMPLEMENT + +#ifdef DOCTEST_UNDEF_WIN32_LEAN_AND_MEAN +#undef WIN32_LEAN_AND_MEAN +#undef DOCTEST_UNDEF_WIN32_LEAN_AND_MEAN +#endif // DOCTEST_UNDEF_WIN32_LEAN_AND_MEAN + +#ifdef DOCTEST_UNDEF_NOMINMAX +#undef NOMINMAX +#undef DOCTEST_UNDEF_NOMINMAX +#endif // DOCTEST_UNDEF_NOMINMAX