summaryrefslogtreecommitdiff
path: root/json4cpp/tests/thirdparty
diff options
context:
space:
mode:
Diffstat (limited to 'json4cpp/tests/thirdparty')
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/CMakeLists.txt45
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/FuzzerCorpus.h217
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/FuzzerCrossOver.cpp52
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/FuzzerDefs.h89
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/FuzzerDictionary.h124
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/FuzzerDriver.cpp545
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/FuzzerExtFunctions.def50
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/FuzzerExtFunctions.h35
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/FuzzerExtFunctionsDlsym.cpp52
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/FuzzerExtFunctionsWeak.cpp53
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/FuzzerExtFunctionsWeakAlias.cpp56
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/FuzzerFlags.def115
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/FuzzerIO.cpp117
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/FuzzerIO.h64
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/FuzzerIOPosix.cpp88
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/FuzzerIOWindows.cpp282
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/FuzzerInterface.h67
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/FuzzerInternal.h182
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/FuzzerLoop.cpp792
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/FuzzerMain.cpp21
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/FuzzerMerge.cpp261
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/FuzzerMerge.h70
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/FuzzerMutate.cpp527
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/FuzzerMutate.h145
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/FuzzerOptions.h68
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/FuzzerRandom.h36
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/FuzzerSHA1.cpp222
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/FuzzerSHA1.h33
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/FuzzerTracePC.cpp339
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/FuzzerTracePC.h158
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/FuzzerTraceState.cpp325
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/FuzzerUtil.cpp218
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/FuzzerUtil.h72
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/FuzzerUtilDarwin.cpp152
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/FuzzerUtilLinux.cpp24
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/FuzzerUtilPosix.cpp117
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/FuzzerUtilWindows.cpp182
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/FuzzerValueBitMap.h87
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/README.txt2
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/afl/afl_driver.cpp295
-rwxr-xr-xjson4cpp/tests/thirdparty/Fuzzer/build.sh10
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/cxx.dict122
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/standalone/StandaloneFuzzTargetMain.c41
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/AFLDriverTest.cpp22
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/AbsNegAndConstant64Test.cpp23
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/AbsNegAndConstantTest.cpp23
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/AccumulateAllocationsTest.cpp17
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/BufferOverflowOnInput.cpp23
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/CMakeLists.txt217
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/CallerCalleeTest.cpp59
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/CounterTest.cpp18
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/CustomCrossOverTest.cpp63
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/CustomMutatorTest.cpp38
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/DSO1.cpp12
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/DSO2.cpp12
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/DSOTestExtra.cpp11
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/DSOTestMain.cpp31
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/DivTest.cpp20
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/EmptyTest.cpp11
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/FourIndependentBranchesTest.cpp22
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/FullCoverageSetTest.cpp24
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/FuzzerUnittest.cpp738
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/InitializeTest.cpp28
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/LeakTest.cpp17
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/LeakTimeoutTest.cpp17
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/LoadTest.cpp22
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/MemcmpTest.cpp31
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/NthRunCrashTest.cpp18
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/NullDerefOnEmptyTest.cpp19
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/NullDerefTest.cpp26
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/OneHugeAllocTest.cpp28
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/OutOfMemorySingleLargeMallocTest.cpp27
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/OutOfMemoryTest.cpp31
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/RepeatedBytesTest.cpp29
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/RepeatedMemcmp.cpp22
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/ShrinkControlFlowTest.cpp28
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/ShrinkValueProfileTest.cpp22
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/SignedIntOverflowTest.cpp28
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/SimpleCmpTest.cpp46
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/SimpleDictionaryTest.cpp29
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/SimpleHashTest.cpp40
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/SimpleTest.cpp27
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/SimpleThreadedTest.cpp25
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/SingleMemcmpTest.cpp17
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/SingleStrcmpTest.cpp17
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/SingleStrncmpTest.cpp17
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/SpamyTest.cpp21
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/StrcmpTest.cpp32
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/StrncmpOOBTest.cpp21
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/StrncmpTest.cpp28
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/StrstrTest.cpp28
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/SwapCmpTest.cpp34
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/Switch2Test.cpp35
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/SwitchTest.cpp58
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/ThreadedLeakTest.cpp18
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/ThreadedTest.cpp26
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/TimeoutEmptyTest.cpp14
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/TimeoutTest.cpp26
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/TraceMallocTest.cpp27
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/UninstrumentedTest.cpp11
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/afl-driver-extra-stats.test28
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/afl-driver-stderr.test10
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/caller-callee.test2
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/coverage.test19
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/dict1.txt4
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/dump_coverage.test16
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-customcrossover.test10
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-custommutator.test4
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-dict.test6
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-dirs.test15
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-fdmask.test30
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-finalstats.test11
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-flags.test10
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-jobs.test29
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-leak.test35
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-oom-with-profile.test6
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-oom.test11
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-printcovpcs.test8
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-runs.test8
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-seed.test3
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-segv.test5
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-singleinputs.test16
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-threaded.test7
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-timeout.test19
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-traces-hooks.test25
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-ubsan.test4
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/fuzzer.test57
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/hi.txt1
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/lit.cfg29
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/lit.site.cfg.in4
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/merge.test46
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/minimize_crash.test6
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/no-coverage/CMakeLists.txt29
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/repeated-bytes.test2
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/shrink.test7
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/simple-cmp.test2
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/standalone.test4
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/swap-cmp.test2
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/trace-malloc.test10
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/ubsan/CMakeLists.txt15
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/ulimit.test2
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/uninstrumented/CMakeLists.txt16
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/unit/lit.cfg7
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/unit/lit.site.cfg.in2
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/value-profile-cmp.test2
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/value-profile-cmp2.test2
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/value-profile-cmp3.test2
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/value-profile-cmp4.test2
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/value-profile-div.test3
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/value-profile-load.test3
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/value-profile-mem.test2
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/value-profile-set.test3
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/value-profile-strcmp.test2
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/value-profile-strncmp.test2
-rw-r--r--json4cpp/tests/thirdparty/Fuzzer/test/value-profile-switch.test3
-rw-r--r--json4cpp/tests/thirdparty/doctest/doctest.h7135
-rw-r--r--json4cpp/tests/thirdparty/doctest/doctest_compatibility.h37
-rw-r--r--json4cpp/tests/thirdparty/fifo_map/fifo_map.hpp530
-rwxr-xr-xjson4cpp/tests/thirdparty/imapdl/filterbr.py111
159 files changed, 17347 insertions, 0 deletions
diff --git a/json4cpp/tests/thirdparty/Fuzzer/CMakeLists.txt b/json4cpp/tests/thirdparty/Fuzzer/CMakeLists.txt
new file mode 100644
index 0000000000..70bd017bae
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/CMakeLists.txt
@@ -0,0 +1,45 @@
+set(LIBFUZZER_FLAGS_BASE "${CMAKE_CXX_FLAGS}")
+# Disable the coverage and sanitizer instrumentation for the fuzzer itself.
+set(CMAKE_CXX_FLAGS "${LIBFUZZER_FLAGS_BASE} -fno-sanitize-coverage=trace-pc-guard,edge,trace-cmp,indirect-calls,8bit-counters -Werror")
+if( LLVM_USE_SANITIZE_COVERAGE )
+ if(NOT "${LLVM_USE_SANITIZER}" STREQUAL "Address")
+ message(FATAL_ERROR
+ "LibFuzzer and its tests require LLVM_USE_SANITIZER=Address and "
+ "LLVM_USE_SANITIZE_COVERAGE=YES to be set."
+ )
+ endif()
+ add_library(LLVMFuzzerNoMainObjects OBJECT
+ FuzzerCrossOver.cpp
+ FuzzerDriver.cpp
+ FuzzerExtFunctionsDlsym.cpp
+ FuzzerExtFunctionsWeak.cpp
+ FuzzerExtFunctionsWeakAlias.cpp
+ FuzzerIO.cpp
+ FuzzerIOPosix.cpp
+ FuzzerIOWindows.cpp
+ FuzzerLoop.cpp
+ FuzzerMerge.cpp
+ FuzzerMutate.cpp
+ FuzzerSHA1.cpp
+ FuzzerTracePC.cpp
+ FuzzerTraceState.cpp
+ FuzzerUtil.cpp
+ FuzzerUtilDarwin.cpp
+ FuzzerUtilLinux.cpp
+ FuzzerUtilPosix.cpp
+ FuzzerUtilWindows.cpp
+ )
+ add_library(LLVMFuzzerNoMain STATIC
+ $<TARGET_OBJECTS:LLVMFuzzerNoMainObjects>
+ )
+ target_link_libraries(LLVMFuzzerNoMain ${PTHREAD_LIB})
+ add_library(LLVMFuzzer STATIC
+ FuzzerMain.cpp
+ $<TARGET_OBJECTS:LLVMFuzzerNoMainObjects>
+ )
+ target_link_libraries(LLVMFuzzer ${PTHREAD_LIB})
+
+ if( LLVM_INCLUDE_TESTS )
+ add_subdirectory(test)
+ endif()
+endif()
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerCorpus.h b/json4cpp/tests/thirdparty/Fuzzer/FuzzerCorpus.h
new file mode 100644
index 0000000000..663c5854b4
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerCorpus.h
@@ -0,0 +1,217 @@
+//===- FuzzerCorpus.h - Internal header for the Fuzzer ----------*- C++ -* ===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// fuzzer::InputCorpus
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_FUZZER_CORPUS
+#define LLVM_FUZZER_CORPUS
+
+#include "FuzzerDefs.h"
+#include "FuzzerIO.h"
+#include "FuzzerRandom.h"
+#include "FuzzerSHA1.h"
+#include "FuzzerTracePC.h"
+#include <numeric>
+#include <random>
+#include <unordered_set>
+
+namespace fuzzer {
+
+struct InputInfo {
+ Unit U; // The actual input data.
+ uint8_t Sha1[kSHA1NumBytes]; // Checksum.
+ // Number of features that this input has and no smaller input has.
+ size_t NumFeatures = 0;
+ size_t Tmp = 0; // Used by ValidateFeatureSet.
+ // Stats.
+ size_t NumExecutedMutations = 0;
+ size_t NumSuccessfullMutations = 0;
+ bool MayDeleteFile = false;
+};
+
+class InputCorpus {
+ public:
+ static const size_t kFeatureSetSize = 1 << 16;
+ InputCorpus(const std::string &OutputCorpus) : OutputCorpus(OutputCorpus) {
+ memset(InputSizesPerFeature, 0, sizeof(InputSizesPerFeature));
+ memset(SmallestElementPerFeature, 0, sizeof(SmallestElementPerFeature));
+ }
+ ~InputCorpus() {
+ for (auto II : Inputs)
+ delete II;
+ }
+ size_t size() const { return Inputs.size(); }
+ size_t SizeInBytes() const {
+ size_t Res = 0;
+ for (auto II : Inputs)
+ Res += II->U.size();
+ return Res;
+ }
+ size_t NumActiveUnits() const {
+ size_t Res = 0;
+ for (auto II : Inputs)
+ Res += !II->U.empty();
+ return Res;
+ }
+ bool empty() const { return Inputs.empty(); }
+ const Unit &operator[] (size_t Idx) const { return Inputs[Idx]->U; }
+ void AddToCorpus(const Unit &U, size_t NumFeatures, bool MayDeleteFile = false) {
+ assert(!U.empty());
+ uint8_t Hash[kSHA1NumBytes];
+ if (FeatureDebug)
+ Printf("ADD_TO_CORPUS %zd NF %zd\n", Inputs.size(), NumFeatures);
+ ComputeSHA1(U.data(), U.size(), Hash);
+ Hashes.insert(Sha1ToString(Hash));
+ Inputs.push_back(new InputInfo());
+ InputInfo &II = *Inputs.back();
+ II.U = U;
+ II.NumFeatures = NumFeatures;
+ II.MayDeleteFile = MayDeleteFile;
+ memcpy(II.Sha1, Hash, kSHA1NumBytes);
+ UpdateCorpusDistribution();
+ ValidateFeatureSet();
+ }
+
+ bool HasUnit(const Unit &U) { return Hashes.count(Hash(U)); }
+ bool HasUnit(const std::string &H) { return Hashes.count(H); }
+ InputInfo &ChooseUnitToMutate(Random &Rand) {
+ InputInfo &II = *Inputs[ChooseUnitIdxToMutate(Rand)];
+ assert(!II.U.empty());
+ return II;
+ };
+
+ // Returns an index of random unit from the corpus to mutate.
+ // Hypothesis: units added to the corpus last are more likely to be
+ // interesting. This function gives more weight to the more recent units.
+ size_t ChooseUnitIdxToMutate(Random &Rand) {
+ size_t Idx = static_cast<size_t>(CorpusDistribution(Rand.Get_mt19937()));
+ assert(Idx < Inputs.size());
+ return Idx;
+ }
+
+ void PrintStats() {
+ for (size_t i = 0; i < Inputs.size(); i++) {
+ const auto &II = *Inputs[i];
+ Printf(" [%zd %s]\tsz: %zd\truns: %zd\tsucc: %zd\n", i,
+ Sha1ToString(II.Sha1).c_str(), II.U.size(),
+ II.NumExecutedMutations, II.NumSuccessfullMutations);
+ }
+ }
+
+ void PrintFeatureSet() {
+ for (size_t i = 0; i < kFeatureSetSize; i++) {
+ if(size_t Sz = GetFeature(i))
+ Printf("[%zd: id %zd sz%zd] ", i, SmallestElementPerFeature[i], Sz);
+ }
+ Printf("\n\t");
+ for (size_t i = 0; i < Inputs.size(); i++)
+ if (size_t N = Inputs[i]->NumFeatures)
+ Printf(" %zd=>%zd ", i, N);
+ Printf("\n");
+ }
+
+ void DeleteInput(size_t Idx) {
+ InputInfo &II = *Inputs[Idx];
+ if (!OutputCorpus.empty() && II.MayDeleteFile)
+ RemoveFile(DirPlusFile(OutputCorpus, Sha1ToString(II.Sha1)));
+ Unit().swap(II.U);
+ if (FeatureDebug)
+ Printf("EVICTED %zd\n", Idx);
+ }
+
+ bool AddFeature(size_t Idx, uint32_t NewSize, bool Shrink) {
+ assert(NewSize);
+ Idx = Idx % kFeatureSetSize;
+ uint32_t OldSize = GetFeature(Idx);
+ if (OldSize == 0 || (Shrink && OldSize > NewSize)) {
+ if (OldSize > 0) {
+ size_t OldIdx = SmallestElementPerFeature[Idx];
+ InputInfo &II = *Inputs[OldIdx];
+ assert(II.NumFeatures > 0);
+ II.NumFeatures--;
+ if (II.NumFeatures == 0)
+ DeleteInput(OldIdx);
+ }
+ if (FeatureDebug)
+ Printf("ADD FEATURE %zd sz %d\n", Idx, NewSize);
+ SmallestElementPerFeature[Idx] = Inputs.size();
+ InputSizesPerFeature[Idx] = NewSize;
+ CountingFeatures = true;
+ return true;
+ }
+ return false;
+ }
+
+ size_t NumFeatures() const {
+ size_t Res = 0;
+ for (size_t i = 0; i < kFeatureSetSize; i++)
+ Res += GetFeature(i) != 0;
+ return Res;
+ }
+
+ void ResetFeatureSet() {
+ assert(Inputs.empty());
+ memset(InputSizesPerFeature, 0, sizeof(InputSizesPerFeature));
+ memset(SmallestElementPerFeature, 0, sizeof(SmallestElementPerFeature));
+ }
+
+private:
+
+ static const bool FeatureDebug = false;
+
+ size_t GetFeature(size_t Idx) const { return InputSizesPerFeature[Idx]; }
+
+ void ValidateFeatureSet() {
+ if (!CountingFeatures) return;
+ if (FeatureDebug)
+ PrintFeatureSet();
+ for (size_t Idx = 0; Idx < kFeatureSetSize; Idx++)
+ if (GetFeature(Idx))
+ Inputs[SmallestElementPerFeature[Idx]]->Tmp++;
+ for (auto II: Inputs) {
+ if (II->Tmp != II->NumFeatures)
+ Printf("ZZZ %zd %zd\n", II->Tmp, II->NumFeatures);
+ assert(II->Tmp == II->NumFeatures);
+ II->Tmp = 0;
+ }
+ }
+
+ // Updates the probability distribution for the units in the corpus.
+ // Must be called whenever the corpus or unit weights are changed.
+ void UpdateCorpusDistribution() {
+ size_t N = Inputs.size();
+ Intervals.resize(N + 1);
+ Weights.resize(N);
+ std::iota(Intervals.begin(), Intervals.end(), 0);
+ if (CountingFeatures)
+ for (size_t i = 0; i < N; i++)
+ Weights[i] = Inputs[i]->NumFeatures * (i + 1);
+ else
+ std::iota(Weights.begin(), Weights.end(), 1);
+ CorpusDistribution = std::piecewise_constant_distribution<double>(
+ Intervals.begin(), Intervals.end(), Weights.begin());
+ }
+ std::piecewise_constant_distribution<double> CorpusDistribution;
+
+ std::vector<double> Intervals;
+ std::vector<double> Weights;
+
+ std::unordered_set<std::string> Hashes;
+ std::vector<InputInfo*> Inputs;
+
+ bool CountingFeatures = false;
+ uint32_t InputSizesPerFeature[kFeatureSetSize];
+ uint32_t SmallestElementPerFeature[kFeatureSetSize];
+
+ std::string OutputCorpus;
+};
+
+} // namespace fuzzer
+
+#endif // LLVM_FUZZER_CORPUS
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerCrossOver.cpp b/json4cpp/tests/thirdparty/Fuzzer/FuzzerCrossOver.cpp
new file mode 100644
index 0000000000..8b0fd7d529
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerCrossOver.cpp
@@ -0,0 +1,52 @@
+//===- FuzzerCrossOver.cpp - Cross over two test inputs -------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// Cross over test inputs.
+//===----------------------------------------------------------------------===//
+
+#include "FuzzerDefs.h"
+#include "FuzzerMutate.h"
+#include "FuzzerRandom.h"
+#include <cstring>
+
+namespace fuzzer {
+
+// Cross Data1 and Data2, store the result (up to MaxOutSize bytes) in Out.
+size_t MutationDispatcher::CrossOver(const uint8_t *Data1, size_t Size1,
+ const uint8_t *Data2, size_t Size2,
+ uint8_t *Out, size_t MaxOutSize) {
+ assert(Size1 || Size2);
+ MaxOutSize = Rand(MaxOutSize) + 1;
+ size_t OutPos = 0;
+ size_t Pos1 = 0;
+ size_t Pos2 = 0;
+ size_t *InPos = &Pos1;
+ size_t InSize = Size1;
+ const uint8_t *Data = Data1;
+ bool CurrentlyUsingFirstData = true;
+ while (OutPos < MaxOutSize && (Pos1 < Size1 || Pos2 < Size2)) {
+ // Merge a part of Data into Out.
+ size_t OutSizeLeft = MaxOutSize - OutPos;
+ if (*InPos < InSize) {
+ size_t InSizeLeft = InSize - *InPos;
+ size_t MaxExtraSize = std::min(OutSizeLeft, InSizeLeft);
+ size_t ExtraSize = Rand(MaxExtraSize) + 1;
+ memcpy(Out + OutPos, Data + *InPos, ExtraSize);
+ OutPos += ExtraSize;
+ (*InPos) += ExtraSize;
+ }
+ // Use the other input data on the next iteration.
+ InPos = CurrentlyUsingFirstData ? &Pos2 : &Pos1;
+ InSize = CurrentlyUsingFirstData ? Size2 : Size1;
+ Data = CurrentlyUsingFirstData ? Data2 : Data1;
+ CurrentlyUsingFirstData = !CurrentlyUsingFirstData;
+ }
+ return OutPos;
+}
+
+} // namespace fuzzer
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerDefs.h b/json4cpp/tests/thirdparty/Fuzzer/FuzzerDefs.h
new file mode 100644
index 0000000000..0f5b8a7cf2
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerDefs.h
@@ -0,0 +1,89 @@
+//===- FuzzerDefs.h - Internal header for the Fuzzer ------------*- C++ -* ===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// Basic definitions.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_FUZZER_DEFS_H
+#define LLVM_FUZZER_DEFS_H
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <string>
+#include <vector>
+
+// Platform detection.
+#ifdef __linux__
+#define LIBFUZZER_APPLE 0
+#define LIBFUZZER_LINUX 1
+#define LIBFUZZER_WINDOWS 0
+#elif __APPLE__
+#define LIBFUZZER_APPLE 1
+#define LIBFUZZER_LINUX 0
+#define LIBFUZZER_WINDOWS 0
+#elif _WIN32
+#define LIBFUZZER_APPLE 0
+#define LIBFUZZER_LINUX 0
+#define LIBFUZZER_WINDOWS 1
+#else
+#error "Support for your platform has not been implemented"
+#endif
+
+#define LIBFUZZER_POSIX LIBFUZZER_APPLE || LIBFUZZER_LINUX
+
+#ifdef __x86_64
+#define ATTRIBUTE_TARGET_POPCNT __attribute__((target("popcnt")))
+#else
+#define ATTRIBUTE_TARGET_POPCNT
+#endif
+
+
+#ifdef __clang__ // avoid gcc warning.
+# define ATTRIBUTE_NO_SANITIZE_MEMORY __attribute__((no_sanitize("memory")))
+#else
+# define ATTRIBUTE_NO_SANITIZE_MEMORY
+#endif
+
+namespace fuzzer {
+
+template <class T> T Min(T a, T b) { return a < b ? a : b; }
+template <class T> T Max(T a, T b) { return a > b ? a : b; }
+
+class Random;
+class Dictionary;
+class DictionaryEntry;
+class MutationDispatcher;
+struct FuzzingOptions;
+class InputCorpus;
+struct InputInfo;
+struct ExternalFunctions;
+
+// Global interface to functions that may or may not be available.
+extern ExternalFunctions *EF;
+
+typedef std::vector<uint8_t> Unit;
+typedef std::vector<Unit> UnitVector;
+typedef int (*UserCallback)(const uint8_t *Data, size_t Size);
+
+int FuzzerDriver(int *argc, char ***argv, UserCallback Callback);
+
+struct ScopedDoingMyOwnMemmem {
+ ScopedDoingMyOwnMemmem();
+ ~ScopedDoingMyOwnMemmem();
+};
+
+inline uint8_t Bswap(uint8_t x) { return x; }
+inline uint16_t Bswap(uint16_t x) { return __builtin_bswap16(x); }
+inline uint32_t Bswap(uint32_t x) { return __builtin_bswap32(x); }
+inline uint64_t Bswap(uint64_t x) { return __builtin_bswap64(x); }
+
+} // namespace fuzzer
+
+#endif // LLVM_FUZZER_DEFS_H
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerDictionary.h b/json4cpp/tests/thirdparty/Fuzzer/FuzzerDictionary.h
new file mode 100644
index 0000000000..eba0eabb68
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerDictionary.h
@@ -0,0 +1,124 @@
+//===- FuzzerDictionary.h - Internal header for the Fuzzer ------*- C++ -* ===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// fuzzer::Dictionary
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_FUZZER_DICTIONARY_H
+#define LLVM_FUZZER_DICTIONARY_H
+
+#include "FuzzerDefs.h"
+#include "FuzzerIO.h"
+#include "FuzzerUtil.h"
+#include <algorithm>
+#include <limits>
+
+namespace fuzzer {
+// A simple POD sized array of bytes.
+template <size_t kMaxSize> class FixedWord {
+public:
+ FixedWord() {}
+ FixedWord(const uint8_t *B, uint8_t S) { Set(B, S); }
+
+ void Set(const uint8_t *B, uint8_t S) {
+ assert(S <= kMaxSize);
+ memcpy(Data, B, S);
+ Size = S;
+ }
+
+ bool operator==(const FixedWord<kMaxSize> &w) const {
+ return Size == w.Size && 0 == memcmp(Data, w.Data, Size);
+ }
+
+ bool operator<(const FixedWord<kMaxSize> &w) const {
+ if (Size != w.Size)
+ return Size < w.Size;
+ return memcmp(Data, w.Data, Size) < 0;
+ }
+
+ static size_t GetMaxSize() { return kMaxSize; }
+ const uint8_t *data() const { return Data; }
+ uint8_t size() const { return Size; }
+
+private:
+ uint8_t Size = 0;
+ uint8_t Data[kMaxSize];
+};
+
+typedef FixedWord<27> Word; // 28 bytes.
+
+class DictionaryEntry {
+ public:
+ DictionaryEntry() {}
+ DictionaryEntry(Word W) : W(W) {}
+ DictionaryEntry(Word W, size_t PositionHint) : W(W), PositionHint(PositionHint) {}
+ const Word &GetW() const { return W; }
+
+ bool HasPositionHint() const { return PositionHint != std::numeric_limits<size_t>::max(); }
+ size_t GetPositionHint() const {
+ assert(HasPositionHint());
+ return PositionHint;
+ }
+ void IncUseCount() { UseCount++; }
+ void IncSuccessCount() { SuccessCount++; }
+ size_t GetUseCount() const { return UseCount; }
+ size_t GetSuccessCount() const {return SuccessCount; }
+
+ void Print(const char *PrintAfter = "\n") {
+ PrintASCII(W.data(), W.size());
+ if (HasPositionHint())
+ Printf("@%zd", GetPositionHint());
+ Printf("%s", PrintAfter);
+ }
+
+private:
+ Word W;
+ size_t PositionHint = std::numeric_limits<size_t>::max();
+ size_t UseCount = 0;
+ size_t SuccessCount = 0;
+};
+
+class Dictionary {
+ public:
+ static const size_t kMaxDictSize = 1 << 14;
+
+ bool ContainsWord(const Word &W) const {
+ return std::any_of(begin(), end(), [&](const DictionaryEntry &DE) {
+ return DE.GetW() == W;
+ });
+ }
+ const DictionaryEntry *begin() const { return &DE[0]; }
+ const DictionaryEntry *end() const { return begin() + Size; }
+ DictionaryEntry & operator[] (size_t Idx) {
+ assert(Idx < Size);
+ return DE[Idx];
+ }
+ void push_back(DictionaryEntry DE) {
+ if (Size < kMaxDictSize)
+ this->DE[Size++] = DE;
+ }
+ void clear() { Size = 0; }
+ bool empty() const { return Size == 0; }
+ size_t size() const { return Size; }
+
+private:
+ DictionaryEntry DE[kMaxDictSize];
+ size_t Size = 0;
+};
+
+// Parses one dictionary entry.
+// If successfull, write the enty to Unit and returns true,
+// otherwise returns false.
+bool ParseOneDictionaryEntry(const std::string &Str, Unit *U);
+// Parses the dictionary file, fills Units, returns true iff all lines
+// were parsed succesfully.
+bool ParseDictionaryFile(const std::string &Text, std::vector<Unit> *Units);
+
+} // namespace fuzzer
+
+#endif // LLVM_FUZZER_DICTIONARY_H
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerDriver.cpp b/json4cpp/tests/thirdparty/Fuzzer/FuzzerDriver.cpp
new file mode 100644
index 0000000000..5ab6df2d56
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerDriver.cpp
@@ -0,0 +1,545 @@
+//===- FuzzerDriver.cpp - FuzzerDriver function and flags -----------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// FuzzerDriver and flag parsing.
+//===----------------------------------------------------------------------===//
+
+#include "FuzzerCorpus.h"
+#include "FuzzerInterface.h"
+#include "FuzzerInternal.h"
+#include "FuzzerIO.h"
+#include "FuzzerMutate.h"
+#include "FuzzerRandom.h"
+#include "FuzzerTracePC.h"
+#include <algorithm>
+#include <atomic>
+#include <chrono>
+#include <cstring>
+#include <mutex>
+#include <string>
+#include <thread>
+
+// This function should be present in the libFuzzer so that the client
+// binary can test for its existence.
+extern "C" __attribute__((used)) void __libfuzzer_is_present() {}
+
+namespace fuzzer {
+
+// Program arguments.
+struct FlagDescription {
+ const char *Name;
+ const char *Description;
+ int Default;
+ int *IntFlag;
+ const char **StrFlag;
+ unsigned int *UIntFlag;
+};
+
+struct {
+#define FUZZER_DEPRECATED_FLAG(Name)
+#define FUZZER_FLAG_INT(Name, Default, Description) int Name;
+#define FUZZER_FLAG_UNSIGNED(Name, Default, Description) unsigned int Name;
+#define FUZZER_FLAG_STRING(Name, Description) const char *Name;
+#include "FuzzerFlags.def"
+#undef FUZZER_DEPRECATED_FLAG
+#undef FUZZER_FLAG_INT
+#undef FUZZER_FLAG_UNSIGNED
+#undef FUZZER_FLAG_STRING
+} Flags;
+
+static const FlagDescription FlagDescriptions [] {
+#define FUZZER_DEPRECATED_FLAG(Name) \
+ {#Name, "Deprecated; don't use", 0, nullptr, nullptr, nullptr},
+#define FUZZER_FLAG_INT(Name, Default, Description) \
+ {#Name, Description, Default, &Flags.Name, nullptr, nullptr},
+#define FUZZER_FLAG_UNSIGNED(Name, Default, Description) \
+ {#Name, Description, static_cast<int>(Default), \
+ nullptr, nullptr, &Flags.Name},
+#define FUZZER_FLAG_STRING(Name, Description) \
+ {#Name, Description, 0, nullptr, &Flags.Name, nullptr},
+#include "FuzzerFlags.def"
+#undef FUZZER_DEPRECATED_FLAG
+#undef FUZZER_FLAG_INT
+#undef FUZZER_FLAG_UNSIGNED
+#undef FUZZER_FLAG_STRING
+};
+
+static const size_t kNumFlags =
+ sizeof(FlagDescriptions) / sizeof(FlagDescriptions[0]);
+
+static std::vector<std::string> *Inputs;
+static std::string *ProgName;
+
+static void PrintHelp() {
+ Printf("Usage:\n");
+ auto Prog = ProgName->c_str();
+ Printf("\nTo run fuzzing pass 0 or more directories.\n");
+ Printf("%s [-flag1=val1 [-flag2=val2 ...] ] [dir1 [dir2 ...] ]\n", Prog);
+
+ Printf("\nTo run individual tests without fuzzing pass 1 or more files:\n");
+ Printf("%s [-flag1=val1 [-flag2=val2 ...] ] file1 [file2 ...]\n", Prog);
+
+ Printf("\nFlags: (strictly in form -flag=value)\n");
+ size_t MaxFlagLen = 0;
+ for (size_t F = 0; F < kNumFlags; F++)
+ MaxFlagLen = std::max(strlen(FlagDescriptions[F].Name), MaxFlagLen);
+
+ for (size_t F = 0; F < kNumFlags; F++) {
+ const auto &D = FlagDescriptions[F];
+ if (strstr(D.Description, "internal flag") == D.Description) continue;
+ Printf(" %s", D.Name);
+ for (size_t i = 0, n = MaxFlagLen - strlen(D.Name); i < n; i++)
+ Printf(" ");
+ Printf("\t");
+ Printf("%d\t%s\n", D.Default, D.Description);
+ }
+ Printf("\nFlags starting with '--' will be ignored and "
+ "will be passed verbatim to subprocesses.\n");
+}
+
+static const char *FlagValue(const char *Param, const char *Name) {
+ size_t Len = strlen(Name);
+ if (Param[0] == '-' && strstr(Param + 1, Name) == Param + 1 &&
+ Param[Len + 1] == '=')
+ return &Param[Len + 2];
+ return nullptr;
+}
+
+// Avoid calling stol as it triggers a bug in clang/glibc build.
+static long MyStol(const char *Str) {
+ long Res = 0;
+ long Sign = 1;
+ if (*Str == '-') {
+ Str++;
+ Sign = -1;
+ }
+ for (size_t i = 0; Str[i]; i++) {
+ char Ch = Str[i];
+ if (Ch < '0' || Ch > '9')
+ return Res;
+ Res = Res * 10 + (Ch - '0');
+ }
+ return Res * Sign;
+}
+
+static bool ParseOneFlag(const char *Param) {
+ if (Param[0] != '-') return false;
+ if (Param[1] == '-') {
+ static bool PrintedWarning = false;
+ if (!PrintedWarning) {
+ PrintedWarning = true;
+ Printf("INFO: libFuzzer ignores flags that start with '--'\n");
+ }
+ for (size_t F = 0; F < kNumFlags; F++)
+ if (FlagValue(Param + 1, FlagDescriptions[F].Name))
+ Printf("WARNING: did you mean '%s' (single dash)?\n", Param + 1);
+ return true;
+ }
+ for (size_t F = 0; F < kNumFlags; F++) {
+ const char *Name = FlagDescriptions[F].Name;
+ const char *Str = FlagValue(Param, Name);
+ if (Str) {
+ if (FlagDescriptions[F].IntFlag) {
+ int Val = MyStol(Str);
+ *FlagDescriptions[F].IntFlag = Val;
+ if (Flags.verbosity >= 2)
+ Printf("Flag: %s %d\n", Name, Val);
+ return true;
+ } else if (FlagDescriptions[F].UIntFlag) {
+ unsigned int Val = std::stoul(Str);
+ *FlagDescriptions[F].UIntFlag = Val;
+ if (Flags.verbosity >= 2)
+ Printf("Flag: %s %u\n", Name, Val);
+ return true;
+ } else if (FlagDescriptions[F].StrFlag) {
+ *FlagDescriptions[F].StrFlag = Str;
+ if (Flags.verbosity >= 2)
+ Printf("Flag: %s %s\n", Name, Str);
+ return true;
+ } else { // Deprecated flag.
+ Printf("Flag: %s: deprecated, don't use\n", Name);
+ return true;
+ }
+ }
+ }
+ Printf("\n\nWARNING: unrecognized flag '%s'; "
+ "use -help=1 to list all flags\n\n", Param);
+ return true;
+}
+
+// We don't use any library to minimize dependencies.
+static void ParseFlags(const std::vector<std::string> &Args) {
+ for (size_t F = 0; F < kNumFlags; F++) {
+ if (FlagDescriptions[F].IntFlag)
+ *FlagDescriptions[F].IntFlag = FlagDescriptions[F].Default;
+ if (FlagDescriptions[F].UIntFlag)
+ *FlagDescriptions[F].UIntFlag =
+ static_cast<unsigned int>(FlagDescriptions[F].Default);
+ if (FlagDescriptions[F].StrFlag)
+ *FlagDescriptions[F].StrFlag = nullptr;
+ }
+ Inputs = new std::vector<std::string>;
+ for (size_t A = 1; A < Args.size(); A++) {
+ if (ParseOneFlag(Args[A].c_str())) continue;
+ Inputs->push_back(Args[A]);
+ }
+}
+
+static std::mutex Mu;
+
+static void PulseThread() {
+ while (true) {
+ SleepSeconds(600);
+ std::lock_guard<std::mutex> Lock(Mu);
+ Printf("pulse...\n");
+ }
+}
+
+static void WorkerThread(const std::string &Cmd, std::atomic<unsigned> *Counter,
+ unsigned NumJobs, std::atomic<bool> *HasErrors) {
+ while (true) {
+ unsigned C = (*Counter)++;
+ if (C >= NumJobs) break;
+ std::string Log = "fuzz-" + std::to_string(C) + ".log";
+ std::string ToRun = Cmd + " > " + Log + " 2>&1\n";
+ if (Flags.verbosity)
+ Printf("%s", ToRun.c_str());
+ int ExitCode = ExecuteCommand(ToRun);
+ if (ExitCode != 0)
+ *HasErrors = true;
+ std::lock_guard<std::mutex> Lock(Mu);
+ Printf("================== Job %u exited with exit code %d ============\n",
+ C, ExitCode);
+ fuzzer::CopyFileToErr(Log);
+ }
+}
+
+std::string CloneArgsWithoutX(const std::vector<std::string> &Args,
+ const char *X1, const char *X2) {
+ std::string Cmd;
+ for (auto &S : Args) {
+ if (FlagValue(S.c_str(), X1) || FlagValue(S.c_str(), X2))
+ continue;
+ Cmd += S + " ";
+ }
+ return Cmd;
+}
+
+static int RunInMultipleProcesses(const std::vector<std::string> &Args,
+ unsigned NumWorkers, unsigned NumJobs) {
+ std::atomic<unsigned> Counter(0);
+ std::atomic<bool> HasErrors(false);
+ std::string Cmd = CloneArgsWithoutX(Args, "jobs", "workers");
+ std::vector<std::thread> V;
+ std::thread Pulse(PulseThread);
+ Pulse.detach();
+ for (unsigned i = 0; i < NumWorkers; i++)
+ V.push_back(std::thread(WorkerThread, Cmd, &Counter, NumJobs, &HasErrors));
+ for (auto &T : V)
+ T.join();
+ return HasErrors ? 1 : 0;
+}
+
+static void RssThread(Fuzzer *F, size_t RssLimitMb) {
+ while (true) {
+ SleepSeconds(1);
+ size_t Peak = GetPeakRSSMb();
+ if (Peak > RssLimitMb)
+ F->RssLimitCallback();
+ }
+}
+
+static void StartRssThread(Fuzzer *F, size_t RssLimitMb) {
+ if (!RssLimitMb) return;
+ std::thread T(RssThread, F, RssLimitMb);
+ T.detach();
+}
+
+int RunOneTest(Fuzzer *F, const char *InputFilePath, size_t MaxLen) {
+ Unit U = FileToVector(InputFilePath);
+ if (MaxLen && MaxLen < U.size())
+ U.resize(MaxLen);
+ F->RunOne(U.data(), U.size());
+ F->TryDetectingAMemoryLeak(U.data(), U.size(), true);
+ return 0;
+}
+
+static bool AllInputsAreFiles() {
+ if (Inputs->empty()) return false;
+ for (auto &Path : *Inputs)
+ if (!IsFile(Path))
+ return false;
+ return true;
+}
+
+int MinimizeCrashInput(const std::vector<std::string> &Args) {
+ if (Inputs->size() != 1) {
+ Printf("ERROR: -minimize_crash should be given one input file\n");
+ exit(1);
+ }
+ std::string InputFilePath = Inputs->at(0);
+ std::string BaseCmd =
+ CloneArgsWithoutX(Args, "minimize_crash", "exact_artifact_path");
+ auto InputPos = BaseCmd.find(" " + InputFilePath + " ");
+ assert(InputPos != std::string::npos);
+ BaseCmd.erase(InputPos, InputFilePath.size() + 1);
+ if (Flags.runs <= 0 && Flags.max_total_time == 0) {
+ Printf("INFO: you need to specify -runs=N or "
+ "-max_total_time=N with -minimize_crash=1\n"
+ "INFO: defaulting to -max_total_time=600\n");
+ BaseCmd += " -max_total_time=600";
+ }
+ // BaseCmd += " > /dev/null 2>&1 ";
+
+ std::string CurrentFilePath = InputFilePath;
+ while (true) {
+ Unit U = FileToVector(CurrentFilePath);
+ if (U.size() < 2) {
+ Printf("CRASH_MIN: '%s' is small enough\n", CurrentFilePath.c_str());
+ return 0;
+ }
+ Printf("CRASH_MIN: minimizing crash input: '%s' (%zd bytes)\n",
+ CurrentFilePath.c_str(), U.size());
+
+ auto Cmd = BaseCmd + " " + CurrentFilePath;
+
+ Printf("CRASH_MIN: executing: %s\n", Cmd.c_str());
+ int ExitCode = ExecuteCommand(Cmd);
+ if (ExitCode == 0) {
+ Printf("ERROR: the input %s did not crash\n", CurrentFilePath.c_str());
+ exit(1);
+ }
+ Printf("CRASH_MIN: '%s' (%zd bytes) caused a crash. Will try to minimize "
+ "it further\n",
+ CurrentFilePath.c_str(), U.size());
+
+ std::string ArtifactPath = "minimized-from-" + Hash(U);
+ Cmd += " -minimize_crash_internal_step=1 -exact_artifact_path=" +
+ ArtifactPath;
+ Printf("CRASH_MIN: executing: %s\n", Cmd.c_str());
+ ExitCode = ExecuteCommand(Cmd);
+ if (ExitCode == 0) {
+ if (Flags.exact_artifact_path) {
+ CurrentFilePath = Flags.exact_artifact_path;
+ WriteToFile(U, CurrentFilePath);
+ }
+ Printf("CRASH_MIN: failed to minimize beyond %s (%d bytes), exiting\n",
+ CurrentFilePath.c_str(), U.size());
+ return 0;
+ }
+ CurrentFilePath = ArtifactPath;
+ Printf("\n\n\n\n\n\n*********************************\n");
+ }
+ return 0;
+}
+
+int MinimizeCrashInputInternalStep(Fuzzer *F, InputCorpus *Corpus) {
+ assert(Inputs->size() == 1);
+ std::string InputFilePath = Inputs->at(0);
+ Unit U = FileToVector(InputFilePath);
+ assert(U.size() > 2);
+ Printf("INFO: Starting MinimizeCrashInputInternalStep: %zd\n", U.size());
+ Corpus->AddToCorpus(U, 0);
+ F->SetMaxInputLen(U.size());
+ F->SetMaxMutationLen(U.size() - 1);
+ F->MinimizeCrashLoop(U);
+ Printf("INFO: Done MinimizeCrashInputInternalStep, no crashes found\n");
+ exit(0);
+ return 0;
+}
+
+int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
+ using namespace fuzzer;
+ assert(argc && argv && "Argument pointers cannot be nullptr");
+ EF = new ExternalFunctions();
+ if (EF->LLVMFuzzerInitialize)
+ EF->LLVMFuzzerInitialize(argc, argv);
+ const std::vector<std::string> Args(*argv, *argv + *argc);
+ assert(!Args.empty());
+ ProgName = new std::string(Args[0]);
+ ParseFlags(Args);
+ if (Flags.help) {
+ PrintHelp();
+ return 0;
+ }
+
+ if (Flags.minimize_crash)
+ return MinimizeCrashInput(Args);
+
+ if (Flags.close_fd_mask & 2)
+ DupAndCloseStderr();
+ if (Flags.close_fd_mask & 1)
+ CloseStdout();
+
+ if (Flags.jobs > 0 && Flags.workers == 0) {
+ Flags.workers = std::min(NumberOfCpuCores() / 2, Flags.jobs);
+ if (Flags.workers > 1)
+ Printf("Running %u workers\n", Flags.workers);
+ }
+
+ if (Flags.workers > 0 && Flags.jobs > 0)
+ return RunInMultipleProcesses(Args, Flags.workers, Flags.jobs);
+
+ const size_t kMaxSaneLen = 1 << 20;
+ const size_t kMinDefaultLen = 64;
+ FuzzingOptions Options;
+ Options.Verbosity = Flags.verbosity;
+ Options.MaxLen = Flags.max_len;
+ Options.UnitTimeoutSec = Flags.timeout;
+ Options.ErrorExitCode = Flags.error_exitcode;
+ Options.TimeoutExitCode = Flags.timeout_exitcode;
+ Options.MaxTotalTimeSec = Flags.max_total_time;
+ Options.DoCrossOver = Flags.cross_over;
+ Options.MutateDepth = Flags.mutate_depth;
+ Options.UseCounters = Flags.use_counters;
+ Options.UseIndirCalls = Flags.use_indir_calls;
+ Options.UseMemcmp = Flags.use_memcmp;
+ Options.UseMemmem = Flags.use_memmem;
+ Options.UseCmp = Flags.use_cmp;
+ Options.UseValueProfile = Flags.use_value_profile;
+ Options.Shrink = Flags.shrink;
+ Options.ShuffleAtStartUp = Flags.shuffle;
+ Options.PreferSmall = Flags.prefer_small;
+ Options.ReloadIntervalSec = Flags.reload;
+ Options.OnlyASCII = Flags.only_ascii;
+ Options.OutputCSV = Flags.output_csv;
+ Options.DetectLeaks = Flags.detect_leaks;
+ Options.TraceMalloc = Flags.trace_malloc;
+ Options.RssLimitMb = Flags.rss_limit_mb;
+ if (Flags.runs >= 0)
+ Options.MaxNumberOfRuns = Flags.runs;
+ if (!Inputs->empty() && !Flags.minimize_crash_internal_step)
+ Options.OutputCorpus = (*Inputs)[0];
+ Options.ReportSlowUnits = Flags.report_slow_units;
+ if (Flags.artifact_prefix)
+ Options.ArtifactPrefix = Flags.artifact_prefix;
+ if (Flags.exact_artifact_path)
+ Options.ExactArtifactPath = Flags.exact_artifact_path;
+ std::vector<Unit> Dictionary;
+ if (Flags.dict)
+ if (!ParseDictionaryFile(FileToString(Flags.dict), &Dictionary))
+ return 1;
+ if (Flags.verbosity > 0 && !Dictionary.empty())
+ Printf("Dictionary: %zd entries\n", Dictionary.size());
+ bool DoPlainRun = AllInputsAreFiles();
+ Options.SaveArtifacts =
+ !DoPlainRun || Flags.minimize_crash_internal_step;
+ Options.PrintNewCovPcs = Flags.print_pcs;
+ Options.PrintFinalStats = Flags.print_final_stats;
+ Options.PrintCorpusStats = Flags.print_corpus_stats;
+ Options.PrintCoverage = Flags.print_coverage;
+ Options.DumpCoverage = Flags.dump_coverage;
+ if (Flags.exit_on_src_pos)
+ Options.ExitOnSrcPos = Flags.exit_on_src_pos;
+ if (Flags.exit_on_item)
+ Options.ExitOnItem = Flags.exit_on_item;
+
+ unsigned Seed = Flags.seed;
+ // Initialize Seed.
+ if (Seed == 0)
+ Seed = (std::chrono::system_clock::now().time_since_epoch().count() << 10) +
+ GetPid();
+ if (Flags.verbosity)
+ Printf("INFO: Seed: %u\n", Seed);
+
+ Random Rand(Seed);
+ auto *MD = new MutationDispatcher(Rand, Options);
+ auto *Corpus = new InputCorpus(Options.OutputCorpus);
+ auto *F = new Fuzzer(Callback, *Corpus, *MD, Options);
+
+ for (auto &U: Dictionary)
+ if (U.size() <= Word::GetMaxSize())
+ MD->AddWordToManualDictionary(Word(U.data(), U.size()));
+
+ StartRssThread(F, Flags.rss_limit_mb);
+
+ Options.HandleAbrt = Flags.handle_abrt;
+ Options.HandleBus = Flags.handle_bus;
+ Options.HandleFpe = Flags.handle_fpe;
+ Options.HandleIll = Flags.handle_ill;
+ Options.HandleInt = Flags.handle_int;
+ Options.HandleSegv = Flags.handle_segv;
+ Options.HandleTerm = Flags.handle_term;
+ SetSignalHandler(Options);
+
+ if (Flags.minimize_crash_internal_step)
+ return MinimizeCrashInputInternalStep(F, Corpus);
+
+ if (DoPlainRun) {
+ Options.SaveArtifacts = false;
+ int Runs = std::max(1, Flags.runs);
+ Printf("%s: Running %zd inputs %d time(s) each.\n", ProgName->c_str(),
+ Inputs->size(), Runs);
+ for (auto &Path : *Inputs) {
+ auto StartTime = system_clock::now();
+ Printf("Running: %s\n", Path.c_str());
+ for (int Iter = 0; Iter < Runs; Iter++)
+ RunOneTest(F, Path.c_str(), Options.MaxLen);
+ auto StopTime = system_clock::now();
+ auto MS = duration_cast<milliseconds>(StopTime - StartTime).count();
+ Printf("Executed %s in %zd ms\n", Path.c_str(), (long)MS);
+ }
+ Printf("***\n"
+ "*** NOTE: fuzzing was not performed, you have only\n"
+ "*** executed the target code on a fixed set of inputs.\n"
+ "***\n");
+ F->PrintFinalStats();
+ exit(0);
+ }
+
+ if (Flags.merge) {
+ if (Options.MaxLen == 0)
+ F->SetMaxInputLen(kMaxSaneLen);
+ if (TPC.UsingTracePcGuard()) {
+ if (Flags.merge_control_file)
+ F->CrashResistantMergeInternalStep(Flags.merge_control_file);
+ else
+ F->CrashResistantMerge(Args, *Inputs);
+ } else {
+ F->Merge(*Inputs);
+ }
+ exit(0);
+ }
+
+ size_t TemporaryMaxLen = Options.MaxLen ? Options.MaxLen : kMaxSaneLen;
+
+ UnitVector InitialCorpus;
+ for (auto &Inp : *Inputs) {
+ Printf("Loading corpus dir: %s\n", Inp.c_str());
+ ReadDirToVectorOfUnits(Inp.c_str(), &InitialCorpus, nullptr,
+ TemporaryMaxLen, /*ExitOnError=*/false);
+ }
+
+ if (Options.MaxLen == 0) {
+ size_t MaxLen = 0;
+ for (auto &U : InitialCorpus)
+ MaxLen = std::max(U.size(), MaxLen);
+ F->SetMaxInputLen(std::min(std::max(kMinDefaultLen, MaxLen), kMaxSaneLen));
+ }
+
+ if (InitialCorpus.empty()) {
+ InitialCorpus.push_back(Unit({'\n'})); // Valid ASCII input.
+ if (Options.Verbosity)
+ Printf("INFO: A corpus is not provided, starting from an empty corpus\n");
+ }
+ F->ShuffleAndMinimize(&InitialCorpus);
+ InitialCorpus.clear(); // Don't need this memory any more.
+ F->Loop();
+
+ if (Flags.verbosity)
+ Printf("Done %d runs in %zd second(s)\n", F->getTotalNumberOfRuns(),
+ F->secondsSinceProcessStartUp());
+ F->PrintFinalStats();
+
+ exit(0); // Don't let F destroy itself.
+}
+
+// Storage for global ExternalFunctions object.
+ExternalFunctions *EF = nullptr;
+
+} // namespace fuzzer
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerExtFunctions.def b/json4cpp/tests/thirdparty/Fuzzer/FuzzerExtFunctions.def
new file mode 100644
index 0000000000..61c72e4a20
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerExtFunctions.def
@@ -0,0 +1,50 @@
+//===- FuzzerExtFunctions.def - External functions --------------*- C++ -* ===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// This defines the external function pointers that
+// ``fuzzer::ExternalFunctions`` should contain and try to initialize. The
+// EXT_FUNC macro must be defined at the point of inclusion. The signature of
+// the macro is:
+//
+// EXT_FUNC(<name>, <return_type>, <function_signature>, <warn_if_missing>)
+//===----------------------------------------------------------------------===//
+
+// Optional user functions
+EXT_FUNC(LLVMFuzzerInitialize, int, (int *argc, char ***argv), false);
+EXT_FUNC(LLVMFuzzerCustomMutator, size_t,
+ (uint8_t * Data, size_t Size, size_t MaxSize, unsigned int Seed),
+ false);
+EXT_FUNC(LLVMFuzzerCustomCrossOver, size_t,
+ (const uint8_t * Data1, size_t Size1,
+ const uint8_t * Data2, size_t Size2,
+ uint8_t * Out, size_t MaxOutSize, unsigned int Seed),
+ false);
+
+// Sanitizer functions
+EXT_FUNC(__lsan_enable, void, (), false);
+EXT_FUNC(__lsan_disable, void, (), false);
+EXT_FUNC(__lsan_do_recoverable_leak_check, int, (), false);
+EXT_FUNC(__sanitizer_get_number_of_counters, size_t, (), false);
+EXT_FUNC(__sanitizer_install_malloc_and_free_hooks, int,
+ (void (*malloc_hook)(const volatile void *, size_t),
+ void (*free_hook)(const volatile void *)),
+ false);
+EXT_FUNC(__sanitizer_get_total_unique_caller_callee_pairs, size_t, (), false);
+EXT_FUNC(__sanitizer_get_total_unique_coverage, size_t, (), true);
+EXT_FUNC(__sanitizer_print_memory_profile, int, (size_t), false);
+EXT_FUNC(__sanitizer_print_stack_trace, void, (), true);
+EXT_FUNC(__sanitizer_symbolize_pc, void,
+ (void *, const char *fmt, char *out_buf, size_t out_buf_size), false);
+EXT_FUNC(__sanitizer_get_module_and_offset_for_pc, int,
+ (void *pc, char *module_path,
+ size_t module_path_len,void **pc_offset), false);
+EXT_FUNC(__sanitizer_reset_coverage, void, (), true);
+EXT_FUNC(__sanitizer_set_death_callback, void, (void (*)(void)), true);
+EXT_FUNC(__sanitizer_set_report_fd, void, (void*), false);
+EXT_FUNC(__sanitizer_update_counter_bitset_and_clear_counters, uintptr_t,
+ (uint8_t*), false);
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerExtFunctions.h b/json4cpp/tests/thirdparty/Fuzzer/FuzzerExtFunctions.h
new file mode 100644
index 0000000000..2672a38547
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerExtFunctions.h
@@ -0,0 +1,35 @@
+//===- FuzzerExtFunctions.h - Interface to external functions ---*- C++ -* ===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// Defines an interface to (possibly optional) functions.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_FUZZER_EXT_FUNCTIONS_H
+#define LLVM_FUZZER_EXT_FUNCTIONS_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+namespace fuzzer {
+
+struct ExternalFunctions {
+ // Initialize function pointers. Functions that are not available will be set
+ // to nullptr. Do not call this constructor before ``main()`` has been
+ // entered.
+ ExternalFunctions();
+
+#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
+ RETURN_TYPE(*NAME) FUNC_SIG = nullptr
+
+#include "FuzzerExtFunctions.def"
+
+#undef EXT_FUNC
+};
+} // namespace fuzzer
+
+#endif
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerExtFunctionsDlsym.cpp b/json4cpp/tests/thirdparty/Fuzzer/FuzzerExtFunctionsDlsym.cpp
new file mode 100644
index 0000000000..06bddd5de3
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerExtFunctionsDlsym.cpp
@@ -0,0 +1,52 @@
+//===- FuzzerExtFunctionsDlsym.cpp - Interface to external functions ------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// Implementation for operating systems that support dlsym(). We only use it on
+// Apple platforms for now. We don't use this approach on Linux because it
+// requires that clients of LibFuzzer pass ``--export-dynamic`` to the linker.
+// That is a complication we don't wish to expose to clients right now.
+//===----------------------------------------------------------------------===//
+#include "FuzzerDefs.h"
+#if LIBFUZZER_APPLE
+
+#include "FuzzerExtFunctions.h"
+#include "FuzzerIO.h"
+#include <dlfcn.h>
+
+using namespace fuzzer;
+
+template <typename T>
+static T GetFnPtr(const char *FnName, bool WarnIfMissing) {
+ dlerror(); // Clear any previous errors.
+ void *Fn = dlsym(RTLD_DEFAULT, FnName);
+ if (Fn == nullptr) {
+ if (WarnIfMissing) {
+ const char *ErrorMsg = dlerror();
+ Printf("WARNING: Failed to find function \"%s\".", FnName);
+ if (ErrorMsg)
+ Printf(" Reason %s.", ErrorMsg);
+ Printf("\n");
+ }
+ }
+ return reinterpret_cast<T>(Fn);
+}
+
+namespace fuzzer {
+
+ExternalFunctions::ExternalFunctions() {
+#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
+ this->NAME = GetFnPtr<decltype(ExternalFunctions::NAME)>(#NAME, WARN)
+
+#include "FuzzerExtFunctions.def"
+
+#undef EXT_FUNC
+}
+
+} // namespace fuzzer
+
+#endif // LIBFUZZER_APPLE
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerExtFunctionsWeak.cpp b/json4cpp/tests/thirdparty/Fuzzer/FuzzerExtFunctionsWeak.cpp
new file mode 100644
index 0000000000..7b02b6f0b7
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerExtFunctionsWeak.cpp
@@ -0,0 +1,53 @@
+//===- FuzzerExtFunctionsWeak.cpp - Interface to external functions -------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// Implementation for Linux. This relies on the linker's support for weak
+// symbols. We don't use this approach on Apple platforms because it requires
+// clients of LibFuzzer to pass ``-U _<symbol_name>`` to the linker to allow
+// weak symbols to be undefined. That is a complication we don't want to expose
+// to clients right now.
+//===----------------------------------------------------------------------===//
+#include "FuzzerDefs.h"
+#if LIBFUZZER_LINUX
+
+#include "FuzzerExtFunctions.h"
+#include "FuzzerIO.h"
+
+extern "C" {
+// Declare these symbols as weak to allow them to be optionally defined.
+#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
+ __attribute__((weak)) RETURN_TYPE NAME FUNC_SIG
+
+#include "FuzzerExtFunctions.def"
+
+#undef EXT_FUNC
+}
+
+using namespace fuzzer;
+
+static void CheckFnPtr(void *FnPtr, const char *FnName, bool WarnIfMissing) {
+ if (FnPtr == nullptr && WarnIfMissing) {
+ Printf("WARNING: Failed to find function \"%s\".\n", FnName);
+ }
+}
+
+namespace fuzzer {
+
+ExternalFunctions::ExternalFunctions() {
+#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
+ this->NAME = ::NAME; \
+ CheckFnPtr((void *)::NAME, #NAME, WARN);
+
+#include "FuzzerExtFunctions.def"
+
+#undef EXT_FUNC
+}
+
+} // namespace fuzzer
+
+#endif // LIBFUZZER_LINUX
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerExtFunctionsWeakAlias.cpp b/json4cpp/tests/thirdparty/Fuzzer/FuzzerExtFunctionsWeakAlias.cpp
new file mode 100644
index 0000000000..e10f7b4dca
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerExtFunctionsWeakAlias.cpp
@@ -0,0 +1,56 @@
+//===- FuzzerExtFunctionsWeakAlias.cpp - Interface to external functions --===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// Implementation using weak aliases. Works for Windows.
+//===----------------------------------------------------------------------===//
+#include "FuzzerDefs.h"
+#if LIBFUZZER_WINDOWS
+
+#include "FuzzerExtFunctions.h"
+#include "FuzzerIO.h"
+
+using namespace fuzzer;
+
+extern "C" {
+// Declare these symbols as weak to allow them to be optionally defined.
+#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
+ RETURN_TYPE NAME##Def FUNC_SIG { \
+ Printf("ERROR: Function \"%s\" not defined.\n", #NAME); \
+ exit(1); \
+ } \
+ RETURN_TYPE NAME FUNC_SIG __attribute__((weak, alias(#NAME "Def")));
+
+#include "FuzzerExtFunctions.def"
+
+#undef EXT_FUNC
+}
+
+template <typename T>
+static T *GetFnPtr(T *Fun, T *FunDef, const char *FnName, bool WarnIfMissing) {
+ if (Fun == FunDef) {
+ if (WarnIfMissing)
+ Printf("WARNING: Failed to find function \"%s\".\n", FnName);
+ return nullptr;
+ }
+ return Fun;
+}
+
+namespace fuzzer {
+
+ExternalFunctions::ExternalFunctions() {
+#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
+ this->NAME = GetFnPtr<decltype(::NAME)>(::NAME, ::NAME##Def, #NAME, WARN);
+
+#include "FuzzerExtFunctions.def"
+
+#undef EXT_FUNC
+}
+
+} // namespace fuzzer
+
+#endif // LIBFUZZER_WINDOWS
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerFlags.def b/json4cpp/tests/thirdparty/Fuzzer/FuzzerFlags.def
new file mode 100644
index 0000000000..25ef1741a9
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerFlags.def
@@ -0,0 +1,115 @@
+//===- FuzzerFlags.def - Run-time flags -------------------------*- C++ -* ===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// Flags. FUZZER_FLAG_INT/FUZZER_FLAG_STRING macros should be defined at the
+// point of inclusion. We are not using any flag parsing library for better
+// portability and independence.
+//===----------------------------------------------------------------------===//
+FUZZER_FLAG_INT(verbosity, 1, "Verbosity level.")
+FUZZER_FLAG_UNSIGNED(seed, 0, "Random seed. If 0, seed is generated.")
+FUZZER_FLAG_INT(runs, -1,
+ "Number of individual test runs (-1 for infinite runs).")
+FUZZER_FLAG_INT(max_len, 0, "Maximum length of the test input. "
+ "If 0, libFuzzer tries to guess a good value based on the corpus "
+ "and reports it. ")
+FUZZER_FLAG_INT(cross_over, 1, "If 1, cross over inputs.")
+FUZZER_FLAG_INT(mutate_depth, 5,
+ "Apply this number of consecutive mutations to each input.")
+FUZZER_FLAG_INT(shuffle, 1, "Shuffle inputs at startup")
+FUZZER_FLAG_INT(prefer_small, 1,
+ "If 1, always prefer smaller inputs during the corpus shuffle.")
+FUZZER_FLAG_INT(
+ timeout, 1200,
+ "Timeout in seconds (if positive). "
+ "If one unit runs more than this number of seconds the process will abort.")
+FUZZER_FLAG_INT(error_exitcode, 77, "When libFuzzer itself reports a bug "
+ "this exit code will be used.")
+FUZZER_FLAG_INT(timeout_exitcode, 77, "When libFuzzer reports a timeout "
+ "this exit code will be used.")
+FUZZER_FLAG_INT(max_total_time, 0, "If positive, indicates the maximal total "
+ "time in seconds to run the fuzzer.")
+FUZZER_FLAG_INT(help, 0, "Print help.")
+FUZZER_FLAG_INT(merge, 0, "If 1, the 2-nd, 3-rd, etc corpora will be "
+ "merged into the 1-st corpus. Only interesting units will be taken. "
+ "This flag can be used to minimize a corpus.")
+FUZZER_FLAG_STRING(merge_control_file, "internal flag")
+FUZZER_FLAG_INT(minimize_crash, 0, "If 1, minimizes the provided"
+ " crash input. Use with -runs=N or -max_total_time=N to limit "
+ "the number attempts")
+FUZZER_FLAG_INT(minimize_crash_internal_step, 0, "internal flag")
+FUZZER_FLAG_INT(use_counters, 1, "Use coverage counters")
+FUZZER_FLAG_INT(use_indir_calls, 1, "Use indirect caller-callee counters")
+FUZZER_FLAG_INT(use_memcmp, 1,
+ "Use hints from intercepting memcmp, strcmp, etc")
+FUZZER_FLAG_INT(use_memmem, 1,
+ "Use hints from intercepting memmem, strstr, etc")
+FUZZER_FLAG_INT(use_value_profile, 0,
+ "Experimental. Use value profile to guide fuzzing.")
+FUZZER_FLAG_INT(use_cmp, 1, "Use CMP traces to guide mutations")
+FUZZER_FLAG_INT(shrink, 0, "Experimental. Try to shrink corpus elements.")
+FUZZER_FLAG_UNSIGNED(jobs, 0, "Number of jobs to run. If jobs >= 1 we spawn"
+ " this number of jobs in separate worker processes"
+ " with stdout/stderr redirected to fuzz-JOB.log.")
+FUZZER_FLAG_UNSIGNED(workers, 0,
+ "Number of simultaneous worker processes to run the jobs."
+ " If zero, \"min(jobs,NumberOfCpuCores()/2)\" is used.")
+FUZZER_FLAG_INT(reload, 1,
+ "Reload the main corpus every <N> seconds to get new units"
+ " discovered by other processes. If 0, disabled")
+FUZZER_FLAG_INT(report_slow_units, 10,
+ "Report slowest units if they run for more than this number of seconds.")
+FUZZER_FLAG_INT(only_ascii, 0,
+ "If 1, generate only ASCII (isprint+isspace) inputs.")
+FUZZER_FLAG_STRING(dict, "Experimental. Use the dictionary file.")
+FUZZER_FLAG_STRING(artifact_prefix, "Write fuzzing artifacts (crash, "
+ "timeout, or slow inputs) as "
+ "$(artifact_prefix)file")
+FUZZER_FLAG_STRING(exact_artifact_path,
+ "Write the single artifact on failure (crash, timeout) "
+ "as $(exact_artifact_path). This overrides -artifact_prefix "
+ "and will not use checksum in the file name. Do not "
+ "use the same path for several parallel processes.")
+FUZZER_FLAG_INT(output_csv, 0, "Enable pulse output in CSV format.")
+FUZZER_FLAG_INT(print_pcs, 0, "If 1, print out newly covered PCs.")
+FUZZER_FLAG_INT(print_final_stats, 0, "If 1, print statistics at exit.")
+FUZZER_FLAG_INT(print_corpus_stats, 0,
+ "If 1, print statistics on corpus elements at exit.")
+FUZZER_FLAG_INT(print_coverage, 0, "If 1, print coverage information at exit."
+ " Experimental, only with trace-pc-guard")
+FUZZER_FLAG_INT(dump_coverage, 0, "If 1, dump coverage information at exit."
+ " Experimental, only with trace-pc-guard")
+FUZZER_FLAG_INT(handle_segv, 1, "If 1, try to intercept SIGSEGV.")
+FUZZER_FLAG_INT(handle_bus, 1, "If 1, try to intercept SIGSEGV.")
+FUZZER_FLAG_INT(handle_abrt, 1, "If 1, try to intercept SIGABRT.")
+FUZZER_FLAG_INT(handle_ill, 1, "If 1, try to intercept SIGILL.")
+FUZZER_FLAG_INT(handle_fpe, 1, "If 1, try to intercept SIGFPE.")
+FUZZER_FLAG_INT(handle_int, 1, "If 1, try to intercept SIGINT.")
+FUZZER_FLAG_INT(handle_term, 1, "If 1, try to intercept SIGTERM.")
+FUZZER_FLAG_INT(close_fd_mask, 0, "If 1, close stdout at startup; "
+ "if 2, close stderr; if 3, close both. "
+ "Be careful, this will also close e.g. asan's stderr/stdout.")
+FUZZER_FLAG_INT(detect_leaks, 1, "If 1, and if LeakSanitizer is enabled "
+ "try to detect memory leaks during fuzzing (i.e. not only at shut down).")
+FUZZER_FLAG_INT(trace_malloc, 0, "If >= 1 will print all mallocs/frees. "
+ "If >= 2 will also print stack traces.")
+FUZZER_FLAG_INT(rss_limit_mb, 2048, "If non-zero, the fuzzer will exit upon"
+ "reaching this limit of RSS memory usage.")
+FUZZER_FLAG_STRING(exit_on_src_pos, "Exit if a newly found PC originates"
+ " from the given source location. Example: -exit_on_src_pos=foo.cc:123. "
+ "Used primarily for testing libFuzzer itself.")
+FUZZER_FLAG_STRING(exit_on_item, "Exit if an item with a given sha1 sum"
+ " was added to the corpus. "
+ "Used primarily for testing libFuzzer itself.")
+
+FUZZER_DEPRECATED_FLAG(exit_on_first)
+FUZZER_DEPRECATED_FLAG(save_minimized_corpus)
+FUZZER_DEPRECATED_FLAG(sync_command)
+FUZZER_DEPRECATED_FLAG(sync_timeout)
+FUZZER_DEPRECATED_FLAG(test_single_input)
+FUZZER_DEPRECATED_FLAG(drill)
+FUZZER_DEPRECATED_FLAG(truncate_units)
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerIO.cpp b/json4cpp/tests/thirdparty/Fuzzer/FuzzerIO.cpp
new file mode 100644
index 0000000000..eda8e87729
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerIO.cpp
@@ -0,0 +1,117 @@
+//===- FuzzerIO.cpp - IO utils. -------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// IO functions.
+//===----------------------------------------------------------------------===//
+
+#include "FuzzerIO.h"
+#include "FuzzerDefs.h"
+#include "FuzzerExtFunctions.h"
+#include <algorithm>
+#include <cstdarg>
+#include <fstream>
+#include <iterator>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+namespace fuzzer {
+
+static FILE *OutputFile = stderr;
+
+long GetEpoch(const std::string &Path) {
+ struct stat St;
+ if (stat(Path.c_str(), &St))
+ return 0; // Can't stat, be conservative.
+ return St.st_mtime;
+}
+
+Unit FileToVector(const std::string &Path, size_t MaxSize, bool ExitOnError) {
+ std::ifstream T(Path);
+ if (ExitOnError && !T) {
+ Printf("No such directory: %s; exiting\n", Path.c_str());
+ exit(1);
+ }
+
+ T.seekg(0, T.end);
+ size_t FileLen = T.tellg();
+ if (MaxSize)
+ FileLen = std::min(FileLen, MaxSize);
+
+ T.seekg(0, T.beg);
+ Unit Res(FileLen);
+ T.read(reinterpret_cast<char *>(Res.data()), FileLen);
+ return Res;
+}
+
+std::string FileToString(const std::string &Path) {
+ std::ifstream T(Path);
+ return std::string((std::istreambuf_iterator<char>(T)),
+ std::istreambuf_iterator<char>());
+}
+
+void CopyFileToErr(const std::string &Path) {
+ Printf("%s", FileToString(Path).c_str());
+}
+
+void WriteToFile(const Unit &U, const std::string &Path) {
+ // Use raw C interface because this function may be called from a sig handler.
+ FILE *Out = fopen(Path.c_str(), "w");
+ if (!Out) return;
+ fwrite(U.data(), sizeof(U[0]), U.size(), Out);
+ fclose(Out);
+}
+
+void ReadDirToVectorOfUnits(const char *Path, std::vector<Unit> *V,
+ long *Epoch, size_t MaxSize, bool ExitOnError) {
+ long E = Epoch ? *Epoch : 0;
+ std::vector<std::string> Files;
+ ListFilesInDirRecursive(Path, Epoch, &Files, /*TopDir*/true);
+ size_t NumLoaded = 0;
+ for (size_t i = 0; i < Files.size(); i++) {
+ auto &X = Files[i];
+ if (Epoch && GetEpoch(X) < E) continue;
+ NumLoaded++;
+ if ((NumLoaded & (NumLoaded - 1)) == 0 && NumLoaded >= 1024)
+ Printf("Loaded %zd/%zd files from %s\n", NumLoaded, Files.size(), Path);
+ auto S = FileToVector(X, MaxSize, ExitOnError);
+ if (!S.empty())
+ V->push_back(S);
+ }
+}
+
+std::string DirPlusFile(const std::string &DirPath,
+ const std::string &FileName) {
+ return DirPath + GetSeparator() + FileName;
+}
+
+void DupAndCloseStderr() {
+ int OutputFd = DuplicateFile(2);
+ if (OutputFd > 0) {
+ FILE *NewOutputFile = OpenFile(OutputFd, "w");
+ if (NewOutputFile) {
+ OutputFile = NewOutputFile;
+ if (EF->__sanitizer_set_report_fd)
+ EF->__sanitizer_set_report_fd(reinterpret_cast<void *>(OutputFd));
+ CloseFile(2);
+ }
+ }
+}
+
+void CloseStdout() {
+ CloseFile(1);
+}
+
+void Printf(const char *Fmt, ...) {
+ va_list ap;
+ va_start(ap, Fmt);
+ vfprintf(OutputFile, Fmt, ap);
+ va_end(ap);
+ fflush(OutputFile);
+}
+
+} // namespace fuzzer
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerIO.h b/json4cpp/tests/thirdparty/Fuzzer/FuzzerIO.h
new file mode 100644
index 0000000000..741fecf415
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerIO.h
@@ -0,0 +1,64 @@
+//===- FuzzerIO.h - Internal header for IO utils ----------------*- C++ -* ===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// IO interface.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_FUZZER_IO_H
+#define LLVM_FUZZER_IO_H
+
+#include "FuzzerDefs.h"
+
+namespace fuzzer {
+
+long GetEpoch(const std::string &Path);
+
+Unit FileToVector(const std::string &Path, size_t MaxSize = 0,
+ bool ExitOnError = true);
+
+std::string FileToString(const std::string &Path);
+
+void CopyFileToErr(const std::string &Path);
+
+void WriteToFile(const Unit &U, const std::string &Path);
+
+void ReadDirToVectorOfUnits(const char *Path, std::vector<Unit> *V,
+ long *Epoch, size_t MaxSize, bool ExitOnError);
+
+// Returns "Dir/FileName" or equivalent for the current OS.
+std::string DirPlusFile(const std::string &DirPath,
+ const std::string &FileName);
+
+// Returns the name of the dir, similar to the 'dirname' utility.
+std::string DirName(const std::string &FileName);
+
+void DupAndCloseStderr();
+
+void CloseStdout();
+
+void Printf(const char *Fmt, ...);
+
+// Platform specific functions:
+bool IsFile(const std::string &Path);
+
+void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
+ std::vector<std::string> *V, bool TopDir);
+
+char GetSeparator();
+
+FILE* OpenFile(int Fd, const char *Mode);
+
+int CloseFile(int Fd);
+
+int DuplicateFile(int Fd);
+
+void RemoveFile(const std::string &Path);
+
+} // namespace fuzzer
+
+#endif // LLVM_FUZZER_IO_H
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerIOPosix.cpp b/json4cpp/tests/thirdparty/Fuzzer/FuzzerIOPosix.cpp
new file mode 100644
index 0000000000..720bc13045
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerIOPosix.cpp
@@ -0,0 +1,88 @@
+//===- FuzzerIOPosix.cpp - IO utils for Posix. ----------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// IO functions implementation using Posix API.
+//===----------------------------------------------------------------------===//
+#include "FuzzerDefs.h"
+#if LIBFUZZER_POSIX
+
+#include "FuzzerExtFunctions.h"
+#include "FuzzerIO.h"
+#include <cstdarg>
+#include <cstdio>
+#include <dirent.h>
+#include <fstream>
+#include <iterator>
+#include <libgen.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+namespace fuzzer {
+
+bool IsFile(const std::string &Path) {
+ struct stat St;
+ if (stat(Path.c_str(), &St))
+ return false;
+ return S_ISREG(St.st_mode);
+}
+
+void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
+ std::vector<std::string> *V, bool TopDir) {
+ auto E = GetEpoch(Dir);
+ if (Epoch)
+ if (E && *Epoch >= E) return;
+
+ DIR *D = opendir(Dir.c_str());
+ if (!D) {
+ Printf("No such directory: %s; exiting\n", Dir.c_str());
+ exit(1);
+ }
+ while (auto E = readdir(D)) {
+ std::string Path = DirPlusFile(Dir, E->d_name);
+ if (E->d_type == DT_REG || E->d_type == DT_LNK)
+ V->push_back(Path);
+ else if (E->d_type == DT_DIR && *E->d_name != '.')
+ ListFilesInDirRecursive(Path, Epoch, V, false);
+ }
+ closedir(D);
+ if (Epoch && TopDir)
+ *Epoch = E;
+}
+
+char GetSeparator() {
+ return '/';
+}
+
+FILE* OpenFile(int Fd, const char* Mode) {
+ return fdopen(Fd, Mode);
+}
+
+int CloseFile(int fd) {
+ return close(fd);
+}
+
+int DuplicateFile(int Fd) {
+ return dup(Fd);
+}
+
+void RemoveFile(const std::string &Path) {
+ unlink(Path.c_str());
+}
+
+std::string DirName(const std::string &FileName) {
+ char *Tmp = new char[FileName.size() + 1];
+ memcpy(Tmp, FileName.c_str(), FileName.size() + 1);
+ std::string Res = dirname(Tmp);
+ delete [] Tmp;
+ return Res;
+}
+
+} // namespace fuzzer
+
+#endif // LIBFUZZER_POSIX
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerIOWindows.cpp b/json4cpp/tests/thirdparty/Fuzzer/FuzzerIOWindows.cpp
new file mode 100644
index 0000000000..a4738eb9df
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerIOWindows.cpp
@@ -0,0 +1,282 @@
+//===- FuzzerIOWindows.cpp - IO utils for Windows. ------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// IO functions implementation for Windows.
+//===----------------------------------------------------------------------===//
+#include "FuzzerDefs.h"
+#if LIBFUZZER_WINDOWS
+
+#include "FuzzerExtFunctions.h"
+#include "FuzzerIO.h"
+#include <cstdarg>
+#include <cstdio>
+#include <fstream>
+#include <io.h>
+#include <iterator>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <windows.h>
+
+namespace fuzzer {
+
+static bool IsFile(const std::string &Path, const DWORD &FileAttributes) {
+
+ if (FileAttributes & FILE_ATTRIBUTE_NORMAL)
+ return true;
+
+ if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ return false;
+
+ HANDLE FileHandle(
+ CreateFileA(Path.c_str(), 0, FILE_SHARE_READ, NULL, OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS, 0));
+
+ if (FileHandle == INVALID_HANDLE_VALUE) {
+ Printf("CreateFileA() failed for \"%s\" (Error code: %lu).\n", Path.c_str(),
+ GetLastError());
+ return false;
+ }
+
+ DWORD FileType = GetFileType(FileHandle);
+
+ if (FileType == FILE_TYPE_UNKNOWN) {
+ Printf("GetFileType() failed for \"%s\" (Error code: %lu).\n", Path.c_str(),
+ GetLastError());
+ CloseHandle(FileHandle);
+ return false;
+ }
+
+ if (FileType != FILE_TYPE_DISK) {
+ CloseHandle(FileHandle);
+ return false;
+ }
+
+ CloseHandle(FileHandle);
+ return true;
+}
+
+bool IsFile(const std::string &Path) {
+ DWORD Att = GetFileAttributesA(Path.c_str());
+
+ if (Att == INVALID_FILE_ATTRIBUTES) {
+ Printf("GetFileAttributesA() failed for \"%s\" (Error code: %lu).\n",
+ Path.c_str(), GetLastError());
+ return false;
+ }
+
+ return IsFile(Path, Att);
+}
+
+void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
+ std::vector<std::string> *V, bool TopDir) {
+ auto E = GetEpoch(Dir);
+ if (Epoch)
+ if (E && *Epoch >= E) return;
+
+ std::string Path(Dir);
+ assert(!Path.empty());
+ if (Path.back() != '\\')
+ Path.push_back('\\');
+ Path.push_back('*');
+
+ // Get the first directory entry.
+ WIN32_FIND_DATAA FindInfo;
+ HANDLE FindHandle(FindFirstFileA(Path.c_str(), &FindInfo));
+ if (FindHandle == INVALID_HANDLE_VALUE)
+ {
+ Printf("No file found in: %s.\n", Dir.c_str());
+ return;
+ }
+
+ do {
+ std::string FileName = DirPlusFile(Dir, FindInfo.cFileName);
+
+ if (FindInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ size_t FilenameLen = strlen(FindInfo.cFileName);
+ if ((FilenameLen == 1 && FindInfo.cFileName[0] == '.') ||
+ (FilenameLen == 2 && FindInfo.cFileName[0] == '.' &&
+ FindInfo.cFileName[1] == '.'))
+ continue;
+
+ ListFilesInDirRecursive(FileName, Epoch, V, false);
+ }
+ else if (IsFile(FileName, FindInfo.dwFileAttributes))
+ V->push_back(FileName);
+ } while (FindNextFileA(FindHandle, &FindInfo));
+
+ DWORD LastError = GetLastError();
+ if (LastError != ERROR_NO_MORE_FILES)
+ Printf("FindNextFileA failed (Error code: %lu).\n", LastError);
+
+ FindClose(FindHandle);
+
+ if (Epoch && TopDir)
+ *Epoch = E;
+}
+
+char GetSeparator() {
+ return '\\';
+}
+
+FILE* OpenFile(int Fd, const char* Mode) {
+ return _fdopen(Fd, Mode);
+}
+
+int CloseFile(int Fd) {
+ return _close(Fd);
+}
+
+int DuplicateFile(int Fd) {
+ return _dup(Fd);
+}
+
+void RemoveFile(const std::string &Path) {
+ _unlink(Path.c_str());
+}
+
+static bool IsSeparator(char C) {
+ return C == '\\' || C == '/';
+}
+
+// Parse disk designators, like "C:\". If Relative == true, also accepts: "C:".
+// Returns number of characters considered if successful.
+static size_t ParseDrive(const std::string &FileName, const size_t Offset,
+ bool Relative = true) {
+ if (Offset + 1 >= FileName.size() || FileName[Offset + 1] != ':')
+ return 0;
+ if (Offset + 2 >= FileName.size() || !IsSeparator(FileName[Offset + 2])) {
+ if (!Relative) // Accept relative path?
+ return 0;
+ else
+ return 2;
+ }
+ return 3;
+}
+
+// Parse a file name, like: SomeFile.txt
+// Returns number of characters considered if successful.
+static size_t ParseFileName(const std::string &FileName, const size_t Offset) {
+ size_t Pos = Offset;
+ const size_t End = FileName.size();
+ for(; Pos < End && !IsSeparator(FileName[Pos]); ++Pos)
+ ;
+ return Pos - Offset;
+}
+
+// Parse a directory ending in separator, like: SomeDir\
+// Returns number of characters considered if successful.
+static size_t ParseDir(const std::string &FileName, const size_t Offset) {
+ size_t Pos = Offset;
+ const size_t End = FileName.size();
+ if (Pos >= End || IsSeparator(FileName[Pos]))
+ return 0;
+ for(; Pos < End && !IsSeparator(FileName[Pos]); ++Pos)
+ ;
+ if (Pos >= End)
+ return 0;
+ ++Pos; // Include separator.
+ return Pos - Offset;
+}
+
+// Parse a servername and share, like: SomeServer\SomeShare\
+// Returns number of characters considered if successful.
+static size_t ParseServerAndShare(const std::string &FileName,
+ const size_t Offset) {
+ size_t Pos = Offset, Res;
+ if (!(Res = ParseDir(FileName, Pos)))
+ return 0;
+ Pos += Res;
+ if (!(Res = ParseDir(FileName, Pos)))
+ return 0;
+ Pos += Res;
+ return Pos - Offset;
+}
+
+// Parse the given Ref string from the position Offset, to exactly match the given
+// string Patt.
+// Returns number of characters considered if successful.
+static size_t ParseCustomString(const std::string &Ref, size_t Offset,
+ const char *Patt) {
+ size_t Len = strlen(Patt);
+ if (Offset + Len > Ref.size())
+ return 0;
+ return Ref.compare(Offset, Len, Patt) == 0 ? Len : 0;
+}
+
+// Parse a location, like:
+// \\?\UNC\Server\Share\ \\?\C:\ \\Server\Share\ \ C:\ C:
+// Returns number of characters considered if successful.
+static size_t ParseLocation(const std::string &FileName) {
+ size_t Pos = 0, Res;
+
+ if ((Res = ParseCustomString(FileName, Pos, R"(\\?\)"))) {
+ Pos += Res;
+ if ((Res = ParseCustomString(FileName, Pos, R"(UNC\)"))) {
+ Pos += Res;
+ if ((Res = ParseServerAndShare(FileName, Pos)))
+ return Pos + Res;
+ return 0;
+ }
+ if ((Res = ParseDrive(FileName, Pos, false)))
+ return Pos + Res;
+ return 0;
+ }
+
+ if (Pos < FileName.size() && IsSeparator(FileName[Pos])) {
+ ++Pos;
+ if (Pos < FileName.size() && IsSeparator(FileName[Pos])) {
+ ++Pos;
+ if ((Res = ParseServerAndShare(FileName, Pos)))
+ return Pos + Res;
+ return 0;
+ }
+ return Pos;
+ }
+
+ if ((Res = ParseDrive(FileName, Pos)))
+ return Pos + Res;
+
+ return Pos;
+}
+
+std::string DirName(const std::string &FileName) {
+ size_t LocationLen = ParseLocation(FileName);
+ size_t DirLen = 0, Res;
+ while ((Res = ParseDir(FileName, LocationLen + DirLen)))
+ DirLen += Res;
+ size_t FileLen = ParseFileName(FileName, LocationLen + DirLen);
+
+ if (LocationLen + DirLen + FileLen != FileName.size()) {
+ Printf("DirName() failed for \"%s\", invalid path.\n", FileName.c_str());
+ exit(1);
+ }
+
+ if (DirLen) {
+ --DirLen; // Remove trailing separator.
+ if (!FileLen) { // Path ended in separator.
+ assert(DirLen);
+ // Remove file name from Dir.
+ while (DirLen && !IsSeparator(FileName[LocationLen + DirLen - 1]))
+ --DirLen;
+ if (DirLen) // Remove trailing separator.
+ --DirLen;
+ }
+ }
+
+ if (!LocationLen) { // Relative path.
+ if (!DirLen)
+ return ".";
+ return std::string(".\\").append(FileName, 0, DirLen);
+ }
+
+ return FileName.substr(0, LocationLen + DirLen);
+}
+
+} // namespace fuzzer
+
+#endif // LIBFUZZER_WINDOWS
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerInterface.h b/json4cpp/tests/thirdparty/Fuzzer/FuzzerInterface.h
new file mode 100644
index 0000000000..d47e20e3a2
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerInterface.h
@@ -0,0 +1,67 @@
+//===- FuzzerInterface.h - Interface header for the Fuzzer ------*- C++ -* ===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// Define the interface between libFuzzer and the library being tested.
+//===----------------------------------------------------------------------===//
+
+// NOTE: the libFuzzer interface is thin and in the majority of cases
+// you should not include this file into your target. In 95% of cases
+// all you need is to define the following function in your file:
+// extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
+
+// WARNING: keep the interface in C.
+
+#ifndef LLVM_FUZZER_INTERFACE_H
+#define LLVM_FUZZER_INTERFACE_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+// Mandatory user-provided target function.
+// Executes the code under test with [Data, Data+Size) as the input.
+// libFuzzer will invoke this function *many* times with different inputs.
+// Must return 0.
+int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
+
+// Optional user-provided initialization function.
+// If provided, this function will be called by libFuzzer once at startup.
+// It may read and modify argc/argv.
+// Must return 0.
+int LLVMFuzzerInitialize(int *argc, char ***argv);
+
+// Optional user-provided custom mutator.
+// Mutates raw data in [Data, Data+Size) inplace.
+// Returns the new size, which is not greater than MaxSize.
+// Given the same Seed produces the same mutation.
+size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize,
+ unsigned int Seed);
+
+// Optional user-provided custom cross-over function.
+// Combines pieces of Data1 & Data2 together into Out.
+// Returns the new size, which is not greater than MaxOutSize.
+// Should produce the same mutation given the same Seed.
+size_t LLVMFuzzerCustomCrossOver(const uint8_t *Data1, size_t Size1,
+ const uint8_t *Data2, size_t Size2,
+ uint8_t *Out, size_t MaxOutSize,
+ unsigned int Seed);
+
+// Experimental, may go away in future.
+// libFuzzer-provided function to be used inside LLVMFuzzerTestOneInput.
+// Mutates raw data in [Data, Data+Size) inplace.
+// Returns the new size, which is not greater than MaxSize.
+size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif // __cplusplus
+
+#endif // LLVM_FUZZER_INTERFACE_H
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerInternal.h b/json4cpp/tests/thirdparty/Fuzzer/FuzzerInternal.h
new file mode 100644
index 0000000000..c041706092
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerInternal.h
@@ -0,0 +1,182 @@
+//===- FuzzerInternal.h - Internal header for the Fuzzer --------*- C++ -* ===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// Define the main class fuzzer::Fuzzer and most functions.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_FUZZER_INTERNAL_H
+#define LLVM_FUZZER_INTERNAL_H
+
+#include "FuzzerDefs.h"
+#include "FuzzerExtFunctions.h"
+#include "FuzzerInterface.h"
+#include "FuzzerOptions.h"
+#include "FuzzerSHA1.h"
+#include "FuzzerValueBitMap.h"
+#include <algorithm>
+#include <atomic>
+#include <chrono>
+#include <climits>
+#include <cstdlib>
+#include <string.h>
+
+namespace fuzzer {
+
+using namespace std::chrono;
+
+class Fuzzer {
+public:
+
+ // Aggregates all available coverage measurements.
+ struct Coverage {
+ Coverage() { Reset(); }
+
+ void Reset() {
+ BlockCoverage = 0;
+ CallerCalleeCoverage = 0;
+ CounterBitmapBits = 0;
+ CounterBitmap.clear();
+ VPMap.Reset();
+ }
+
+ size_t BlockCoverage;
+ size_t CallerCalleeCoverage;
+ // Precalculated number of bits in CounterBitmap.
+ size_t CounterBitmapBits;
+ std::vector<uint8_t> CounterBitmap;
+ ValueBitMap VPMap;
+ };
+
+ Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD,
+ FuzzingOptions Options);
+ ~Fuzzer();
+ void Loop();
+ void MinimizeCrashLoop(const Unit &U);
+ void ShuffleAndMinimize(UnitVector *V);
+ void InitializeTraceState();
+ void RereadOutputCorpus(size_t MaxSize);
+
+ size_t secondsSinceProcessStartUp() {
+ return duration_cast<seconds>(system_clock::now() - ProcessStartTime)
+ .count();
+ }
+
+ bool TimedOut() {
+ return Options.MaxTotalTimeSec > 0 &&
+ secondsSinceProcessStartUp() >
+ static_cast<size_t>(Options.MaxTotalTimeSec);
+ }
+
+ size_t execPerSec() {
+ size_t Seconds = secondsSinceProcessStartUp();
+ return Seconds ? TotalNumberOfRuns / Seconds : 0;
+ }
+
+ size_t getTotalNumberOfRuns() { return TotalNumberOfRuns; }
+
+ static void StaticAlarmCallback();
+ static void StaticCrashSignalCallback();
+ static void StaticInterruptCallback();
+
+ void ExecuteCallback(const uint8_t *Data, size_t Size);
+ size_t RunOne(const uint8_t *Data, size_t Size);
+
+ // Merge Corpora[1:] into Corpora[0].
+ void Merge(const std::vector<std::string> &Corpora);
+ void CrashResistantMerge(const std::vector<std::string> &Args,
+ const std::vector<std::string> &Corpora);
+ void CrashResistantMergeInternalStep(const std::string &ControlFilePath);
+ // Returns a subset of 'Extra' that adds coverage to 'Initial'.
+ UnitVector FindExtraUnits(const UnitVector &Initial, const UnitVector &Extra);
+ MutationDispatcher &GetMD() { return MD; }
+ void PrintFinalStats();
+ void SetMaxInputLen(size_t MaxInputLen);
+ void SetMaxMutationLen(size_t MaxMutationLen);
+ void RssLimitCallback();
+
+ // Public for tests.
+ void ResetCoverage();
+
+ bool InFuzzingThread() const { return IsMyThread; }
+ size_t GetCurrentUnitInFuzzingThead(const uint8_t **Data) const;
+ void TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size,
+ bool DuringInitialCorpusExecution);
+
+ void HandleMalloc(size_t Size);
+
+private:
+ void AlarmCallback();
+ void CrashCallback();
+ void InterruptCallback();
+ void MutateAndTestOne();
+ void ReportNewCoverage(InputInfo *II, const Unit &U);
+ size_t RunOne(const Unit &U) { return RunOne(U.data(), U.size()); }
+ void WriteToOutputCorpus(const Unit &U);
+ void WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix);
+ void PrintStats(const char *Where, const char *End = "\n", size_t Units = 0);
+ void PrintStatusForNewUnit(const Unit &U);
+ void ShuffleCorpus(UnitVector *V);
+ void AddToCorpus(const Unit &U);
+ void CheckExitOnSrcPosOrItem();
+
+ // Trace-based fuzzing: we run a unit with some kind of tracing
+ // enabled and record potentially useful mutations. Then
+ // We apply these mutations one by one to the unit and run it again.
+
+ // Start tracing; forget all previously proposed mutations.
+ void StartTraceRecording();
+ // Stop tracing.
+ void StopTraceRecording();
+
+ void SetDeathCallback();
+ static void StaticDeathCallback();
+ void DumpCurrentUnit(const char *Prefix);
+ void DeathCallback();
+
+ void ResetEdgeCoverage();
+ void ResetCounters();
+ void PrepareCounters(Fuzzer::Coverage *C);
+ bool RecordMaxCoverage(Fuzzer::Coverage *C);
+
+ void AllocateCurrentUnitData();
+ uint8_t *CurrentUnitData = nullptr;
+ std::atomic<size_t> CurrentUnitSize;
+ uint8_t BaseSha1[kSHA1NumBytes]; // Checksum of the base unit.
+ bool RunningCB = false;
+
+ size_t TotalNumberOfRuns = 0;
+ size_t NumberOfNewUnitsAdded = 0;
+
+ bool HasMoreMallocsThanFrees = false;
+ size_t NumberOfLeakDetectionAttempts = 0;
+
+ UserCallback CB;
+ InputCorpus &Corpus;
+ MutationDispatcher &MD;
+ FuzzingOptions Options;
+
+ system_clock::time_point ProcessStartTime = system_clock::now();
+ system_clock::time_point UnitStartTime, UnitStopTime;
+ long TimeOfLongestUnitInSeconds = 0;
+ long EpochOfLastReadOfOutputCorpus = 0;
+
+ // Maximum recorded coverage.
+ Coverage MaxCoverage;
+
+ size_t MaxInputLen = 0;
+ size_t MaxMutationLen = 0;
+
+ // Need to know our own thread.
+ static thread_local bool IsMyThread;
+
+ bool InMergeMode = false;
+};
+
+}; // namespace fuzzer
+
+#endif // LLVM_FUZZER_INTERNAL_H
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerLoop.cpp b/json4cpp/tests/thirdparty/Fuzzer/FuzzerLoop.cpp
new file mode 100644
index 0000000000..73e058ff34
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerLoop.cpp
@@ -0,0 +1,792 @@
+//===- FuzzerLoop.cpp - Fuzzer's main loop --------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// Fuzzer's main loop.
+//===----------------------------------------------------------------------===//
+
+#include "FuzzerCorpus.h"
+#include "FuzzerInternal.h"
+#include "FuzzerIO.h"
+#include "FuzzerMutate.h"
+#include "FuzzerRandom.h"
+#include "FuzzerTracePC.h"
+#include <algorithm>
+#include <cstring>
+#include <memory>
+#include <set>
+
+#if defined(__has_include)
+#if __has_include(<sanitizer / coverage_interface.h>)
+#include <sanitizer/coverage_interface.h>
+#endif
+#if __has_include(<sanitizer / lsan_interface.h>)
+#include <sanitizer/lsan_interface.h>
+#endif
+#endif
+
+#define NO_SANITIZE_MEMORY
+#if defined(__has_feature)
+#if __has_feature(memory_sanitizer)
+#undef NO_SANITIZE_MEMORY
+#define NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory))
+#endif
+#endif
+
+namespace fuzzer {
+static const size_t kMaxUnitSizeToPrint = 256;
+
+thread_local bool Fuzzer::IsMyThread;
+
+static void MissingExternalApiFunction(const char *FnName) {
+ Printf("ERROR: %s is not defined. Exiting.\n"
+ "Did you use -fsanitize-coverage=... to build your code?\n",
+ FnName);
+ exit(1);
+}
+
+#define CHECK_EXTERNAL_FUNCTION(fn) \
+ do { \
+ if (!(EF->fn)) \
+ MissingExternalApiFunction(#fn); \
+ } while (false)
+
+// Only one Fuzzer per process.
+static Fuzzer *F;
+
+void Fuzzer::ResetEdgeCoverage() {
+ CHECK_EXTERNAL_FUNCTION(__sanitizer_reset_coverage);
+ EF->__sanitizer_reset_coverage();
+}
+
+void Fuzzer::ResetCounters() {
+ if (Options.UseCounters)
+ EF->__sanitizer_update_counter_bitset_and_clear_counters(0);
+}
+
+void Fuzzer::PrepareCounters(Fuzzer::Coverage *C) {
+ if (Options.UseCounters) {
+ size_t NumCounters = EF->__sanitizer_get_number_of_counters();
+ C->CounterBitmap.resize(NumCounters);
+ }
+}
+
+// Records data to a maximum coverage tracker. Returns true if additional
+// coverage was discovered.
+bool Fuzzer::RecordMaxCoverage(Fuzzer::Coverage *C) {
+ bool Res = false;
+
+ uint64_t NewBlockCoverage = EF->__sanitizer_get_total_unique_coverage();
+ if (NewBlockCoverage > C->BlockCoverage) {
+ Res = true;
+ C->BlockCoverage = NewBlockCoverage;
+ }
+
+ if (Options.UseIndirCalls &&
+ EF->__sanitizer_get_total_unique_caller_callee_pairs) {
+ uint64_t NewCallerCalleeCoverage =
+ EF->__sanitizer_get_total_unique_caller_callee_pairs();
+ if (NewCallerCalleeCoverage > C->CallerCalleeCoverage) {
+ Res = true;
+ C->CallerCalleeCoverage = NewCallerCalleeCoverage;
+ }
+ }
+
+ if (Options.UseCounters) {
+ uint64_t CounterDelta =
+ EF->__sanitizer_update_counter_bitset_and_clear_counters(
+ C->CounterBitmap.data());
+ if (CounterDelta > 0) {
+ Res = true;
+ C->CounterBitmapBits += CounterDelta;
+ }
+ }
+
+ return Res;
+}
+
+// Leak detection is expensive, so we first check if there were more mallocs
+// than frees (using the sanitizer malloc hooks) and only then try to call lsan.
+struct MallocFreeTracer {
+ void Start(int TraceLevel) {
+ this->TraceLevel = TraceLevel;
+ if (TraceLevel)
+ Printf("MallocFreeTracer: START\n");
+ Mallocs = 0;
+ Frees = 0;
+ }
+ // Returns true if there were more mallocs than frees.
+ bool Stop() {
+ if (TraceLevel)
+ Printf("MallocFreeTracer: STOP %zd %zd (%s)\n", Mallocs.load(),
+ Frees.load(), Mallocs == Frees ? "same" : "DIFFERENT");
+ bool Result = Mallocs > Frees;
+ Mallocs = 0;
+ Frees = 0;
+ TraceLevel = 0;
+ return Result;
+ }
+ std::atomic<size_t> Mallocs;
+ std::atomic<size_t> Frees;
+ int TraceLevel = 0;
+};
+
+static MallocFreeTracer AllocTracer;
+
+ATTRIBUTE_NO_SANITIZE_MEMORY
+void MallocHook(const volatile void *ptr, size_t size) {
+ size_t N = AllocTracer.Mallocs++;
+ F->HandleMalloc(size);
+ if (int TraceLevel = AllocTracer.TraceLevel) {
+ Printf("MALLOC[%zd] %p %zd\n", N, ptr, size);
+ if (TraceLevel >= 2 && EF)
+ EF->__sanitizer_print_stack_trace();
+ }
+}
+
+ATTRIBUTE_NO_SANITIZE_MEMORY
+void FreeHook(const volatile void *ptr) {
+ size_t N = AllocTracer.Frees++;
+ if (int TraceLevel = AllocTracer.TraceLevel) {
+ Printf("FREE[%zd] %p\n", N, ptr);
+ if (TraceLevel >= 2 && EF)
+ EF->__sanitizer_print_stack_trace();
+ }
+}
+
+// Crash on a single malloc that exceeds the rss limit.
+void Fuzzer::HandleMalloc(size_t Size) {
+ if (!Options.RssLimitMb || (Size >> 20) < (size_t)Options.RssLimitMb)
+ return;
+ Printf("==%d== ERROR: libFuzzer: out-of-memory (malloc(%zd))\n", GetPid(),
+ Size);
+ Printf(" To change the out-of-memory limit use -rss_limit_mb=<N>\n\n");
+ if (EF->__sanitizer_print_stack_trace)
+ EF->__sanitizer_print_stack_trace();
+ DumpCurrentUnit("oom-");
+ Printf("SUMMARY: libFuzzer: out-of-memory\n");
+ PrintFinalStats();
+ _Exit(Options.ErrorExitCode); // Stop right now.
+}
+
+Fuzzer::Fuzzer(UserCallback CB, InputCorpus &Corpus, MutationDispatcher &MD,
+ FuzzingOptions Options)
+ : CB(CB), Corpus(Corpus), MD(MD), Options(Options) {
+ SetDeathCallback();
+ InitializeTraceState();
+ assert(!F);
+ F = this;
+ TPC.ResetMaps();
+ ResetCoverage();
+ IsMyThread = true;
+ if (Options.DetectLeaks && EF->__sanitizer_install_malloc_and_free_hooks)
+ EF->__sanitizer_install_malloc_and_free_hooks(MallocHook, FreeHook);
+ TPC.SetUseCounters(Options.UseCounters);
+ TPC.SetUseValueProfile(Options.UseValueProfile);
+ TPC.SetPrintNewPCs(Options.PrintNewCovPcs);
+
+ if (Options.Verbosity)
+ TPC.PrintModuleInfo();
+ if (!Options.OutputCorpus.empty() && Options.ReloadIntervalSec)
+ EpochOfLastReadOfOutputCorpus = GetEpoch(Options.OutputCorpus);
+ MaxInputLen = MaxMutationLen = Options.MaxLen;
+ AllocateCurrentUnitData();
+ CurrentUnitSize = 0;
+ memset(BaseSha1, 0, sizeof(BaseSha1));
+}
+
+Fuzzer::~Fuzzer() { }
+
+void Fuzzer::AllocateCurrentUnitData() {
+ if (CurrentUnitData || MaxInputLen == 0) return;
+ CurrentUnitData = new uint8_t[MaxInputLen];
+}
+
+void Fuzzer::SetDeathCallback() {
+ CHECK_EXTERNAL_FUNCTION(__sanitizer_set_death_callback);
+ EF->__sanitizer_set_death_callback(StaticDeathCallback);
+}
+
+void Fuzzer::StaticDeathCallback() {
+ assert(F);
+ F->DeathCallback();
+}
+
+static void WarnOnUnsuccessfullMerge(bool DoWarn) {
+ if (!DoWarn) return;
+ Printf(
+ "***\n"
+ "***\n"
+ "***\n"
+ "*** NOTE: merge did not succeed due to a failure on one of the inputs.\n"
+ "*** You will need to filter out crashes from the corpus, e.g. like this:\n"
+ "*** for f in WITH_CRASHES/*; do ./fuzzer $f && cp $f NO_CRASHES; done\n"
+ "*** Future versions may have crash-resistant merge, stay tuned.\n"
+ "***\n"
+ "***\n"
+ "***\n");
+}
+
+void Fuzzer::DumpCurrentUnit(const char *Prefix) {
+ WarnOnUnsuccessfullMerge(InMergeMode);
+ if (!CurrentUnitData) return; // Happens when running individual inputs.
+ MD.PrintMutationSequence();
+ Printf("; base unit: %s\n", Sha1ToString(BaseSha1).c_str());
+ size_t UnitSize = CurrentUnitSize;
+ if (UnitSize <= kMaxUnitSizeToPrint) {
+ PrintHexArray(CurrentUnitData, UnitSize, "\n");
+ PrintASCII(CurrentUnitData, UnitSize, "\n");
+ }
+ WriteUnitToFileWithPrefix({CurrentUnitData, CurrentUnitData + UnitSize},
+ Prefix);
+}
+
+NO_SANITIZE_MEMORY
+void Fuzzer::DeathCallback() {
+ DumpCurrentUnit("crash-");
+ PrintFinalStats();
+}
+
+void Fuzzer::StaticAlarmCallback() {
+ assert(F);
+ F->AlarmCallback();
+}
+
+void Fuzzer::StaticCrashSignalCallback() {
+ assert(F);
+ F->CrashCallback();
+}
+
+void Fuzzer::StaticInterruptCallback() {
+ assert(F);
+ F->InterruptCallback();
+}
+
+void Fuzzer::CrashCallback() {
+ Printf("==%lu== ERROR: libFuzzer: deadly signal\n", GetPid());
+ if (EF->__sanitizer_print_stack_trace)
+ EF->__sanitizer_print_stack_trace();
+ Printf("NOTE: libFuzzer has rudimentary signal handlers.\n"
+ " Combine libFuzzer with AddressSanitizer or similar for better "
+ "crash reports.\n");
+ Printf("SUMMARY: libFuzzer: deadly signal\n");
+ DumpCurrentUnit("crash-");
+ PrintFinalStats();
+ exit(Options.ErrorExitCode);
+}
+
+void Fuzzer::InterruptCallback() {
+ Printf("==%lu== libFuzzer: run interrupted; exiting\n", GetPid());
+ PrintFinalStats();
+ _Exit(0); // Stop right now, don't perform any at-exit actions.
+}
+
+NO_SANITIZE_MEMORY
+void Fuzzer::AlarmCallback() {
+ assert(Options.UnitTimeoutSec > 0);
+ if (!InFuzzingThread()) return;
+ if (!RunningCB)
+ return; // We have not started running units yet.
+ size_t Seconds =
+ duration_cast<seconds>(system_clock::now() - UnitStartTime).count();
+ if (Seconds == 0)
+ return;
+ if (Options.Verbosity >= 2)
+ Printf("AlarmCallback %zd\n", Seconds);
+ if (Seconds >= (size_t)Options.UnitTimeoutSec) {
+ Printf("ALARM: working on the last Unit for %zd seconds\n", Seconds);
+ Printf(" and the timeout value is %d (use -timeout=N to change)\n",
+ Options.UnitTimeoutSec);
+ DumpCurrentUnit("timeout-");
+ Printf("==%lu== ERROR: libFuzzer: timeout after %d seconds\n", GetPid(),
+ Seconds);
+ if (EF->__sanitizer_print_stack_trace)
+ EF->__sanitizer_print_stack_trace();
+ Printf("SUMMARY: libFuzzer: timeout\n");
+ PrintFinalStats();
+ _Exit(Options.TimeoutExitCode); // Stop right now.
+ }
+}
+
+void Fuzzer::RssLimitCallback() {
+ Printf(
+ "==%lu== ERROR: libFuzzer: out-of-memory (used: %zdMb; limit: %zdMb)\n",
+ GetPid(), GetPeakRSSMb(), Options.RssLimitMb);
+ Printf(" To change the out-of-memory limit use -rss_limit_mb=<N>\n\n");
+ if (EF->__sanitizer_print_memory_profile)
+ EF->__sanitizer_print_memory_profile(95);
+ DumpCurrentUnit("oom-");
+ Printf("SUMMARY: libFuzzer: out-of-memory\n");
+ PrintFinalStats();
+ _Exit(Options.ErrorExitCode); // Stop right now.
+}
+
+void Fuzzer::PrintStats(const char *Where, const char *End, size_t Units) {
+ size_t ExecPerSec = execPerSec();
+ if (Options.OutputCSV) {
+ static bool csvHeaderPrinted = false;
+ if (!csvHeaderPrinted) {
+ csvHeaderPrinted = true;
+ Printf("runs,block_cov,bits,cc_cov,corpus,execs_per_sec,tbms,reason\n");
+ }
+ Printf("%zd,%zd,%zd,%zd,%zd,%zd,%s\n", TotalNumberOfRuns,
+ MaxCoverage.BlockCoverage, MaxCoverage.CounterBitmapBits,
+ MaxCoverage.CallerCalleeCoverage, Corpus.size(), ExecPerSec, Where);
+ }
+
+ if (!Options.Verbosity)
+ return;
+ Printf("#%zd\t%s", TotalNumberOfRuns, Where);
+ if (MaxCoverage.BlockCoverage)
+ Printf(" cov: %zd", MaxCoverage.BlockCoverage);
+ if (size_t N = MaxCoverage.VPMap.GetNumBitsSinceLastMerge())
+ Printf(" vp: %zd", N);
+ if (size_t N = TPC.GetTotalPCCoverage())
+ Printf(" cov: %zd", N);
+ if (auto TB = MaxCoverage.CounterBitmapBits)
+ Printf(" bits: %zd", TB);
+ if (size_t N = Corpus.NumFeatures())
+ Printf( " ft: %zd", N);
+ if (MaxCoverage.CallerCalleeCoverage)
+ Printf(" indir: %zd", MaxCoverage.CallerCalleeCoverage);
+ if (!Corpus.empty()) {
+ Printf(" corp: %zd", Corpus.NumActiveUnits());
+ if (size_t N = Corpus.SizeInBytes()) {
+ if (N < (1<<14))
+ Printf("/%zdb", N);
+ else if (N < (1 << 24))
+ Printf("/%zdKb", N >> 10);
+ else
+ Printf("/%zdMb", N >> 20);
+ }
+ }
+ if (Units)
+ Printf(" units: %zd", Units);
+
+ Printf(" exec/s: %zd", ExecPerSec);
+ Printf(" rss: %zdMb", GetPeakRSSMb());
+ Printf("%s", End);
+}
+
+void Fuzzer::PrintFinalStats() {
+ if (Options.PrintCoverage)
+ TPC.PrintCoverage();
+ if (Options.DumpCoverage)
+ TPC.DumpCoverage();
+ if (Options.PrintCorpusStats)
+ Corpus.PrintStats();
+ if (!Options.PrintFinalStats) return;
+ size_t ExecPerSec = execPerSec();
+ Printf("stat::number_of_executed_units: %zd\n", TotalNumberOfRuns);
+ Printf("stat::average_exec_per_sec: %zd\n", ExecPerSec);
+ Printf("stat::new_units_added: %zd\n", NumberOfNewUnitsAdded);
+ Printf("stat::slowest_unit_time_sec: %zd\n", TimeOfLongestUnitInSeconds);
+ Printf("stat::peak_rss_mb: %zd\n", GetPeakRSSMb());
+}
+
+void Fuzzer::SetMaxInputLen(size_t MaxInputLen) {
+ assert(this->MaxInputLen == 0); // Can only reset MaxInputLen from 0 to non-0.
+ assert(MaxInputLen);
+ this->MaxInputLen = MaxInputLen;
+ this->MaxMutationLen = MaxInputLen;
+ AllocateCurrentUnitData();
+ Printf("INFO: -max_len is not provided, using %zd\n", MaxInputLen);
+}
+
+void Fuzzer::SetMaxMutationLen(size_t MaxMutationLen) {
+ assert(MaxMutationLen && MaxMutationLen <= MaxInputLen);
+ this->MaxMutationLen = MaxMutationLen;
+}
+
+void Fuzzer::CheckExitOnSrcPosOrItem() {
+ if (!Options.ExitOnSrcPos.empty()) {
+ static auto *PCsSet = new std::set<uintptr_t>;
+ for (size_t i = 1, N = TPC.GetNumPCs(); i < N; i++) {
+ uintptr_t PC = TPC.GetPC(i);
+ if (!PC) continue;
+ if (!PCsSet->insert(PC).second) continue;
+ std::string Descr = DescribePC("%L", PC);
+ if (Descr.find(Options.ExitOnSrcPos) != std::string::npos) {
+ Printf("INFO: found line matching '%s', exiting.\n",
+ Options.ExitOnSrcPos.c_str());
+ _Exit(0);
+ }
+ }
+ }
+ if (!Options.ExitOnItem.empty()) {
+ if (Corpus.HasUnit(Options.ExitOnItem)) {
+ Printf("INFO: found item with checksum '%s', exiting.\n",
+ Options.ExitOnItem.c_str());
+ _Exit(0);
+ }
+ }
+}
+
+void Fuzzer::RereadOutputCorpus(size_t MaxSize) {
+ if (Options.OutputCorpus.empty() || !Options.ReloadIntervalSec) return;
+ std::vector<Unit> AdditionalCorpus;
+ ReadDirToVectorOfUnits(Options.OutputCorpus.c_str(), &AdditionalCorpus,
+ &EpochOfLastReadOfOutputCorpus, MaxSize,
+ /*ExitOnError*/ false);
+ if (Options.Verbosity >= 2)
+ Printf("Reload: read %zd new units.\n", AdditionalCorpus.size());
+ bool Reloaded = false;
+ for (auto &U : AdditionalCorpus) {
+ if (U.size() > MaxSize)
+ U.resize(MaxSize);
+ if (!Corpus.HasUnit(U)) {
+ if (size_t NumFeatures = RunOne(U)) {
+ CheckExitOnSrcPosOrItem();
+ Corpus.AddToCorpus(U, NumFeatures);
+ Reloaded = true;
+ }
+ }
+ }
+ if (Reloaded)
+ PrintStats("RELOAD");
+}
+
+void Fuzzer::ShuffleCorpus(UnitVector *V) {
+ std::random_shuffle(V->begin(), V->end(), MD.GetRand());
+ if (Options.PreferSmall)
+ std::stable_sort(V->begin(), V->end(), [](const Unit &A, const Unit &B) {
+ return A.size() < B.size();
+ });
+}
+
+void Fuzzer::ShuffleAndMinimize(UnitVector *InitialCorpus) {
+ Printf("#0\tREAD units: %zd\n", InitialCorpus->size());
+ if (Options.ShuffleAtStartUp)
+ ShuffleCorpus(InitialCorpus);
+
+ // Test the callback with empty input and never try it again.
+ uint8_t dummy;
+ ExecuteCallback(&dummy, 0);
+
+ for (const auto &U : *InitialCorpus) {
+ if (size_t NumFeatures = RunOne(U)) {
+ CheckExitOnSrcPosOrItem();
+ Corpus.AddToCorpus(U, NumFeatures);
+ if (Options.Verbosity >= 2)
+ Printf("NEW0: %zd L %zd\n", MaxCoverage.BlockCoverage, U.size());
+ }
+ TryDetectingAMemoryLeak(U.data(), U.size(),
+ /*DuringInitialCorpusExecution*/ true);
+ }
+ PrintStats("INITED");
+ if (Corpus.empty()) {
+ Printf("ERROR: no interesting inputs were found. "
+ "Is the code instrumented for coverage? Exiting.\n");
+ exit(1);
+ }
+}
+
+size_t Fuzzer::RunOne(const uint8_t *Data, size_t Size) {
+ if (!Size) return 0;
+ TotalNumberOfRuns++;
+
+ ExecuteCallback(Data, Size);
+
+ size_t Res = 0;
+ if (size_t NumFeatures = TPC.CollectFeatures([&](size_t Feature) -> bool {
+ return Corpus.AddFeature(Feature, Size, Options.Shrink);
+ }))
+ Res = NumFeatures;
+
+ if (!TPC.UsingTracePcGuard()) {
+ if (TPC.UpdateValueProfileMap(&MaxCoverage.VPMap))
+ Res = 1;
+ if (!Res && RecordMaxCoverage(&MaxCoverage))
+ Res = 1;
+ }
+
+ auto TimeOfUnit =
+ duration_cast<seconds>(UnitStopTime - UnitStartTime).count();
+ if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1)) &&
+ secondsSinceProcessStartUp() >= 2)
+ PrintStats("pulse ");
+ if (TimeOfUnit > TimeOfLongestUnitInSeconds * 1.1 &&
+ TimeOfUnit >= Options.ReportSlowUnits) {
+ TimeOfLongestUnitInSeconds = TimeOfUnit;
+ Printf("Slowest unit: %zd s:\n", TimeOfLongestUnitInSeconds);
+ WriteUnitToFileWithPrefix({Data, Data + Size}, "slow-unit-");
+ }
+ return Res;
+}
+
+size_t Fuzzer::GetCurrentUnitInFuzzingThead(const uint8_t **Data) const {
+ assert(InFuzzingThread());
+ *Data = CurrentUnitData;
+ return CurrentUnitSize;
+}
+
+void Fuzzer::ExecuteCallback(const uint8_t *Data, size_t Size) {
+ assert(InFuzzingThread());
+ // We copy the contents of Unit into a separate heap buffer
+ // so that we reliably find buffer overflows in it.
+ uint8_t *DataCopy = new uint8_t[Size];
+ memcpy(DataCopy, Data, Size);
+ if (CurrentUnitData && CurrentUnitData != Data)
+ memcpy(CurrentUnitData, Data, Size);
+ CurrentUnitSize = Size;
+ AllocTracer.Start(Options.TraceMalloc);
+ UnitStartTime = system_clock::now();
+ ResetCounters(); // Reset coverage right before the callback.
+ TPC.ResetMaps();
+ RunningCB = true;
+ int Res = CB(DataCopy, Size);
+ RunningCB = false;
+ UnitStopTime = system_clock::now();
+ (void)Res;
+ assert(Res == 0);
+ HasMoreMallocsThanFrees = AllocTracer.Stop();
+ CurrentUnitSize = 0;
+ delete[] DataCopy;
+}
+
+void Fuzzer::WriteToOutputCorpus(const Unit &U) {
+ if (Options.OnlyASCII)
+ assert(IsASCII(U));
+ if (Options.OutputCorpus.empty())
+ return;
+ std::string Path = DirPlusFile(Options.OutputCorpus, Hash(U));
+ WriteToFile(U, Path);
+ if (Options.Verbosity >= 2)
+ Printf("Written to %s\n", Path.c_str());
+}
+
+void Fuzzer::WriteUnitToFileWithPrefix(const Unit &U, const char *Prefix) {
+ if (!Options.SaveArtifacts)
+ return;
+ std::string Path = Options.ArtifactPrefix + Prefix + Hash(U);
+ if (!Options.ExactArtifactPath.empty())
+ Path = Options.ExactArtifactPath; // Overrides ArtifactPrefix.
+ WriteToFile(U, Path);
+ Printf("artifact_prefix='%s'; Test unit written to %s\n",
+ Options.ArtifactPrefix.c_str(), Path.c_str());
+ if (U.size() <= kMaxUnitSizeToPrint)
+ Printf("Base64: %s\n", Base64(U).c_str());
+}
+
+void Fuzzer::PrintStatusForNewUnit(const Unit &U) {
+ if (!Options.PrintNEW)
+ return;
+ PrintStats("NEW ", "");
+ if (Options.Verbosity) {
+ Printf(" L: %zd ", U.size());
+ MD.PrintMutationSequence();
+ Printf("\n");
+ }
+}
+
+void Fuzzer::ReportNewCoverage(InputInfo *II, const Unit &U) {
+ II->NumSuccessfullMutations++;
+ MD.RecordSuccessfulMutationSequence();
+ PrintStatusForNewUnit(U);
+ WriteToOutputCorpus(U);
+ NumberOfNewUnitsAdded++;
+ TPC.PrintNewPCs();
+}
+
+// Finds minimal number of units in 'Extra' that add coverage to 'Initial'.
+// We do it by actually executing the units, sometimes more than once,
+// because we may be using different coverage-like signals and the only
+// common thing between them is that we can say "this unit found new stuff".
+UnitVector Fuzzer::FindExtraUnits(const UnitVector &Initial,
+ const UnitVector &Extra) {
+ UnitVector Res = Extra;
+ UnitVector Tmp;
+ size_t OldSize = Res.size();
+ for (int Iter = 0; Iter < 10; Iter++) {
+ ShuffleCorpus(&Res);
+ TPC.ResetMaps();
+ Corpus.ResetFeatureSet();
+ ResetCoverage();
+
+ for (auto &U : Initial) {
+ TPC.ResetMaps();
+ RunOne(U);
+ }
+
+ Tmp.clear();
+ for (auto &U : Res) {
+ TPC.ResetMaps();
+ if (RunOne(U))
+ Tmp.push_back(U);
+ }
+
+ char Stat[7] = "MIN ";
+ Stat[3] = '0' + Iter;
+ PrintStats(Stat, "\n", Tmp.size());
+
+ size_t NewSize = Tmp.size();
+ assert(NewSize <= OldSize);
+ Res.swap(Tmp);
+
+ if (NewSize + 5 >= OldSize)
+ break;
+ OldSize = NewSize;
+ }
+ return Res;
+}
+
+void Fuzzer::Merge(const std::vector<std::string> &Corpora) {
+ if (Corpora.size() <= 1) {
+ Printf("Merge requires two or more corpus dirs\n");
+ return;
+ }
+ InMergeMode = true;
+ std::vector<std::string> ExtraCorpora(Corpora.begin() + 1, Corpora.end());
+
+ assert(MaxInputLen > 0);
+ UnitVector Initial, Extra;
+ ReadDirToVectorOfUnits(Corpora[0].c_str(), &Initial, nullptr, MaxInputLen,
+ true);
+ for (auto &C : ExtraCorpora)
+ ReadDirToVectorOfUnits(C.c_str(), &Extra, nullptr, MaxInputLen, true);
+
+ if (!Initial.empty()) {
+ Printf("=== Minimizing the initial corpus of %zd units\n", Initial.size());
+ Initial = FindExtraUnits({}, Initial);
+ }
+
+ Printf("=== Merging extra %zd units\n", Extra.size());
+ auto Res = FindExtraUnits(Initial, Extra);
+
+ for (auto &U: Res)
+ WriteToOutputCorpus(U);
+
+ Printf("=== Merge: written %zd units\n", Res.size());
+}
+
+// Tries detecting a memory leak on the particular input that we have just
+// executed before calling this function.
+void Fuzzer::TryDetectingAMemoryLeak(const uint8_t *Data, size_t Size,
+ bool DuringInitialCorpusExecution) {
+ if (!HasMoreMallocsThanFrees) return; // mallocs==frees, a leak is unlikely.
+ if (!Options.DetectLeaks) return;
+ if (!&(EF->__lsan_enable) || !&(EF->__lsan_disable) ||
+ !(EF->__lsan_do_recoverable_leak_check))
+ return; // No lsan.
+ // Run the target once again, but with lsan disabled so that if there is
+ // a real leak we do not report it twice.
+ EF->__lsan_disable();
+ ExecuteCallback(Data, Size);
+ EF->__lsan_enable();
+ if (!HasMoreMallocsThanFrees) return; // a leak is unlikely.
+ if (NumberOfLeakDetectionAttempts++ > 1000) {
+ Options.DetectLeaks = false;
+ Printf("INFO: libFuzzer disabled leak detection after every mutation.\n"
+ " Most likely the target function accumulates allocated\n"
+ " memory in a global state w/o actually leaking it.\n"
+ " You may try running this binary with -trace_malloc=[12]"
+ " to get a trace of mallocs and frees.\n"
+ " If LeakSanitizer is enabled in this process it will still\n"
+ " run on the process shutdown.\n");
+ return;
+ }
+ // Now perform the actual lsan pass. This is expensive and we must ensure
+ // we don't call it too often.
+ if (EF->__lsan_do_recoverable_leak_check()) { // Leak is found, report it.
+ if (DuringInitialCorpusExecution)
+ Printf("\nINFO: a leak has been found in the initial corpus.\n\n");
+ Printf("INFO: to ignore leaks on libFuzzer side use -detect_leaks=0.\n\n");
+ CurrentUnitSize = Size;
+ DumpCurrentUnit("leak-");
+ PrintFinalStats();
+ _Exit(Options.ErrorExitCode); // not exit() to disable lsan further on.
+ }
+}
+
+void Fuzzer::MutateAndTestOne() {
+ MD.StartMutationSequence();
+
+ auto &II = Corpus.ChooseUnitToMutate(MD.GetRand());
+ const auto &U = II.U;
+ memcpy(BaseSha1, II.Sha1, sizeof(BaseSha1));
+ assert(CurrentUnitData);
+ size_t Size = U.size();
+ assert(Size <= MaxInputLen && "Oversized Unit");
+ memcpy(CurrentUnitData, U.data(), Size);
+
+ assert(MaxMutationLen > 0);
+
+ for (int i = 0; i < Options.MutateDepth; i++) {
+ if (TotalNumberOfRuns >= Options.MaxNumberOfRuns)
+ break;
+ size_t NewSize = 0;
+ NewSize = MD.Mutate(CurrentUnitData, Size, MaxMutationLen);
+ assert(NewSize > 0 && "Mutator returned empty unit");
+ assert(NewSize <= MaxMutationLen && "Mutator return overisized unit");
+ Size = NewSize;
+ if (i == 0)
+ StartTraceRecording();
+ II.NumExecutedMutations++;
+ if (size_t NumFeatures = RunOne(CurrentUnitData, Size)) {
+ Corpus.AddToCorpus({CurrentUnitData, CurrentUnitData + Size}, NumFeatures,
+ /*MayDeleteFile=*/true);
+ ReportNewCoverage(&II, {CurrentUnitData, CurrentUnitData + Size});
+ CheckExitOnSrcPosOrItem();
+ }
+ StopTraceRecording();
+ TryDetectingAMemoryLeak(CurrentUnitData, Size,
+ /*DuringInitialCorpusExecution*/ false);
+ }
+}
+
+void Fuzzer::ResetCoverage() {
+ ResetEdgeCoverage();
+ MaxCoverage.Reset();
+ PrepareCounters(&MaxCoverage);
+}
+
+void Fuzzer::Loop() {
+ system_clock::time_point LastCorpusReload = system_clock::now();
+ if (Options.DoCrossOver)
+ MD.SetCorpus(&Corpus);
+ while (true) {
+ auto Now = system_clock::now();
+ if (duration_cast<seconds>(Now - LastCorpusReload).count() >=
+ Options.ReloadIntervalSec) {
+ RereadOutputCorpus(MaxInputLen);
+ LastCorpusReload = system_clock::now();
+ }
+ if (TotalNumberOfRuns >= Options.MaxNumberOfRuns)
+ break;
+ if (TimedOut()) break;
+ // Perform several mutations and runs.
+ MutateAndTestOne();
+ }
+
+ PrintStats("DONE ", "\n");
+ MD.PrintRecommendedDictionary();
+}
+
+void Fuzzer::MinimizeCrashLoop(const Unit &U) {
+ if (U.size() <= 2) return;
+ while (!TimedOut() && TotalNumberOfRuns < Options.MaxNumberOfRuns) {
+ MD.StartMutationSequence();
+ memcpy(CurrentUnitData, U.data(), U.size());
+ for (int i = 0; i < Options.MutateDepth; i++) {
+ size_t NewSize = MD.Mutate(CurrentUnitData, U.size(), MaxMutationLen);
+ assert(NewSize > 0 && NewSize <= MaxMutationLen);
+ RunOne(CurrentUnitData, NewSize);
+ TryDetectingAMemoryLeak(CurrentUnitData, NewSize,
+ /*DuringInitialCorpusExecution*/ false);
+ }
+ }
+}
+
+} // namespace fuzzer
+
+extern "C" {
+
+size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) {
+ assert(fuzzer::F);
+ return fuzzer::F->GetMD().DefaultMutate(Data, Size, MaxSize);
+}
+} // extern "C"
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerMain.cpp b/json4cpp/tests/thirdparty/Fuzzer/FuzzerMain.cpp
new file mode 100644
index 0000000000..af8657200b
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerMain.cpp
@@ -0,0 +1,21 @@
+//===- FuzzerMain.cpp - main() function and flags -------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// main() and flags.
+//===----------------------------------------------------------------------===//
+
+#include "FuzzerDefs.h"
+
+extern "C" {
+// This function should be defined by the user.
+int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
+} // extern "C"
+
+int main(int argc, char **argv) {
+ return fuzzer::FuzzerDriver(&argc, &argv, LLVMFuzzerTestOneInput);
+}
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerMerge.cpp b/json4cpp/tests/thirdparty/Fuzzer/FuzzerMerge.cpp
new file mode 100644
index 0000000000..84660e0fe5
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerMerge.cpp
@@ -0,0 +1,261 @@
+//===- FuzzerMerge.cpp - merging corpora ----------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// Merging corpora.
+//===----------------------------------------------------------------------===//
+
+#include "FuzzerInternal.h"
+#include "FuzzerIO.h"
+#include "FuzzerMerge.h"
+#include "FuzzerTracePC.h"
+#include "FuzzerUtil.h"
+
+#include <fstream>
+#include <iterator>
+#include <sstream>
+
+namespace fuzzer {
+
+bool Merger::Parse(const std::string &Str, bool ParseCoverage) {
+ std::istringstream SS(Str);
+ return Parse(SS, ParseCoverage);
+}
+
+void Merger::ParseOrExit(std::istream &IS, bool ParseCoverage) {
+ if (!Parse(IS, ParseCoverage)) {
+ Printf("MERGE: failed to parse the control file (unexpected error)\n");
+ exit(1);
+ }
+}
+
+// The control file example:
+//
+// 3 # The number of inputs
+// 1 # The number of inputs in the first corpus, <= the previous number
+// file0
+// file1
+// file2 # One file name per line.
+// STARTED 0 123 # FileID, file size
+// DONE 0 1 4 6 8 # FileID COV1 COV2 ...
+// STARTED 1 456 # If DONE is missing, the input crashed while processing.
+// STARTED 2 567
+// DONE 2 8 9
+bool Merger::Parse(std::istream &IS, bool ParseCoverage) {
+ LastFailure.clear();
+ std::string Line;
+
+ // Parse NumFiles.
+ if (!std::getline(IS, Line, '\n')) return false;
+ std::istringstream L1(Line);
+ size_t NumFiles = 0;
+ L1 >> NumFiles;
+ if (NumFiles == 0 || NumFiles > 10000000) return false;
+
+ // Parse NumFilesInFirstCorpus.
+ if (!std::getline(IS, Line, '\n')) return false;
+ std::istringstream L2(Line);
+ NumFilesInFirstCorpus = NumFiles + 1;
+ L2 >> NumFilesInFirstCorpus;
+ if (NumFilesInFirstCorpus > NumFiles) return false;
+
+ // Parse file names.
+ Files.resize(NumFiles);
+ for (size_t i = 0; i < NumFiles; i++)
+ if (!std::getline(IS, Files[i].Name, '\n'))
+ return false;
+
+ // Parse STARTED and DONE lines.
+ size_t ExpectedStartMarker = 0;
+ const size_t kInvalidStartMarker = -1;
+ size_t LastSeenStartMarker = kInvalidStartMarker;
+ while (std::getline(IS, Line, '\n')) {
+ std::istringstream ISS1(Line);
+ std::string Marker;
+ size_t N;
+ ISS1 >> Marker;
+ ISS1 >> N;
+ if (Marker == "STARTED") {
+ // STARTED FILE_ID FILE_SIZE
+ if (ExpectedStartMarker != N)
+ return false;
+ ISS1 >> Files[ExpectedStartMarker].Size;
+ LastSeenStartMarker = ExpectedStartMarker;
+ assert(ExpectedStartMarker < Files.size());
+ ExpectedStartMarker++;
+ } else if (Marker == "DONE") {
+ // DONE FILE_SIZE COV1 COV2 COV3 ...
+ size_t CurrentFileIdx = N;
+ if (CurrentFileIdx != LastSeenStartMarker)
+ return false;
+ LastSeenStartMarker = kInvalidStartMarker;
+ if (ParseCoverage) {
+ auto &V = Files[CurrentFileIdx].Features;
+ V.clear();
+ while (ISS1 >> std::hex >> N)
+ V.push_back(N);
+ std::sort(V.begin(), V.end());
+ }
+ } else {
+ return false;
+ }
+ }
+ if (LastSeenStartMarker != kInvalidStartMarker)
+ LastFailure = Files[LastSeenStartMarker].Name;
+
+ FirstNotProcessedFile = ExpectedStartMarker;
+ return true;
+}
+
+// Decides which files need to be merged (add thost to NewFiles).
+// Returns the number of new features added.
+size_t Merger::Merge(std::vector<std::string> *NewFiles) {
+ NewFiles->clear();
+ assert(NumFilesInFirstCorpus <= Files.size());
+ std::set<uint32_t> AllFeatures;
+
+ // What features are in the initial corpus?
+ for (size_t i = 0; i < NumFilesInFirstCorpus; i++) {
+ auto &Cur = Files[i].Features;
+ AllFeatures.insert(Cur.begin(), Cur.end());
+ }
+ size_t InitialNumFeatures = AllFeatures.size();
+
+ // Remove all features that we already know from all other inputs.
+ for (size_t i = NumFilesInFirstCorpus; i < Files.size(); i++) {
+ auto &Cur = Files[i].Features;
+ std::vector<uint32_t> Tmp;
+ std::set_difference(Cur.begin(), Cur.end(), AllFeatures.begin(),
+ AllFeatures.end(), std::inserter(Tmp, Tmp.begin()));
+ Cur.swap(Tmp);
+ }
+
+ // Sort. Give preference to
+ // * smaller files
+ // * files with more features.
+ std::sort(Files.begin() + NumFilesInFirstCorpus, Files.end(),
+ [&](const MergeFileInfo &a, const MergeFileInfo &b) -> bool {
+ if (a.Size != b.Size)
+ return a.Size < b.Size;
+ return a.Features.size() > b.Features.size();
+ });
+
+ // One greedy pass: add the file's features to AllFeatures.
+ // If new features were added, add this file to NewFiles.
+ for (size_t i = NumFilesInFirstCorpus; i < Files.size(); i++) {
+ auto &Cur = Files[i].Features;
+ // Printf("%s -> sz %zd ft %zd\n", Files[i].Name.c_str(),
+ // Files[i].Size, Cur.size());
+ size_t OldSize = AllFeatures.size();
+ AllFeatures.insert(Cur.begin(), Cur.end());
+ if (AllFeatures.size() > OldSize)
+ NewFiles->push_back(Files[i].Name);
+ }
+ return AllFeatures.size() - InitialNumFeatures;
+}
+
+// Inner process. May crash if the target crashes.
+void Fuzzer::CrashResistantMergeInternalStep(const std::string &CFPath) {
+ Printf("MERGE-INNER: using the control file '%s'\n", CFPath.c_str());
+ Merger M;
+ std::ifstream IF(CFPath);
+ M.ParseOrExit(IF, false);
+ IF.close();
+ if (!M.LastFailure.empty())
+ Printf("MERGE-INNER: '%s' caused a failure at the previous merge step\n",
+ M.LastFailure.c_str());
+
+ Printf("MERGE-INNER: %zd total files;"
+ " %zd processed earlier; will process %zd files now\n",
+ M.Files.size(), M.FirstNotProcessedFile,
+ M.Files.size() - M.FirstNotProcessedFile);
+
+ std::ofstream OF(CFPath, std::ofstream::out | std::ofstream::app);
+ for (size_t i = M.FirstNotProcessedFile; i < M.Files.size(); i++) {
+ auto U = FileToVector(M.Files[i].Name);
+ if (U.size() > MaxInputLen) {
+ U.resize(MaxInputLen);
+ U.shrink_to_fit();
+ }
+ std::ostringstream StartedLine;
+ // Write the pre-run marker.
+ OF << "STARTED " << std::dec << i << " " << U.size() << "\n";
+ OF.flush(); // Flush is important since ExecuteCommand may crash.
+ // Run.
+ TPC.ResetMaps();
+ ExecuteCallback(U.data(), U.size());
+ // Collect coverage.
+ std::set<size_t> Features;
+ TPC.CollectFeatures([&](size_t Feature) -> bool {
+ Features.insert(Feature);
+ return true;
+ });
+ // Show stats.
+ TotalNumberOfRuns++;
+ if (!(TotalNumberOfRuns & (TotalNumberOfRuns - 1)))
+ PrintStats("pulse ");
+ // Write the post-run marker and the coverage.
+ OF << "DONE " << i;
+ for (size_t F : Features)
+ OF << " " << std::hex << F;
+ OF << "\n";
+ }
+}
+
+// Outer process. Does not call the target code and thus sohuld not fail.
+void Fuzzer::CrashResistantMerge(const std::vector<std::string> &Args,
+ const std::vector<std::string> &Corpora) {
+ if (Corpora.size() <= 1) {
+ Printf("Merge requires two or more corpus dirs\n");
+ return;
+ }
+ std::vector<std::string> AllFiles;
+ ListFilesInDirRecursive(Corpora[0], nullptr, &AllFiles, /*TopDir*/true);
+ size_t NumFilesInFirstCorpus = AllFiles.size();
+ for (size_t i = 1; i < Corpora.size(); i++)
+ ListFilesInDirRecursive(Corpora[i], nullptr, &AllFiles, /*TopDir*/true);
+ Printf("MERGE-OUTER: %zd files, %zd in the initial corpus\n",
+ AllFiles.size(), NumFilesInFirstCorpus);
+ std::string CFPath =
+ "libFuzzerTemp." + std::to_string(GetPid()) + ".txt";
+ // Write the control file.
+ RemoveFile(CFPath);
+ std::ofstream ControlFile(CFPath);
+ ControlFile << AllFiles.size() << "\n";
+ ControlFile << NumFilesInFirstCorpus << "\n";
+ for (auto &Path: AllFiles)
+ ControlFile << Path << "\n";
+ ControlFile.close();
+
+ // Execute the inner process untill it passes.
+ // Every inner process should execute at least one input.
+ std::string BaseCmd = CloneArgsWithoutX(Args, "keep-all-flags");
+ for (size_t i = 1; i <= AllFiles.size(); i++) {
+ Printf("MERGE-OUTER: attempt %zd\n", i);
+ auto ExitCode =
+ ExecuteCommand(BaseCmd + " -merge_control_file=" + CFPath);
+ if (!ExitCode) {
+ Printf("MERGE-OUTER: succesfull in %zd attempt(s)\n", i);
+ break;
+ }
+ }
+ // Read the control file and do the merge.
+ Merger M;
+ std::ifstream IF(CFPath);
+ M.ParseOrExit(IF, true);
+ IF.close();
+ std::vector<std::string> NewFiles;
+ size_t NumNewFeatures = M.Merge(&NewFiles);
+ Printf("MERGE-OUTER: %zd new files with %zd new features added\n",
+ NewFiles.size(), NumNewFeatures);
+ for (auto &F: NewFiles)
+ WriteToOutputCorpus(FileToVector(F));
+ // We are done, delete the control file.
+ RemoveFile(CFPath);
+}
+
+} // namespace fuzzer
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerMerge.h b/json4cpp/tests/thirdparty/Fuzzer/FuzzerMerge.h
new file mode 100644
index 0000000000..8a2fe5d74f
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerMerge.h
@@ -0,0 +1,70 @@
+//===- FuzzerMerge.h - merging corpa ----------------------------*- C++ -* ===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// Merging Corpora.
+//
+// The task:
+// Take the existing corpus (possibly empty) and merge new inputs into
+// it so that only inputs with new coverage ('features') are added.
+// The process should tolerate the crashes, OOMs, leaks, etc.
+//
+// Algorithm:
+// The outter process collects the set of files and writes their names
+// into a temporary "control" file, then repeatedly launches the inner
+// process until all inputs are processed.
+// The outer process does not actually execute the target code.
+//
+// The inner process reads the control file and sees a) list of all the inputs
+// and b) the last processed input. Then it starts processing the inputs one
+// by one. Before processing every input it writes one line to control file:
+// STARTED INPUT_ID INPUT_SIZE
+// After processing an input it write another line:
+// DONE INPUT_ID Feature1 Feature2 Feature3 ...
+// If a crash happens while processing an input the last line in the control
+// file will be "STARTED INPUT_ID" and so the next process will know
+// where to resume.
+//
+// Once all inputs are processed by the innner process(es) the outer process
+// reads the control files and does the merge based entirely on the contents
+// of control file.
+// It uses a single pass greedy algorithm choosing first the smallest inputs
+// within the same size the inputs that have more new features.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_FUZZER_MERGE_H
+#define LLVM_FUZZER_MERGE_H
+
+#include "FuzzerDefs.h"
+
+#include <istream>
+#include <set>
+
+namespace fuzzer {
+
+struct MergeFileInfo {
+ std::string Name;
+ size_t Size = 0;
+ std::vector<uint32_t> Features;
+};
+
+struct Merger {
+ std::vector<MergeFileInfo> Files;
+ size_t NumFilesInFirstCorpus = 0;
+ size_t FirstNotProcessedFile = 0;
+ std::string LastFailure;
+
+ bool Parse(std::istream &IS, bool ParseCoverage);
+ bool Parse(const std::string &Str, bool ParseCoverage);
+ void ParseOrExit(std::istream &IS, bool ParseCoverage);
+ size_t Merge(std::vector<std::string> *NewFiles);
+};
+
+} // namespace fuzzer
+
+#endif // LLVM_FUZZER_MERGE_H
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerMutate.cpp b/json4cpp/tests/thirdparty/Fuzzer/FuzzerMutate.cpp
new file mode 100644
index 0000000000..80b7a5f961
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerMutate.cpp
@@ -0,0 +1,527 @@
+//===- FuzzerMutate.cpp - Mutate a test input -----------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// Mutate a test input.
+//===----------------------------------------------------------------------===//
+
+#include "FuzzerCorpus.h"
+#include "FuzzerDefs.h"
+#include "FuzzerExtFunctions.h"
+#include "FuzzerIO.h"
+#include "FuzzerMutate.h"
+#include "FuzzerOptions.h"
+
+namespace fuzzer {
+
+const size_t Dictionary::kMaxDictSize;
+
+static void PrintASCII(const Word &W, const char *PrintAfter) {
+ PrintASCII(W.data(), W.size(), PrintAfter);
+}
+
+MutationDispatcher::MutationDispatcher(Random &Rand,
+ const FuzzingOptions &Options)
+ : Rand(Rand), Options(Options) {
+ DefaultMutators.insert(
+ DefaultMutators.begin(),
+ {
+ {&MutationDispatcher::Mutate_EraseBytes, "EraseBytes"},
+ {&MutationDispatcher::Mutate_InsertByte, "InsertByte"},
+ {&MutationDispatcher::Mutate_InsertRepeatedBytes,
+ "InsertRepeatedBytes"},
+ {&MutationDispatcher::Mutate_ChangeByte, "ChangeByte"},
+ {&MutationDispatcher::Mutate_ChangeBit, "ChangeBit"},
+ {&MutationDispatcher::Mutate_ShuffleBytes, "ShuffleBytes"},
+ {&MutationDispatcher::Mutate_ChangeASCIIInteger, "ChangeASCIIInt"},
+ {&MutationDispatcher::Mutate_ChangeBinaryInteger, "ChangeBinInt"},
+ {&MutationDispatcher::Mutate_CopyPart, "CopyPart"},
+ {&MutationDispatcher::Mutate_CrossOver, "CrossOver"},
+ {&MutationDispatcher::Mutate_AddWordFromManualDictionary,
+ "ManualDict"},
+ {&MutationDispatcher::Mutate_AddWordFromTemporaryAutoDictionary,
+ "TempAutoDict"},
+ {&MutationDispatcher::Mutate_AddWordFromPersistentAutoDictionary,
+ "PersAutoDict"},
+ });
+ if(Options.UseCmp)
+ DefaultMutators.push_back(
+ {&MutationDispatcher::Mutate_AddWordFromTORC, "CMP"});
+
+ if (EF->LLVMFuzzerCustomMutator)
+ Mutators.push_back({&MutationDispatcher::Mutate_Custom, "Custom"});
+ else
+ Mutators = DefaultMutators;
+
+ if (EF->LLVMFuzzerCustomCrossOver)
+ Mutators.push_back(
+ {&MutationDispatcher::Mutate_CustomCrossOver, "CustomCrossOver"});
+}
+
+static char RandCh(Random &Rand) {
+ if (Rand.RandBool()) return Rand(256);
+ const char *Special = "!*'();:@&=+$,/?%#[]012Az-`~.\xff\x00";
+ return Special[Rand(sizeof(Special) - 1)];
+}
+
+size_t MutationDispatcher::Mutate_Custom(uint8_t *Data, size_t Size,
+ size_t MaxSize) {
+ return EF->LLVMFuzzerCustomMutator(Data, Size, MaxSize, Rand.Rand());
+}
+
+size_t MutationDispatcher::Mutate_CustomCrossOver(uint8_t *Data, size_t Size,
+ size_t MaxSize) {
+ if (!Corpus || Corpus->size() < 2 || Size == 0)
+ return 0;
+ size_t Idx = Rand(Corpus->size());
+ const Unit &Other = (*Corpus)[Idx];
+ if (Other.empty())
+ return 0;
+ MutateInPlaceHere.resize(MaxSize);
+ auto &U = MutateInPlaceHere;
+ size_t NewSize = EF->LLVMFuzzerCustomCrossOver(
+ Data, Size, Other.data(), Other.size(), U.data(), U.size(), Rand.Rand());
+ if (!NewSize)
+ return 0;
+ assert(NewSize <= MaxSize && "CustomCrossOver returned overisized unit");
+ memcpy(Data, U.data(), NewSize);
+ return NewSize;
+}
+
+size_t MutationDispatcher::Mutate_ShuffleBytes(uint8_t *Data, size_t Size,
+ size_t MaxSize) {
+ if (Size > MaxSize) return 0;
+ assert(Size);
+ size_t ShuffleAmount =
+ Rand(std::min(Size, (size_t)8)) + 1; // [1,8] and <= Size.
+ size_t ShuffleStart = Rand(Size - ShuffleAmount);
+ assert(ShuffleStart + ShuffleAmount <= Size);
+ std::random_shuffle(Data + ShuffleStart, Data + ShuffleStart + ShuffleAmount,
+ Rand);
+ return Size;
+}
+
+size_t MutationDispatcher::Mutate_EraseBytes(uint8_t *Data, size_t Size,
+ size_t MaxSize) {
+ assert(Size);
+ if (Size == 1) return 0;
+ size_t N = Rand(Size / 2) + 1;
+ assert(N < Size);
+ size_t Idx = Rand(Size - N + 1);
+ // Erase Data[Idx:Idx+N].
+ memmove(Data + Idx, Data + Idx + N, Size - Idx - N);
+ // Printf("Erase: %zd %zd => %zd; Idx %zd\n", N, Size, Size - N, Idx);
+ return Size - N;
+}
+
+size_t MutationDispatcher::Mutate_InsertByte(uint8_t *Data, size_t Size,
+ size_t MaxSize) {
+ if (Size >= MaxSize) return 0;
+ size_t Idx = Rand(Size + 1);
+ // Insert new value at Data[Idx].
+ memmove(Data + Idx + 1, Data + Idx, Size - Idx);
+ Data[Idx] = RandCh(Rand);
+ return Size + 1;
+}
+
+size_t MutationDispatcher::Mutate_InsertRepeatedBytes(uint8_t *Data,
+ size_t Size,
+ size_t MaxSize) {
+ const size_t kMinBytesToInsert = 3;
+ if (Size + kMinBytesToInsert >= MaxSize) return 0;
+ size_t MaxBytesToInsert = std::min(MaxSize - Size, (size_t)128);
+ size_t N = Rand(MaxBytesToInsert - kMinBytesToInsert + 1) + kMinBytesToInsert;
+ assert(Size + N <= MaxSize && N);
+ size_t Idx = Rand(Size + 1);
+ // Insert new values at Data[Idx].
+ memmove(Data + Idx + N, Data + Idx, Size - Idx);
+ // Give preference to 0x00 and 0xff.
+ uint8_t Byte = Rand.RandBool() ? Rand(256) : (Rand.RandBool() ? 0 : 255);
+ for (size_t i = 0; i < N; i++)
+ Data[Idx + i] = Byte;
+ return Size + N;
+}
+
+size_t MutationDispatcher::Mutate_ChangeByte(uint8_t *Data, size_t Size,
+ size_t MaxSize) {
+ if (Size > MaxSize) return 0;
+ size_t Idx = Rand(Size);
+ Data[Idx] = RandCh(Rand);
+ return Size;
+}
+
+size_t MutationDispatcher::Mutate_ChangeBit(uint8_t *Data, size_t Size,
+ size_t MaxSize) {
+ if (Size > MaxSize) return 0;
+ size_t Idx = Rand(Size);
+ Data[Idx] ^= 1 << Rand(8);
+ return Size;
+}
+
+size_t MutationDispatcher::Mutate_AddWordFromManualDictionary(uint8_t *Data,
+ size_t Size,
+ size_t MaxSize) {
+ return AddWordFromDictionary(ManualDictionary, Data, Size, MaxSize);
+}
+
+size_t MutationDispatcher::Mutate_AddWordFromTemporaryAutoDictionary(
+ uint8_t *Data, size_t Size, size_t MaxSize) {
+ return AddWordFromDictionary(TempAutoDictionary, Data, Size, MaxSize);
+}
+
+size_t MutationDispatcher::ApplyDictionaryEntry(uint8_t *Data, size_t Size,
+ size_t MaxSize,
+ DictionaryEntry &DE) {
+ const Word &W = DE.GetW();
+ bool UsePositionHint = DE.HasPositionHint() &&
+ DE.GetPositionHint() + W.size() < Size &&
+ Rand.RandBool();
+ if (Rand.RandBool()) { // Insert W.
+ if (Size + W.size() > MaxSize) return 0;
+ size_t Idx = UsePositionHint ? DE.GetPositionHint() : Rand(Size + 1);
+ memmove(Data + Idx + W.size(), Data + Idx, Size - Idx);
+ memcpy(Data + Idx, W.data(), W.size());
+ Size += W.size();
+ } else { // Overwrite some bytes with W.
+ if (W.size() > Size) return 0;
+ size_t Idx = UsePositionHint ? DE.GetPositionHint() : Rand(Size - W.size());
+ memcpy(Data + Idx, W.data(), W.size());
+ }
+ return Size;
+}
+
+// Somewhere in the past we have observed a comparison instructions
+// with arguments Arg1 Arg2. This function tries to guess a dictionary
+// entry that will satisfy that comparison.
+// It first tries to find one of the arguments (possibly swapped) in the
+// input and if it succeeds it creates a DE with a position hint.
+// Otherwise it creates a DE with one of the arguments w/o a position hint.
+template <class T>
+DictionaryEntry MutationDispatcher::MakeDictionaryEntryFromCMP(
+ T Arg1, T Arg2, const uint8_t *Data, size_t Size) {
+ ScopedDoingMyOwnMemmem scoped_doing_my_own_memmem;
+ bool HandleFirst = Rand.RandBool();
+ T ExistingBytes, DesiredBytes;
+ Word W;
+ const uint8_t *End = Data + Size;
+ for (int Arg = 0; Arg < 2; Arg++) {
+ ExistingBytes = HandleFirst ? Arg1 : Arg2;
+ DesiredBytes = HandleFirst ? Arg2 : Arg1;
+ DesiredBytes += Rand(-1, 1);
+ if (Rand.RandBool()) ExistingBytes = Bswap(ExistingBytes);
+ if (Rand.RandBool()) DesiredBytes = Bswap(DesiredBytes);
+ HandleFirst = !HandleFirst;
+ W.Set(reinterpret_cast<uint8_t*>(&DesiredBytes), sizeof(T));
+ const size_t kMaxNumPositions = 8;
+ size_t Positions[kMaxNumPositions];
+ size_t NumPositions = 0;
+ for (const uint8_t *Cur = Data;
+ Cur < End && NumPositions < kMaxNumPositions; Cur++) {
+ Cur = (uint8_t *)SearchMemory(Cur, End - Cur, &ExistingBytes, sizeof(T));
+ if (!Cur) break;
+ Positions[NumPositions++] = Cur - Data;
+ }
+ if (!NumPositions) break;
+ return DictionaryEntry(W, Positions[Rand(NumPositions)]);
+ }
+ DictionaryEntry DE(W);
+ return DE;
+}
+
+size_t MutationDispatcher::Mutate_AddWordFromTORC(
+ uint8_t *Data, size_t Size, size_t MaxSize) {
+ Word W;
+ DictionaryEntry DE;
+ if (Rand.RandBool()) {
+ auto X = TPC.TORC8.Get(Rand.Rand());
+ DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size);
+ } else {
+ auto X = TPC.TORC4.Get(Rand.Rand());
+ if ((X.A >> 16) == 0 && (X.B >> 16) == 0 && Rand.RandBool())
+ DE = MakeDictionaryEntryFromCMP((uint16_t)X.A, (uint16_t)X.B, Data,
+ Size);
+ else
+ DE = MakeDictionaryEntryFromCMP(X.A, X.B, Data, Size);
+ }
+ Size = ApplyDictionaryEntry(Data, Size, MaxSize, DE);
+ if (!Size) return 0;
+ DictionaryEntry &DERef =
+ CmpDictionaryEntriesDeque[CmpDictionaryEntriesDequeIdx++ %
+ kCmpDictionaryEntriesDequeSize];
+ DERef = DE;
+ CurrentDictionaryEntrySequence.push_back(&DERef);
+ return Size;
+}
+
+size_t MutationDispatcher::Mutate_AddWordFromPersistentAutoDictionary(
+ uint8_t *Data, size_t Size, size_t MaxSize) {
+ return AddWordFromDictionary(PersistentAutoDictionary, Data, Size, MaxSize);
+}
+
+size_t MutationDispatcher::AddWordFromDictionary(Dictionary &D, uint8_t *Data,
+ size_t Size, size_t MaxSize) {
+ if (Size > MaxSize) return 0;
+ if (D.empty()) return 0;
+ DictionaryEntry &DE = D[Rand(D.size())];
+ Size = ApplyDictionaryEntry(Data, Size, MaxSize, DE);
+ if (!Size) return 0;
+ DE.IncUseCount();
+ CurrentDictionaryEntrySequence.push_back(&DE);
+ return Size;
+}
+
+// Overwrites part of To[0,ToSize) with a part of From[0,FromSize).
+// Returns ToSize.
+size_t MutationDispatcher::CopyPartOf(const uint8_t *From, size_t FromSize,
+ uint8_t *To, size_t ToSize) {
+ // Copy From[FromBeg, FromBeg + CopySize) into To[ToBeg, ToBeg + CopySize).
+ size_t ToBeg = Rand(ToSize);
+ size_t CopySize = Rand(ToSize - ToBeg) + 1;
+ assert(ToBeg + CopySize <= ToSize);
+ CopySize = std::min(CopySize, FromSize);
+ size_t FromBeg = Rand(FromSize - CopySize + 1);
+ assert(FromBeg + CopySize <= FromSize);
+ memmove(To + ToBeg, From + FromBeg, CopySize);
+ return ToSize;
+}
+
+// Inserts part of From[0,ToSize) into To.
+// Returns new size of To on success or 0 on failure.
+size_t MutationDispatcher::InsertPartOf(const uint8_t *From, size_t FromSize,
+ uint8_t *To, size_t ToSize,
+ size_t MaxToSize) {
+ if (ToSize >= MaxToSize) return 0;
+ size_t AvailableSpace = MaxToSize - ToSize;
+ size_t MaxCopySize = std::min(AvailableSpace, FromSize);
+ size_t CopySize = Rand(MaxCopySize) + 1;
+ size_t FromBeg = Rand(FromSize - CopySize + 1);
+ assert(FromBeg + CopySize <= FromSize);
+ size_t ToInsertPos = Rand(ToSize + 1);
+ assert(ToInsertPos + CopySize <= MaxToSize);
+ size_t TailSize = ToSize - ToInsertPos;
+ if (To == From) {
+ MutateInPlaceHere.resize(MaxToSize);
+ memcpy(MutateInPlaceHere.data(), From + FromBeg, CopySize);
+ memmove(To + ToInsertPos + CopySize, To + ToInsertPos, TailSize);
+ memmove(To + ToInsertPos, MutateInPlaceHere.data(), CopySize);
+ } else {
+ memmove(To + ToInsertPos + CopySize, To + ToInsertPos, TailSize);
+ memmove(To + ToInsertPos, From + FromBeg, CopySize);
+ }
+ return ToSize + CopySize;
+}
+
+size_t MutationDispatcher::Mutate_CopyPart(uint8_t *Data, size_t Size,
+ size_t MaxSize) {
+ if (Size > MaxSize) return 0;
+ if (Rand.RandBool())
+ return CopyPartOf(Data, Size, Data, Size);
+ else
+ return InsertPartOf(Data, Size, Data, Size, MaxSize);
+}
+
+size_t MutationDispatcher::Mutate_ChangeASCIIInteger(uint8_t *Data, size_t Size,
+ size_t MaxSize) {
+ if (Size > MaxSize) return 0;
+ size_t B = Rand(Size);
+ while (B < Size && !isdigit(Data[B])) B++;
+ if (B == Size) return 0;
+ size_t E = B;
+ while (E < Size && isdigit(Data[E])) E++;
+ assert(B < E);
+ // now we have digits in [B, E).
+ // strtol and friends don't accept non-zero-teminated data, parse it manually.
+ uint64_t Val = Data[B] - '0';
+ for (size_t i = B + 1; i < E; i++)
+ Val = Val * 10 + Data[i] - '0';
+
+ // Mutate the integer value.
+ switch(Rand(5)) {
+ case 0: Val++; break;
+ case 1: Val--; break;
+ case 2: Val /= 2; break;
+ case 3: Val *= 2; break;
+ case 4: Val = Rand(Val * Val); break;
+ default: assert(0);
+ }
+ // Just replace the bytes with the new ones, don't bother moving bytes.
+ for (size_t i = B; i < E; i++) {
+ size_t Idx = E + B - i - 1;
+ assert(Idx >= B && Idx < E);
+ Data[Idx] = (Val % 10) + '0';
+ Val /= 10;
+ }
+ return Size;
+}
+
+template<class T>
+size_t ChangeBinaryInteger(uint8_t *Data, size_t Size, Random &Rand) {
+ if (Size < sizeof(T)) return 0;
+ size_t Off = Rand(Size - sizeof(T) + 1);
+ assert(Off + sizeof(T) <= Size);
+ T Val;
+ if (Off < 64 && !Rand(4)) {
+ Val = Size;
+ if (Rand.RandBool())
+ Val = Bswap(Val);
+ } else {
+ memcpy(&Val, Data + Off, sizeof(Val));
+ T Add = Rand(21);
+ Add -= 10;
+ if (Rand.RandBool())
+ Val = Bswap(T(Bswap(Val) + Add)); // Add assuming different endiannes.
+ else
+ Val = Val + Add; // Add assuming current endiannes.
+ if (Add == 0 || Rand.RandBool()) // Maybe negate.
+ Val = -Val;
+ }
+ memcpy(Data + Off, &Val, sizeof(Val));
+ return Size;
+}
+
+size_t MutationDispatcher::Mutate_ChangeBinaryInteger(uint8_t *Data,
+ size_t Size,
+ size_t MaxSize) {
+ if (Size > MaxSize) return 0;
+ switch (Rand(4)) {
+ case 3: return ChangeBinaryInteger<uint64_t>(Data, Size, Rand);
+ case 2: return ChangeBinaryInteger<uint32_t>(Data, Size, Rand);
+ case 1: return ChangeBinaryInteger<uint16_t>(Data, Size, Rand);
+ case 0: return ChangeBinaryInteger<uint8_t>(Data, Size, Rand);
+ default: assert(0);
+ }
+ return 0;
+}
+
+size_t MutationDispatcher::Mutate_CrossOver(uint8_t *Data, size_t Size,
+ size_t MaxSize) {
+ if (Size > MaxSize) return 0;
+ if (!Corpus || Corpus->size() < 2 || Size == 0) return 0;
+ size_t Idx = Rand(Corpus->size());
+ const Unit &O = (*Corpus)[Idx];
+ if (O.empty()) return 0;
+ MutateInPlaceHere.resize(MaxSize);
+ auto &U = MutateInPlaceHere;
+ size_t NewSize = 0;
+ switch(Rand(3)) {
+ case 0:
+ NewSize = CrossOver(Data, Size, O.data(), O.size(), U.data(), U.size());
+ break;
+ case 1:
+ NewSize = InsertPartOf(O.data(), O.size(), U.data(), U.size(), MaxSize);
+ if (NewSize)
+ break;
+ // LLVM_FALLTHROUGH;
+ case 2:
+ NewSize = CopyPartOf(O.data(), O.size(), U.data(), U.size());
+ break;
+ default: assert(0);
+ }
+ assert(NewSize > 0 && "CrossOver returned empty unit");
+ assert(NewSize <= MaxSize && "CrossOver returned overisized unit");
+ memcpy(Data, U.data(), NewSize);
+ return NewSize;
+}
+
+void MutationDispatcher::StartMutationSequence() {
+ CurrentMutatorSequence.clear();
+ CurrentDictionaryEntrySequence.clear();
+}
+
+// Copy successful dictionary entries to PersistentAutoDictionary.
+void MutationDispatcher::RecordSuccessfulMutationSequence() {
+ for (auto DE : CurrentDictionaryEntrySequence) {
+ // PersistentAutoDictionary.AddWithSuccessCountOne(DE);
+ DE->IncSuccessCount();
+ // Linear search is fine here as this happens seldom.
+ if (!PersistentAutoDictionary.ContainsWord(DE->GetW()))
+ PersistentAutoDictionary.push_back({DE->GetW(), 1});
+ }
+}
+
+void MutationDispatcher::PrintRecommendedDictionary() {
+ std::vector<DictionaryEntry> V;
+ for (auto &DE : PersistentAutoDictionary)
+ if (!ManualDictionary.ContainsWord(DE.GetW()))
+ V.push_back(DE);
+ if (V.empty()) return;
+ Printf("###### Recommended dictionary. ######\n");
+ for (auto &DE: V) {
+ Printf("\"");
+ PrintASCII(DE.GetW(), "\"");
+ Printf(" # Uses: %zd\n", DE.GetUseCount());
+ }
+ Printf("###### End of recommended dictionary. ######\n");
+}
+
+void MutationDispatcher::PrintMutationSequence() {
+ Printf("MS: %zd ", CurrentMutatorSequence.size());
+ for (auto M : CurrentMutatorSequence)
+ Printf("%s-", M.Name);
+ if (!CurrentDictionaryEntrySequence.empty()) {
+ Printf(" DE: ");
+ for (auto DE : CurrentDictionaryEntrySequence) {
+ Printf("\"");
+ PrintASCII(DE->GetW(), "\"-");
+ }
+ }
+}
+
+size_t MutationDispatcher::Mutate(uint8_t *Data, size_t Size, size_t MaxSize) {
+ return MutateImpl(Data, Size, MaxSize, Mutators);
+}
+
+size_t MutationDispatcher::DefaultMutate(uint8_t *Data, size_t Size,
+ size_t MaxSize) {
+ return MutateImpl(Data, Size, MaxSize, DefaultMutators);
+}
+
+// Mutates Data in place, returns new size.
+size_t MutationDispatcher::MutateImpl(uint8_t *Data, size_t Size,
+ size_t MaxSize,
+ const std::vector<Mutator> &Mutators) {
+ assert(MaxSize > 0);
+ if (Size == 0) {
+ for (size_t i = 0; i < MaxSize; i++)
+ Data[i] = RandCh(Rand);
+ if (Options.OnlyASCII)
+ ToASCII(Data, MaxSize);
+ return MaxSize;
+ }
+ assert(Size > 0);
+ // Some mutations may fail (e.g. can't insert more bytes if Size == MaxSize),
+ // in which case they will return 0.
+ // Try several times before returning un-mutated data.
+ for (int Iter = 0; Iter < 100; Iter++) {
+ auto M = Mutators[Rand(Mutators.size())];
+ size_t NewSize = (this->*(M.Fn))(Data, Size, MaxSize);
+ if (NewSize && NewSize <= MaxSize) {
+ if (Options.OnlyASCII)
+ ToASCII(Data, NewSize);
+ CurrentMutatorSequence.push_back(M);
+ return NewSize;
+ }
+ }
+ return std::min(Size, MaxSize);
+}
+
+void MutationDispatcher::AddWordToManualDictionary(const Word &W) {
+ ManualDictionary.push_back(
+ {W, std::numeric_limits<size_t>::max()});
+}
+
+void MutationDispatcher::AddWordToAutoDictionary(DictionaryEntry DE) {
+ static const size_t kMaxAutoDictSize = 1 << 14;
+ if (TempAutoDictionary.size() >= kMaxAutoDictSize) return;
+ TempAutoDictionary.push_back(DE);
+}
+
+void MutationDispatcher::ClearAutoDictionary() {
+ TempAutoDictionary.clear();
+}
+
+} // namespace fuzzer
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerMutate.h b/json4cpp/tests/thirdparty/Fuzzer/FuzzerMutate.h
new file mode 100644
index 0000000000..d3c0b00124
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerMutate.h
@@ -0,0 +1,145 @@
+//===- FuzzerMutate.h - Internal header for the Fuzzer ----------*- C++ -* ===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// fuzzer::MutationDispatcher
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_FUZZER_MUTATE_H
+#define LLVM_FUZZER_MUTATE_H
+
+#include "FuzzerDefs.h"
+#include "FuzzerDictionary.h"
+#include "FuzzerRandom.h"
+
+namespace fuzzer {
+
+class MutationDispatcher {
+public:
+ MutationDispatcher(Random &Rand, const FuzzingOptions &Options);
+ ~MutationDispatcher() {}
+ /// Indicate that we are about to start a new sequence of mutations.
+ void StartMutationSequence();
+ /// Print the current sequence of mutations.
+ void PrintMutationSequence();
+ /// Indicate that the current sequence of mutations was successfull.
+ void RecordSuccessfulMutationSequence();
+ /// Mutates data by invoking user-provided mutator.
+ size_t Mutate_Custom(uint8_t *Data, size_t Size, size_t MaxSize);
+ /// Mutates data by invoking user-provided crossover.
+ size_t Mutate_CustomCrossOver(uint8_t *Data, size_t Size, size_t MaxSize);
+ /// Mutates data by shuffling bytes.
+ size_t Mutate_ShuffleBytes(uint8_t *Data, size_t Size, size_t MaxSize);
+ /// Mutates data by erasing bytes.
+ size_t Mutate_EraseBytes(uint8_t *Data, size_t Size, size_t MaxSize);
+ /// Mutates data by inserting a byte.
+ size_t Mutate_InsertByte(uint8_t *Data, size_t Size, size_t MaxSize);
+ /// Mutates data by inserting several repeated bytes.
+ size_t Mutate_InsertRepeatedBytes(uint8_t *Data, size_t Size, size_t MaxSize);
+ /// Mutates data by chanding one byte.
+ size_t Mutate_ChangeByte(uint8_t *Data, size_t Size, size_t MaxSize);
+ /// Mutates data by chanding one bit.
+ size_t Mutate_ChangeBit(uint8_t *Data, size_t Size, size_t MaxSize);
+ /// Mutates data by copying/inserting a part of data into a different place.
+ size_t Mutate_CopyPart(uint8_t *Data, size_t Size, size_t MaxSize);
+
+ /// Mutates data by adding a word from the manual dictionary.
+ size_t Mutate_AddWordFromManualDictionary(uint8_t *Data, size_t Size,
+ size_t MaxSize);
+
+ /// Mutates data by adding a word from the temporary automatic dictionary.
+ size_t Mutate_AddWordFromTemporaryAutoDictionary(uint8_t *Data, size_t Size,
+ size_t MaxSize);
+
+ /// Mutates data by adding a word from the TORC.
+ size_t Mutate_AddWordFromTORC(uint8_t *Data, size_t Size, size_t MaxSize);
+
+ /// Mutates data by adding a word from the persistent automatic dictionary.
+ size_t Mutate_AddWordFromPersistentAutoDictionary(uint8_t *Data, size_t Size,
+ size_t MaxSize);
+
+ /// Tries to find an ASCII integer in Data, changes it to another ASCII int.
+ size_t Mutate_ChangeASCIIInteger(uint8_t *Data, size_t Size, size_t MaxSize);
+ /// Change a 1-, 2-, 4-, or 8-byte integer in interesting ways.
+ size_t Mutate_ChangeBinaryInteger(uint8_t *Data, size_t Size, size_t MaxSize);
+
+ /// CrossOver Data with some other element of the corpus.
+ size_t Mutate_CrossOver(uint8_t *Data, size_t Size, size_t MaxSize);
+
+ /// Applies one of the configured mutations.
+ /// Returns the new size of data which could be up to MaxSize.
+ size_t Mutate(uint8_t *Data, size_t Size, size_t MaxSize);
+ /// Applies one of the default mutations. Provided as a service
+ /// to mutation authors.
+ size_t DefaultMutate(uint8_t *Data, size_t Size, size_t MaxSize);
+
+ /// Creates a cross-over of two pieces of Data, returns its size.
+ size_t CrossOver(const uint8_t *Data1, size_t Size1, const uint8_t *Data2,
+ size_t Size2, uint8_t *Out, size_t MaxOutSize);
+
+ void AddWordToManualDictionary(const Word &W);
+
+ void AddWordToAutoDictionary(DictionaryEntry DE);
+ void ClearAutoDictionary();
+ void PrintRecommendedDictionary();
+
+ void SetCorpus(const InputCorpus *Corpus) { this->Corpus = Corpus; }
+
+ Random &GetRand() { return Rand; }
+
+private:
+
+ struct Mutator {
+ size_t (MutationDispatcher::*Fn)(uint8_t *Data, size_t Size, size_t Max);
+ const char *Name;
+ };
+
+ size_t AddWordFromDictionary(Dictionary &D, uint8_t *Data, size_t Size,
+ size_t MaxSize);
+ size_t MutateImpl(uint8_t *Data, size_t Size, size_t MaxSize,
+ const std::vector<Mutator> &Mutators);
+
+ size_t InsertPartOf(const uint8_t *From, size_t FromSize, uint8_t *To,
+ size_t ToSize, size_t MaxToSize);
+ size_t CopyPartOf(const uint8_t *From, size_t FromSize, uint8_t *To,
+ size_t ToSize);
+ size_t ApplyDictionaryEntry(uint8_t *Data, size_t Size, size_t MaxSize,
+ DictionaryEntry &DE);
+
+ template <class T>
+ DictionaryEntry MakeDictionaryEntryFromCMP(T Arg1, T Arg2,
+ const uint8_t *Data, size_t Size);
+
+ Random &Rand;
+ const FuzzingOptions &Options;
+
+ // Dictionary provided by the user via -dict=DICT_FILE.
+ Dictionary ManualDictionary;
+ // Temporary dictionary modified by the fuzzer itself,
+ // recreated periodically.
+ Dictionary TempAutoDictionary;
+ // Persistent dictionary modified by the fuzzer, consists of
+ // entries that led to successfull discoveries in the past mutations.
+ Dictionary PersistentAutoDictionary;
+
+ std::vector<Mutator> CurrentMutatorSequence;
+ std::vector<DictionaryEntry *> CurrentDictionaryEntrySequence;
+
+ static const size_t kCmpDictionaryEntriesDequeSize = 16;
+ DictionaryEntry CmpDictionaryEntriesDeque[kCmpDictionaryEntriesDequeSize];
+ size_t CmpDictionaryEntriesDequeIdx = 0;
+
+ const InputCorpus *Corpus = nullptr;
+ std::vector<uint8_t> MutateInPlaceHere;
+
+ std::vector<Mutator> Mutators;
+ std::vector<Mutator> DefaultMutators;
+};
+
+} // namespace fuzzer
+
+#endif // LLVM_FUZZER_MUTATE_H
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerOptions.h b/json4cpp/tests/thirdparty/Fuzzer/FuzzerOptions.h
new file mode 100644
index 0000000000..34e93fc34a
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerOptions.h
@@ -0,0 +1,68 @@
+//===- FuzzerOptions.h - Internal header for the Fuzzer ---------*- C++ -* ===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// fuzzer::FuzzingOptions
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_FUZZER_OPTIONS_H
+#define LLVM_FUZZER_OPTIONS_H
+
+#include "FuzzerDefs.h"
+
+namespace fuzzer {
+
+struct FuzzingOptions {
+ int Verbosity = 1;
+ size_t MaxLen = 0;
+ int UnitTimeoutSec = 300;
+ int TimeoutExitCode = 77;
+ int ErrorExitCode = 77;
+ int MaxTotalTimeSec = 0;
+ int RssLimitMb = 0;
+ bool DoCrossOver = true;
+ int MutateDepth = 5;
+ bool UseCounters = false;
+ bool UseIndirCalls = true;
+ bool UseMemcmp = true;
+ bool UseMemmem = true;
+ bool UseCmp = false;
+ bool UseValueProfile = false;
+ bool Shrink = false;
+ int ReloadIntervalSec = 1;
+ bool ShuffleAtStartUp = true;
+ bool PreferSmall = true;
+ size_t MaxNumberOfRuns = -1L;
+ int ReportSlowUnits = 10;
+ bool OnlyASCII = false;
+ std::string OutputCorpus;
+ std::string ArtifactPrefix = "./";
+ std::string ExactArtifactPath;
+ std::string ExitOnSrcPos;
+ std::string ExitOnItem;
+ bool SaveArtifacts = true;
+ bool PrintNEW = true; // Print a status line when new units are found;
+ bool OutputCSV = false;
+ bool PrintNewCovPcs = false;
+ bool PrintFinalStats = false;
+ bool PrintCorpusStats = false;
+ bool PrintCoverage = false;
+ bool DumpCoverage = false;
+ bool DetectLeaks = true;
+ int TraceMalloc = 0;
+ bool HandleAbrt = false;
+ bool HandleBus = false;
+ bool HandleFpe = false;
+ bool HandleIll = false;
+ bool HandleInt = false;
+ bool HandleSegv = false;
+ bool HandleTerm = false;
+};
+
+} // namespace fuzzer
+
+#endif // LLVM_FUZZER_OPTIONS_H
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerRandom.h b/json4cpp/tests/thirdparty/Fuzzer/FuzzerRandom.h
new file mode 100644
index 0000000000..b1be0bb935
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerRandom.h
@@ -0,0 +1,36 @@
+//===- FuzzerRandom.h - Internal header for the Fuzzer ----------*- C++ -* ===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// fuzzer::Random
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_FUZZER_RANDOM_H
+#define LLVM_FUZZER_RANDOM_H
+
+#include <random>
+
+namespace fuzzer {
+class Random {
+ public:
+ Random(unsigned int seed) : R(seed) {}
+ size_t Rand() { return R(); }
+ size_t RandBool() { return Rand() % 2; }
+ size_t operator()(size_t n) { return n ? Rand() % n : 0; }
+ intptr_t operator()(intptr_t From, intptr_t To) {
+ assert(From < To);
+ intptr_t RangeSize = To - From + 1;
+ return operator()(RangeSize) + From;
+ }
+ std::mt19937 &Get_mt19937() { return R; }
+ private:
+ std::mt19937 R;
+};
+
+} // namespace fuzzer
+
+#endif // LLVM_FUZZER_RANDOM_H
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerSHA1.cpp b/json4cpp/tests/thirdparty/Fuzzer/FuzzerSHA1.cpp
new file mode 100644
index 0000000000..d2f8e811bb
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerSHA1.cpp
@@ -0,0 +1,222 @@
+//===- FuzzerSHA1.h - Private copy of the SHA1 implementation ---*- C++ -* ===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// This code is taken from public domain
+// (http://oauth.googlecode.com/svn/code/c/liboauth/src/sha1.c)
+// and modified by adding anonymous namespace, adding an interface
+// function fuzzer::ComputeSHA1() and removing unnecessary code.
+//
+// lib/Fuzzer can not use SHA1 implementation from openssl because
+// openssl may not be available and because we may be fuzzing openssl itself.
+// For the same reason we do not want to depend on SHA1 from LLVM tree.
+//===----------------------------------------------------------------------===//
+
+#include "FuzzerSHA1.h"
+#include "FuzzerDefs.h"
+
+/* This code is public-domain - it is based on libcrypt
+ * placed in the public domain by Wei Dai and other contributors.
+ */
+
+#include <iomanip>
+#include <sstream>
+#include <stdint.h>
+#include <string.h>
+
+namespace { // Added for LibFuzzer
+
+#ifdef __BIG_ENDIAN__
+# define SHA_BIG_ENDIAN
+#elif defined __LITTLE_ENDIAN__
+/* override */
+#elif defined __BYTE_ORDER
+# if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+# define SHA_BIG_ENDIAN
+# endif
+#else // ! defined __LITTLE_ENDIAN__
+# include <endian.h> // machine/endian.h
+# if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+# define SHA_BIG_ENDIAN
+# endif
+#endif
+
+
+/* header */
+
+#define HASH_LENGTH 20
+#define BLOCK_LENGTH 64
+
+typedef struct sha1nfo {
+ uint32_t buffer[BLOCK_LENGTH/4];
+ uint32_t state[HASH_LENGTH/4];
+ uint32_t byteCount;
+ uint8_t bufferOffset;
+ uint8_t keyBuffer[BLOCK_LENGTH];
+ uint8_t innerHash[HASH_LENGTH];
+} sha1nfo;
+
+/* public API - prototypes - TODO: doxygen*/
+
+/**
+ */
+void sha1_init(sha1nfo *s);
+/**
+ */
+void sha1_writebyte(sha1nfo *s, uint8_t data);
+/**
+ */
+void sha1_write(sha1nfo *s, const char *data, size_t len);
+/**
+ */
+uint8_t* sha1_result(sha1nfo *s);
+
+
+/* code */
+#define SHA1_K0 0x5a827999
+#define SHA1_K20 0x6ed9eba1
+#define SHA1_K40 0x8f1bbcdc
+#define SHA1_K60 0xca62c1d6
+
+void sha1_init(sha1nfo *s) {
+ s->state[0] = 0x67452301;
+ s->state[1] = 0xefcdab89;
+ s->state[2] = 0x98badcfe;
+ s->state[3] = 0x10325476;
+ s->state[4] = 0xc3d2e1f0;
+ s->byteCount = 0;
+ s->bufferOffset = 0;
+}
+
+uint32_t sha1_rol32(uint32_t number, uint8_t bits) {
+ return ((number << bits) | (number >> (32-bits)));
+}
+
+void sha1_hashBlock(sha1nfo *s) {
+ uint8_t i;
+ uint32_t a,b,c,d,e,t;
+
+ a=s->state[0];
+ b=s->state[1];
+ c=s->state[2];
+ d=s->state[3];
+ e=s->state[4];
+ for (i=0; i<80; i++) {
+ if (i>=16) {
+ t = s->buffer[(i+13)&15] ^ s->buffer[(i+8)&15] ^ s->buffer[(i+2)&15] ^ s->buffer[i&15];
+ s->buffer[i&15] = sha1_rol32(t,1);
+ }
+ if (i<20) {
+ t = (d ^ (b & (c ^ d))) + SHA1_K0;
+ } else if (i<40) {
+ t = (b ^ c ^ d) + SHA1_K20;
+ } else if (i<60) {
+ t = ((b & c) | (d & (b | c))) + SHA1_K40;
+ } else {
+ t = (b ^ c ^ d) + SHA1_K60;
+ }
+ t+=sha1_rol32(a,5) + e + s->buffer[i&15];
+ e=d;
+ d=c;
+ c=sha1_rol32(b,30);
+ b=a;
+ a=t;
+ }
+ s->state[0] += a;
+ s->state[1] += b;
+ s->state[2] += c;
+ s->state[3] += d;
+ s->state[4] += e;
+}
+
+void sha1_addUncounted(sha1nfo *s, uint8_t data) {
+ uint8_t * const b = (uint8_t*) s->buffer;
+#ifdef SHA_BIG_ENDIAN
+ b[s->bufferOffset] = data;
+#else
+ b[s->bufferOffset ^ 3] = data;
+#endif
+ s->bufferOffset++;
+ if (s->bufferOffset == BLOCK_LENGTH) {
+ sha1_hashBlock(s);
+ s->bufferOffset = 0;
+ }
+}
+
+void sha1_writebyte(sha1nfo *s, uint8_t data) {
+ ++s->byteCount;
+ sha1_addUncounted(s, data);
+}
+
+void sha1_write(sha1nfo *s, const char *data, size_t len) {
+ for (;len--;) sha1_writebyte(s, (uint8_t) *data++);
+}
+
+void sha1_pad(sha1nfo *s) {
+ // Implement SHA-1 padding (fips180-2 §5.1.1)
+
+ // Pad with 0x80 followed by 0x00 until the end of the block
+ sha1_addUncounted(s, 0x80);
+ while (s->bufferOffset != 56) sha1_addUncounted(s, 0x00);
+
+ // Append length in the last 8 bytes
+ sha1_addUncounted(s, 0); // We're only using 32 bit lengths
+ sha1_addUncounted(s, 0); // But SHA-1 supports 64 bit lengths
+ sha1_addUncounted(s, 0); // So zero pad the top bits
+ sha1_addUncounted(s, s->byteCount >> 29); // Shifting to multiply by 8
+ sha1_addUncounted(s, s->byteCount >> 21); // as SHA-1 supports bitstreams as well as
+ sha1_addUncounted(s, s->byteCount >> 13); // byte.
+ sha1_addUncounted(s, s->byteCount >> 5);
+ sha1_addUncounted(s, s->byteCount << 3);
+}
+
+uint8_t* sha1_result(sha1nfo *s) {
+ // Pad to complete the last block
+ sha1_pad(s);
+
+#ifndef SHA_BIG_ENDIAN
+ // Swap byte order back
+ int i;
+ for (i=0; i<5; i++) {
+ s->state[i]=
+ (((s->state[i])<<24)& 0xff000000)
+ | (((s->state[i])<<8) & 0x00ff0000)
+ | (((s->state[i])>>8) & 0x0000ff00)
+ | (((s->state[i])>>24)& 0x000000ff);
+ }
+#endif
+
+ // Return pointer to hash (20 characters)
+ return (uint8_t*) s->state;
+}
+
+} // namespace; Added for LibFuzzer
+
+namespace fuzzer {
+
+// The rest is added for LibFuzzer
+void ComputeSHA1(const uint8_t *Data, size_t Len, uint8_t *Out) {
+ sha1nfo s;
+ sha1_init(&s);
+ sha1_write(&s, (const char*)Data, Len);
+ memcpy(Out, sha1_result(&s), HASH_LENGTH);
+}
+
+std::string Sha1ToString(const uint8_t Sha1[kSHA1NumBytes]) {
+ std::stringstream SS;
+ for (int i = 0; i < kSHA1NumBytes; i++)
+ SS << std::hex << std::setfill('0') << std::setw(2) << (unsigned)Sha1[i];
+ return SS.str();
+}
+
+std::string Hash(const Unit &U) {
+ uint8_t Hash[kSHA1NumBytes];
+ ComputeSHA1(U.data(), U.size(), Hash);
+ return Sha1ToString(Hash);
+}
+
+}
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerSHA1.h b/json4cpp/tests/thirdparty/Fuzzer/FuzzerSHA1.h
new file mode 100644
index 0000000000..3b5e6e807f
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerSHA1.h
@@ -0,0 +1,33 @@
+//===- FuzzerSHA1.h - Internal header for the SHA1 utils --------*- C++ -* ===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// SHA1 utils.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_FUZZER_SHA1_H
+#define LLVM_FUZZER_SHA1_H
+
+#include "FuzzerDefs.h"
+#include <cstddef>
+#include <stdint.h>
+
+namespace fuzzer {
+
+// Private copy of SHA1 implementation.
+static const int kSHA1NumBytes = 20;
+
+// Computes SHA1 hash of 'Len' bytes in 'Data', writes kSHA1NumBytes to 'Out'.
+void ComputeSHA1(const uint8_t *Data, size_t Len, uint8_t *Out);
+
+std::string Sha1ToString(const uint8_t Sha1[kSHA1NumBytes]);
+
+std::string Hash(const Unit &U);
+
+} // namespace fuzzer
+
+#endif // LLVM_FUZZER_SHA1_H
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerTracePC.cpp b/json4cpp/tests/thirdparty/Fuzzer/FuzzerTracePC.cpp
new file mode 100644
index 0000000000..81c084b589
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerTracePC.cpp
@@ -0,0 +1,339 @@
+//===- FuzzerTracePC.cpp - PC tracing--------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// Trace PCs.
+// This module implements __sanitizer_cov_trace_pc_guard[_init],
+// the callback required for -fsanitize-coverage=trace-pc-guard instrumentation.
+//
+//===----------------------------------------------------------------------===//
+
+#include "FuzzerCorpus.h"
+#include "FuzzerDefs.h"
+#include "FuzzerDictionary.h"
+#include "FuzzerExtFunctions.h"
+#include "FuzzerIO.h"
+#include "FuzzerTracePC.h"
+#include "FuzzerValueBitMap.h"
+#include <map>
+#include <sanitizer/coverage_interface.h>
+#include <set>
+#include <sstream>
+
+namespace fuzzer {
+
+TracePC TPC;
+
+void TracePC::HandleTrace(uint32_t *Guard, uintptr_t PC) {
+ uint32_t Idx = *Guard;
+ if (!Idx) return;
+ PCs[Idx % kNumPCs] = PC;
+ Counters[Idx % kNumCounters]++;
+}
+
+size_t TracePC::GetTotalPCCoverage() {
+ size_t Res = 0;
+ for (size_t i = 1; i < GetNumPCs(); i++)
+ if (PCs[i])
+ Res++;
+ return Res;
+}
+
+void TracePC::HandleInit(uint32_t *Start, uint32_t *Stop) {
+ if (Start == Stop || *Start) return;
+ assert(NumModules < sizeof(Modules) / sizeof(Modules[0]));
+ for (uint32_t *P = Start; P < Stop; P++)
+ *P = ++NumGuards;
+ Modules[NumModules].Start = Start;
+ Modules[NumModules].Stop = Stop;
+ NumModules++;
+}
+
+void TracePC::PrintModuleInfo() {
+ Printf("INFO: Loaded %zd modules (%zd guards): ", NumModules, NumGuards);
+ for (size_t i = 0; i < NumModules; i++)
+ Printf("[%p, %p), ", Modules[i].Start, Modules[i].Stop);
+ Printf("\n");
+}
+
+void TracePC::HandleCallerCallee(uintptr_t Caller, uintptr_t Callee) {
+ const uintptr_t kBits = 12;
+ const uintptr_t kMask = (1 << kBits) - 1;
+ uintptr_t Idx = (Caller & kMask) | ((Callee & kMask) << kBits);
+ HandleValueProfile(Idx);
+}
+
+static bool IsInterestingCoverageFile(std::string &File) {
+ if (File.find("compiler-rt/lib/") != std::string::npos)
+ return false; // sanitizer internal.
+ if (File.find("/usr/lib/") != std::string::npos)
+ return false;
+ if (File.find("/usr/include/") != std::string::npos)
+ return false;
+ if (File == "<null>")
+ return false;
+ return true;
+}
+
+void TracePC::PrintNewPCs() {
+ if (DoPrintNewPCs) {
+ if (!PrintedPCs)
+ PrintedPCs = new std::set<uintptr_t>;
+ for (size_t i = 1; i < GetNumPCs(); i++)
+ if (PCs[i] && PrintedPCs->insert(PCs[i]).second)
+ PrintPC("\tNEW_PC: %p %F %L\n", "\tNEW_PC: %p\n", PCs[i]);
+ }
+}
+
+void TracePC::PrintCoverage() {
+ if (!EF->__sanitizer_symbolize_pc ||
+ !EF->__sanitizer_get_module_and_offset_for_pc) {
+ Printf("INFO: __sanitizer_symbolize_pc or "
+ "__sanitizer_get_module_and_offset_for_pc is not available,"
+ " not printing coverage\n");
+ return;
+ }
+ std::map<std::string, std::vector<uintptr_t>> CoveredPCsPerModule;
+ std::map<std::string, uintptr_t> ModuleOffsets;
+ std::set<std::string> CoveredDirs, CoveredFiles, CoveredFunctions,
+ CoveredLines;
+ Printf("COVERAGE:\n");
+ for (size_t i = 1; i < GetNumPCs(); i++) {
+ if (!PCs[i]) continue;
+ std::string FileStr = DescribePC("%s", PCs[i]);
+ if (!IsInterestingCoverageFile(FileStr)) continue;
+ std::string FixedPCStr = DescribePC("%p", PCs[i]);
+ std::string FunctionStr = DescribePC("%F", PCs[i]);
+ std::string LineStr = DescribePC("%l", PCs[i]);
+ char ModulePathRaw[4096] = ""; // What's PATH_MAX in portable C++?
+ void *OffsetRaw = nullptr;
+ if (!EF->__sanitizer_get_module_and_offset_for_pc(
+ reinterpret_cast<void *>(PCs[i]), ModulePathRaw,
+ sizeof(ModulePathRaw), &OffsetRaw))
+ continue;
+ std::string Module = ModulePathRaw;
+ uintptr_t FixedPC = std::stol(FixedPCStr, 0, 16);
+ uintptr_t PcOffset = reinterpret_cast<uintptr_t>(OffsetRaw);
+ ModuleOffsets[Module] = FixedPC - PcOffset;
+ CoveredPCsPerModule[Module].push_back(PcOffset);
+ CoveredFunctions.insert(FunctionStr);
+ CoveredFiles.insert(FileStr);
+ CoveredDirs.insert(DirName(FileStr));
+ if (!CoveredLines.insert(FileStr + ":" + LineStr).second)
+ continue;
+ Printf("COVERED: %s %s:%s\n", FunctionStr.c_str(),
+ FileStr.c_str(), LineStr.c_str());
+ }
+
+ std::string CoveredDirsStr;
+ for (auto &Dir : CoveredDirs) {
+ if (!CoveredDirsStr.empty())
+ CoveredDirsStr += ",";
+ CoveredDirsStr += Dir;
+ }
+ Printf("COVERED_DIRS: %s\n", CoveredDirsStr.c_str());
+
+ for (auto &M : CoveredPCsPerModule) {
+ std::set<std::string> UncoveredFiles, UncoveredFunctions;
+ std::map<std::string, std::set<int> > UncoveredLines; // Func+File => lines
+ auto &ModuleName = M.first;
+ auto &CoveredOffsets = M.second;
+ uintptr_t ModuleOffset = ModuleOffsets[ModuleName];
+ std::sort(CoveredOffsets.begin(), CoveredOffsets.end());
+ Printf("MODULE_WITH_COVERAGE: %s\n", ModuleName.c_str());
+ // sancov does not yet fully support DSOs.
+ // std::string Cmd = "sancov -print-coverage-pcs " + ModuleName;
+ std::string Cmd = "objdump -d " + ModuleName +
+ " | grep 'call.*__sanitizer_cov_trace_pc_guard' | awk -F: '{print $1}'";
+ std::string SanCovOutput;
+ if (!ExecuteCommandAndReadOutput(Cmd, &SanCovOutput)) {
+ Printf("INFO: Command failed: %s\n", Cmd.c_str());
+ continue;
+ }
+ std::istringstream ISS(SanCovOutput);
+ std::string S;
+ while (std::getline(ISS, S, '\n')) {
+ uintptr_t PcOffset = std::stol(S, 0, 16);
+ if (!std::binary_search(CoveredOffsets.begin(), CoveredOffsets.end(),
+ PcOffset)) {
+ uintptr_t PC = ModuleOffset + PcOffset;
+ auto FileStr = DescribePC("%s", PC);
+ if (!IsInterestingCoverageFile(FileStr)) continue;
+ if (CoveredFiles.count(FileStr) == 0) {
+ UncoveredFiles.insert(FileStr);
+ continue;
+ }
+ auto FunctionStr = DescribePC("%F", PC);
+ if (CoveredFunctions.count(FunctionStr) == 0) {
+ UncoveredFunctions.insert(FunctionStr);
+ continue;
+ }
+ std::string LineStr = DescribePC("%l", PC);
+ uintptr_t Line = std::stoi(LineStr);
+ std::string FileLineStr = FileStr + ":" + LineStr;
+ if (CoveredLines.count(FileLineStr) == 0)
+ UncoveredLines[FunctionStr + " " + FileStr].insert(Line);
+ }
+ }
+ for (auto &FileLine: UncoveredLines)
+ for (int Line : FileLine.second)
+ Printf("UNCOVERED_LINE: %s:%d\n", FileLine.first.c_str(), Line);
+ for (auto &Func : UncoveredFunctions)
+ Printf("UNCOVERED_FUNC: %s\n", Func.c_str());
+ for (auto &File : UncoveredFiles)
+ Printf("UNCOVERED_FILE: %s\n", File.c_str());
+ }
+}
+
+void TracePC::DumpCoverage() {
+ __sanitizer_dump_coverage(PCs, GetNumPCs());
+}
+
+// Value profile.
+// We keep track of various values that affect control flow.
+// These values are inserted into a bit-set-based hash map.
+// Every new bit in the map is treated as a new coverage.
+//
+// For memcmp/strcmp/etc the interesting value is the length of the common
+// prefix of the parameters.
+// For cmp instructions the interesting value is a XOR of the parameters.
+// The interesting value is mixed up with the PC and is then added to the map.
+
+ATTRIBUTE_NO_SANITIZE_MEMORY
+void TracePC::AddValueForMemcmp(void *caller_pc, const void *s1, const void *s2,
+ size_t n) {
+ if (!n) return;
+ size_t Len = std::min(n, (size_t)32);
+ const uint8_t *A1 = reinterpret_cast<const uint8_t *>(s1);
+ const uint8_t *A2 = reinterpret_cast<const uint8_t *>(s2);
+ size_t I = 0;
+ for (; I < Len; I++)
+ if (A1[I] != A2[I])
+ break;
+ size_t PC = reinterpret_cast<size_t>(caller_pc);
+ size_t Idx = I;
+ // if (I < Len)
+ // Idx += __builtin_popcountl((A1[I] ^ A2[I])) - 1;
+ TPC.HandleValueProfile((PC & 4095) | (Idx << 12));
+}
+
+ATTRIBUTE_NO_SANITIZE_MEMORY
+void TracePC::AddValueForStrcmp(void *caller_pc, const char *s1, const char *s2,
+ size_t n) {
+ if (!n) return;
+ size_t Len = std::min(n, (size_t)32);
+ const uint8_t *A1 = reinterpret_cast<const uint8_t *>(s1);
+ const uint8_t *A2 = reinterpret_cast<const uint8_t *>(s2);
+ size_t I = 0;
+ for (; I < Len; I++)
+ if (A1[I] != A2[I] || A1[I] == 0)
+ break;
+ size_t PC = reinterpret_cast<size_t>(caller_pc);
+ size_t Idx = I;
+ // if (I < Len && A1[I])
+ // Idx += __builtin_popcountl((A1[I] ^ A2[I])) - 1;
+ TPC.HandleValueProfile((PC & 4095) | (Idx << 12));
+}
+
+template <class T>
+ATTRIBUTE_TARGET_POPCNT
+#ifdef __clang__ // g++ can't handle this __attribute__ here :(
+__attribute__((always_inline))
+#endif // __clang__
+void TracePC::HandleCmp(void *PC, T Arg1, T Arg2) {
+ uintptr_t PCuint = reinterpret_cast<uintptr_t>(PC);
+ uint64_t ArgXor = Arg1 ^ Arg2;
+ uint64_t ArgDistance = __builtin_popcountl(ArgXor) + 1; // [1,65]
+ uintptr_t Idx = ((PCuint & 4095) + 1) * ArgDistance;
+ if (sizeof(T) == 4)
+ TORC4.Insert(ArgXor, Arg1, Arg2);
+ else if (sizeof(T) == 8)
+ TORC8.Insert(ArgXor, Arg1, Arg2);
+ HandleValueProfile(Idx);
+}
+
+} // namespace fuzzer
+
+extern "C" {
+__attribute__((visibility("default")))
+void __sanitizer_cov_trace_pc_guard(uint32_t *Guard) {
+ uintptr_t PC = (uintptr_t)__builtin_return_address(0);
+ fuzzer::TPC.HandleTrace(Guard, PC);
+}
+
+__attribute__((visibility("default")))
+void __sanitizer_cov_trace_pc_guard_init(uint32_t *Start, uint32_t *Stop) {
+ fuzzer::TPC.HandleInit(Start, Stop);
+}
+
+__attribute__((visibility("default")))
+void __sanitizer_cov_trace_pc_indir(uintptr_t Callee) {
+ uintptr_t PC = (uintptr_t)__builtin_return_address(0);
+ fuzzer::TPC.HandleCallerCallee(PC, Callee);
+}
+
+__attribute__((visibility("default")))
+void __sanitizer_cov_trace_cmp8(uint64_t Arg1, uint64_t Arg2) {
+ fuzzer::TPC.HandleCmp(__builtin_return_address(0), Arg1, Arg2);
+}
+__attribute__((visibility("default")))
+void __sanitizer_cov_trace_cmp4(uint32_t Arg1, uint32_t Arg2) {
+ fuzzer::TPC.HandleCmp(__builtin_return_address(0), Arg1, Arg2);
+}
+__attribute__((visibility("default")))
+void __sanitizer_cov_trace_cmp2(uint16_t Arg1, uint16_t Arg2) {
+ fuzzer::TPC.HandleCmp(__builtin_return_address(0), Arg1, Arg2);
+}
+__attribute__((visibility("default")))
+void __sanitizer_cov_trace_cmp1(uint8_t Arg1, uint8_t Arg2) {
+ fuzzer::TPC.HandleCmp(__builtin_return_address(0), Arg1, Arg2);
+}
+
+__attribute__((visibility("default")))
+void __sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases) {
+ // Updates the value profile based on the relative position of Val and Cases.
+ // We want to handle one random case at every call (handling all is slow).
+ // Since none of the arguments contain any random bits we use a thread-local
+ // counter to choose the random case to handle.
+ static thread_local size_t Counter;
+ Counter++;
+ uint64_t N = Cases[0];
+ uint64_t *Vals = Cases + 2;
+ char *PC = (char*)__builtin_return_address(0);
+ // We need a random number < N using Counter as a seed. But w/o DIV.
+ // * find a power of two >= N
+ // * mask Counter with this power of two.
+ // * maybe subtract N.
+ size_t Nlog = sizeof(long) * 8 - __builtin_clzl((long)N);
+ size_t PowerOfTwoGeN = 1U << Nlog;
+ assert(PowerOfTwoGeN >= N);
+ size_t Idx = Counter & (PowerOfTwoGeN - 1);
+ if (Idx >= N)
+ Idx -= N;
+ assert(Idx < N);
+ uint64_t TwoIn32 = 1ULL << 32;
+ if ((Val | Vals[Idx]) < TwoIn32)
+ fuzzer::TPC.HandleCmp(PC + Idx, static_cast<uint32_t>(Val),
+ static_cast<uint32_t>(Vals[Idx]));
+ else
+ fuzzer::TPC.HandleCmp(PC + Idx, Val, Vals[Idx]);
+}
+
+__attribute__((visibility("default")))
+void __sanitizer_cov_trace_div4(uint32_t Val) {
+ fuzzer::TPC.HandleCmp(__builtin_return_address(0), Val, (uint32_t)0);
+}
+__attribute__((visibility("default")))
+void __sanitizer_cov_trace_div8(uint64_t Val) {
+ fuzzer::TPC.HandleCmp(__builtin_return_address(0), Val, (uint64_t)0);
+}
+__attribute__((visibility("default")))
+void __sanitizer_cov_trace_gep(uintptr_t Idx) {
+ fuzzer::TPC.HandleCmp(__builtin_return_address(0), Idx, (uintptr_t)0);
+}
+
+} // extern "C"
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerTracePC.h b/json4cpp/tests/thirdparty/Fuzzer/FuzzerTracePC.h
new file mode 100644
index 0000000000..68827f80cb
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerTracePC.h
@@ -0,0 +1,158 @@
+//===- FuzzerTracePC.h - Internal header for the Fuzzer ---------*- C++ -* ===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// fuzzer::TracePC
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_FUZZER_TRACE_PC
+#define LLVM_FUZZER_TRACE_PC
+
+#include "FuzzerDefs.h"
+#include "FuzzerValueBitMap.h"
+#include <set>
+
+namespace fuzzer {
+
+// TableOfRecentCompares (TORC) remembers the most recently performed
+// comparisons of type T.
+// We record the arguments of CMP instructions in this table unconditionally
+// because it seems cheaper this way than to compute some expensive
+// conditions inside __sanitizer_cov_trace_cmp*.
+// After the unit has been executed we may decide to use the contents of
+// this table to populate a Dictionary.
+template<class T, size_t kSizeT>
+struct TableOfRecentCompares {
+ static const size_t kSize = kSizeT;
+ struct Pair {
+ T A, B;
+ };
+ void Insert(size_t Idx, T Arg1, T Arg2) {
+ Idx = Idx % kSize;
+ Table[Idx].A = Arg1;
+ Table[Idx].B = Arg2;
+ }
+
+ Pair Get(size_t I) { return Table[I % kSize]; }
+
+ Pair Table[kSize];
+};
+
+class TracePC {
+ public:
+ static const size_t kFeatureSetSize = ValueBitMap::kNumberOfItems;
+
+ void HandleTrace(uint32_t *guard, uintptr_t PC);
+ void HandleInit(uint32_t *start, uint32_t *stop);
+ void HandleCallerCallee(uintptr_t Caller, uintptr_t Callee);
+ void HandleValueProfile(size_t Value) { ValueProfileMap.AddValue(Value); }
+ template <class T> void HandleCmp(void *PC, T Arg1, T Arg2);
+ size_t GetTotalPCCoverage();
+ void SetUseCounters(bool UC) { UseCounters = UC; }
+ void SetUseValueProfile(bool VP) { UseValueProfile = VP; }
+ void SetPrintNewPCs(bool P) { DoPrintNewPCs = P; }
+ template <class Callback> size_t CollectFeatures(Callback CB);
+ bool UpdateValueProfileMap(ValueBitMap *MaxValueProfileMap) {
+ return UseValueProfile && MaxValueProfileMap->MergeFrom(ValueProfileMap);
+ }
+
+ void ResetMaps() {
+ ValueProfileMap.Reset();
+ memset(Counters, 0, sizeof(Counters));
+ }
+
+ void UpdateFeatureSet(size_t CurrentElementIdx, size_t CurrentElementSize);
+ void PrintFeatureSet();
+
+ void PrintModuleInfo();
+
+ void PrintCoverage();
+ void DumpCoverage();
+
+ void AddValueForMemcmp(void *caller_pc, const void *s1, const void *s2,
+ size_t n);
+ void AddValueForStrcmp(void *caller_pc, const char *s1, const char *s2,
+ size_t n);
+
+ bool UsingTracePcGuard() const {return NumModules; }
+
+ static const size_t kTORCSize = 1 << 5;
+ TableOfRecentCompares<uint32_t, kTORCSize> TORC4;
+ TableOfRecentCompares<uint64_t, kTORCSize> TORC8;
+
+ void PrintNewPCs();
+ size_t GetNumPCs() const { return Min(kNumPCs, NumGuards + 1); }
+ uintptr_t GetPC(size_t Idx) {
+ assert(Idx < GetNumPCs());
+ return PCs[Idx];
+ }
+
+private:
+ bool UseCounters = false;
+ bool UseValueProfile = false;
+ bool DoPrintNewPCs = false;
+
+ struct Module {
+ uint32_t *Start, *Stop;
+ };
+
+ Module Modules[4096];
+ size_t NumModules; // linker-initialized.
+ size_t NumGuards; // linker-initialized.
+
+ static const size_t kNumCounters = 1 << 14;
+ alignas(8) uint8_t Counters[kNumCounters];
+
+ static const size_t kNumPCs = 1 << 24;
+ uintptr_t PCs[kNumPCs];
+
+ std::set<uintptr_t> *PrintedPCs;
+
+ ValueBitMap ValueProfileMap;
+};
+
+template <class Callback>
+size_t TracePC::CollectFeatures(Callback CB) {
+ if (!UsingTracePcGuard()) return 0;
+ size_t Res = 0;
+ const size_t Step = 8;
+ assert(reinterpret_cast<uintptr_t>(Counters) % Step == 0);
+ size_t N = Min(kNumCounters, NumGuards + 1);
+ N = (N + Step - 1) & ~(Step - 1); // Round up.
+ for (size_t Idx = 0; Idx < N; Idx += Step) {
+ uint64_t Bundle = *reinterpret_cast<uint64_t*>(&Counters[Idx]);
+ if (!Bundle) continue;
+ for (size_t i = Idx; i < Idx + Step; i++) {
+ uint8_t Counter = (Bundle >> ((i - Idx) * 8)) & 0xff;
+ if (!Counter) continue;
+ Counters[i] = 0;
+ unsigned Bit = 0;
+ /**/ if (Counter >= 128) Bit = 7;
+ else if (Counter >= 32) Bit = 6;
+ else if (Counter >= 16) Bit = 5;
+ else if (Counter >= 8) Bit = 4;
+ else if (Counter >= 4) Bit = 3;
+ else if (Counter >= 3) Bit = 2;
+ else if (Counter >= 2) Bit = 1;
+ size_t Feature = (i * 8 + Bit);
+ if (CB(Feature))
+ Res++;
+ }
+ }
+ if (UseValueProfile)
+ ValueProfileMap.ForEach([&](size_t Idx) {
+ if (CB(NumGuards * 8 + Idx))
+ Res++;
+ });
+ return Res;
+}
+
+extern TracePC TPC;
+
+} // namespace fuzzer
+
+#endif // LLVM_FUZZER_TRACE_PC
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerTraceState.cpp b/json4cpp/tests/thirdparty/Fuzzer/FuzzerTraceState.cpp
new file mode 100644
index 0000000000..be62a6624b
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerTraceState.cpp
@@ -0,0 +1,325 @@
+//===- FuzzerTraceState.cpp - Trace-based fuzzer mutator ------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// Data tracing.
+//===----------------------------------------------------------------------===//
+
+#include "FuzzerDictionary.h"
+#include "FuzzerInternal.h"
+#include "FuzzerIO.h"
+#include "FuzzerMutate.h"
+#include "FuzzerRandom.h"
+#include "FuzzerTracePC.h"
+#include <algorithm>
+#include <cstring>
+#include <map>
+#include <set>
+#include <thread>
+
+namespace fuzzer {
+
+// For now, very simple: put Size bytes of Data at position Pos.
+struct TraceBasedMutation {
+ uint32_t Pos;
+ Word W;
+};
+
+// Declared as static globals for faster checks inside the hooks.
+static bool RecordingMemcmp = false;
+static bool RecordingMemmem = false;
+static bool DoingMyOwnMemmem = false;
+
+ScopedDoingMyOwnMemmem::ScopedDoingMyOwnMemmem() { DoingMyOwnMemmem = true; }
+ScopedDoingMyOwnMemmem::~ScopedDoingMyOwnMemmem() { DoingMyOwnMemmem = false; }
+
+class TraceState {
+public:
+ TraceState(MutationDispatcher &MD, const FuzzingOptions &Options,
+ const Fuzzer *F)
+ : MD(MD), Options(Options), F(F) {}
+
+ void TraceMemcmpCallback(size_t CmpSize, const uint8_t *Data1,
+ const uint8_t *Data2);
+
+ void TraceSwitchCallback(uintptr_t PC, size_t ValSizeInBits, uint64_t Val,
+ size_t NumCases, uint64_t *Cases);
+ int TryToAddDesiredData(uint64_t PresentData, uint64_t DesiredData,
+ size_t DataSize);
+ int TryToAddDesiredData(const uint8_t *PresentData,
+ const uint8_t *DesiredData, size_t DataSize);
+
+ void StartTraceRecording() {
+ if (!Options.UseMemcmp)
+ return;
+ RecordingMemcmp = Options.UseMemcmp;
+ RecordingMemmem = Options.UseMemmem;
+ NumMutations = 0;
+ InterestingWords.clear();
+ MD.ClearAutoDictionary();
+ }
+
+ void StopTraceRecording() {
+ if (!RecordingMemcmp)
+ return;
+ RecordingMemcmp = false;
+ for (size_t i = 0; i < NumMutations; i++) {
+ auto &M = Mutations[i];
+ if (Options.Verbosity >= 2) {
+ AutoDictUnitCounts[M.W]++;
+ AutoDictAdds++;
+ if ((AutoDictAdds & (AutoDictAdds - 1)) == 0) {
+ typedef std::pair<size_t, Word> CU;
+ std::vector<CU> CountedUnits;
+ for (auto &I : AutoDictUnitCounts)
+ CountedUnits.push_back(std::make_pair(I.second, I.first));
+ std::sort(CountedUnits.begin(), CountedUnits.end(),
+ [](const CU &a, const CU &b) { return a.first > b.first; });
+ Printf("AutoDict:\n");
+ for (auto &I : CountedUnits) {
+ Printf(" %zd ", I.first);
+ PrintASCII(I.second.data(), I.second.size());
+ Printf("\n");
+ }
+ }
+ }
+ MD.AddWordToAutoDictionary({M.W, M.Pos});
+ }
+ for (auto &W : InterestingWords)
+ MD.AddWordToAutoDictionary({W});
+ }
+
+ void AddMutation(uint32_t Pos, uint32_t Size, const uint8_t *Data) {
+ if (NumMutations >= kMaxMutations) return;
+ auto &M = Mutations[NumMutations++];
+ M.Pos = Pos;
+ M.W.Set(Data, Size);
+ }
+
+ void AddMutation(uint32_t Pos, uint32_t Size, uint64_t Data) {
+ assert(Size <= sizeof(Data));
+ AddMutation(Pos, Size, reinterpret_cast<uint8_t*>(&Data));
+ }
+
+ void AddInterestingWord(const uint8_t *Data, size_t Size) {
+ if (!RecordingMemmem || !F->InFuzzingThread()) return;
+ if (Size <= 1) return;
+ Size = std::min(Size, Word::GetMaxSize());
+ Word W(Data, Size);
+ InterestingWords.insert(W);
+ }
+
+ private:
+ bool IsTwoByteData(uint64_t Data) {
+ int64_t Signed = static_cast<int64_t>(Data);
+ Signed >>= 16;
+ return Signed == 0 || Signed == -1L;
+ }
+
+ // We don't want to create too many trace-based mutations as it is both
+ // expensive and useless. So after some number of mutations is collected,
+ // start rejecting some of them. The more there are mutations the more we
+ // reject.
+ bool WantToHandleOneMoreMutation() {
+ const size_t FirstN = 64;
+ // Gladly handle first N mutations.
+ if (NumMutations <= FirstN) return true;
+ size_t Diff = NumMutations - FirstN;
+ size_t DiffLog = sizeof(long) * 8 - __builtin_clzl((long)Diff);
+ assert(DiffLog > 0 && DiffLog < 64);
+ bool WantThisOne = MD.GetRand()(1 << DiffLog) == 0; // 1 out of DiffLog.
+ return WantThisOne;
+ }
+
+ static const size_t kMaxMutations = 1 << 16;
+ size_t NumMutations;
+ TraceBasedMutation Mutations[kMaxMutations];
+ // TODO: std::set is too inefficient, need to have a custom DS here.
+ std::set<Word> InterestingWords;
+ MutationDispatcher &MD;
+ const FuzzingOptions Options;
+ const Fuzzer *F;
+ std::map<Word, size_t> AutoDictUnitCounts;
+ size_t AutoDictAdds = 0;
+};
+
+int TraceState::TryToAddDesiredData(uint64_t PresentData, uint64_t DesiredData,
+ size_t DataSize) {
+ if (NumMutations >= kMaxMutations || !WantToHandleOneMoreMutation()) return 0;
+ ScopedDoingMyOwnMemmem scoped_doing_my_own_memmem;
+ const uint8_t *UnitData;
+ auto UnitSize = F->GetCurrentUnitInFuzzingThead(&UnitData);
+ int Res = 0;
+ const uint8_t *Beg = UnitData;
+ const uint8_t *End = Beg + UnitSize;
+ for (const uint8_t *Cur = Beg; Cur < End; Cur++) {
+ Cur = (uint8_t *)SearchMemory(Cur, End - Cur, &PresentData, DataSize);
+ if (!Cur)
+ break;
+ size_t Pos = Cur - Beg;
+ assert(Pos < UnitSize);
+ AddMutation(Pos, DataSize, DesiredData);
+ AddMutation(Pos, DataSize, DesiredData + 1);
+ AddMutation(Pos, DataSize, DesiredData - 1);
+ Res++;
+ }
+ return Res;
+}
+
+int TraceState::TryToAddDesiredData(const uint8_t *PresentData,
+ const uint8_t *DesiredData,
+ size_t DataSize) {
+ if (NumMutations >= kMaxMutations || !WantToHandleOneMoreMutation()) return 0;
+ ScopedDoingMyOwnMemmem scoped_doing_my_own_memmem;
+ const uint8_t *UnitData;
+ auto UnitSize = F->GetCurrentUnitInFuzzingThead(&UnitData);
+ int Res = 0;
+ const uint8_t *Beg = UnitData;
+ const uint8_t *End = Beg + UnitSize;
+ for (const uint8_t *Cur = Beg; Cur < End; Cur++) {
+ Cur = (uint8_t *)SearchMemory(Cur, End - Cur, PresentData, DataSize);
+ if (!Cur)
+ break;
+ size_t Pos = Cur - Beg;
+ assert(Pos < UnitSize);
+ AddMutation(Pos, DataSize, DesiredData);
+ Res++;
+ }
+ return Res;
+}
+
+void TraceState::TraceMemcmpCallback(size_t CmpSize, const uint8_t *Data1,
+ const uint8_t *Data2) {
+ if (!RecordingMemcmp || !F->InFuzzingThread()) return;
+ CmpSize = std::min(CmpSize, Word::GetMaxSize());
+ int Added2 = TryToAddDesiredData(Data1, Data2, CmpSize);
+ int Added1 = TryToAddDesiredData(Data2, Data1, CmpSize);
+ if ((Added1 || Added2) && Options.Verbosity >= 3) {
+ Printf("MemCmp Added %d%d: ", Added1, Added2);
+ if (Added1) PrintASCII(Data1, CmpSize);
+ if (Added2) PrintASCII(Data2, CmpSize);
+ Printf("\n");
+ }
+}
+
+void TraceState::TraceSwitchCallback(uintptr_t PC, size_t ValSizeInBits,
+ uint64_t Val, size_t NumCases,
+ uint64_t *Cases) {
+ if (F->InFuzzingThread()) return;
+ size_t ValSize = ValSizeInBits / 8;
+ bool TryShort = IsTwoByteData(Val);
+ for (size_t i = 0; i < NumCases; i++)
+ TryShort &= IsTwoByteData(Cases[i]);
+
+ if (Options.Verbosity >= 3)
+ Printf("TraceSwitch: %p %zd # %zd; TryShort %d\n", PC, Val, NumCases,
+ TryShort);
+
+ for (size_t i = 0; i < NumCases; i++) {
+ TryToAddDesiredData(Val, Cases[i], ValSize);
+ if (TryShort)
+ TryToAddDesiredData(Val, Cases[i], 2);
+ }
+}
+
+static TraceState *TS;
+
+void Fuzzer::StartTraceRecording() {
+ if (!TS) return;
+ TS->StartTraceRecording();
+}
+
+void Fuzzer::StopTraceRecording() {
+ if (!TS) return;
+ TS->StopTraceRecording();
+}
+
+void Fuzzer::InitializeTraceState() {
+ if (!Options.UseMemcmp) return;
+ TS = new TraceState(MD, Options, this);
+}
+
+static size_t InternalStrnlen(const char *S, size_t MaxLen) {
+ size_t Len = 0;
+ for (; Len < MaxLen && S[Len]; Len++) {}
+ return Len;
+}
+
+} // namespace fuzzer
+
+using fuzzer::TS;
+using fuzzer::RecordingMemcmp;
+
+extern "C" {
+
+// We may need to avoid defining weak hooks to stay compatible with older clang.
+#ifndef LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS
+# define LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS 1
+#endif
+
+#if LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS
+void __sanitizer_weak_hook_memcmp(void *caller_pc, const void *s1,
+ const void *s2, size_t n, int result) {
+ fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, n);
+ if (!RecordingMemcmp) return;
+ if (result == 0) return; // No reason to mutate.
+ if (n <= 1) return; // Not interesting.
+ TS->TraceMemcmpCallback(n, reinterpret_cast<const uint8_t *>(s1),
+ reinterpret_cast<const uint8_t *>(s2));
+}
+
+void __sanitizer_weak_hook_strncmp(void *caller_pc, const char *s1,
+ const char *s2, size_t n, int result) {
+ fuzzer::TPC.AddValueForStrcmp(caller_pc, s1, s2, n);
+ if (!RecordingMemcmp) return;
+ if (result == 0) return; // No reason to mutate.
+ size_t Len1 = fuzzer::InternalStrnlen(s1, n);
+ size_t Len2 = fuzzer::InternalStrnlen(s2, n);
+ n = std::min(n, Len1);
+ n = std::min(n, Len2);
+ if (n <= 1) return; // Not interesting.
+ TS->TraceMemcmpCallback(n, reinterpret_cast<const uint8_t *>(s1),
+ reinterpret_cast<const uint8_t *>(s2));
+}
+
+void __sanitizer_weak_hook_strcmp(void *caller_pc, const char *s1,
+ const char *s2, int result) {
+ fuzzer::TPC.AddValueForStrcmp(caller_pc, s1, s2, 64);
+ if (!RecordingMemcmp) return;
+ if (result == 0) return; // No reason to mutate.
+ size_t Len1 = strlen(s1);
+ size_t Len2 = strlen(s2);
+ size_t N = std::min(Len1, Len2);
+ if (N <= 1) return; // Not interesting.
+ TS->TraceMemcmpCallback(N, reinterpret_cast<const uint8_t *>(s1),
+ reinterpret_cast<const uint8_t *>(s2));
+}
+
+void __sanitizer_weak_hook_strncasecmp(void *called_pc, const char *s1,
+ const char *s2, size_t n, int result) {
+ return __sanitizer_weak_hook_strncmp(called_pc, s1, s2, n, result);
+}
+void __sanitizer_weak_hook_strcasecmp(void *called_pc, const char *s1,
+ const char *s2, int result) {
+ return __sanitizer_weak_hook_strcmp(called_pc, s1, s2, result);
+}
+void __sanitizer_weak_hook_strstr(void *called_pc, const char *s1,
+ const char *s2, char *result) {
+ TS->AddInterestingWord(reinterpret_cast<const uint8_t *>(s2), strlen(s2));
+}
+void __sanitizer_weak_hook_strcasestr(void *called_pc, const char *s1,
+ const char *s2, char *result) {
+ TS->AddInterestingWord(reinterpret_cast<const uint8_t *>(s2), strlen(s2));
+}
+void __sanitizer_weak_hook_memmem(void *called_pc, const void *s1, size_t len1,
+ const void *s2, size_t len2, void *result) {
+ if (fuzzer::DoingMyOwnMemmem) return;
+ TS->AddInterestingWord(reinterpret_cast<const uint8_t *>(s2), len2);
+}
+
+#endif // LLVM_FUZZER_DEFINES_SANITIZER_WEAK_HOOOKS
+} // extern "C"
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerUtil.cpp b/json4cpp/tests/thirdparty/Fuzzer/FuzzerUtil.cpp
new file mode 100644
index 0000000000..f5fd3a8518
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerUtil.cpp
@@ -0,0 +1,218 @@
+//===- FuzzerUtil.cpp - Misc utils ----------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// Misc utils.
+//===----------------------------------------------------------------------===//
+
+#include "FuzzerUtil.h"
+#include "FuzzerIO.h"
+#include "FuzzerInternal.h"
+#include <cassert>
+#include <chrono>
+#include <cstring>
+#include <errno.h>
+#include <signal.h>
+#include <sstream>
+#include <stdio.h>
+#include <sys/types.h>
+#include <thread>
+
+namespace fuzzer {
+
+void PrintHexArray(const uint8_t *Data, size_t Size,
+ const char *PrintAfter) {
+ for (size_t i = 0; i < Size; i++)
+ Printf("0x%x,", (unsigned)Data[i]);
+ Printf("%s", PrintAfter);
+}
+
+void Print(const Unit &v, const char *PrintAfter) {
+ PrintHexArray(v.data(), v.size(), PrintAfter);
+}
+
+void PrintASCIIByte(uint8_t Byte) {
+ if (Byte == '\\')
+ Printf("\\\\");
+ else if (Byte == '"')
+ Printf("\\\"");
+ else if (Byte >= 32 && Byte < 127)
+ Printf("%c", Byte);
+ else
+ Printf("\\x%02x", Byte);
+}
+
+void PrintASCII(const uint8_t *Data, size_t Size, const char *PrintAfter) {
+ for (size_t i = 0; i < Size; i++)
+ PrintASCIIByte(Data[i]);
+ Printf("%s", PrintAfter);
+}
+
+void PrintASCII(const Unit &U, const char *PrintAfter) {
+ PrintASCII(U.data(), U.size(), PrintAfter);
+}
+
+bool ToASCII(uint8_t *Data, size_t Size) {
+ bool Changed = false;
+ for (size_t i = 0; i < Size; i++) {
+ uint8_t &X = Data[i];
+ auto NewX = X;
+ NewX &= 127;
+ if (!isspace(NewX) && !isprint(NewX))
+ NewX = ' ';
+ Changed |= NewX != X;
+ X = NewX;
+ }
+ return Changed;
+}
+
+bool IsASCII(const Unit &U) { return IsASCII(U.data(), U.size()); }
+
+bool IsASCII(const uint8_t *Data, size_t Size) {
+ for (size_t i = 0; i < Size; i++)
+ if (!(isprint(Data[i]) || isspace(Data[i]))) return false;
+ return true;
+}
+
+bool ParseOneDictionaryEntry(const std::string &Str, Unit *U) {
+ U->clear();
+ if (Str.empty()) return false;
+ size_t L = 0, R = Str.size() - 1; // We are parsing the range [L,R].
+ // Skip spaces from both sides.
+ while (L < R && isspace(Str[L])) L++;
+ while (R > L && isspace(Str[R])) R--;
+ if (R - L < 2) return false;
+ // Check the closing "
+ if (Str[R] != '"') return false;
+ R--;
+ // Find the opening "
+ while (L < R && Str[L] != '"') L++;
+ if (L >= R) return false;
+ assert(Str[L] == '\"');
+ L++;
+ assert(L <= R);
+ for (size_t Pos = L; Pos <= R; Pos++) {
+ uint8_t V = (uint8_t)Str[Pos];
+ if (!isprint(V) && !isspace(V)) return false;
+ if (V =='\\') {
+ // Handle '\\'
+ if (Pos + 1 <= R && (Str[Pos + 1] == '\\' || Str[Pos + 1] == '"')) {
+ U->push_back(Str[Pos + 1]);
+ Pos++;
+ continue;
+ }
+ // Handle '\xAB'
+ if (Pos + 3 <= R && Str[Pos + 1] == 'x'
+ && isxdigit(Str[Pos + 2]) && isxdigit(Str[Pos + 3])) {
+ char Hex[] = "0xAA";
+ Hex[2] = Str[Pos + 2];
+ Hex[3] = Str[Pos + 3];
+ U->push_back(strtol(Hex, nullptr, 16));
+ Pos += 3;
+ continue;
+ }
+ return false; // Invalid escape.
+ } else {
+ // Any other character.
+ U->push_back(V);
+ }
+ }
+ return true;
+}
+
+bool ParseDictionaryFile(const std::string &Text, std::vector<Unit> *Units) {
+ if (Text.empty()) {
+ Printf("ParseDictionaryFile: file does not exist or is empty\n");
+ return false;
+ }
+ std::istringstream ISS(Text);
+ Units->clear();
+ Unit U;
+ int LineNo = 0;
+ std::string S;
+ while (std::getline(ISS, S, '\n')) {
+ LineNo++;
+ size_t Pos = 0;
+ while (Pos < S.size() && isspace(S[Pos])) Pos++; // Skip spaces.
+ if (Pos == S.size()) continue; // Empty line.
+ if (S[Pos] == '#') continue; // Comment line.
+ if (ParseOneDictionaryEntry(S, &U)) {
+ Units->push_back(U);
+ } else {
+ Printf("ParseDictionaryFile: error in line %d\n\t\t%s\n", LineNo,
+ S.c_str());
+ return false;
+ }
+ }
+ return true;
+}
+
+std::string Base64(const Unit &U) {
+ static const char Table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789+/";
+ std::string Res;
+ size_t i;
+ for (i = 0; i + 2 < U.size(); i += 3) {
+ uint32_t x = (U[i] << 16) + (U[i + 1] << 8) + U[i + 2];
+ Res += Table[(x >> 18) & 63];
+ Res += Table[(x >> 12) & 63];
+ Res += Table[(x >> 6) & 63];
+ Res += Table[x & 63];
+ }
+ if (i + 1 == U.size()) {
+ uint32_t x = (U[i] << 16);
+ Res += Table[(x >> 18) & 63];
+ Res += Table[(x >> 12) & 63];
+ Res += "==";
+ } else if (i + 2 == U.size()) {
+ uint32_t x = (U[i] << 16) + (U[i + 1] << 8);
+ Res += Table[(x >> 18) & 63];
+ Res += Table[(x >> 12) & 63];
+ Res += Table[(x >> 6) & 63];
+ Res += "=";
+ }
+ return Res;
+}
+
+std::string DescribePC(const char *SymbolizedFMT, uintptr_t PC) {
+ if (!EF->__sanitizer_symbolize_pc) return "<can not symbolize>";
+ char PcDescr[1024];
+ EF->__sanitizer_symbolize_pc(reinterpret_cast<void*>(PC),
+ SymbolizedFMT, PcDescr, sizeof(PcDescr));
+ PcDescr[sizeof(PcDescr) - 1] = 0; // Just in case.
+ return PcDescr;
+}
+
+void PrintPC(const char *SymbolizedFMT, const char *FallbackFMT, uintptr_t PC) {
+ if (EF->__sanitizer_symbolize_pc)
+ Printf("%s", DescribePC(SymbolizedFMT, PC).c_str());
+ else
+ Printf(FallbackFMT, PC);
+}
+
+unsigned NumberOfCpuCores() {
+ unsigned N = std::thread::hardware_concurrency();
+ if (!N) {
+ Printf("WARNING: std::thread::hardware_concurrency not well defined for "
+ "your platform. Assuming CPU count of 1.\n");
+ N = 1;
+ }
+ return N;
+}
+
+bool ExecuteCommandAndReadOutput(const std::string &Command, std::string *Out) {
+ FILE *Pipe = OpenProcessPipe(Command.c_str(), "r");
+ if (!Pipe) return false;
+ char Buff[1024];
+ size_t N;
+ while ((N = fread(Buff, 1, sizeof(Buff), Pipe)) > 0)
+ Out->append(Buff, N);
+ return true;
+}
+
+} // namespace fuzzer
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerUtil.h b/json4cpp/tests/thirdparty/Fuzzer/FuzzerUtil.h
new file mode 100644
index 0000000000..08058c56e4
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerUtil.h
@@ -0,0 +1,72 @@
+//===- FuzzerUtil.h - Internal header for the Fuzzer Utils ------*- C++ -* ===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// Util functions.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_FUZZER_UTIL_H
+#define LLVM_FUZZER_UTIL_H
+
+#include "FuzzerDefs.h"
+
+namespace fuzzer {
+
+void PrintHexArray(const Unit &U, const char *PrintAfter = "");
+
+void PrintHexArray(const uint8_t *Data, size_t Size,
+ const char *PrintAfter = "");
+
+void PrintASCII(const uint8_t *Data, size_t Size, const char *PrintAfter = "");
+
+void PrintASCII(const Unit &U, const char *PrintAfter = "");
+
+// Changes U to contain only ASCII (isprint+isspace) characters.
+// Returns true iff U has been changed.
+bool ToASCII(uint8_t *Data, size_t Size);
+
+bool IsASCII(const Unit &U);
+
+bool IsASCII(const uint8_t *Data, size_t Size);
+
+std::string Base64(const Unit &U);
+
+void PrintPC(const char *SymbolizedFMT, const char *FallbackFMT, uintptr_t PC);
+
+std::string DescribePC(const char *SymbolizedFMT, uintptr_t PC);
+
+unsigned NumberOfCpuCores();
+
+bool ExecuteCommandAndReadOutput(const std::string &Command, std::string *Out);
+
+// Platform specific functions.
+void SetSignalHandler(const FuzzingOptions& Options);
+
+void SleepSeconds(int Seconds);
+
+unsigned long GetPid();
+
+size_t GetPeakRSSMb();
+
+int ExecuteCommand(const std::string &Command);
+
+FILE *OpenProcessPipe(const char *Command, const char *Mode);
+
+const void *SearchMemory(const void *haystack, size_t haystacklen,
+ const void *needle, size_t needlelen);
+
+std::string CloneArgsWithoutX(const std::vector<std::string> &Args,
+ const char *X1, const char *X2);
+
+inline std::string CloneArgsWithoutX(const std::vector<std::string> &Args,
+ const char *X) {
+ return CloneArgsWithoutX(Args, X, X);
+}
+
+} // namespace fuzzer
+
+#endif // LLVM_FUZZER_UTIL_H
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerUtilDarwin.cpp b/json4cpp/tests/thirdparty/Fuzzer/FuzzerUtilDarwin.cpp
new file mode 100644
index 0000000000..9674368c35
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerUtilDarwin.cpp
@@ -0,0 +1,152 @@
+//===- FuzzerUtilDarwin.cpp - Misc utils ----------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// Misc utils for Darwin.
+//===----------------------------------------------------------------------===//
+#include "FuzzerDefs.h"
+#if LIBFUZZER_APPLE
+
+#include "FuzzerIO.h"
+#include <mutex>
+#include <signal.h>
+#include <spawn.h>
+#include <sys/wait.h>
+
+// There is no header for this on macOS so declare here
+extern "C" char **environ;
+
+namespace fuzzer {
+
+static std::mutex SignalMutex;
+// Global variables used to keep track of how signal handling should be
+// restored. They should **not** be accessed without holding `SignalMutex`.
+static int ActiveThreadCount = 0;
+static struct sigaction OldSigIntAction;
+static struct sigaction OldSigQuitAction;
+static sigset_t OldBlockedSignalsSet;
+
+// This is a reimplementation of Libc's `system()`. On Darwin the Libc
+// implementation contains a mutex which prevents it from being used
+// concurrently. This implementation **can** be used concurrently. It sets the
+// signal handlers when the first thread enters and restores them when the last
+// thread finishes execution of the function and ensures this is not racey by
+// using a mutex.
+int ExecuteCommand(const std::string &Command) {
+ posix_spawnattr_t SpawnAttributes;
+ if (posix_spawnattr_init(&SpawnAttributes))
+ return -1;
+ // Block and ignore signals of the current process when the first thread
+ // enters.
+ {
+ std::lock_guard<std::mutex> Lock(SignalMutex);
+ if (ActiveThreadCount == 0) {
+ static struct sigaction IgnoreSignalAction;
+ sigset_t BlockedSignalsSet;
+ memset(&IgnoreSignalAction, 0, sizeof(IgnoreSignalAction));
+ IgnoreSignalAction.sa_handler = SIG_IGN;
+
+ if (sigaction(SIGINT, &IgnoreSignalAction, &OldSigIntAction) == -1) {
+ Printf("Failed to ignore SIGINT\n");
+ (void)posix_spawnattr_destroy(&SpawnAttributes);
+ return -1;
+ }
+ if (sigaction(SIGQUIT, &IgnoreSignalAction, &OldSigQuitAction) == -1) {
+ Printf("Failed to ignore SIGQUIT\n");
+ // Try our best to restore the signal handlers.
+ (void)sigaction(SIGINT, &OldSigIntAction, NULL);
+ (void)posix_spawnattr_destroy(&SpawnAttributes);
+ return -1;
+ }
+
+ (void)sigemptyset(&BlockedSignalsSet);
+ (void)sigaddset(&BlockedSignalsSet, SIGCHLD);
+ if (sigprocmask(SIG_BLOCK, &BlockedSignalsSet, &OldBlockedSignalsSet) ==
+ -1) {
+ Printf("Failed to block SIGCHLD\n");
+ // Try our best to restore the signal handlers.
+ (void)sigaction(SIGQUIT, &OldSigQuitAction, NULL);
+ (void)sigaction(SIGINT, &OldSigIntAction, NULL);
+ (void)posix_spawnattr_destroy(&SpawnAttributes);
+ return -1;
+ }
+ }
+ ++ActiveThreadCount;
+ }
+
+ // NOTE: Do not introduce any new `return` statements past this
+ // point. It is important that `ActiveThreadCount` always be decremented
+ // when leaving this function.
+
+ // Make sure the child process uses the default handlers for the
+ // following signals rather than inheriting what the parent has.
+ sigset_t DefaultSigSet;
+ (void)sigemptyset(&DefaultSigSet);
+ (void)sigaddset(&DefaultSigSet, SIGQUIT);
+ (void)sigaddset(&DefaultSigSet, SIGINT);
+ (void)posix_spawnattr_setsigdefault(&SpawnAttributes, &DefaultSigSet);
+ // Make sure the child process doesn't block SIGCHLD
+ (void)posix_spawnattr_setsigmask(&SpawnAttributes, &OldBlockedSignalsSet);
+ short SpawnFlags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK;
+ (void)posix_spawnattr_setflags(&SpawnAttributes, SpawnFlags);
+
+ pid_t Pid;
+ char **Environ = environ; // Read from global
+ const char *CommandCStr = Command.c_str();
+ const char *Argv[] = {"sh", "-c", CommandCStr, NULL};
+ int ErrorCode = 0, ProcessStatus = 0;
+ // FIXME: We probably shouldn't hardcode the shell path.
+ ErrorCode = posix_spawn(&Pid, "/bin/sh", NULL, &SpawnAttributes,
+ (char *const *)Argv, Environ);
+ (void)posix_spawnattr_destroy(&SpawnAttributes);
+ if (!ErrorCode) {
+ pid_t SavedPid = Pid;
+ do {
+ // Repeat until call completes uninterrupted.
+ Pid = waitpid(SavedPid, &ProcessStatus, /*options=*/0);
+ } while (Pid == -1 && errno == EINTR);
+ if (Pid == -1) {
+ // Fail for some other reason.
+ ProcessStatus = -1;
+ }
+ } else if (ErrorCode == ENOMEM || ErrorCode == EAGAIN) {
+ // Fork failure.
+ ProcessStatus = -1;
+ } else {
+ // Shell execution failure.
+ ProcessStatus = W_EXITCODE(127, 0);
+ }
+
+ // Restore the signal handlers of the current process when the last thread
+ // using this function finishes.
+ {
+ std::lock_guard<std::mutex> Lock(SignalMutex);
+ --ActiveThreadCount;
+ if (ActiveThreadCount == 0) {
+ bool FailedRestore = false;
+ if (sigaction(SIGINT, &OldSigIntAction, NULL) == -1) {
+ Printf("Failed to restore SIGINT handling\n");
+ FailedRestore = true;
+ }
+ if (sigaction(SIGQUIT, &OldSigQuitAction, NULL) == -1) {
+ Printf("Failed to restore SIGQUIT handling\n");
+ FailedRestore = true;
+ }
+ if (sigprocmask(SIG_BLOCK, &OldBlockedSignalsSet, NULL) == -1) {
+ Printf("Failed to unblock SIGCHLD\n");
+ FailedRestore = true;
+ }
+ if (FailedRestore)
+ ProcessStatus = -1;
+ }
+ }
+ return ProcessStatus;
+}
+
+} // namespace fuzzer
+
+#endif // LIBFUZZER_APPLE
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerUtilLinux.cpp b/json4cpp/tests/thirdparty/Fuzzer/FuzzerUtilLinux.cpp
new file mode 100644
index 0000000000..dfe7e6f4e1
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerUtilLinux.cpp
@@ -0,0 +1,24 @@
+//===- FuzzerUtilLinux.cpp - Misc utils for Linux. ------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// Misc utils for Linux.
+//===----------------------------------------------------------------------===//
+#include "FuzzerDefs.h"
+#if LIBFUZZER_LINUX
+
+#include <stdlib.h>
+
+namespace fuzzer {
+
+int ExecuteCommand(const std::string &Command) {
+ return system(Command.c_str());
+}
+
+} // namespace fuzzer
+
+#endif // LIBFUZZER_LINUX
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerUtilPosix.cpp b/json4cpp/tests/thirdparty/Fuzzer/FuzzerUtilPosix.cpp
new file mode 100644
index 0000000000..8b484b8eff
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerUtilPosix.cpp
@@ -0,0 +1,117 @@
+//===- FuzzerUtilPosix.cpp - Misc utils for Posix. ------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// Misc utils implementation using Posix API.
+//===----------------------------------------------------------------------===//
+#include "FuzzerDefs.h"
+#if LIBFUZZER_POSIX
+#include "FuzzerIO.h"
+#include "FuzzerInternal.h"
+#include <cassert>
+#include <chrono>
+#include <cstring>
+#include <errno.h>
+#include <iomanip>
+#include <signal.h>
+#include <sstream>
+#include <stdio.h>
+#include <sys/resource.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <thread>
+#include <unistd.h>
+
+namespace fuzzer {
+
+static void AlarmHandler(int, siginfo_t *, void *) {
+ Fuzzer::StaticAlarmCallback();
+}
+
+static void CrashHandler(int, siginfo_t *, void *) {
+ Fuzzer::StaticCrashSignalCallback();
+}
+
+static void InterruptHandler(int, siginfo_t *, void *) {
+ Fuzzer::StaticInterruptCallback();
+}
+
+static void SetSigaction(int signum,
+ void (*callback)(int, siginfo_t *, void *)) {
+ struct sigaction sigact;
+ memset(&sigact, 0, sizeof(sigact));
+ sigact.sa_sigaction = callback;
+ if (sigaction(signum, &sigact, 0)) {
+ Printf("libFuzzer: sigaction failed with %d\n", errno);
+ exit(1);
+ }
+}
+
+void SetTimer(int Seconds) {
+ struct itimerval T {
+ {Seconds, 0}, { Seconds, 0 }
+ };
+ if (setitimer(ITIMER_REAL, &T, nullptr)) {
+ Printf("libFuzzer: setitimer failed with %d\n", errno);
+ exit(1);
+ }
+ SetSigaction(SIGALRM, AlarmHandler);
+}
+
+void SetSignalHandler(const FuzzingOptions& Options) {
+ if (Options.UnitTimeoutSec > 0)
+ SetTimer(Options.UnitTimeoutSec / 2 + 1);
+ if (Options.HandleInt)
+ SetSigaction(SIGINT, InterruptHandler);
+ if (Options.HandleTerm)
+ SetSigaction(SIGTERM, InterruptHandler);
+ if (Options.HandleSegv)
+ SetSigaction(SIGSEGV, CrashHandler);
+ if (Options.HandleBus)
+ SetSigaction(SIGBUS, CrashHandler);
+ if (Options.HandleAbrt)
+ SetSigaction(SIGABRT, CrashHandler);
+ if (Options.HandleIll)
+ SetSigaction(SIGILL, CrashHandler);
+ if (Options.HandleFpe)
+ SetSigaction(SIGFPE, CrashHandler);
+}
+
+void SleepSeconds(int Seconds) {
+ sleep(Seconds); // Use C API to avoid coverage from instrumented libc++.
+}
+
+unsigned long GetPid() { return (unsigned long)getpid(); }
+
+size_t GetPeakRSSMb() {
+ struct rusage usage;
+ if (getrusage(RUSAGE_SELF, &usage))
+ return 0;
+ if (LIBFUZZER_LINUX) {
+ // ru_maxrss is in KiB
+ return usage.ru_maxrss >> 10;
+ } else if (LIBFUZZER_APPLE) {
+ // ru_maxrss is in bytes
+ return usage.ru_maxrss >> 20;
+ }
+ assert(0 && "GetPeakRSSMb() is not implemented for your platform");
+ return 0;
+}
+
+FILE *OpenProcessPipe(const char *Command, const char *Mode) {
+ return popen(Command, Mode);
+}
+
+const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt,
+ size_t PattLen) {
+ return memmem(Data, DataLen, Patt, PattLen);
+}
+
+} // namespace fuzzer
+
+#endif // LIBFUZZER_POSIX
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerUtilWindows.cpp b/json4cpp/tests/thirdparty/Fuzzer/FuzzerUtilWindows.cpp
new file mode 100644
index 0000000000..64adb7cd13
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerUtilWindows.cpp
@@ -0,0 +1,182 @@
+//===- FuzzerUtilWindows.cpp - Misc utils for Windows. --------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// Misc utils implementation for Windows.
+//===----------------------------------------------------------------------===//
+#include "FuzzerDefs.h"
+#if LIBFUZZER_WINDOWS
+#include "FuzzerIO.h"
+#include "FuzzerInternal.h"
+#include <cassert>
+#include <chrono>
+#include <cstring>
+#include <errno.h>
+#include <iomanip>
+#include <signal.h>
+#include <sstream>
+#include <stdio.h>
+#include <sys/types.h>
+#include <windows.h>
+#include <Psapi.h>
+
+namespace fuzzer {
+
+static const FuzzingOptions* HandlerOpt = nullptr;
+
+LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) {
+ switch (ExceptionInfo->ExceptionRecord->ExceptionCode) {
+ case EXCEPTION_ACCESS_VIOLATION:
+ case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
+ case EXCEPTION_STACK_OVERFLOW:
+ if (HandlerOpt->HandleSegv)
+ Fuzzer::StaticCrashSignalCallback();
+ break;
+ case EXCEPTION_DATATYPE_MISALIGNMENT:
+ case EXCEPTION_IN_PAGE_ERROR:
+ if (HandlerOpt->HandleBus)
+ Fuzzer::StaticCrashSignalCallback();
+ break;
+ case EXCEPTION_ILLEGAL_INSTRUCTION:
+ case EXCEPTION_PRIV_INSTRUCTION:
+ if (HandlerOpt->HandleIll)
+ Fuzzer::StaticCrashSignalCallback();
+ break;
+ case EXCEPTION_FLT_DENORMAL_OPERAND:
+ case EXCEPTION_FLT_DIVIDE_BY_ZERO:
+ case EXCEPTION_FLT_INEXACT_RESULT:
+ case EXCEPTION_FLT_INVALID_OPERATION:
+ case EXCEPTION_FLT_OVERFLOW:
+ case EXCEPTION_FLT_STACK_CHECK:
+ case EXCEPTION_FLT_UNDERFLOW:
+ case EXCEPTION_INT_DIVIDE_BY_ZERO:
+ case EXCEPTION_INT_OVERFLOW:
+ if (HandlerOpt->HandleFpe)
+ Fuzzer::StaticCrashSignalCallback();
+ break;
+ }
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
+BOOL WINAPI CtrlHandler(DWORD dwCtrlType) {
+ switch (dwCtrlType) {
+ case CTRL_C_EVENT:
+ if (HandlerOpt->HandleInt)
+ Fuzzer::StaticInterruptCallback();
+ return TRUE;
+ case CTRL_BREAK_EVENT:
+ if (HandlerOpt->HandleTerm)
+ Fuzzer::StaticInterruptCallback();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void CALLBACK AlarmHandler(PVOID, BOOLEAN) {
+ Fuzzer::StaticAlarmCallback();
+}
+
+class TimerQ {
+ HANDLE TimerQueue;
+ public:
+ TimerQ() : TimerQueue(NULL) {};
+ ~TimerQ() {
+ if (TimerQueue)
+ DeleteTimerQueueEx(TimerQueue, NULL);
+ };
+ void SetTimer(int Seconds) {
+ if (!TimerQueue) {
+ TimerQueue = CreateTimerQueue();
+ if (!TimerQueue) {
+ Printf("libFuzzer: CreateTimerQueue failed.\n");
+ exit(1);
+ }
+ }
+ HANDLE Timer;
+ if (!CreateTimerQueueTimer(&Timer, TimerQueue, AlarmHandler, NULL,
+ Seconds*1000, Seconds*1000, 0)) {
+ Printf("libFuzzer: CreateTimerQueueTimer failed.\n");
+ exit(1);
+ }
+ };
+};
+
+static TimerQ Timer;
+
+static void CrashHandler(int) { Fuzzer::StaticCrashSignalCallback(); }
+
+void SetSignalHandler(const FuzzingOptions& Options) {
+ HandlerOpt = &Options;
+
+ if (Options.UnitTimeoutSec > 0)
+ Timer.SetTimer(Options.UnitTimeoutSec / 2 + 1);
+
+ if (Options.HandleInt || Options.HandleTerm)
+ if (!SetConsoleCtrlHandler(CtrlHandler, TRUE)) {
+ DWORD LastError = GetLastError();
+ Printf("libFuzzer: SetConsoleCtrlHandler failed (Error code: %lu).\n",
+ LastError);
+ exit(1);
+ }
+
+ if (Options.HandleSegv || Options.HandleBus || Options.HandleIll ||
+ Options.HandleFpe)
+ if (!AddVectoredExceptionHandler(1, ExceptionHandler)) {
+ Printf("libFuzzer: AddVectoredExceptionHandler failed.\n");
+ exit(1);
+ }
+
+ if (Options.HandleAbrt)
+ if (SIG_ERR == signal(SIGABRT, CrashHandler)) {
+ Printf("libFuzzer: signal failed with %d\n", errno);
+ exit(1);
+ }
+}
+
+void SleepSeconds(int Seconds) { Sleep(Seconds * 1000); }
+
+unsigned long GetPid() { return GetCurrentProcessId(); }
+
+size_t GetPeakRSSMb() {
+ PROCESS_MEMORY_COUNTERS info;
+ if (!GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info)))
+ return 0;
+ return info.PeakWorkingSetSize >> 20;
+}
+
+FILE *OpenProcessPipe(const char *Command, const char *Mode) {
+ return _popen(Command, Mode);
+}
+
+int ExecuteCommand(const std::string &Command) {
+ return system(Command.c_str());
+}
+
+const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt,
+ size_t PattLen) {
+ // TODO: make this implementation more efficient.
+ const char *Cdata = (const char *)Data;
+ const char *Cpatt = (const char *)Patt;
+
+ if (!Data || !Patt || DataLen == 0 || PattLen == 0 || DataLen < PattLen)
+ return NULL;
+
+ if (PattLen == 1)
+ return memchr(Data, *Cpatt, DataLen);
+
+ const char *End = Cdata + DataLen - PattLen + 1;
+
+ for (const char *It = Cdata; It < End; ++It)
+ if (It[0] == Cpatt[0] && memcmp(It, Cpatt, PattLen) == 0)
+ return It;
+
+ return NULL;
+}
+
+} // namespace fuzzer
+
+#endif // LIBFUZZER_WINDOWS
diff --git a/json4cpp/tests/thirdparty/Fuzzer/FuzzerValueBitMap.h b/json4cpp/tests/thirdparty/Fuzzer/FuzzerValueBitMap.h
new file mode 100644
index 0000000000..0692acd13e
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/FuzzerValueBitMap.h
@@ -0,0 +1,87 @@
+//===- FuzzerValueBitMap.h - INTERNAL - Bit map -----------------*- C++ -* ===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// ValueBitMap.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_FUZZER_VALUE_BIT_MAP_H
+#define LLVM_FUZZER_VALUE_BIT_MAP_H
+
+#include "FuzzerDefs.h"
+
+namespace fuzzer {
+
+// A bit map containing kMapSizeInWords bits.
+struct ValueBitMap {
+ static const size_t kMapSizeInBits = 65371; // Prime.
+ static const size_t kMapSizeInBitsAligned = 65536; // 2^16
+ static const size_t kBitsInWord = (sizeof(uintptr_t) * 8);
+ static const size_t kMapSizeInWords = kMapSizeInBitsAligned / kBitsInWord;
+ public:
+ static const size_t kNumberOfItems = kMapSizeInBits;
+ // Clears all bits.
+ void Reset() { memset(Map, 0, sizeof(Map)); }
+
+ // Computes a hash function of Value and sets the corresponding bit.
+ // Returns true if the bit was changed from 0 to 1.
+ inline bool AddValue(uintptr_t Value) {
+ uintptr_t Idx = Value < kMapSizeInBits ? Value : Value % kMapSizeInBits;
+ uintptr_t WordIdx = Idx / kBitsInWord;
+ uintptr_t BitIdx = Idx % kBitsInWord;
+ uintptr_t Old = Map[WordIdx];
+ uintptr_t New = Old | (1UL << BitIdx);
+ Map[WordIdx] = New;
+ return New != Old;
+ }
+
+ inline bool Get(uintptr_t Idx) {
+ assert(Idx < kMapSizeInBits);
+ uintptr_t WordIdx = Idx / kBitsInWord;
+ uintptr_t BitIdx = Idx % kBitsInWord;
+ return Map[WordIdx] & (1UL << BitIdx);
+ }
+
+ size_t GetNumBitsSinceLastMerge() const { return NumBits; }
+
+ // Merges 'Other' into 'this', clears 'Other', updates NumBits,
+ // returns true if new bits were added.
+ ATTRIBUTE_TARGET_POPCNT
+ bool MergeFrom(ValueBitMap &Other) {
+ uintptr_t Res = 0;
+ size_t OldNumBits = NumBits;
+ for (size_t i = 0; i < kMapSizeInWords; i++) {
+ auto O = Other.Map[i];
+ auto M = Map[i];
+ if (O) {
+ Map[i] = (M |= O);
+ Other.Map[i] = 0;
+ }
+ if (M)
+ Res += __builtin_popcountl(M);
+ }
+ NumBits = Res;
+ return OldNumBits < NumBits;
+ }
+
+ template <class Callback>
+ void ForEach(Callback CB) {
+ for (size_t i = 0; i < kMapSizeInWords; i++)
+ if (uintptr_t M = Map[i])
+ for (size_t j = 0; j < sizeof(M) * 8; j++)
+ if (M & ((uintptr_t)1 << j))
+ CB(i * sizeof(M) * 8 + j);
+ }
+
+ private:
+ size_t NumBits = 0;
+ uintptr_t Map[kMapSizeInWords] __attribute__((aligned(512)));
+};
+
+} // namespace fuzzer
+
+#endif // LLVM_FUZZER_VALUE_BIT_MAP_H
diff --git a/json4cpp/tests/thirdparty/Fuzzer/README.txt b/json4cpp/tests/thirdparty/Fuzzer/README.txt
new file mode 100644
index 0000000000..79f49b5509
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/README.txt
@@ -0,0 +1,2 @@
+Move to http://llvm.org/docs/LibFuzzer.html
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/afl/afl_driver.cpp b/json4cpp/tests/thirdparty/Fuzzer/afl/afl_driver.cpp
new file mode 100644
index 0000000000..57ee56109d
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/afl/afl_driver.cpp
@@ -0,0 +1,295 @@
+//===- afl_driver.cpp - a glue between AFL and libFuzzer --------*- C++ -* ===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//===----------------------------------------------------------------------===//
+
+/* This file allows to fuzz libFuzzer-style target functions
+ (LLVMFuzzerTestOneInput) with AFL using AFL's persistent (in-process) mode.
+
+Usage:
+################################################################################
+cat << EOF > test_fuzzer.cc
+#include <stdint.h>
+#include <stddef.h>
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (size > 0 && data[0] == 'H')
+ if (size > 1 && data[1] == 'I')
+ if (size > 2 && data[2] == '!')
+ __builtin_trap();
+ return 0;
+}
+EOF
+# Build your target with -fsanitize-coverage=trace-pc using fresh clang.
+clang -g -fsanitize-coverage=trace-pc test_fuzzer.cc -c
+# Build afl-llvm-rt.o.c from the AFL distribution.
+clang -c -w $AFL_HOME/llvm_mode/afl-llvm-rt.o.c
+# Build this file, link it with afl-llvm-rt.o.o and the target code.
+clang++ afl_driver.cpp test_fuzzer.o afl-llvm-rt.o.o
+# Run AFL:
+rm -rf IN OUT; mkdir IN OUT; echo z > IN/z;
+$AFL_HOME/afl-fuzz -i IN -o OUT ./a.out
+################################################################################
+Environment Variables:
+There are a few environment variables that can be set to use features that
+afl-fuzz doesn't have.
+
+AFL_DRIVER_STDERR_DUPLICATE_FILENAME: Setting this *appends* stderr to the file
+specified. If the file does not exist, it is created. This is useful for getting
+stack traces (when using ASAN for example) or original error messages on hard to
+reproduce bugs.
+
+AFL_DRIVER_EXTRA_STATS_FILENAME: Setting this causes afl_driver to write extra
+statistics to the file specified. Currently these are peak_rss_mb
+(the peak amount of virtual memory used in MB) and slowest_unit_time_secs. If
+the file does not exist it is created. If the file does exist then
+afl_driver assumes it was restarted by afl-fuzz and will try to read old
+statistics from the file. If that fails then the process will quit.
+
+*/
+#include <assert.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+// Platform detection. Copied from FuzzerInternal.h
+#ifdef __linux__
+#define LIBFUZZER_LINUX 1
+#define LIBFUZZER_APPLE 0
+#elif __APPLE__
+#define LIBFUZZER_LINUX 0
+#define LIBFUZZER_APPLE 1
+#else
+#error "Support for your platform has not been implemented"
+#endif
+
+// Used to avoid repeating error checking boilerplate. If cond is false, a
+// fatal error has occurred in the program. In this event print error_message
+// to stderr and abort(). Otherwise do nothing. Note that setting
+// AFL_DRIVER_STDERR_DUPLICATE_FILENAME may cause error_message to be appended
+// to the file as well, if the error occurs after the duplication is performed.
+#define CHECK_ERROR(cond, error_message) \
+ if (!(cond)) { \
+ fprintf(stderr, (error_message)); \
+ abort(); \
+ }
+
+// libFuzzer interface is thin, so we don't include any libFuzzer headers.
+extern "C" {
+int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
+__attribute__((weak)) int LLVMFuzzerInitialize(int *argc, char ***argv);
+}
+
+// Notify AFL about persistent mode.
+static volatile char AFL_PERSISTENT[] = "##SIG_AFL_PERSISTENT##";
+extern "C" int __afl_persistent_loop(unsigned int);
+static volatile char suppress_warning2 = AFL_PERSISTENT[0];
+
+// Notify AFL about deferred forkserver.
+static volatile char AFL_DEFER_FORKSVR[] = "##SIG_AFL_DEFER_FORKSRV##";
+extern "C" void __afl_manual_init();
+static volatile char suppress_warning1 = AFL_DEFER_FORKSVR[0];
+
+// Input buffer.
+static const size_t kMaxAflInputSize = 1 << 20;
+static uint8_t AflInputBuf[kMaxAflInputSize];
+
+// Variables we need for writing to the extra stats file.
+static FILE *extra_stats_file = NULL;
+static uint32_t previous_peak_rss = 0;
+static time_t slowest_unit_time_secs = 0;
+static const int kNumExtraStats = 2;
+static const char *kExtraStatsFormatString = "peak_rss_mb : %u\n"
+ "slowest_unit_time_sec : %u\n";
+
+// Copied from FuzzerUtil.cpp.
+size_t GetPeakRSSMb() {
+ struct rusage usage;
+ if (getrusage(RUSAGE_SELF, &usage))
+ return 0;
+ if (LIBFUZZER_LINUX) {
+ // ru_maxrss is in KiB
+ return usage.ru_maxrss >> 10;
+ } else if (LIBFUZZER_APPLE) {
+ // ru_maxrss is in bytes
+ return usage.ru_maxrss >> 20;
+ }
+ assert(0 && "GetPeakRSSMb() is not implemented for your platform");
+ return 0;
+}
+
+// Based on SetSigaction in FuzzerUtil.cpp
+static void SetSigaction(int signum,
+ void (*callback)(int, siginfo_t *, void *)) {
+ struct sigaction sigact;
+ memset(&sigact, 0, sizeof(sigact));
+ sigact.sa_sigaction = callback;
+ if (sigaction(signum, &sigact, 0)) {
+ fprintf(stderr, "libFuzzer: sigaction failed with %d\n", errno);
+ exit(1);
+ }
+}
+
+// Write extra stats to the file specified by the user. If none is specified
+// this function will never be called.
+static void write_extra_stats() {
+ uint32_t peak_rss = GetPeakRSSMb();
+
+ if (peak_rss < previous_peak_rss)
+ peak_rss = previous_peak_rss;
+
+ int chars_printed = fprintf(extra_stats_file, kExtraStatsFormatString,
+ peak_rss, slowest_unit_time_secs);
+
+ CHECK_ERROR(chars_printed != 0, "Failed to write extra_stats_file");
+
+ CHECK_ERROR(fclose(extra_stats_file) == 0,
+ "Failed to close extra_stats_file");
+}
+
+// Call write_extra_stats before we exit.
+static void crash_handler(int, siginfo_t *, void *) {
+ // Make sure we don't try calling write_extra_stats again if we crashed while
+ // trying to call it.
+ static bool first_crash = true;
+ CHECK_ERROR(first_crash,
+ "Crashed in crash signal handler. This is a bug in the fuzzer.");
+
+ first_crash = false;
+ write_extra_stats();
+}
+
+// If the user has specified an extra_stats_file through the environment
+// variable AFL_DRIVER_EXTRA_STATS_FILENAME, then perform necessary set up
+// to write stats to it on exit. If no file is specified, do nothing. Otherwise
+// install signal and exit handlers to write to the file when the process exits.
+// Then if the file doesn't exist create it and set extra stats to 0. But if it
+// does exist then read the initial values of the extra stats from the file
+// and check that the file is writable.
+static void maybe_initialize_extra_stats() {
+ // If AFL_DRIVER_EXTRA_STATS_FILENAME isn't set then we have nothing to do.
+ char *extra_stats_filename = getenv("AFL_DRIVER_EXTRA_STATS_FILENAME");
+ if (!extra_stats_filename)
+ return;
+
+ // Open the file and find the previous peak_rss_mb value.
+ // This is necessary because the fuzzing process is restarted after N
+ // iterations are completed. So we may need to get this value from a previous
+ // process to be accurate.
+ extra_stats_file = fopen(extra_stats_filename, "r");
+
+ // If extra_stats_file already exists: read old stats from it.
+ if (extra_stats_file) {
+ int matches = fscanf(extra_stats_file, kExtraStatsFormatString,
+ &previous_peak_rss, &slowest_unit_time_secs);
+
+ // Make sure we have read a real extra stats file and that we have used it
+ // to set slowest_unit_time_secs and previous_peak_rss.
+ CHECK_ERROR(matches == kNumExtraStats, "Extra stats file is corrupt");
+
+ CHECK_ERROR(fclose(extra_stats_file) == 0, "Failed to close file");
+
+ // Now open the file for writing.
+ extra_stats_file = fopen(extra_stats_filename, "w");
+ CHECK_ERROR(extra_stats_file,
+ "Failed to open extra stats file for writing");
+ } else {
+ // Looks like this is the first time in a fuzzing job this is being called.
+ extra_stats_file = fopen(extra_stats_filename, "w+");
+ CHECK_ERROR(extra_stats_file, "failed to create extra stats file");
+ }
+
+ // Make sure that crash_handler gets called on any kind of fatal error.
+ int crash_signals[] = {SIGSEGV, SIGBUS, SIGABRT, SIGILL, SIGFPE, SIGINT,
+ SIGTERM};
+
+ const size_t num_signals = sizeof(crash_signals) / sizeof(crash_signals[0]);
+
+ for (size_t idx = 0; idx < num_signals; idx++)
+ SetSigaction(crash_signals[idx], crash_handler);
+
+ // Make sure it gets called on other kinds of exits.
+ atexit(write_extra_stats);
+}
+
+// If the user asks us to duplicate stderr, then do it.
+static void maybe_duplicate_stderr() {
+ char* stderr_duplicate_filename =
+ getenv("AFL_DRIVER_STDERR_DUPLICATE_FILENAME");
+
+ if (!stderr_duplicate_filename)
+ return;
+
+ FILE* stderr_duplicate_stream =
+ freopen(stderr_duplicate_filename, "a+", stderr);
+
+ if (!stderr_duplicate_stream) {
+ fprintf(
+ stderr,
+ "Failed to duplicate stderr to AFL_DRIVER_STDERR_DUPLICATE_FILENAME");
+ abort();
+ }
+}
+
+int main(int argc, char **argv) {
+ fprintf(stderr, "======================= INFO =========================\n"
+ "This binary is built for AFL-fuzz.\n"
+ "To run the target function on a single input execute this:\n"
+ " %s < INPUT_FILE\n"
+ "To run the fuzzing execute this:\n"
+ " afl-fuzz [afl-flags] %s [N] "
+ "-- run N fuzzing iterations before "
+ "re-spawning the process (default: 1000)\n"
+ "======================================================\n",
+ argv[0], argv[0]);
+ if (LLVMFuzzerInitialize)
+ LLVMFuzzerInitialize(&argc, &argv);
+ // Do any other expensive one-time initialization here.
+
+ maybe_duplicate_stderr();
+ maybe_initialize_extra_stats();
+
+ __afl_manual_init();
+
+ int N = 1000;
+ if (argc >= 2)
+ N = atoi(argv[1]);
+ assert(N > 0);
+ time_t unit_time_secs;
+ int num_runs = 0;
+ while (__afl_persistent_loop(N)) {
+ ssize_t n_read = read(0, AflInputBuf, kMaxAflInputSize);
+ if (n_read > 0) {
+ // Copy AflInputBuf into a separate buffer to let asan find buffer
+ // overflows. Don't use unique_ptr/etc to avoid extra dependencies.
+ uint8_t *copy = new uint8_t[n_read];
+ memcpy(copy, AflInputBuf, n_read);
+
+ struct timeval unit_start_time;
+ CHECK_ERROR(gettimeofday(&unit_start_time, NULL) == 0,
+ "Calling gettimeofday failed");
+
+ num_runs++;
+ LLVMFuzzerTestOneInput(copy, n_read);
+
+ struct timeval unit_stop_time;
+ CHECK_ERROR(gettimeofday(&unit_stop_time, NULL) == 0,
+ "Calling gettimeofday failed");
+
+ // Update slowest_unit_time_secs if we see a new max.
+ unit_time_secs = unit_stop_time.tv_sec - unit_start_time.tv_sec;
+ if (slowest_unit_time_secs < unit_time_secs)
+ slowest_unit_time_secs = unit_time_secs;
+
+ delete[] copy;
+ }
+ }
+ fprintf(stderr, "%s: successfully executed %d input(s)\n", argv[0], num_runs);
+}
diff --git a/json4cpp/tests/thirdparty/Fuzzer/build.sh b/json4cpp/tests/thirdparty/Fuzzer/build.sh
new file mode 100755
index 0000000000..27c148ad43
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/build.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+LIBFUZZER_SRC_DIR=$(dirname $0)
+for f in $LIBFUZZER_SRC_DIR/*.cpp; do
+ clang -g -O2 -fno-omit-frame-pointer -std=c++11 $f -c &
+done
+wait
+rm -f libFuzzer.a
+ar ru libFuzzer.a Fuzzer*.o
+rm -f Fuzzer*.o
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/cxx.dict b/json4cpp/tests/thirdparty/Fuzzer/cxx.dict
new file mode 100644
index 0000000000..41350f4755
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/cxx.dict
@@ -0,0 +1,122 @@
+"++"
+"--"
+"<<"
+">>"
+"+="
+"-="
+"*="
+"/="
+">>="
+"<<="
+"&="
+"|="
+"^="
+"%="
+"!="
+"&&"
+"||"
+"=="
+">="
+"<="
+"->"
+"alignas"
+"alignof"
+"and"
+"and_eq"
+"asm"
+"auto"
+"bitand"
+"bitor"
+"bool"
+"break"
+"case"
+"catch"
+"char"
+"char16_t"
+"char32_t"
+"class"
+"compl"
+"concept"
+"const"
+"constexpr"
+"const_cast"
+"continue"
+"decltype"
+"default"
+"delete"
+"do"
+"double"
+"dynamic_cast"
+"else"
+"enum"
+"explicit"
+"export"
+"extern"
+"false"
+"float"
+"for"
+"friend"
+"goto"
+"if"
+"inline"
+"int"
+"long"
+"mutable"
+"namespace"
+"new"
+"noexcept"
+"not"
+"not_eq"
+"nullptr"
+"operator"
+"or"
+"or_eq"
+"private"
+"protected"
+"public"
+"register"
+"reinterpret_cast"
+"requires"
+"return"
+"short"
+"signed"
+"sizeof"
+"static"
+"static_assert"
+"static_cast"
+"struct"
+"switch"
+"template"
+"this"
+"thread_local"
+"throw"
+"true"
+"try"
+"typedef"
+"typeid"
+"typename"
+"union"
+"unsigned"
+"using"
+"virtual"
+"void"
+"volatile"
+"wchar_t"
+"while"
+"xor"
+"xor_eq"
+"if"
+"elif"
+"else"
+"endif"
+"defined"
+"ifdef"
+"ifndef"
+"define"
+"undef"
+"include"
+"line"
+"error"
+"pragma"
+"override"
+"final"
diff --git a/json4cpp/tests/thirdparty/Fuzzer/standalone/StandaloneFuzzTargetMain.c b/json4cpp/tests/thirdparty/Fuzzer/standalone/StandaloneFuzzTargetMain.c
new file mode 100644
index 0000000000..0d76ea49e7
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/standalone/StandaloneFuzzTargetMain.c
@@ -0,0 +1,41 @@
+/*===- StandaloneFuzzTargetMain.c - standalone main() for fuzz targets. ---===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// This main() function can be linked to a fuzz target (i.e. a library
+// that exports LLVMFuzzerTestOneInput() and possibly LLVMFuzzerInitialize())
+// instead of libFuzzer. This main() function will not perform any fuzzing
+// but will simply feed all input files one by one to the fuzz target.
+//
+// Use this file to provide reproducers for bugs when linking against libFuzzer
+// or other fuzzing engine is undesirable.
+//===----------------------------------------------------------------------===*/
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+extern int LLVMFuzzerTestOneInput(const unsigned char *data, size_t size);
+__attribute__((weak)) extern int LLVMFuzzerInitialize(int *argc, char ***argv);
+int main(int argc, char **argv) {
+ fprintf(stderr, "StandaloneFuzzTargetMain: running %d inputs\n", argc - 1);
+ if (LLVMFuzzerInitialize)
+ LLVMFuzzerInitialize(&argc, &argv);
+ for (int i = 1; i < argc; i++) {
+ fprintf(stderr, "Running: %s\n", argv[i]);
+ FILE *f = fopen(argv[i], "r");
+ assert(f);
+ fseek(f, 0, SEEK_END);
+ size_t len = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ unsigned char *buf = (unsigned char*)malloc(len);
+ size_t n_read = fread(buf, 1, len, f);
+ assert(n_read == len);
+ LLVMFuzzerTestOneInput(buf, len);
+ free(buf);
+ fprintf(stderr, "Done: %s: (%zd bytes)\n", argv[i], n_read);
+ }
+}
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/AFLDriverTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/AFLDriverTest.cpp
new file mode 100644
index 0000000000..3dd0b61173
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/AFLDriverTest.cpp
@@ -0,0 +1,22 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Contains dummy functions used to avoid dependency on AFL.
+#include <stdint.h>
+#include <stdlib.h>
+
+extern "C" void __afl_manual_init() {}
+
+extern "C" int __afl_persistent_loop(unsigned int) {
+ return 0;
+}
+
+// This declaration exists to prevent the Darwin linker
+// from complaining about this being a missing weak symbol.
+extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) {
+ return 0;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ return 0;
+}
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/AbsNegAndConstant64Test.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/AbsNegAndConstant64Test.cpp
new file mode 100644
index 0000000000..577481431a
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/AbsNegAndConstant64Test.cpp
@@ -0,0 +1,23 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// abs(x) < 0 and y == Const puzzle, 64-bit variant.
+#include <cstring>
+#include <cstdint>
+#include <cstdlib>
+#include <cstddef>
+#include <cstdio>
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ if (Size < 16) return 0;
+ int64_t x;
+ uint64_t y;
+ memcpy(&x, Data, sizeof(x));
+ memcpy(&y, Data + sizeof(x), sizeof(y));
+ if (labs(x) < 0 && y == 0xbaddcafedeadbeefUL) {
+ printf("BINGO; Found the target, exiting; x = 0x%lx y 0x%lx\n", x, y);
+ exit(1);
+ }
+ return 0;
+}
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/AbsNegAndConstantTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/AbsNegAndConstantTest.cpp
new file mode 100644
index 0000000000..69075a454c
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/AbsNegAndConstantTest.cpp
@@ -0,0 +1,23 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// abs(x) < 0 and y == Const puzzle.
+#include <cstring>
+#include <cstdint>
+#include <cstdlib>
+#include <cstddef>
+#include <cstdio>
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ if (Size < 8) return 0;
+ int x;
+ unsigned y;
+ memcpy(&x, Data, sizeof(x));
+ memcpy(&y, Data + sizeof(x), sizeof(y));
+ if (abs(x) < 0 && y == 0xbaddcafe) {
+ printf("BINGO; Found the target, exiting; x = 0x%x y 0x%x\n", x, y);
+ exit(1);
+ }
+ return 0;
+}
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/AccumulateAllocationsTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/AccumulateAllocationsTest.cpp
new file mode 100644
index 0000000000..604d8fa299
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/AccumulateAllocationsTest.cpp
@@ -0,0 +1,17 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Test with a more mallocs than frees, but no leak.
+#include <cstdint>
+#include <cstddef>
+
+const int kAllocatedPointersSize = 10000;
+int NumAllocatedPointers = 0;
+int *AllocatedPointers[kAllocatedPointersSize];
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ if (NumAllocatedPointers < kAllocatedPointersSize)
+ AllocatedPointers[NumAllocatedPointers++] = new int;
+ return 0;
+}
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/BufferOverflowOnInput.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/BufferOverflowOnInput.cpp
new file mode 100644
index 0000000000..b9d14052ae
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/BufferOverflowOnInput.cpp
@@ -0,0 +1,23 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Simple test for a fuzzer. The fuzzer must find the string "Hi!".
+#include <assert.h>
+#include <cstdint>
+#include <cstdlib>
+#include <cstddef>
+#include <iostream>
+
+static volatile bool SeedLargeBuffer;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ assert(Data);
+ if (Size >= 4)
+ SeedLargeBuffer = true;
+ if (Size == 3 && SeedLargeBuffer && Data[3]) {
+ std::cout << "Woops, reading Data[3] w/o crashing\n";
+ exit(1);
+ }
+ return 0;
+}
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/CMakeLists.txt b/json4cpp/tests/thirdparty/Fuzzer/test/CMakeLists.txt
new file mode 100644
index 0000000000..c0457746a0
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/CMakeLists.txt
@@ -0,0 +1,217 @@
+# Build all these tests with -O0, otherwise optimizations may merge some
+# basic blocks and we'll fail to discover the targets.
+# We change the flags for every build type because we might be doing
+# a multi-configuration build (e.g. Xcode) where CMAKE_BUILD_TYPE doesn't
+# mean anything.
+set(variables_to_filter
+ CMAKE_CXX_FLAGS_RELEASE
+ CMAKE_CXX_FLAGS_DEBUG
+ CMAKE_CXX_FLAGS_RELWITHDEBINFO
+ CMAKE_CXX_FLAGS_MINSIZEREL
+ LIBFUZZER_FLAGS_BASE
+ )
+foreach (VARNAME ${variables_to_filter})
+ string(REPLACE " " ";" BUILD_FLAGS_AS_LIST "${${VARNAME}}")
+ set(new_flags "")
+ foreach (flag ${BUILD_FLAGS_AS_LIST})
+ # NOTE: Use of XX here is to avoid a CMake warning due to CMP0054
+ if (NOT ("XX${flag}" MATCHES "XX-O[0123s]"))
+ set(new_flags "${new_flags} ${flag}")
+ else()
+ set(new_flags "${new_flags} -O0")
+ endif()
+ endforeach()
+ set(${VARNAME} "${new_flags}")
+endforeach()
+
+# Enable the coverage instrumentation (it is disabled for the Fuzzer lib).
+set(CMAKE_CXX_FLAGS "${LIBFUZZER_FLAGS_BASE} -fsanitize-coverage=trace-pc-guard,indirect-calls,trace-cmp,trace-div,trace-gep -g")
+
+# add_libfuzzer_test(<name>
+# SOURCES source0.cpp [source1.cpp ...]
+# )
+#
+# Declares a LibFuzzer test executable with target name LLVMFuzzer-<name>.
+#
+# One or more source files to be compiled into the binary must be declared
+# after the SOURCES keyword.
+function(add_libfuzzer_test name)
+ set(multi_arg_options "SOURCES")
+ cmake_parse_arguments(
+ "add_libfuzzer_test" "" "" "${multi_arg_options}" ${ARGN})
+ if ("${add_libfuzzer_test_SOURCES}" STREQUAL "")
+ message(FATAL_ERROR "Source files must be specified")
+ endif()
+ add_executable(LLVMFuzzer-${name}
+ ${add_libfuzzer_test_SOURCES}
+ )
+ target_link_libraries(LLVMFuzzer-${name} LLVMFuzzer)
+ # Place binary where llvm-lit expects to find it
+ set_target_properties(LLVMFuzzer-${name}
+ PROPERTIES RUNTIME_OUTPUT_DIRECTORY
+ "${CMAKE_BINARY_DIR}/lib/Fuzzer/test"
+ )
+ set(TestBinaries ${TestBinaries} LLVMFuzzer-${name} PARENT_SCOPE)
+endfunction()
+
+# Variable to keep track of all test targets
+set(TestBinaries)
+
+###############################################################################
+# Basic tests
+###############################################################################
+
+set(Tests
+ AbsNegAndConstantTest
+ AbsNegAndConstant64Test
+ AccumulateAllocationsTest
+ BufferOverflowOnInput
+ CallerCalleeTest
+ CounterTest
+ CustomCrossOverTest
+ CustomMutatorTest
+ DivTest
+ EmptyTest
+ FourIndependentBranchesTest
+ FullCoverageSetTest
+ InitializeTest
+ MemcmpTest
+ LeakTest
+ LeakTimeoutTest
+ LoadTest
+ NullDerefTest
+ NullDerefOnEmptyTest
+ NthRunCrashTest
+ OneHugeAllocTest
+ OutOfMemoryTest
+ OutOfMemorySingleLargeMallocTest
+ RepeatedMemcmp
+ RepeatedBytesTest
+ SimpleCmpTest
+ SimpleDictionaryTest
+ SimpleHashTest
+ SimpleTest
+ SimpleThreadedTest
+ SingleMemcmpTest
+ SingleStrcmpTest
+ SingleStrncmpTest
+ SpamyTest
+ ShrinkControlFlowTest
+ ShrinkValueProfileTest
+ StrcmpTest
+ StrncmpOOBTest
+ StrncmpTest
+ StrstrTest
+ SwapCmpTest
+ SwitchTest
+ Switch2Test
+ ThreadedLeakTest
+ ThreadedTest
+ TimeoutTest
+ TimeoutEmptyTest
+ TraceMallocTest
+ )
+
+if(APPLE)
+ # LeakSanitizer is not supported on OSX right now
+ set(HAS_LSAN 0)
+ message(WARNING "LeakSanitizer is not supported on Apple platforms."
+ " Building and running LibFuzzer LeakSanitizer tests is disabled."
+ )
+else()
+ set(HAS_LSAN 1)
+endif()
+
+foreach(Test ${Tests})
+ add_libfuzzer_test(${Test} SOURCES ${Test}.cpp)
+endforeach()
+
+
+###############################################################################
+# Unit tests
+###############################################################################
+
+add_executable(LLVMFuzzer-Unittest
+ FuzzerUnittest.cpp
+ )
+
+add_executable(LLVMFuzzer-StandaloneInitializeTest
+ InitializeTest.cpp
+ ../standalone/StandaloneFuzzTargetMain.c
+ )
+
+target_link_libraries(LLVMFuzzer-Unittest
+ gtest
+ gtest_main
+ LLVMFuzzerNoMain
+ )
+
+target_include_directories(LLVMFuzzer-Unittest PRIVATE
+ "${LLVM_MAIN_SRC_DIR}/utils/unittest/googletest/include"
+ )
+
+set(TestBinaries ${TestBinaries} LLVMFuzzer-Unittest)
+set_target_properties(LLVMFuzzer-Unittest
+ PROPERTIES RUNTIME_OUTPUT_DIRECTORY
+ "${CMAKE_CURRENT_BINARY_DIR}"
+)
+
+set(TestBinaries ${TestBinaries} LLVMFuzzer-StandaloneInitializeTest)
+set_target_properties(LLVMFuzzer-StandaloneInitializeTest
+ PROPERTIES RUNTIME_OUTPUT_DIRECTORY
+ "${CMAKE_CURRENT_BINARY_DIR}"
+)
+
+###############################################################################
+# Additional tests
+###############################################################################
+
+include_directories(..)
+
+# add_subdirectory(uninstrumented)
+add_subdirectory(no-coverage)
+add_subdirectory(ubsan)
+
+add_library(LLVMFuzzer-DSO1 SHARED DSO1.cpp)
+add_library(LLVMFuzzer-DSO2 SHARED DSO2.cpp)
+
+add_executable(LLVMFuzzer-DSOTest
+ DSOTestMain.cpp
+ DSOTestExtra.cpp)
+
+target_link_libraries(LLVMFuzzer-DSOTest
+ LLVMFuzzer-DSO1
+ LLVMFuzzer-DSO2
+ LLVMFuzzer
+ )
+
+set_target_properties(LLVMFuzzer-DSOTest PROPERTIES RUNTIME_OUTPUT_DIRECTORY
+ "${CMAKE_BINARY_DIR}/lib/Fuzzer/test")
+set_target_properties(LLVMFuzzer-DSO1 PROPERTIES LIBRARY_OUTPUT_DIRECTORY
+ "${CMAKE_BINARY_DIR}/lib/Fuzzer/lib")
+set_target_properties(LLVMFuzzer-DSO2 PROPERTIES LIBRARY_OUTPUT_DIRECTORY
+ "${CMAKE_BINARY_DIR}/lib/Fuzzer/lib")
+
+set(TestBinaries ${TestBinaries} LLVMFuzzer-DSOTest)
+
+###############################################################################
+# Configure lit to run the tests
+#
+# Note this is done after declaring all tests so we can inform lit if any tests
+# need to be disabled.
+###############################################################################
+
+configure_lit_site_cfg(
+ ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
+ ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
+ )
+
+configure_lit_site_cfg(
+ ${CMAKE_CURRENT_SOURCE_DIR}/unit/lit.site.cfg.in
+ ${CMAKE_CURRENT_BINARY_DIR}/unit/lit.site.cfg
+ )
+
+add_lit_testsuite(check-fuzzer "Running Fuzzer tests"
+ ${CMAKE_CURRENT_BINARY_DIR}
+ DEPENDS ${TestBinaries} FileCheck not
+ )
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/CallerCalleeTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/CallerCalleeTest.cpp
new file mode 100644
index 0000000000..3ec025d023
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/CallerCalleeTest.cpp
@@ -0,0 +1,59 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Simple test for a fuzzer.
+// Try to find the target using the indirect caller-callee pairs.
+#include <cstdint>
+#include <cstdlib>
+#include <cstddef>
+#include <cstring>
+#include <iostream>
+
+typedef void (*F)();
+static F t[256];
+
+void f34() {
+ std::cerr << "BINGO\n";
+ exit(1);
+}
+void f23() { t[(unsigned)'d'] = f34;}
+void f12() { t[(unsigned)'c'] = f23;}
+void f01() { t[(unsigned)'b'] = f12;}
+void f00() {}
+
+static F t0[256] = {
+ f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
+ f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
+ f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
+ f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
+ f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
+ f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
+ f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
+ f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
+ f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
+ f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
+ f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
+ f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
+ f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
+ f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
+ f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
+ f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00, f00,
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ if (Size < 4) return 0;
+ // Spoof the counters.
+ for (int i = 0; i < 200; i++) {
+ f23();
+ f12();
+ f01();
+ }
+ memcpy(t, t0, sizeof(t));
+ t[(unsigned)'a'] = f01;
+ t[Data[0]]();
+ t[Data[1]]();
+ t[Data[2]]();
+ t[Data[3]]();
+ return 0;
+}
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/CounterTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/CounterTest.cpp
new file mode 100644
index 0000000000..4917934c62
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/CounterTest.cpp
@@ -0,0 +1,18 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Test for a fuzzer: must find the case where a particular basic block is
+// executed many times.
+#include <iostream>
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ int Num = 0;
+ for (size_t i = 0; i < Size; i++)
+ if (Data[i] == 'A' + i)
+ Num++;
+ if (Num >= 4) {
+ std::cerr << "BINGO!\n";
+ exit(1);
+ }
+ return 0;
+}
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/CustomCrossOverTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/CustomCrossOverTest.cpp
new file mode 100644
index 0000000000..b624088b90
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/CustomCrossOverTest.cpp
@@ -0,0 +1,63 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Simple test for a cutom mutator.
+#include <assert.h>
+#include <cstddef>
+#include <cstdint>
+#include <cstdlib>
+#include <iostream>
+#include <random>
+#include <string.h>
+
+#include "FuzzerInterface.h"
+
+static const char *Separator = "-_^_-";
+static const char *Target = "012-_^_-abc";
+
+static volatile int sink;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ assert(Data);
+ std::string Str(reinterpret_cast<const char *>(Data), Size);
+
+ // Ensure that two different elements exist in the corpus.
+ if (Size && Data[0] == '0') sink++;
+ if (Size && Data[0] == 'a') sink--;
+
+ if (Str.find(Target) != std::string::npos) {
+ std::cout << "BINGO; Found the target, exiting\n";
+ exit(1);
+ }
+ return 0;
+}
+
+extern "C" size_t LLVMFuzzerCustomCrossOver(const uint8_t *Data1, size_t Size1,
+ const uint8_t *Data2, size_t Size2,
+ uint8_t *Out, size_t MaxOutSize,
+ unsigned int Seed) {
+ static bool Printed;
+ static size_t SeparatorLen = strlen(Separator);
+
+ if (!Printed) {
+ std::cerr << "In LLVMFuzzerCustomCrossover\n";
+ Printed = true;
+ }
+
+ std::mt19937 R(Seed);
+
+ size_t Offset1 = 0;
+ size_t Len1 = R() % (Size1 - Offset1);
+ size_t Offset2 = 0;
+ size_t Len2 = R() % (Size2 - Offset2);
+ size_t Size = Len1 + Len2 + SeparatorLen;
+
+ if (Size > MaxOutSize)
+ return 0;
+
+ memcpy(Out, Data1 + Offset1, Len1);
+ memcpy(Out + Len1, Separator, SeparatorLen);
+ memcpy(Out + Len1 + SeparatorLen, Data2 + Offset2, Len2);
+
+ return Len1 + Len2 + SeparatorLen;
+}
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/CustomMutatorTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/CustomMutatorTest.cpp
new file mode 100644
index 0000000000..4f84519a90
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/CustomMutatorTest.cpp
@@ -0,0 +1,38 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Simple test for a cutom mutator.
+#include <assert.h>
+#include <cstdint>
+#include <cstdlib>
+#include <cstddef>
+#include <iostream>
+
+#include "FuzzerInterface.h"
+
+static volatile int Sink;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ assert(Data);
+ if (Size > 0 && Data[0] == 'H') {
+ Sink = 1;
+ if (Size > 1 && Data[1] == 'i') {
+ Sink = 2;
+ if (Size > 2 && Data[2] == '!') {
+ std::cout << "BINGO; Found the target, exiting\n";
+ exit(1);
+ }
+ }
+ }
+ return 0;
+}
+
+extern "C" size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
+ size_t MaxSize, unsigned int Seed) {
+ static bool Printed;
+ if (!Printed) {
+ std::cerr << "In LLVMFuzzerCustomMutator\n";
+ Printed = true;
+ }
+ return LLVMFuzzerMutate(Data, Size, MaxSize);
+}
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/DSO1.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/DSO1.cpp
new file mode 100644
index 0000000000..4a293890f4
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/DSO1.cpp
@@ -0,0 +1,12 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Source code for a simple DSO.
+
+int DSO1(int a) {
+ if (a < 123456)
+ return 0;
+ return 1;
+}
+
+void Uncovered1() { }
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/DSO2.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/DSO2.cpp
new file mode 100644
index 0000000000..04b308d193
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/DSO2.cpp
@@ -0,0 +1,12 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Source code for a simple DSO.
+
+int DSO2(int a) {
+ if (a < 3598235)
+ return 0;
+ return 1;
+}
+
+void Uncovered2() {}
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/DSOTestExtra.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/DSOTestExtra.cpp
new file mode 100644
index 0000000000..a2274d070e
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/DSOTestExtra.cpp
@@ -0,0 +1,11 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Source code for a simple DSO.
+
+int DSOTestExtra(int a) {
+ if (a < 452345)
+ return 0;
+ return 1;
+}
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/DSOTestMain.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/DSOTestMain.cpp
new file mode 100644
index 0000000000..3e225d8861
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/DSOTestMain.cpp
@@ -0,0 +1,31 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Source code for a simple DSO.
+
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
+#include <cstdio>
+extern int DSO1(int a);
+extern int DSO2(int a);
+extern int DSOTestExtra(int a);
+
+static volatile int *nil = 0;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ int x, y, z;
+ if (Size < sizeof(int) * 3) {
+ x = y = z = 0;
+ } else {
+ memcpy(&x, Data + 0 * sizeof(int), sizeof(int));
+ memcpy(&y, Data + 1 * sizeof(int), sizeof(int));
+ memcpy(&z, Data + 2 * sizeof(int), sizeof(int));
+ }
+ int sum = DSO1(x) + DSO2(y) + (z ? DSOTestExtra(z) : 0);
+ if (sum == 3) {
+ fprintf(stderr, "BINGO %d %d %d\n", x, y, z);
+ *nil = 0;
+ }
+ return 0;
+}
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/DivTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/DivTest.cpp
new file mode 100644
index 0000000000..63f6960f4e
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/DivTest.cpp
@@ -0,0 +1,20 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Simple test for a fuzzer: find the interesting argument for div.
+#include <assert.h>
+#include <cstdint>
+#include <cstring>
+#include <cstddef>
+#include <iostream>
+
+static volatile int Sink;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ if (Size < 4) return 0;
+ int a;
+ memcpy(&a, Data, 4);
+ Sink = 12345678 / (987654 - a);
+ return 0;
+}
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/EmptyTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/EmptyTest.cpp
new file mode 100644
index 0000000000..5e843308fa
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/EmptyTest.cpp
@@ -0,0 +1,11 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+// A fuzzer with empty target function.
+
+#include <cstdint>
+#include <cstdlib>
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ return 0;
+}
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/FourIndependentBranchesTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/FourIndependentBranchesTest.cpp
new file mode 100644
index 0000000000..62b3be76e3
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/FourIndependentBranchesTest.cpp
@@ -0,0 +1,22 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Simple test for a fuzzer. The fuzzer must find the string "FUZZ".
+#include <cstdint>
+#include <cstdlib>
+#include <cstddef>
+#include <iostream>
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ int bits = 0;
+ if (Size > 0 && Data[0] == 'F') bits |= 1;
+ if (Size > 1 && Data[1] == 'U') bits |= 2;
+ if (Size > 2 && Data[2] == 'Z') bits |= 4;
+ if (Size > 3 && Data[3] == 'Z') bits |= 8;
+ if (bits == 15) {
+ std::cerr << "BINGO!\n";
+ exit(1);
+ }
+ return 0;
+}
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/FullCoverageSetTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/FullCoverageSetTest.cpp
new file mode 100644
index 0000000000..415e0b4760
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/FullCoverageSetTest.cpp
@@ -0,0 +1,24 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Simple test for a fuzzer. The fuzzer must find the string "FUZZER".
+#include <cstdint>
+#include <cstdlib>
+#include <cstddef>
+#include <iostream>
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ int bits = 0;
+ if (Size > 0 && Data[0] == 'F') bits |= 1;
+ if (Size > 1 && Data[1] == 'U') bits |= 2;
+ if (Size > 2 && Data[2] == 'Z') bits |= 4;
+ if (Size > 3 && Data[3] == 'Z') bits |= 8;
+ if (Size > 4 && Data[4] == 'E') bits |= 16;
+ if (Size > 5 && Data[5] == 'R') bits |= 32;
+ if (bits == 63) {
+ std::cerr << "BINGO!\n";
+ exit(1);
+ }
+ return 0;
+}
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/FuzzerUnittest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/FuzzerUnittest.cpp
new file mode 100644
index 0000000000..4992ef57b6
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/FuzzerUnittest.cpp
@@ -0,0 +1,738 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Avoid ODR violations (LibFuzzer is built without ASan and this test is built
+// with ASan) involving C++ standard library types when using libcxx.
+#define _LIBCPP_HAS_NO_ASAN
+
+#include "FuzzerCorpus.h"
+#include "FuzzerInternal.h"
+#include "FuzzerDictionary.h"
+#include "FuzzerMerge.h"
+#include "FuzzerMutate.h"
+#include "FuzzerRandom.h"
+#include "gtest/gtest.h"
+#include <memory>
+#include <set>
+
+using namespace fuzzer;
+
+// For now, have LLVMFuzzerTestOneInput just to make it link.
+// Later we may want to make unittests that actually call LLVMFuzzerTestOneInput.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ abort();
+}
+
+TEST(Fuzzer, CrossOver) {
+ std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
+ fuzzer::EF = t.get();
+ Random Rand(0);
+ MutationDispatcher MD(Rand, {});
+ Unit A({0, 1, 2}), B({5, 6, 7});
+ Unit C;
+ Unit Expected[] = {
+ { 0 },
+ { 0, 1 },
+ { 0, 5 },
+ { 0, 1, 2 },
+ { 0, 1, 5 },
+ { 0, 5, 1 },
+ { 0, 5, 6 },
+ { 0, 1, 2, 5 },
+ { 0, 1, 5, 2 },
+ { 0, 1, 5, 6 },
+ { 0, 5, 1, 2 },
+ { 0, 5, 1, 6 },
+ { 0, 5, 6, 1 },
+ { 0, 5, 6, 7 },
+ { 0, 1, 2, 5, 6 },
+ { 0, 1, 5, 2, 6 },
+ { 0, 1, 5, 6, 2 },
+ { 0, 1, 5, 6, 7 },
+ { 0, 5, 1, 2, 6 },
+ { 0, 5, 1, 6, 2 },
+ { 0, 5, 1, 6, 7 },
+ { 0, 5, 6, 1, 2 },
+ { 0, 5, 6, 1, 7 },
+ { 0, 5, 6, 7, 1 },
+ { 0, 1, 2, 5, 6, 7 },
+ { 0, 1, 5, 2, 6, 7 },
+ { 0, 1, 5, 6, 2, 7 },
+ { 0, 1, 5, 6, 7, 2 },
+ { 0, 5, 1, 2, 6, 7 },
+ { 0, 5, 1, 6, 2, 7 },
+ { 0, 5, 1, 6, 7, 2 },
+ { 0, 5, 6, 1, 2, 7 },
+ { 0, 5, 6, 1, 7, 2 },
+ { 0, 5, 6, 7, 1, 2 }
+ };
+ for (size_t Len = 1; Len < 8; Len++) {
+ std::set<Unit> FoundUnits, ExpectedUnitsWitThisLength;
+ for (int Iter = 0; Iter < 3000; Iter++) {
+ C.resize(Len);
+ size_t NewSize = MD.CrossOver(A.data(), A.size(), B.data(), B.size(),
+ C.data(), C.size());
+ C.resize(NewSize);
+ FoundUnits.insert(C);
+ }
+ for (const Unit &U : Expected)
+ if (U.size() <= Len)
+ ExpectedUnitsWitThisLength.insert(U);
+ EXPECT_EQ(ExpectedUnitsWitThisLength, FoundUnits);
+ }
+}
+
+TEST(Fuzzer, Hash) {
+ uint8_t A[] = {'a', 'b', 'c'};
+ fuzzer::Unit U(A, A + sizeof(A));
+ EXPECT_EQ("a9993e364706816aba3e25717850c26c9cd0d89d", fuzzer::Hash(U));
+ U.push_back('d');
+ EXPECT_EQ("81fe8bfe87576c3ecb22426f8e57847382917acf", fuzzer::Hash(U));
+}
+
+typedef size_t (MutationDispatcher::*Mutator)(uint8_t *Data, size_t Size,
+ size_t MaxSize);
+
+void TestEraseBytes(Mutator M, int NumIter) {
+ std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
+ fuzzer::EF = t.get();
+ uint8_t REM0[8] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
+ uint8_t REM1[8] = {0x00, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
+ uint8_t REM2[8] = {0x00, 0x11, 0x33, 0x44, 0x55, 0x66, 0x77};
+ uint8_t REM3[8] = {0x00, 0x11, 0x22, 0x44, 0x55, 0x66, 0x77};
+ uint8_t REM4[8] = {0x00, 0x11, 0x22, 0x33, 0x55, 0x66, 0x77};
+ uint8_t REM5[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x66, 0x77};
+ uint8_t REM6[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x77};
+ uint8_t REM7[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
+
+ uint8_t REM8[6] = {0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
+ uint8_t REM9[6] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55};
+ uint8_t REM10[6] = {0x00, 0x11, 0x22, 0x55, 0x66, 0x77};
+
+ uint8_t REM11[5] = {0x33, 0x44, 0x55, 0x66, 0x77};
+ uint8_t REM12[5] = {0x00, 0x11, 0x22, 0x33, 0x44};
+ uint8_t REM13[5] = {0x00, 0x44, 0x55, 0x66, 0x77};
+
+
+ Random Rand(0);
+ MutationDispatcher MD(Rand, {});
+ int FoundMask = 0;
+ for (int i = 0; i < NumIter; i++) {
+ uint8_t T[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
+ size_t NewSize = (MD.*M)(T, sizeof(T), sizeof(T));
+ if (NewSize == 7 && !memcmp(REM0, T, 7)) FoundMask |= 1 << 0;
+ if (NewSize == 7 && !memcmp(REM1, T, 7)) FoundMask |= 1 << 1;
+ if (NewSize == 7 && !memcmp(REM2, T, 7)) FoundMask |= 1 << 2;
+ if (NewSize == 7 && !memcmp(REM3, T, 7)) FoundMask |= 1 << 3;
+ if (NewSize == 7 && !memcmp(REM4, T, 7)) FoundMask |= 1 << 4;
+ if (NewSize == 7 && !memcmp(REM5, T, 7)) FoundMask |= 1 << 5;
+ if (NewSize == 7 && !memcmp(REM6, T, 7)) FoundMask |= 1 << 6;
+ if (NewSize == 7 && !memcmp(REM7, T, 7)) FoundMask |= 1 << 7;
+
+ if (NewSize == 6 && !memcmp(REM8, T, 6)) FoundMask |= 1 << 8;
+ if (NewSize == 6 && !memcmp(REM9, T, 6)) FoundMask |= 1 << 9;
+ if (NewSize == 6 && !memcmp(REM10, T, 6)) FoundMask |= 1 << 10;
+
+ if (NewSize == 5 && !memcmp(REM11, T, 5)) FoundMask |= 1 << 11;
+ if (NewSize == 5 && !memcmp(REM12, T, 5)) FoundMask |= 1 << 12;
+ if (NewSize == 5 && !memcmp(REM13, T, 5)) FoundMask |= 1 << 13;
+ }
+ EXPECT_EQ(FoundMask, (1 << 14) - 1);
+}
+
+TEST(FuzzerMutate, EraseBytes1) {
+ TestEraseBytes(&MutationDispatcher::Mutate_EraseBytes, 200);
+}
+TEST(FuzzerMutate, EraseBytes2) {
+ TestEraseBytes(&MutationDispatcher::Mutate, 2000);
+}
+
+void TestInsertByte(Mutator M, int NumIter) {
+ std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
+ fuzzer::EF = t.get();
+ Random Rand(0);
+ MutationDispatcher MD(Rand, {});
+ int FoundMask = 0;
+ uint8_t INS0[8] = {0xF1, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
+ uint8_t INS1[8] = {0x00, 0xF2, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
+ uint8_t INS2[8] = {0x00, 0x11, 0xF3, 0x22, 0x33, 0x44, 0x55, 0x66};
+ uint8_t INS3[8] = {0x00, 0x11, 0x22, 0xF4, 0x33, 0x44, 0x55, 0x66};
+ uint8_t INS4[8] = {0x00, 0x11, 0x22, 0x33, 0xF5, 0x44, 0x55, 0x66};
+ uint8_t INS5[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0xF6, 0x55, 0x66};
+ uint8_t INS6[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0xF7, 0x66};
+ uint8_t INS7[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0xF8};
+ for (int i = 0; i < NumIter; i++) {
+ uint8_t T[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
+ size_t NewSize = (MD.*M)(T, 7, 8);
+ if (NewSize == 8 && !memcmp(INS0, T, 8)) FoundMask |= 1 << 0;
+ if (NewSize == 8 && !memcmp(INS1, T, 8)) FoundMask |= 1 << 1;
+ if (NewSize == 8 && !memcmp(INS2, T, 8)) FoundMask |= 1 << 2;
+ if (NewSize == 8 && !memcmp(INS3, T, 8)) FoundMask |= 1 << 3;
+ if (NewSize == 8 && !memcmp(INS4, T, 8)) FoundMask |= 1 << 4;
+ if (NewSize == 8 && !memcmp(INS5, T, 8)) FoundMask |= 1 << 5;
+ if (NewSize == 8 && !memcmp(INS6, T, 8)) FoundMask |= 1 << 6;
+ if (NewSize == 8 && !memcmp(INS7, T, 8)) FoundMask |= 1 << 7;
+ }
+ EXPECT_EQ(FoundMask, 255);
+}
+
+TEST(FuzzerMutate, InsertByte1) {
+ TestInsertByte(&MutationDispatcher::Mutate_InsertByte, 1 << 15);
+}
+TEST(FuzzerMutate, InsertByte2) {
+ TestInsertByte(&MutationDispatcher::Mutate, 1 << 17);
+}
+
+void TestInsertRepeatedBytes(Mutator M, int NumIter) {
+ std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
+ fuzzer::EF = t.get();
+ Random Rand(0);
+ MutationDispatcher MD(Rand, {});
+ int FoundMask = 0;
+ uint8_t INS0[7] = {0x00, 0x11, 0x22, 0x33, 'a', 'a', 'a'};
+ uint8_t INS1[7] = {0x00, 0x11, 0x22, 'a', 'a', 'a', 0x33};
+ uint8_t INS2[7] = {0x00, 0x11, 'a', 'a', 'a', 0x22, 0x33};
+ uint8_t INS3[7] = {0x00, 'a', 'a', 'a', 0x11, 0x22, 0x33};
+ uint8_t INS4[7] = {'a', 'a', 'a', 0x00, 0x11, 0x22, 0x33};
+
+ uint8_t INS5[8] = {0x00, 0x11, 0x22, 0x33, 'b', 'b', 'b', 'b'};
+ uint8_t INS6[8] = {0x00, 0x11, 0x22, 'b', 'b', 'b', 'b', 0x33};
+ uint8_t INS7[8] = {0x00, 0x11, 'b', 'b', 'b', 'b', 0x22, 0x33};
+ uint8_t INS8[8] = {0x00, 'b', 'b', 'b', 'b', 0x11, 0x22, 0x33};
+ uint8_t INS9[8] = {'b', 'b', 'b', 'b', 0x00, 0x11, 0x22, 0x33};
+
+ for (int i = 0; i < NumIter; i++) {
+ uint8_t T[8] = {0x00, 0x11, 0x22, 0x33};
+ size_t NewSize = (MD.*M)(T, 4, 8);
+ if (NewSize == 7 && !memcmp(INS0, T, 7)) FoundMask |= 1 << 0;
+ if (NewSize == 7 && !memcmp(INS1, T, 7)) FoundMask |= 1 << 1;
+ if (NewSize == 7 && !memcmp(INS2, T, 7)) FoundMask |= 1 << 2;
+ if (NewSize == 7 && !memcmp(INS3, T, 7)) FoundMask |= 1 << 3;
+ if (NewSize == 7 && !memcmp(INS4, T, 7)) FoundMask |= 1 << 4;
+
+ if (NewSize == 8 && !memcmp(INS5, T, 8)) FoundMask |= 1 << 5;
+ if (NewSize == 8 && !memcmp(INS6, T, 8)) FoundMask |= 1 << 6;
+ if (NewSize == 8 && !memcmp(INS7, T, 8)) FoundMask |= 1 << 7;
+ if (NewSize == 8 && !memcmp(INS8, T, 8)) FoundMask |= 1 << 8;
+ if (NewSize == 8 && !memcmp(INS9, T, 8)) FoundMask |= 1 << 9;
+
+ }
+ EXPECT_EQ(FoundMask, (1 << 10) - 1);
+}
+
+TEST(FuzzerMutate, InsertRepeatedBytes1) {
+ TestInsertRepeatedBytes(&MutationDispatcher::Mutate_InsertRepeatedBytes, 10000);
+}
+TEST(FuzzerMutate, InsertRepeatedBytes2) {
+ TestInsertRepeatedBytes(&MutationDispatcher::Mutate, 300000);
+}
+
+void TestChangeByte(Mutator M, int NumIter) {
+ std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
+ fuzzer::EF = t.get();
+ Random Rand(0);
+ MutationDispatcher MD(Rand, {});
+ int FoundMask = 0;
+ uint8_t CH0[8] = {0xF0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
+ uint8_t CH1[8] = {0x00, 0xF1, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
+ uint8_t CH2[8] = {0x00, 0x11, 0xF2, 0x33, 0x44, 0x55, 0x66, 0x77};
+ uint8_t CH3[8] = {0x00, 0x11, 0x22, 0xF3, 0x44, 0x55, 0x66, 0x77};
+ uint8_t CH4[8] = {0x00, 0x11, 0x22, 0x33, 0xF4, 0x55, 0x66, 0x77};
+ uint8_t CH5[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0xF5, 0x66, 0x77};
+ uint8_t CH6[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0xF5, 0x77};
+ uint8_t CH7[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0xF7};
+ for (int i = 0; i < NumIter; i++) {
+ uint8_t T[9] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
+ size_t NewSize = (MD.*M)(T, 8, 9);
+ if (NewSize == 8 && !memcmp(CH0, T, 8)) FoundMask |= 1 << 0;
+ if (NewSize == 8 && !memcmp(CH1, T, 8)) FoundMask |= 1 << 1;
+ if (NewSize == 8 && !memcmp(CH2, T, 8)) FoundMask |= 1 << 2;
+ if (NewSize == 8 && !memcmp(CH3, T, 8)) FoundMask |= 1 << 3;
+ if (NewSize == 8 && !memcmp(CH4, T, 8)) FoundMask |= 1 << 4;
+ if (NewSize == 8 && !memcmp(CH5, T, 8)) FoundMask |= 1 << 5;
+ if (NewSize == 8 && !memcmp(CH6, T, 8)) FoundMask |= 1 << 6;
+ if (NewSize == 8 && !memcmp(CH7, T, 8)) FoundMask |= 1 << 7;
+ }
+ EXPECT_EQ(FoundMask, 255);
+}
+
+TEST(FuzzerMutate, ChangeByte1) {
+ TestChangeByte(&MutationDispatcher::Mutate_ChangeByte, 1 << 15);
+}
+TEST(FuzzerMutate, ChangeByte2) {
+ TestChangeByte(&MutationDispatcher::Mutate, 1 << 17);
+}
+
+void TestChangeBit(Mutator M, int NumIter) {
+ std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
+ fuzzer::EF = t.get();
+ Random Rand(0);
+ MutationDispatcher MD(Rand, {});
+ int FoundMask = 0;
+ uint8_t CH0[8] = {0x01, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
+ uint8_t CH1[8] = {0x00, 0x13, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
+ uint8_t CH2[8] = {0x00, 0x11, 0x02, 0x33, 0x44, 0x55, 0x66, 0x77};
+ uint8_t CH3[8] = {0x00, 0x11, 0x22, 0x37, 0x44, 0x55, 0x66, 0x77};
+ uint8_t CH4[8] = {0x00, 0x11, 0x22, 0x33, 0x54, 0x55, 0x66, 0x77};
+ uint8_t CH5[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x54, 0x66, 0x77};
+ uint8_t CH6[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x76, 0x77};
+ uint8_t CH7[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0xF7};
+ for (int i = 0; i < NumIter; i++) {
+ uint8_t T[9] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
+ size_t NewSize = (MD.*M)(T, 8, 9);
+ if (NewSize == 8 && !memcmp(CH0, T, 8)) FoundMask |= 1 << 0;
+ if (NewSize == 8 && !memcmp(CH1, T, 8)) FoundMask |= 1 << 1;
+ if (NewSize == 8 && !memcmp(CH2, T, 8)) FoundMask |= 1 << 2;
+ if (NewSize == 8 && !memcmp(CH3, T, 8)) FoundMask |= 1 << 3;
+ if (NewSize == 8 && !memcmp(CH4, T, 8)) FoundMask |= 1 << 4;
+ if (NewSize == 8 && !memcmp(CH5, T, 8)) FoundMask |= 1 << 5;
+ if (NewSize == 8 && !memcmp(CH6, T, 8)) FoundMask |= 1 << 6;
+ if (NewSize == 8 && !memcmp(CH7, T, 8)) FoundMask |= 1 << 7;
+ }
+ EXPECT_EQ(FoundMask, 255);
+}
+
+TEST(FuzzerMutate, ChangeBit1) {
+ TestChangeBit(&MutationDispatcher::Mutate_ChangeBit, 1 << 16);
+}
+TEST(FuzzerMutate, ChangeBit2) {
+ TestChangeBit(&MutationDispatcher::Mutate, 1 << 18);
+}
+
+void TestShuffleBytes(Mutator M, int NumIter) {
+ std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
+ fuzzer::EF = t.get();
+ Random Rand(0);
+ MutationDispatcher MD(Rand, {});
+ int FoundMask = 0;
+ uint8_t CH0[7] = {0x00, 0x22, 0x11, 0x33, 0x44, 0x55, 0x66};
+ uint8_t CH1[7] = {0x11, 0x00, 0x33, 0x22, 0x44, 0x55, 0x66};
+ uint8_t CH2[7] = {0x00, 0x33, 0x11, 0x22, 0x44, 0x55, 0x66};
+ uint8_t CH3[7] = {0x00, 0x11, 0x22, 0x44, 0x55, 0x66, 0x33};
+ uint8_t CH4[7] = {0x00, 0x11, 0x22, 0x33, 0x55, 0x44, 0x66};
+ for (int i = 0; i < NumIter; i++) {
+ uint8_t T[7] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
+ size_t NewSize = (MD.*M)(T, 7, 7);
+ if (NewSize == 7 && !memcmp(CH0, T, 7)) FoundMask |= 1 << 0;
+ if (NewSize == 7 && !memcmp(CH1, T, 7)) FoundMask |= 1 << 1;
+ if (NewSize == 7 && !memcmp(CH2, T, 7)) FoundMask |= 1 << 2;
+ if (NewSize == 7 && !memcmp(CH3, T, 7)) FoundMask |= 1 << 3;
+ if (NewSize == 7 && !memcmp(CH4, T, 7)) FoundMask |= 1 << 4;
+ }
+ EXPECT_EQ(FoundMask, 31);
+}
+
+TEST(FuzzerMutate, ShuffleBytes1) {
+ TestShuffleBytes(&MutationDispatcher::Mutate_ShuffleBytes, 1 << 16);
+}
+TEST(FuzzerMutate, ShuffleBytes2) {
+ TestShuffleBytes(&MutationDispatcher::Mutate, 1 << 20);
+}
+
+void TestCopyPart(Mutator M, int NumIter) {
+ std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
+ fuzzer::EF = t.get();
+ Random Rand(0);
+ MutationDispatcher MD(Rand, {});
+ int FoundMask = 0;
+ uint8_t CH0[7] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x00, 0x11};
+ uint8_t CH1[7] = {0x55, 0x66, 0x22, 0x33, 0x44, 0x55, 0x66};
+ uint8_t CH2[7] = {0x00, 0x55, 0x66, 0x33, 0x44, 0x55, 0x66};
+ uint8_t CH3[7] = {0x00, 0x11, 0x22, 0x00, 0x11, 0x22, 0x66};
+ uint8_t CH4[7] = {0x00, 0x11, 0x11, 0x22, 0x33, 0x55, 0x66};
+
+ for (int i = 0; i < NumIter; i++) {
+ uint8_t T[7] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
+ size_t NewSize = (MD.*M)(T, 7, 7);
+ if (NewSize == 7 && !memcmp(CH0, T, 7)) FoundMask |= 1 << 0;
+ if (NewSize == 7 && !memcmp(CH1, T, 7)) FoundMask |= 1 << 1;
+ if (NewSize == 7 && !memcmp(CH2, T, 7)) FoundMask |= 1 << 2;
+ if (NewSize == 7 && !memcmp(CH3, T, 7)) FoundMask |= 1 << 3;
+ if (NewSize == 7 && !memcmp(CH4, T, 7)) FoundMask |= 1 << 4;
+ }
+
+ uint8_t CH5[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x00, 0x11, 0x22};
+ uint8_t CH6[8] = {0x22, 0x33, 0x44, 0x00, 0x11, 0x22, 0x33, 0x44};
+ uint8_t CH7[8] = {0x00, 0x11, 0x22, 0x00, 0x11, 0x22, 0x33, 0x44};
+ uint8_t CH8[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x22, 0x33, 0x44};
+ uint8_t CH9[8] = {0x00, 0x11, 0x22, 0x22, 0x33, 0x44, 0x33, 0x44};
+
+ for (int i = 0; i < NumIter; i++) {
+ uint8_t T[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
+ size_t NewSize = (MD.*M)(T, 5, 8);
+ if (NewSize == 8 && !memcmp(CH5, T, 8)) FoundMask |= 1 << 5;
+ if (NewSize == 8 && !memcmp(CH6, T, 8)) FoundMask |= 1 << 6;
+ if (NewSize == 8 && !memcmp(CH7, T, 8)) FoundMask |= 1 << 7;
+ if (NewSize == 8 && !memcmp(CH8, T, 8)) FoundMask |= 1 << 8;
+ if (NewSize == 8 && !memcmp(CH9, T, 8)) FoundMask |= 1 << 9;
+ }
+
+ EXPECT_EQ(FoundMask, 1023);
+}
+
+TEST(FuzzerMutate, CopyPart1) {
+ TestCopyPart(&MutationDispatcher::Mutate_CopyPart, 1 << 10);
+}
+TEST(FuzzerMutate, CopyPart2) {
+ TestCopyPart(&MutationDispatcher::Mutate, 1 << 13);
+}
+
+void TestAddWordFromDictionary(Mutator M, int NumIter) {
+ std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
+ fuzzer::EF = t.get();
+ Random Rand(0);
+ MutationDispatcher MD(Rand, {});
+ uint8_t Word1[4] = {0xAA, 0xBB, 0xCC, 0xDD};
+ uint8_t Word2[3] = {0xFF, 0xEE, 0xEF};
+ MD.AddWordToManualDictionary(Word(Word1, sizeof(Word1)));
+ MD.AddWordToManualDictionary(Word(Word2, sizeof(Word2)));
+ int FoundMask = 0;
+ uint8_t CH0[7] = {0x00, 0x11, 0x22, 0xAA, 0xBB, 0xCC, 0xDD};
+ uint8_t CH1[7] = {0x00, 0x11, 0xAA, 0xBB, 0xCC, 0xDD, 0x22};
+ uint8_t CH2[7] = {0x00, 0xAA, 0xBB, 0xCC, 0xDD, 0x11, 0x22};
+ uint8_t CH3[7] = {0xAA, 0xBB, 0xCC, 0xDD, 0x00, 0x11, 0x22};
+ uint8_t CH4[6] = {0x00, 0x11, 0x22, 0xFF, 0xEE, 0xEF};
+ uint8_t CH5[6] = {0x00, 0x11, 0xFF, 0xEE, 0xEF, 0x22};
+ uint8_t CH6[6] = {0x00, 0xFF, 0xEE, 0xEF, 0x11, 0x22};
+ uint8_t CH7[6] = {0xFF, 0xEE, 0xEF, 0x00, 0x11, 0x22};
+ for (int i = 0; i < NumIter; i++) {
+ uint8_t T[7] = {0x00, 0x11, 0x22};
+ size_t NewSize = (MD.*M)(T, 3, 7);
+ if (NewSize == 7 && !memcmp(CH0, T, 7)) FoundMask |= 1 << 0;
+ if (NewSize == 7 && !memcmp(CH1, T, 7)) FoundMask |= 1 << 1;
+ if (NewSize == 7 && !memcmp(CH2, T, 7)) FoundMask |= 1 << 2;
+ if (NewSize == 7 && !memcmp(CH3, T, 7)) FoundMask |= 1 << 3;
+ if (NewSize == 6 && !memcmp(CH4, T, 6)) FoundMask |= 1 << 4;
+ if (NewSize == 6 && !memcmp(CH5, T, 6)) FoundMask |= 1 << 5;
+ if (NewSize == 6 && !memcmp(CH6, T, 6)) FoundMask |= 1 << 6;
+ if (NewSize == 6 && !memcmp(CH7, T, 6)) FoundMask |= 1 << 7;
+ }
+ EXPECT_EQ(FoundMask, 255);
+}
+
+TEST(FuzzerMutate, AddWordFromDictionary1) {
+ TestAddWordFromDictionary(
+ &MutationDispatcher::Mutate_AddWordFromManualDictionary, 1 << 15);
+}
+
+TEST(FuzzerMutate, AddWordFromDictionary2) {
+ TestAddWordFromDictionary(&MutationDispatcher::Mutate, 1 << 15);
+}
+
+void TestAddWordFromDictionaryWithHint(Mutator M, int NumIter) {
+ std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
+ fuzzer::EF = t.get();
+ Random Rand(0);
+ MutationDispatcher MD(Rand, {});
+ uint8_t W[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xFF, 0xEE, 0xEF};
+ size_t PosHint = 7777;
+ MD.AddWordToAutoDictionary({Word(W, sizeof(W)), PosHint});
+ int FoundMask = 0;
+ for (int i = 0; i < NumIter; i++) {
+ uint8_t T[10000];
+ memset(T, 0, sizeof(T));
+ size_t NewSize = (MD.*M)(T, 9000, 10000);
+ if (NewSize >= PosHint + sizeof(W) &&
+ !memcmp(W, T + PosHint, sizeof(W)))
+ FoundMask = 1;
+ }
+ EXPECT_EQ(FoundMask, 1);
+}
+
+TEST(FuzzerMutate, AddWordFromDictionaryWithHint1) {
+ TestAddWordFromDictionaryWithHint(
+ &MutationDispatcher::Mutate_AddWordFromTemporaryAutoDictionary, 1 << 5);
+}
+
+TEST(FuzzerMutate, AddWordFromDictionaryWithHint2) {
+ TestAddWordFromDictionaryWithHint(&MutationDispatcher::Mutate, 1 << 10);
+}
+
+void TestChangeASCIIInteger(Mutator M, int NumIter) {
+ std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
+ fuzzer::EF = t.get();
+ Random Rand(0);
+ MutationDispatcher MD(Rand, {});
+
+ uint8_t CH0[8] = {'1', '2', '3', '4', '5', '6', '7', '7'};
+ uint8_t CH1[8] = {'1', '2', '3', '4', '5', '6', '7', '9'};
+ uint8_t CH2[8] = {'2', '4', '6', '9', '1', '3', '5', '6'};
+ uint8_t CH3[8] = {'0', '6', '1', '7', '2', '8', '3', '9'};
+ int FoundMask = 0;
+ for (int i = 0; i < NumIter; i++) {
+ uint8_t T[8] = {'1', '2', '3', '4', '5', '6', '7', '8'};
+ size_t NewSize = (MD.*M)(T, 8, 8);
+ /**/ if (NewSize == 8 && !memcmp(CH0, T, 8)) FoundMask |= 1 << 0;
+ else if (NewSize == 8 && !memcmp(CH1, T, 8)) FoundMask |= 1 << 1;
+ else if (NewSize == 8 && !memcmp(CH2, T, 8)) FoundMask |= 1 << 2;
+ else if (NewSize == 8 && !memcmp(CH3, T, 8)) FoundMask |= 1 << 3;
+ else if (NewSize == 8) FoundMask |= 1 << 4;
+ }
+ EXPECT_EQ(FoundMask, 31);
+}
+
+TEST(FuzzerMutate, ChangeASCIIInteger1) {
+ TestChangeASCIIInteger(&MutationDispatcher::Mutate_ChangeASCIIInteger,
+ 1 << 15);
+}
+
+TEST(FuzzerMutate, ChangeASCIIInteger2) {
+ TestChangeASCIIInteger(&MutationDispatcher::Mutate, 1 << 15);
+}
+
+void TestChangeBinaryInteger(Mutator M, int NumIter) {
+ std::unique_ptr<ExternalFunctions> t(new ExternalFunctions());
+ fuzzer::EF = t.get();
+ Random Rand(0);
+ MutationDispatcher MD(Rand, {});
+
+ uint8_t CH0[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x79};
+ uint8_t CH1[8] = {0x00, 0x11, 0x22, 0x31, 0x44, 0x55, 0x66, 0x77};
+ uint8_t CH2[8] = {0xff, 0x10, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
+ uint8_t CH3[8] = {0x00, 0x11, 0x2a, 0x33, 0x44, 0x55, 0x66, 0x77};
+ uint8_t CH4[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x4f, 0x66, 0x77};
+ uint8_t CH5[8] = {0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88};
+ uint8_t CH6[8] = {0x00, 0x11, 0x22, 0x00, 0x00, 0x00, 0x08, 0x77}; // Size
+ uint8_t CH7[8] = {0x00, 0x08, 0x00, 0x33, 0x44, 0x55, 0x66, 0x77}; // Sw(Size)
+
+ int FoundMask = 0;
+ for (int i = 0; i < NumIter; i++) {
+ uint8_t T[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
+ size_t NewSize = (MD.*M)(T, 8, 8);
+ /**/ if (NewSize == 8 && !memcmp(CH0, T, 8)) FoundMask |= 1 << 0;
+ else if (NewSize == 8 && !memcmp(CH1, T, 8)) FoundMask |= 1 << 1;
+ else if (NewSize == 8 && !memcmp(CH2, T, 8)) FoundMask |= 1 << 2;
+ else if (NewSize == 8 && !memcmp(CH3, T, 8)) FoundMask |= 1 << 3;
+ else if (NewSize == 8 && !memcmp(CH4, T, 8)) FoundMask |= 1 << 4;
+ else if (NewSize == 8 && !memcmp(CH5, T, 8)) FoundMask |= 1 << 5;
+ else if (NewSize == 8 && !memcmp(CH6, T, 8)) FoundMask |= 1 << 6;
+ else if (NewSize == 8 && !memcmp(CH7, T, 8)) FoundMask |= 1 << 7;
+ }
+ EXPECT_EQ(FoundMask, 255);
+}
+
+TEST(FuzzerMutate, ChangeBinaryInteger1) {
+ TestChangeBinaryInteger(&MutationDispatcher::Mutate_ChangeBinaryInteger,
+ 1 << 12);
+}
+
+TEST(FuzzerMutate, ChangeBinaryInteger2) {
+ TestChangeBinaryInteger(&MutationDispatcher::Mutate, 1 << 15);
+}
+
+
+TEST(FuzzerDictionary, ParseOneDictionaryEntry) {
+ Unit U;
+ EXPECT_FALSE(ParseOneDictionaryEntry("", &U));
+ EXPECT_FALSE(ParseOneDictionaryEntry(" ", &U));
+ EXPECT_FALSE(ParseOneDictionaryEntry("\t ", &U));
+ EXPECT_FALSE(ParseOneDictionaryEntry(" \" ", &U));
+ EXPECT_FALSE(ParseOneDictionaryEntry(" zz\" ", &U));
+ EXPECT_FALSE(ParseOneDictionaryEntry(" \"zz ", &U));
+ EXPECT_FALSE(ParseOneDictionaryEntry(" \"\" ", &U));
+ EXPECT_TRUE(ParseOneDictionaryEntry("\"a\"", &U));
+ EXPECT_EQ(U, Unit({'a'}));
+ EXPECT_TRUE(ParseOneDictionaryEntry("\"abc\"", &U));
+ EXPECT_EQ(U, Unit({'a', 'b', 'c'}));
+ EXPECT_TRUE(ParseOneDictionaryEntry("abc=\"abc\"", &U));
+ EXPECT_EQ(U, Unit({'a', 'b', 'c'}));
+ EXPECT_FALSE(ParseOneDictionaryEntry("\"\\\"", &U));
+ EXPECT_TRUE(ParseOneDictionaryEntry("\"\\\\\"", &U));
+ EXPECT_EQ(U, Unit({'\\'}));
+ EXPECT_TRUE(ParseOneDictionaryEntry("\"\\xAB\"", &U));
+ EXPECT_EQ(U, Unit({0xAB}));
+ EXPECT_TRUE(ParseOneDictionaryEntry("\"\\xABz\\xDE\"", &U));
+ EXPECT_EQ(U, Unit({0xAB, 'z', 0xDE}));
+ EXPECT_TRUE(ParseOneDictionaryEntry("\"#\"", &U));
+ EXPECT_EQ(U, Unit({'#'}));
+ EXPECT_TRUE(ParseOneDictionaryEntry("\"\\\"\"", &U));
+ EXPECT_EQ(U, Unit({'"'}));
+}
+
+TEST(FuzzerDictionary, ParseDictionaryFile) {
+ std::vector<Unit> Units;
+ EXPECT_FALSE(ParseDictionaryFile("zzz\n", &Units));
+ EXPECT_FALSE(ParseDictionaryFile("", &Units));
+ EXPECT_TRUE(ParseDictionaryFile("\n", &Units));
+ EXPECT_EQ(Units.size(), 0U);
+ EXPECT_TRUE(ParseDictionaryFile("#zzzz a b c d\n", &Units));
+ EXPECT_EQ(Units.size(), 0U);
+ EXPECT_TRUE(ParseDictionaryFile(" #zzzz\n", &Units));
+ EXPECT_EQ(Units.size(), 0U);
+ EXPECT_TRUE(ParseDictionaryFile(" #zzzz\n", &Units));
+ EXPECT_EQ(Units.size(), 0U);
+ EXPECT_TRUE(ParseDictionaryFile(" #zzzz\naaa=\"aa\"", &Units));
+ EXPECT_EQ(Units, std::vector<Unit>({Unit({'a', 'a'})}));
+ EXPECT_TRUE(
+ ParseDictionaryFile(" #zzzz\naaa=\"aa\"\n\nabc=\"abc\"", &Units));
+ EXPECT_EQ(Units,
+ std::vector<Unit>({Unit({'a', 'a'}), Unit({'a', 'b', 'c'})}));
+}
+
+TEST(FuzzerUtil, Base64) {
+ EXPECT_EQ("", Base64({}));
+ EXPECT_EQ("YQ==", Base64({'a'}));
+ EXPECT_EQ("eA==", Base64({'x'}));
+ EXPECT_EQ("YWI=", Base64({'a', 'b'}));
+ EXPECT_EQ("eHk=", Base64({'x', 'y'}));
+ EXPECT_EQ("YWJj", Base64({'a', 'b', 'c'}));
+ EXPECT_EQ("eHl6", Base64({'x', 'y', 'z'}));
+ EXPECT_EQ("YWJjeA==", Base64({'a', 'b', 'c', 'x'}));
+ EXPECT_EQ("YWJjeHk=", Base64({'a', 'b', 'c', 'x', 'y'}));
+ EXPECT_EQ("YWJjeHl6", Base64({'a', 'b', 'c', 'x', 'y', 'z'}));
+}
+
+TEST(Corpus, Distribution) {
+ Random Rand(0);
+ InputCorpus C("");
+ size_t N = 10;
+ size_t TriesPerUnit = 1<<16;
+ for (size_t i = 0; i < N; i++)
+ C.AddToCorpus(Unit{ static_cast<uint8_t>(i) }, 0);
+
+ std::vector<size_t> Hist(N);
+ for (size_t i = 0; i < N * TriesPerUnit; i++) {
+ Hist[C.ChooseUnitIdxToMutate(Rand)]++;
+ }
+ for (size_t i = 0; i < N; i++) {
+ // A weak sanity check that every unit gets invoked.
+ EXPECT_GT(Hist[i], TriesPerUnit / N / 3);
+ }
+}
+
+TEST(Merge, Bad) {
+ const char *kInvalidInputs[] = {
+ "",
+ "x",
+ "3\nx",
+ "2\n3",
+ "2\n2",
+ "2\n2\nA\n",
+ "2\n2\nA\nB\nC\n",
+ "0\n0\n",
+ "1\n1\nA\nDONE 0",
+ "1\n1\nA\nSTARTED 1",
+ };
+ Merger M;
+ for (auto S : kInvalidInputs) {
+ // fprintf(stderr, "TESTING:\n%s\n", S);
+ EXPECT_FALSE(M.Parse(S, false));
+ }
+}
+
+void EQ(const std::vector<uint32_t> &A, const std::vector<uint32_t> &B) {
+ EXPECT_EQ(A, B);
+}
+
+void EQ(const std::vector<std::string> &A, const std::vector<std::string> &B) {
+ std::set<std::string> a(A.begin(), A.end());
+ std::set<std::string> b(B.begin(), B.end());
+ EXPECT_EQ(a, b);
+}
+
+static void Merge(const std::string &Input,
+ const std::vector<std::string> Result,
+ size_t NumNewFeatures) {
+ Merger M;
+ std::vector<std::string> NewFiles;
+ EXPECT_TRUE(M.Parse(Input, true));
+ EXPECT_EQ(NumNewFeatures, M.Merge(&NewFiles));
+ EQ(NewFiles, Result);
+}
+
+TEST(Merge, Good) {
+ Merger M;
+
+ EXPECT_TRUE(M.Parse("1\n0\nAA\n", false));
+ EXPECT_EQ(M.Files.size(), 1U);
+ EXPECT_EQ(M.NumFilesInFirstCorpus, 0U);
+ EXPECT_EQ(M.Files[0].Name, "AA");
+ EXPECT_TRUE(M.LastFailure.empty());
+ EXPECT_EQ(M.FirstNotProcessedFile, 0U);
+
+ EXPECT_TRUE(M.Parse("2\n1\nAA\nBB\nSTARTED 0 42\n", false));
+ EXPECT_EQ(M.Files.size(), 2U);
+ EXPECT_EQ(M.NumFilesInFirstCorpus, 1U);
+ EXPECT_EQ(M.Files[0].Name, "AA");
+ EXPECT_EQ(M.Files[1].Name, "BB");
+ EXPECT_EQ(M.LastFailure, "AA");
+ EXPECT_EQ(M.FirstNotProcessedFile, 1U);
+
+ EXPECT_TRUE(M.Parse("3\n1\nAA\nBB\nC\n"
+ "STARTED 0 1000\n"
+ "DONE 0 1 2 3\n"
+ "STARTED 1 1001\n"
+ "DONE 1 4 5 6 \n"
+ "STARTED 2 1002\n"
+ "", true));
+ EXPECT_EQ(M.Files.size(), 3U);
+ EXPECT_EQ(M.NumFilesInFirstCorpus, 1U);
+ EXPECT_EQ(M.Files[0].Name, "AA");
+ EXPECT_EQ(M.Files[0].Size, 1000U);
+ EXPECT_EQ(M.Files[1].Name, "BB");
+ EXPECT_EQ(M.Files[1].Size, 1001U);
+ EXPECT_EQ(M.Files[2].Name, "C");
+ EXPECT_EQ(M.Files[2].Size, 1002U);
+ EXPECT_EQ(M.LastFailure, "C");
+ EXPECT_EQ(M.FirstNotProcessedFile, 3U);
+ EQ(M.Files[0].Features, {1, 2, 3});
+ EQ(M.Files[1].Features, {4, 5, 6});
+
+
+ std::vector<std::string> NewFiles;
+
+ EXPECT_TRUE(M.Parse("3\n2\nAA\nBB\nC\n"
+ "STARTED 0 1000\nDONE 0 1 2 3\n"
+ "STARTED 1 1001\nDONE 1 4 5 6 \n"
+ "STARTED 2 1002\nDONE 2 6 1 3 \n"
+ "", true));
+ EXPECT_EQ(M.Files.size(), 3U);
+ EXPECT_EQ(M.NumFilesInFirstCorpus, 2U);
+ EXPECT_TRUE(M.LastFailure.empty());
+ EXPECT_EQ(M.FirstNotProcessedFile, 3U);
+ EQ(M.Files[0].Features, {1, 2, 3});
+ EQ(M.Files[1].Features, {4, 5, 6});
+ EQ(M.Files[2].Features, {1, 3, 6});
+ EXPECT_EQ(0U, M.Merge(&NewFiles));
+ EQ(NewFiles, {});
+
+ EXPECT_TRUE(M.Parse("3\n1\nA\nB\nC\n"
+ "STARTED 0 1000\nDONE 0 1 2 3\n"
+ "STARTED 1 1001\nDONE 1 4 5 6 \n"
+ "STARTED 2 1002\nDONE 2 6 1 3\n"
+ "", true));
+ EQ(M.Files[0].Features, {1, 2, 3});
+ EQ(M.Files[1].Features, {4, 5, 6});
+ EQ(M.Files[2].Features, {1, 3, 6});
+ EXPECT_EQ(3U, M.Merge(&NewFiles));
+ EQ(NewFiles, {"B"});
+}
+
+TEST(Merge, Merge) {
+
+ Merge("3\n1\nA\nB\nC\n"
+ "STARTED 0 1000\nDONE 0 1 2 3\n"
+ "STARTED 1 1001\nDONE 1 4 5 6 \n"
+ "STARTED 2 1002\nDONE 2 6 1 3 \n",
+ {"B"}, 3);
+
+ Merge("3\n0\nA\nB\nC\n"
+ "STARTED 0 2000\nDONE 0 1 2 3\n"
+ "STARTED 1 1001\nDONE 1 4 5 6 \n"
+ "STARTED 2 1002\nDONE 2 6 1 3 \n",
+ {"A", "B", "C"}, 6);
+
+ Merge("4\n0\nA\nB\nC\nD\n"
+ "STARTED 0 2000\nDONE 0 1 2 3\n"
+ "STARTED 1 1101\nDONE 1 4 5 6 \n"
+ "STARTED 2 1102\nDONE 2 6 1 3 100 \n"
+ "STARTED 3 1000\nDONE 3 1 \n",
+ {"A", "B", "C", "D"}, 7);
+
+ Merge("4\n1\nA\nB\nC\nD\n"
+ "STARTED 0 2000\nDONE 0 4 5 6 7 8\n"
+ "STARTED 1 1100\nDONE 1 1 2 3 \n"
+ "STARTED 2 1100\nDONE 2 2 3 \n"
+ "STARTED 3 1000\nDONE 3 1 \n",
+ {"B", "D"}, 3);
+}
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/InitializeTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/InitializeTest.cpp
new file mode 100644
index 0000000000..0d6a0fda09
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/InitializeTest.cpp
@@ -0,0 +1,28 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Make sure LLVMFuzzerInitialize is called.
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static char *argv0;
+
+extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) {
+ assert(*argc > 0);
+ argv0 = **argv;
+ fprintf(stderr, "LLVMFuzzerInitialize: %s\n", argv0);
+ return 0;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ if (Size == strlen(argv0) &&
+ !strncmp(reinterpret_cast<const char *>(Data), argv0, Size)) {
+ fprintf(stderr, "BINGO %s\n", argv0);
+ exit(1);
+ }
+ return 0;
+}
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/LeakTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/LeakTest.cpp
new file mode 100644
index 0000000000..22e5164050
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/LeakTest.cpp
@@ -0,0 +1,17 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Test with a leak.
+#include <cstdint>
+#include <cstddef>
+
+static volatile void *Sink;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ if (Size > 0 && *Data == 'H') {
+ Sink = new int;
+ Sink = nullptr;
+ }
+ return 0;
+}
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/LeakTimeoutTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/LeakTimeoutTest.cpp
new file mode 100644
index 0000000000..4f31b3e52c
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/LeakTimeoutTest.cpp
@@ -0,0 +1,17 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Test with a leak.
+#include <cstdint>
+#include <cstddef>
+
+static volatile int *Sink;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ if (!Size) return 0;
+ Sink = new int;
+ Sink = new int;
+ while (Sink) *Sink = 0; // Infinite loop.
+ return 0;
+}
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/LoadTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/LoadTest.cpp
new file mode 100644
index 0000000000..c1780d5c7b
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/LoadTest.cpp
@@ -0,0 +1,22 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Simple test for a fuzzer: find interesting value of array index.
+#include <assert.h>
+#include <cstdint>
+#include <cstring>
+#include <cstddef>
+#include <iostream>
+
+static volatile int Sink;
+const int kArraySize = 1234567;
+int array[kArraySize];
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ if (Size < 8) return 0;
+ size_t a = 0;
+ memcpy(&a, Data, 8);
+ Sink = array[a % (kArraySize + 1)];
+ return 0;
+}
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/MemcmpTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/MemcmpTest.cpp
new file mode 100644
index 0000000000..fdbf94683f
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/MemcmpTest.cpp
@@ -0,0 +1,31 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Simple test for a fuzzer. The fuzzer must find a particular string.
+#include <cstring>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ // TODO: check other sizes.
+ if (Size >= 8 && memcmp(Data, "01234567", 8) == 0) {
+ if (Size >= 12 && memcmp(Data + 8, "ABCD", 4) == 0) {
+ if (Size >= 14 && memcmp(Data + 12, "XY", 2) == 0) {
+ if (Size >= 17 && memcmp(Data + 14, "KLM", 3) == 0) {
+ if (Size >= 27 && memcmp(Data + 17, "ABCDE-GHIJ", 10) == 0){
+ fprintf(stderr, "BINGO %zd\n", Size);
+ for (size_t i = 0; i < Size; i++) {
+ uint8_t C = Data[i];
+ if (C >= 32 && C < 127)
+ fprintf(stderr, "%c", C);
+ }
+ fprintf(stderr, "\n");
+ exit(1);
+ }
+ }
+ }
+ }
+ }
+ return 0;
+}
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/NthRunCrashTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/NthRunCrashTest.cpp
new file mode 100644
index 0000000000..b43e69e51b
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/NthRunCrashTest.cpp
@@ -0,0 +1,18 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Crash on the N-th execution.
+#include <cstdint>
+#include <cstddef>
+#include <iostream>
+
+static int Counter;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ if (Counter++ == 1000) {
+ std::cout << "BINGO; Found the target, exiting\n";
+ exit(1);
+ }
+ return 0;
+}
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/NullDerefOnEmptyTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/NullDerefOnEmptyTest.cpp
new file mode 100644
index 0000000000..153710920a
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/NullDerefOnEmptyTest.cpp
@@ -0,0 +1,19 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Simple test for a fuzzer. The fuzzer must find the empty string.
+#include <cstdint>
+#include <cstdlib>
+#include <cstddef>
+#include <iostream>
+
+static volatile int *Null = 0;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ if (Size == 0) {
+ std::cout << "Found the target, dereferencing NULL\n";
+ *Null = 1;
+ }
+ return 0;
+}
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/NullDerefTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/NullDerefTest.cpp
new file mode 100644
index 0000000000..3f03d24981
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/NullDerefTest.cpp
@@ -0,0 +1,26 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Simple test for a fuzzer. The fuzzer must find the string "Hi!".
+#include <cstdint>
+#include <cstdlib>
+#include <cstddef>
+#include <iostream>
+
+static volatile int Sink;
+static volatile int *Null = 0;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ if (Size > 0 && Data[0] == 'H') {
+ Sink = 1;
+ if (Size > 1 && Data[1] == 'i') {
+ Sink = 2;
+ if (Size > 2 && Data[2] == '!') {
+ std::cout << "Found the target, dereferencing NULL\n";
+ *Null = 1;
+ }
+ }
+ }
+ return 0;
+}
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/OneHugeAllocTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/OneHugeAllocTest.cpp
new file mode 100644
index 0000000000..8d3d1d6d30
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/OneHugeAllocTest.cpp
@@ -0,0 +1,28 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Tests OOM handling when there is a single large allocation.
+#include <assert.h>
+#include <cstdint>
+#include <cstdlib>
+#include <cstddef>
+#include <cstring>
+#include <iostream>
+
+static volatile char *SinkPtr;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ if (Size > 0 && Data[0] == 'H') {
+ if (Size > 1 && Data[1] == 'i') {
+ if (Size > 2 && Data[2] == '!') {
+ size_t kSize = (size_t)1 << 31;
+ char *p = new char[kSize];
+ memset(p, 0, kSize);
+ SinkPtr = p;
+ delete [] p;
+ }
+ }
+ }
+ return 0;
+}
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/OutOfMemorySingleLargeMallocTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/OutOfMemorySingleLargeMallocTest.cpp
new file mode 100644
index 0000000000..ea23a601aa
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/OutOfMemorySingleLargeMallocTest.cpp
@@ -0,0 +1,27 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Tests OOM handling.
+#include <assert.h>
+#include <cstdint>
+#include <cstdlib>
+#include <cstddef>
+#include <cstring>
+#include <iostream>
+
+static volatile char *SinkPtr;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ if (Size > 0 && Data[0] == 'H') {
+ if (Size > 1 && Data[1] == 'i') {
+ if (Size > 2 && Data[2] == '!') {
+ size_t kSize = 0xff000000U;
+ char *p = new char[kSize];
+ SinkPtr = p;
+ delete [] p;
+ }
+ }
+ }
+ return 0;
+}
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/OutOfMemoryTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/OutOfMemoryTest.cpp
new file mode 100644
index 0000000000..078a39ee1f
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/OutOfMemoryTest.cpp
@@ -0,0 +1,31 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Tests OOM handling.
+#include <assert.h>
+#include <cstdint>
+#include <cstdlib>
+#include <cstddef>
+#include <cstring>
+#include <iostream>
+#include <thread>
+
+static volatile char *SinkPtr;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ if (Size > 0 && Data[0] == 'H') {
+ if (Size > 1 && Data[1] == 'i') {
+ if (Size > 2 && Data[2] == '!') {
+ while (true) {
+ size_t kSize = 1 << 28;
+ char *p = new char[kSize];
+ memset(p, 0, kSize);
+ SinkPtr = p;
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+ }
+ }
+ }
+ }
+ return 0;
+}
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/RepeatedBytesTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/RepeatedBytesTest.cpp
new file mode 100644
index 0000000000..2fa6c78c26
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/RepeatedBytesTest.cpp
@@ -0,0 +1,29 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Simple test for a fuzzer. The fuzzer must find repeated bytes.
+#include <assert.h>
+#include <cstdint>
+#include <cstdlib>
+#include <cstddef>
+#include <iostream>
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ assert(Data);
+ // Looking for AAAAAAAAAAAAAAAAAAAAAA or some such.
+ size_t CurA = 0, MaxA = 0;
+ for (size_t i = 0; i < Size; i++) {
+ // Make sure there are no conditionals in the loop so that
+ // coverage can't help the fuzzer.
+ int EQ = Data[i] == 'A';
+ CurA = EQ * (CurA + 1);
+ int GT = CurA > MaxA;
+ MaxA = GT * CurA + (!GT) * MaxA;
+ }
+ if (MaxA >= 20) {
+ std::cout << "BINGO; Found the target (Max: " << MaxA << "), exiting\n";
+ exit(0);
+ }
+ return 0;
+}
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/RepeatedMemcmp.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/RepeatedMemcmp.cpp
new file mode 100644
index 0000000000..a327bbee78
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/RepeatedMemcmp.cpp
@@ -0,0 +1,22 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+
+#include <cstring>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ int Matches = 0;
+ for (size_t i = 0; i + 2 < Size; i += 3) {
+ const char *Pat = i % 2 ? "foo" : "bar";
+ if (!memcmp(Data + i, Pat, 3))
+ Matches++;
+ }
+ if (Matches > 20) {
+ fprintf(stderr, "BINGO!\n");
+ exit(1);
+ }
+ return 0;
+}
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/ShrinkControlFlowTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/ShrinkControlFlowTest.cpp
new file mode 100644
index 0000000000..0fd7c5e9a1
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/ShrinkControlFlowTest.cpp
@@ -0,0 +1,28 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Test that we can find the minimal item in the corpus (3 bytes: "FUZ").
+#include <cstdint>
+#include <cstdlib>
+#include <cstddef>
+#include <cstring>
+#include <cstdio>
+
+static volatile int Sink;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ int8_t Ids[256];
+ memset(Ids, -1, sizeof(Ids));
+ for (size_t i = 0; i < Size; i++)
+ if (Ids[Data[i]] == -1)
+ Ids[Data[i]] = i;
+ int F = Ids[(unsigned char)'F'];
+ int U = Ids[(unsigned char)'U'];
+ int Z = Ids[(unsigned char)'Z'];
+ if (F >= 0 && U > F && Z > U) {
+ Sink++;
+ //fprintf(stderr, "IDS: %d %d %d\n", F, U, Z);
+ }
+ return 0;
+}
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/ShrinkValueProfileTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/ShrinkValueProfileTest.cpp
new file mode 100644
index 0000000000..026b8ce265
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/ShrinkValueProfileTest.cpp
@@ -0,0 +1,22 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Test that we can find the minimal item in the corpus (3 bytes: "FUZ").
+#include <cstdint>
+#include <cstdlib>
+#include <cstddef>
+#include <cstring>
+#include <cstdio>
+
+static volatile uint32_t Sink;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ if (Size < sizeof(uint32_t)) return 0;
+ uint32_t X, Y;
+ size_t Offset = Size < 8 ? 0 : Size / 2;
+ memcpy(&X, Data + Offset, sizeof(uint32_t));
+ memcpy(&Y, "FUZZ", sizeof(uint32_t));
+ Sink = X == Y;
+ return 0;
+}
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/SignedIntOverflowTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/SignedIntOverflowTest.cpp
new file mode 100644
index 0000000000..7df32ad579
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/SignedIntOverflowTest.cpp
@@ -0,0 +1,28 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Test for signed-integer-overflow.
+#include <assert.h>
+#include <cstdint>
+#include <cstdlib>
+#include <cstddef>
+#include <iostream>
+#include <climits>
+
+static volatile int Sink;
+static int Large = INT_MAX;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ assert(Data);
+ if (Size > 0 && Data[0] == 'H') {
+ Sink = 1;
+ if (Size > 1 && Data[1] == 'i') {
+ Sink = 2;
+ if (Size > 2 && Data[2] == '!') {
+ Large++; // int overflow.
+ }
+ }
+ }
+ return 0;
+}
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/SimpleCmpTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/SimpleCmpTest.cpp
new file mode 100644
index 0000000000..0220c30f9a
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/SimpleCmpTest.cpp
@@ -0,0 +1,46 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Simple test for a fuzzer. The fuzzer must find several narrow ranges.
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
+#include <cstdio>
+
+extern int AllLines[];
+
+bool PrintOnce(int Line) {
+ if (!AllLines[Line])
+ fprintf(stderr, "Seen line %d\n", Line);
+ AllLines[Line] = 1;
+ return true;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ if (Size != 22) return 0;
+ uint64_t x = 0;
+ int64_t y = 0;
+ int32_t z = 0;
+ uint16_t a = 0;
+ memcpy(&x, Data, 8); // 8
+ memcpy(&y, Data + 8, 8); // 16
+ memcpy(&z, Data + 16, sizeof(z)); // 20
+ memcpy(&a, Data + 20, sizeof(a)); // 22
+
+ if (x > 1234567890 && PrintOnce(__LINE__) &&
+ x < 1234567895 && PrintOnce(__LINE__) &&
+ a == 0x4242 && PrintOnce(__LINE__) &&
+ y >= 987654321 && PrintOnce(__LINE__) &&
+ y <= 987654325 && PrintOnce(__LINE__) &&
+ z < -10000 && PrintOnce(__LINE__) &&
+ z >= -10005 && PrintOnce(__LINE__) &&
+ z != -10003 && PrintOnce(__LINE__) &&
+ true) {
+ fprintf(stderr, "BINGO; Found the target: size %zd (%zd, %zd, %d, %d), exiting.\n",
+ Size, x, y, z, a);
+ exit(1);
+ }
+ return 0;
+}
+
+int AllLines[__LINE__ + 1]; // Must be the last line.
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/SimpleDictionaryTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/SimpleDictionaryTest.cpp
new file mode 100644
index 0000000000..cd7292bd00
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/SimpleDictionaryTest.cpp
@@ -0,0 +1,29 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Simple test for a fuzzer.
+// The fuzzer must find a string based on dictionary words:
+// "Elvis"
+// "Presley"
+#include <cstdint>
+#include <cstdlib>
+#include <cstddef>
+#include <cstring>
+#include <iostream>
+
+static volatile int Zero = 0;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ const char *Expected = "ElvisPresley";
+ if (Size < strlen(Expected)) return 0;
+ size_t Match = 0;
+ for (size_t i = 0; Expected[i]; i++)
+ if (Expected[i] + Zero == Data[i])
+ Match++;
+ if (Match == strlen(Expected)) {
+ std::cout << "BINGO; Found the target, exiting\n";
+ exit(1);
+ }
+ return 0;
+}
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/SimpleHashTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/SimpleHashTest.cpp
new file mode 100644
index 0000000000..00599de78e
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/SimpleHashTest.cpp
@@ -0,0 +1,40 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// This test computes a checksum of the data (all but the last 4 bytes),
+// and then compares the last 4 bytes with the computed value.
+// A fuzzer with cmp traces is expected to defeat this check.
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
+#include <cstdio>
+
+// A modified jenkins_one_at_a_time_hash initialized by non-zero,
+// so that simple_hash(0) != 0. See also
+// https://en.wikipedia.org/wiki/Jenkins_hash_function
+static uint32_t simple_hash(const uint8_t *Data, size_t Size) {
+ uint32_t Hash = 0x12039854;
+ for (uint32_t i = 0; i < Size; i++) {
+ Hash += Data[i];
+ Hash += (Hash << 10);
+ Hash ^= (Hash >> 6);
+ }
+ Hash += (Hash << 3);
+ Hash ^= (Hash >> 11);
+ Hash += (Hash << 15);
+ return Hash;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ if (Size < 14)
+ return 0;
+
+ uint32_t Hash = simple_hash(&Data[0], Size - 4);
+ uint32_t Want = reinterpret_cast<const uint32_t *>(&Data[Size - 4])[0];
+ if (Hash != Want)
+ return 0;
+ fprintf(stderr, "BINGO; simple_hash defeated: %x == %x\n", (unsigned int)Hash,
+ (unsigned int)Want);
+ exit(1);
+ return 0;
+}
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/SimpleTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/SimpleTest.cpp
new file mode 100644
index 0000000000..e53ea160ed
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/SimpleTest.cpp
@@ -0,0 +1,27 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Simple test for a fuzzer. The fuzzer must find the string "Hi!".
+#include <assert.h>
+#include <cstdint>
+#include <cstdlib>
+#include <cstddef>
+#include <iostream>
+
+static volatile int Sink;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ assert(Data);
+ if (Size > 0 && Data[0] == 'H') {
+ Sink = 1;
+ if (Size > 1 && Data[1] == 'i') {
+ Sink = 2;
+ if (Size > 2 && Data[2] == '!') {
+ std::cout << "BINGO; Found the target, exiting\n";
+ exit(0);
+ }
+ }
+ }
+ return 0;
+}
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/SimpleThreadedTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/SimpleThreadedTest.cpp
new file mode 100644
index 0000000000..5f02d3f845
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/SimpleThreadedTest.cpp
@@ -0,0 +1,25 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Threaded test for a fuzzer. The fuzzer should find "H"
+#include <assert.h>
+#include <cstdint>
+#include <cstddef>
+#include <cstring>
+#include <iostream>
+#include <thread>
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ auto C = [&] {
+ if (Size >= 2 && Data[0] == 'H') {
+ std::cout << "BINGO; Found the target, exiting\n";
+ abort();
+ }
+ };
+ std::thread T[] = {std::thread(C), std::thread(C), std::thread(C),
+ std::thread(C), std::thread(C), std::thread(C)};
+ for (auto &X : T)
+ X.join();
+ return 0;
+}
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/SingleMemcmpTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/SingleMemcmpTest.cpp
new file mode 100644
index 0000000000..c73f68a7ee
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/SingleMemcmpTest.cpp
@@ -0,0 +1,17 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Simple test for a fuzzer. The fuzzer must find a particular string.
+#include <cstring>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ char *S = (char*)Data;
+ if (Size >= 6 && !memcmp(S, "qwerty", 6)) {
+ fprintf(stderr, "BINGO\n");
+ exit(1);
+ }
+ return 0;
+}
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/SingleStrcmpTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/SingleStrcmpTest.cpp
new file mode 100644
index 0000000000..73470b527e
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/SingleStrcmpTest.cpp
@@ -0,0 +1,17 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Simple test for a fuzzer. The fuzzer must find a particular string.
+#include <cstring>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ char *S = (char*)Data;
+ if (Size >= 7 && !strcmp(S, "qwerty")) {
+ fprintf(stderr, "BINGO\n");
+ exit(1);
+ }
+ return 0;
+}
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/SingleStrncmpTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/SingleStrncmpTest.cpp
new file mode 100644
index 0000000000..dbcc464b0a
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/SingleStrncmpTest.cpp
@@ -0,0 +1,17 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Simple test for a fuzzer. The fuzzer must find a particular string.
+#include <cstring>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ char *S = (char*)Data;
+ if (Size >= 6 && !strncmp(S, "qwerty", 6)) {
+ fprintf(stderr, "BINGO\n");
+ exit(1);
+ }
+ return 0;
+}
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/SpamyTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/SpamyTest.cpp
new file mode 100644
index 0000000000..d294d4dc53
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/SpamyTest.cpp
@@ -0,0 +1,21 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// The test spams to stderr and stdout.
+#include <assert.h>
+#include <cstdint>
+#include <cstdio>
+#include <cstddef>
+#include <iostream>
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ assert(Data);
+ printf("PRINTF_STDOUT\n");
+ fflush(stdout);
+ fprintf(stderr, "PRINTF_STDERR\n");
+ std::cout << "STREAM_COUT\n";
+ std::cout.flush();
+ std::cerr << "STREAM_CERR\n";
+ return 0;
+}
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/StrcmpTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/StrcmpTest.cpp
new file mode 100644
index 0000000000..cd91dda76f
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/StrcmpTest.cpp
@@ -0,0 +1,32 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Break through a series of strcmp.
+#include <cstring>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <cassert>
+
+bool Eq(const uint8_t *Data, size_t Size, const char *Str) {
+ char Buff[1024];
+ size_t Len = strlen(Str);
+ if (Size < Len) return false;
+ if (Len >= sizeof(Buff)) return false;
+ memcpy(Buff, (char*)Data, Len);
+ Buff[Len] = 0;
+ int res = strcmp(Buff, Str);
+ return res == 0;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ if (Eq(Data, Size, "ABC") &&
+ Size >= 3 && Eq(Data + 3, Size - 3, "QWER") &&
+ Size >= 7 && Eq(Data + 7, Size - 7, "ZXCVN") &&
+ Size >= 14 && Data[13] == 42
+ ) {
+ fprintf(stderr, "BINGO\n");
+ exit(1);
+ }
+ return 0;
+}
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/StrncmpOOBTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/StrncmpOOBTest.cpp
new file mode 100644
index 0000000000..f70b003afa
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/StrncmpOOBTest.cpp
@@ -0,0 +1,21 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Test that libFuzzer itself does not read out of bounds.
+#include <assert.h>
+#include <cstdint>
+#include <cstring>
+#include <cstdlib>
+#include <cstddef>
+#include <iostream>
+
+static volatile int Sink;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ if (Size < 5) return 0;
+ const char *Ch = reinterpret_cast<const char *>(Data);
+ if (Ch[Size - 3] == 'a')
+ Sink = strncmp(Ch + Size - 3, "abcdefg", 6);
+ return 0;
+}
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/StrncmpTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/StrncmpTest.cpp
new file mode 100644
index 0000000000..5ffd011dcd
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/StrncmpTest.cpp
@@ -0,0 +1,28 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Simple test for a fuzzer. The fuzzer must find a particular string.
+#include <cstring>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+
+static volatile int sink;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ // TODO: check other sizes.
+ char *S = (char*)Data;
+ if (Size >= 8 && strncmp(S, "123", 8))
+ sink = 1;
+ if (Size >= 8 && strncmp(S, "01234567", 8) == 0) {
+ if (Size >= 12 && strncmp(S + 8, "ABCD", 4) == 0) {
+ if (Size >= 14 && strncmp(S + 12, "XY", 2) == 0) {
+ if (Size >= 17 && strncmp(S + 14, "KLM", 3) == 0) {
+ fprintf(stderr, "BINGO\n");
+ exit(1);
+ }
+ }
+ }
+ }
+ return 0;
+}
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/StrstrTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/StrstrTest.cpp
new file mode 100644
index 0000000000..f021e75ec0
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/StrstrTest.cpp
@@ -0,0 +1,28 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Test strstr and strcasestr hooks.
+#include <string>
+#include <string.h>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+
+// Windows does not have strcasestr and memmem, so we are not testing them.
+#ifdef _WIN32
+#define strcasestr strstr
+#define memmem(a, b, c, d) true
+#endif
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ if (Size < 4) return 0;
+ std::string s(reinterpret_cast<const char*>(Data), Size);
+ if (strstr(s.c_str(), "FUZZ") &&
+ strcasestr(s.c_str(), "aBcD") &&
+ memmem(s.data(), s.size(), "kuku", 4)
+ ) {
+ fprintf(stderr, "BINGO\n");
+ exit(1);
+ }
+ return 0;
+}
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/SwapCmpTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/SwapCmpTest.cpp
new file mode 100644
index 0000000000..f79db4ccf7
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/SwapCmpTest.cpp
@@ -0,0 +1,34 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// The fuzzer must find several constants with swapped bytes.
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
+#include <cstdio>
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ if (Size < 14) return 0;
+ uint64_t x = 0;
+ uint32_t y = 0;
+ uint16_t z = 0;
+ memcpy(&x, Data, sizeof(x));
+ memcpy(&y, Data + Size / 2, sizeof(y));
+ memcpy(&z, Data + Size - sizeof(z), sizeof(z));
+
+ x = __builtin_bswap64(x);
+ y = __builtin_bswap32(y);
+ z = __builtin_bswap16(z);
+
+ if (x == 0x46555A5A5A5A5546ULL &&
+ z == 0x4F4B &&
+ y == 0x66757A7A &&
+ true
+ ) {
+ if (Data[Size - 3] == 'z') {
+ fprintf(stderr, "BINGO; Found the target\n");
+ exit(1);
+ }
+ }
+ return 0;
+}
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/Switch2Test.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/Switch2Test.cpp
new file mode 100644
index 0000000000..3c6a300490
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/Switch2Test.cpp
@@ -0,0 +1,35 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Simple test for a fuzzer. The fuzzer must find the interesting switch value.
+#include <cstdint>
+#include <cstdlib>
+#include <cstdio>
+#include <cstring>
+#include <cstddef>
+
+int Switch(int a) {
+ switch(a) {
+ case 100001: return 1;
+ case 100002: return 2;
+ case 100003: return 4;
+ }
+ return 0;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ const int N = 3;
+ if (Size < N * sizeof(int)) return 0;
+ int Res = 0;
+ for (int i = 0; i < N; i++) {
+ int X;
+ memcpy(&X, Data + i * sizeof(int), sizeof(int));
+ Res += Switch(X);
+ }
+ if (Res == 5 || Res == 3 || Res == 6 || Res == 7) {
+ fprintf(stderr, "BINGO; Found the target, exiting; Res=%d\n", Res);
+ exit(1);
+ }
+ return 0;
+}
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/SwitchTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/SwitchTest.cpp
new file mode 100644
index 0000000000..74e86c06cb
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/SwitchTest.cpp
@@ -0,0 +1,58 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Simple test for a fuzzer. The fuzzer must find the interesting switch value.
+#include <cstdint>
+#include <cstdlib>
+#include <cstdio>
+#include <cstring>
+#include <cstddef>
+
+static volatile int Sink;
+
+template<class T>
+bool Switch(const uint8_t *Data, size_t Size) {
+ T X;
+ if (Size < sizeof(X)) return false;
+ memcpy(&X, Data, sizeof(X));
+ switch (X) {
+ case 1: Sink = __LINE__; break;
+ case 101: Sink = __LINE__; break;
+ case 1001: Sink = __LINE__; break;
+ case 10001: Sink = __LINE__; break;
+// case 100001: Sink = __LINE__; break;
+// case 1000001: Sink = __LINE__; break;
+ case 10000001: Sink = __LINE__; break;
+ case 100000001: return true;
+ }
+ return false;
+}
+
+bool ShortSwitch(const uint8_t *Data, size_t Size) {
+ short X;
+ if (Size < sizeof(short)) return false;
+ memcpy(&X, Data, sizeof(short));
+ switch(X) {
+ case 42: Sink = __LINE__; break;
+ case 402: Sink = __LINE__; break;
+ case 4002: Sink = __LINE__; break;
+ case 5002: Sink = __LINE__; break;
+ case 7002: Sink = __LINE__; break;
+ case 9002: Sink = __LINE__; break;
+ case 14002: Sink = __LINE__; break;
+ case 21402: return true;
+ }
+ return false;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ if (Size >= 4 && Switch<int>(Data, Size) &&
+ Size >= 12 && Switch<uint64_t>(Data + 4, Size - 4) &&
+ Size >= 14 && ShortSwitch(Data + 12, 2)
+ ) {
+ fprintf(stderr, "BINGO; Found the target, exiting\n");
+ exit(1);
+ }
+ return 0;
+}
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/ThreadedLeakTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/ThreadedLeakTest.cpp
new file mode 100644
index 0000000000..7511071108
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/ThreadedLeakTest.cpp
@@ -0,0 +1,18 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// The fuzzer should find a leak in a non-main thread.
+#include <cstdint>
+#include <cstddef>
+#include <thread>
+
+static volatile int *Sink;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ if (Size == 0) return 0;
+ if (Data[0] != 'F') return 0;
+ std::thread T([&] { Sink = new int; });
+ T.join();
+ return 0;
+}
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/ThreadedTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/ThreadedTest.cpp
new file mode 100644
index 0000000000..09137a9a70
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/ThreadedTest.cpp
@@ -0,0 +1,26 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Threaded test for a fuzzer. The fuzzer should not crash.
+#include <assert.h>
+#include <cstdint>
+#include <cstddef>
+#include <cstring>
+#include <thread>
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ if (Size < 8) return 0;
+ assert(Data);
+ auto C = [&] {
+ size_t Res = 0;
+ for (size_t i = 0; i < Size / 2; i++)
+ Res += memcmp(Data, Data + Size / 2, 4);
+ return Res;
+ };
+ std::thread T[] = {std::thread(C), std::thread(C), std::thread(C),
+ std::thread(C), std::thread(C), std::thread(C)};
+ for (auto &X : T)
+ X.join();
+ return 0;
+}
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/TimeoutEmptyTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/TimeoutEmptyTest.cpp
new file mode 100644
index 0000000000..8066f480b6
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/TimeoutEmptyTest.cpp
@@ -0,0 +1,14 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Simple test for a fuzzer. The fuzzer must find the empty string.
+#include <cstdint>
+#include <cstddef>
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ static volatile int Zero = 0;
+ if (!Size)
+ while(!Zero)
+ ;
+ return 0;
+}
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/TimeoutTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/TimeoutTest.cpp
new file mode 100644
index 0000000000..f8107012c8
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/TimeoutTest.cpp
@@ -0,0 +1,26 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Simple test for a fuzzer. The fuzzer must find the string "Hi!".
+#include <cstdint>
+#include <cstdlib>
+#include <cstddef>
+#include <iostream>
+
+static volatile int Sink;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ if (Size > 0 && Data[0] == 'H') {
+ Sink = 1;
+ if (Size > 1 && Data[1] == 'i') {
+ Sink = 2;
+ if (Size > 2 && Data[2] == '!') {
+ Sink = 2;
+ while (Sink)
+ ;
+ }
+ }
+ }
+ return 0;
+}
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/TraceMallocTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/TraceMallocTest.cpp
new file mode 100644
index 0000000000..43e6950e18
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/TraceMallocTest.cpp
@@ -0,0 +1,27 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Tests -trace_malloc
+#include <assert.h>
+#include <cstdint>
+#include <cstdlib>
+#include <cstddef>
+#include <iostream>
+
+int *Ptr;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ if (!Size) return 0;
+ if (*Data == 1) {
+ delete Ptr;
+ Ptr = nullptr;
+ } else if (*Data == 2) {
+ delete Ptr;
+ Ptr = new int;
+ } else if (*Data == 3) {
+ if (!Ptr)
+ Ptr = new int;
+ }
+ return 0;
+}
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/UninstrumentedTest.cpp b/json4cpp/tests/thirdparty/Fuzzer/test/UninstrumentedTest.cpp
new file mode 100644
index 0000000000..ffe952c749
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/UninstrumentedTest.cpp
@@ -0,0 +1,11 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// This test should not be instrumented.
+#include <cstdint>
+#include <cstddef>
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ return 0;
+}
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/afl-driver-extra-stats.test b/json4cpp/tests/thirdparty/Fuzzer/test/afl-driver-extra-stats.test
new file mode 100644
index 0000000000..81e384e7da
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/afl-driver-extra-stats.test
@@ -0,0 +1,28 @@
+; Test that not specifying an extra stats file isn't broken.
+RUN: unset AFL_DRIVER_EXTRA_STATS_FILENAME
+RUN: AFLDriverTest
+
+; Test that specifying an invalid extra stats file causes a crash.
+RUN: ASAN_OPTIONS= AFL_DRIVER_EXTRA_STATS_FILENAME=%T not --crash AFLDriverTest
+
+; Test that specifying a corrupted stats file causes a crash.
+echo "peak_rss_mb :0" > %t
+ASAN_OPTIONS= AFL_DRIVER_EXTRA_STATS_FILENAME=%t not --crash AFLDriverTest
+
+; Test that specifying a valid nonexistent stats file works.
+RUN: rm -f %t
+RUN: AFL_DRIVER_EXTRA_STATS_FILENAME=%t AFLDriverTest
+RUN: [[ $(grep "peak_rss_mb\|slowest_unit_time_sec" %t | wc -l) -eq 2 ]]
+
+; Test that specifying a valid preexisting stats file works.
+RUN: printf "peak_rss_mb : 0\nslowest_unit_time_sec: 0\n" > %t
+RUN: AFL_DRIVER_EXTRA_STATS_FILENAME=%t AFLDriverTest
+; Check that both lines were printed.
+RUN: [[ $(grep "peak_rss_mb\|slowest_unit_time_sec" %t | wc -l) -eq 2 ]]
+
+; Test that peak_rss_mb and slowest_unit_time_in_secs are only updated when necessary.
+; Check that both lines have 9999 since there's no way we have exceeded that
+; amount of time or virtual memory.
+RUN: printf "peak_rss_mb : 9999\nslowest_unit_time_sec: 9999\n" > %t
+RUN: AFL_DRIVER_EXTRA_STATS_FILENAME=%t AFLDriverTest
+RUN: [[ $(grep "9999" %t | wc -l) -eq 2 ]]
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/afl-driver-stderr.test b/json4cpp/tests/thirdparty/Fuzzer/test/afl-driver-stderr.test
new file mode 100644
index 0000000000..c0f9c8398c
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/afl-driver-stderr.test
@@ -0,0 +1,10 @@
+; Test that not specifying a stderr file isn't broken.
+RUN: unset AFL_DRIVER_STDERR_DUPLICATE_FILENAME
+RUN: AFLDriverTest
+
+; Test that specifying an invalid file causes a crash.
+RUN: ASAN_OPTIONS= AFL_DRIVER_STDERR_DUPLICATE_FILENAME="%T" not --crash AFLDriverTest
+
+; Test that a file is created when specified as the duplicate stderr.
+RUN: AFL_DRIVER_STDERR_DUPLICATE_FILENAME=%t AFLDriverTest
+RUN: stat %t
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/caller-callee.test b/json4cpp/tests/thirdparty/Fuzzer/test/caller-callee.test
new file mode 100644
index 0000000000..76a951c5e0
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/caller-callee.test
@@ -0,0 +1,2 @@
+CHECK: BINGO
+RUN: not LLVMFuzzer-CallerCalleeTest -use_value_profile=1 -cross_over=0 -max_len=6 -seed=1 -runs=10000000 2>&1 | FileCheck %s
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/coverage.test b/json4cpp/tests/thirdparty/Fuzzer/test/coverage.test
new file mode 100644
index 0000000000..fa11be502e
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/coverage.test
@@ -0,0 +1,19 @@
+CHECK: COVERAGE:
+CHECK-DAG: COVERED: {{.*}}in LLVMFuzzerTestOneInput {{.*}}NullDerefTest.cpp:13
+CHECK-DAG: COVERED: {{.*}}in LLVMFuzzerTestOneInput {{.*}}NullDerefTest.cpp:14
+CHECK-DAG: COVERED: {{.*}}in LLVMFuzzerTestOneInput {{.*}}NullDerefTest.cpp:16
+CHECK-DAG: COVERED: {{.*}}in LLVMFuzzerTestOneInput {{.*}}NullDerefTest.cpp:19
+CHECK: COVERED_DIRS: {{.*}}lib/Fuzzer/test
+RUN: not LLVMFuzzer-NullDerefTest -print_coverage=1 2>&1 | FileCheck %s
+
+RUN: LLVMFuzzer-DSOTest -print_coverage=1 -runs=0 2>&1 | FileCheck %s --check-prefix=DSO
+DSO: COVERAGE:
+DSO-DAG: COVERED:{{.*}}DSO1{{.*}}DSO1.cpp
+DSO-DAG: COVERED:{{.*}}DSO2{{.*}}DSO2.cpp
+DSO-DAG: COVERED:{{.*}}LLVMFuzzerTestOneInput{{.*}}DSOTestMain
+DSO-DAG: UNCOVERED_LINE:{{.*}}DSO1{{.*}}DSO1.cpp
+DSO-DAG: UNCOVERED_LINE:{{.*}}DSO2{{.*}}DSO2.cpp
+DSO-DAG: UNCOVERED_FUNC: in Uncovered1
+DSO-DAG: UNCOVERED_FUNC: in Uncovered2
+DSO-DAG: UNCOVERED_LINE: in LLVMFuzzerTestOneInput
+DSO-DAG: UNCOVERED_FILE:{{.*}}DSOTestExtra.cpp
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/dict1.txt b/json4cpp/tests/thirdparty/Fuzzer/test/dict1.txt
new file mode 100644
index 0000000000..520d0cc7b7
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/dict1.txt
@@ -0,0 +1,4 @@
+# Dictionary for SimpleDictionaryTest
+
+a="Elvis"
+b="Presley"
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/dump_coverage.test b/json4cpp/tests/thirdparty/Fuzzer/test/dump_coverage.test
new file mode 100644
index 0000000000..9bd98daa36
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/dump_coverage.test
@@ -0,0 +1,16 @@
+RUN: DIR=%t_workdir
+RUN: BUILD_DIR=$(pwd)
+RUN: rm -rf $DIR && mkdir -p $DIR && cd $DIR
+RUN: not $BUILD_DIR/LLVMFuzzer-NullDerefTest -dump_coverage=1 2>&1 | FileCheck %s
+RUN: $BUILD_DIR/LLVMFuzzer-DSOTest -dump_coverage=1 -runs=0 2>&1 | FileCheck %s --check-prefix=DSO
+RUN: not $BUILD_DIR/LLVMFuzzer-NullDerefTest -dump_coverage=0 2>&1 | FileCheck %s --check-prefix=NOCOV
+RUN: rm -rf $DIR
+
+
+CHECK: SanitizerCoverage: ./LLVMFuzzer-NullDerefTest.{{.*}}.sancov {{.*}} PCs written
+
+DSO: SanitizerCoverage: ./LLVMFuzzer-DSOTest.{{.*}}.sancov {{.*}} PCs written
+DSO-DAG: SanitizerCoverage: ./libLLVMFuzzer-DSO1.{{.*}}.sancov {{.*}} PCs written
+DSO-DAG: SanitizerCoverage: ./libLLVMFuzzer-DSO2.{{.*}}.sancov {{.*}} PCs written
+
+NOCOV-NOT: SanitizerCoverage: {{.*}} PCs written
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-customcrossover.test b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-customcrossover.test
new file mode 100644
index 0000000000..28d39ce31d
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-customcrossover.test
@@ -0,0 +1,10 @@
+RUN: rm -rf %t/CustomCrossover
+RUN: mkdir -p %t/CustomCrossover
+RUN: echo "0123456789" > %t/CustomCrossover/digits
+RUN: echo "abcdefghij" > %t/CustomCrossover/chars
+RUN: not LLVMFuzzer-CustomCrossOverTest -seed=1 -use_memcmp=0 -runs=100000 %t/CustomCrossover 2>&1 | FileCheck %s --check-prefix=LLVMFuzzerCustomCrossover
+RUN: rm -rf %t/CustomCrossover
+
+LLVMFuzzerCustomCrossover: In LLVMFuzzerCustomCrossover
+LLVMFuzzerCustomCrossover: BINGO
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-custommutator.test b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-custommutator.test
new file mode 100644
index 0000000000..fcd740bf54
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-custommutator.test
@@ -0,0 +1,4 @@
+RUN: not LLVMFuzzer-CustomMutatorTest 2>&1 | FileCheck %s --check-prefix=LLVMFuzzerCustomMutator
+LLVMFuzzerCustomMutator: In LLVMFuzzerCustomMutator
+LLVMFuzzerCustomMutator: BINGO
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-dict.test b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-dict.test
new file mode 100644
index 0000000000..dec002f6a3
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-dict.test
@@ -0,0 +1,6 @@
+CHECK: BINGO
+Done1000000: Done 1000000 runs in
+
+RUN: not LLVMFuzzer-SimpleDictionaryTest -dict=%S/dict1.txt -seed=1 -runs=1000003 2>&1 | FileCheck %s
+RUN: LLVMFuzzer-SimpleDictionaryTest -seed=1 -runs=1000000 2>&1 | FileCheck %s --check-prefix=Done1000000
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-dirs.test b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-dirs.test
new file mode 100644
index 0000000000..63afe8dfcf
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-dirs.test
@@ -0,0 +1,15 @@
+RUN: rm -rf %t/SUB1
+RUN: mkdir -p %t/SUB1/SUB2/SUB3
+RUN: echo a > %t/SUB1/a
+RUN: echo b > %t/SUB1/SUB2/b
+RUN: echo c > %t/SUB1/SUB2/SUB3/c
+RUN: LLVMFuzzer-SimpleTest %t/SUB1 -runs=0 2>&1 | FileCheck %s --check-prefix=SUBDIRS
+SUBDIRS: READ units: 3
+RUN: echo zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz > %t/SUB1/long
+RUN: LLVMFuzzer-SimpleTest %t/SUB1 -runs=0 2>&1 | FileCheck %s --check-prefix=LONG
+LONG: INFO: -max_len is not provided, using 94
+RUN: rm -rf %t/SUB1
+
+RUN: not LLVMFuzzer-SimpleTest NONEXISTENT_DIR 2>&1 | FileCheck %s --check-prefix=NONEXISTENT_DIR
+NONEXISTENT_DIR: No such directory: NONEXISTENT_DIR; exiting
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-fdmask.test b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-fdmask.test
new file mode 100644
index 0000000000..abbc4bd641
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-fdmask.test
@@ -0,0 +1,30 @@
+RUN: LLVMFuzzer-SpamyTest -runs=1 2>&1 | FileCheck %s --check-prefix=FD_MASK_0
+RUN: LLVMFuzzer-SpamyTest -runs=1 -close_fd_mask=0 2>&1 | FileCheck %s --check-prefix=FD_MASK_0
+RUN: LLVMFuzzer-SpamyTest -runs=1 -close_fd_mask=1 2>&1 | FileCheck %s --check-prefix=FD_MASK_1
+RUN: LLVMFuzzer-SpamyTest -runs=1 -close_fd_mask=2 2>&1 | FileCheck %s --check-prefix=FD_MASK_2
+RUN: LLVMFuzzer-SpamyTest -runs=1 -close_fd_mask=3 2>&1 | FileCheck %s --check-prefix=FD_MASK_3
+
+FD_MASK_0: PRINTF_STDOUT
+FD_MASK_0: PRINTF_STDERR
+FD_MASK_0: STREAM_COUT
+FD_MASK_0: STREAM_CERR
+FD_MASK_0: INITED
+
+FD_MASK_1-NOT: PRINTF_STDOUT
+FD_MASK_1: PRINTF_STDERR
+FD_MASK_1-NOT: STREAM_COUT
+FD_MASK_1: STREAM_CERR
+FD_MASK_1: INITED
+
+FD_MASK_2: PRINTF_STDOUT
+FD_MASK_2-NOT: PRINTF_STDERR
+FD_MASK_2: STREAM_COUT
+FD_MASK_2-NOTE: STREAM_CERR
+FD_MASK_2: INITED
+
+FD_MASK_3-NOT: PRINTF_STDOUT
+FD_MASK_3-NOT: PRINTF_STDERR
+FD_MASK_3-NOT: STREAM_COUT
+FD_MASK_3-NOT: STREAM_CERR
+FD_MASK_3: INITED
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-finalstats.test b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-finalstats.test
new file mode 100644
index 0000000000..1cbcd10f04
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-finalstats.test
@@ -0,0 +1,11 @@
+RUN: LLVMFuzzer-SimpleTest -seed=1 -runs=77 -print_final_stats=1 2>&1 | FileCheck %s --check-prefix=FINAL_STATS
+FINAL_STATS: stat::number_of_executed_units: 77
+FINAL_STATS: stat::average_exec_per_sec: 0
+FINAL_STATS: stat::new_units_added:
+FINAL_STATS: stat::slowest_unit_time_sec: 0
+FINAL_STATS: stat::peak_rss_mb:
+
+RUN: LLVMFuzzer-SimpleTest %S/dict1.txt -runs=33 -print_final_stats=1 2>&1 | FileCheck %s --check-prefix=FINAL_STATS1
+FINAL_STATS1: stat::number_of_executed_units: 33
+FINAL_STATS1: stat::peak_rss_mb:
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-flags.test b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-flags.test
new file mode 100644
index 0000000000..76ea277057
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-flags.test
@@ -0,0 +1,10 @@
+RUN: LLVMFuzzer-SimpleTest -foo_bar=1 2>&1 | FileCheck %s --check-prefix=FOO_BAR
+FOO_BAR: WARNING: unrecognized flag '-foo_bar=1'; use -help=1 to list all flags
+FOO_BAR: BINGO
+
+RUN: LLVMFuzzer-SimpleTest -runs=10 --max_len=100 2>&1 | FileCheck %s --check-prefix=DASH_DASH
+DASH_DASH: WARNING: did you mean '-max_len=100' (single dash)?
+DASH_DASH: INFO: A corpus is not provided, starting from an empty corpus
+
+RUN: LLVMFuzzer-SimpleTest -help=1 2>&1 | FileCheck %s --check-prefix=NO_INTERNAL
+NO_INTERNAL-NOT: internal flag
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-jobs.test b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-jobs.test
new file mode 100644
index 0000000000..5bf8cfadfb
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-jobs.test
@@ -0,0 +1,29 @@
+RUN: rm -rf %tmp
+RUN: mkdir %tmp && cd %tmp
+# Create a shared corpus directory
+RUN: rm -rf FuzzerJobsTestCORPUS
+RUN: mkdir FuzzerJobsTestCORPUS
+RUN: rm -f fuzz-{0,1}.log
+# Start fuzzer and in parallel check that the output files
+# that should be created exist.
+RUN: LLVMFuzzer-EmptyTest -max_total_time=4 -jobs=2 -workers=2 FuzzerJobsTestCORPUS > %t-fuzzer-jobs-test.log 2>&1 & export FUZZER_PID=$!
+# Wait a short while to give time for the child processes
+# to start fuzzing
+RUN: sleep 2
+# If the instances are running in parallel they should have created their log
+# files by now.
+RUN: ls fuzz-0.log
+RUN: ls fuzz-1.log
+# Wait for libfuzzer to finish.
+# This probably isn't portable but we need a way to block until
+# the fuzzer is done otherwise we might remove the files while
+# they are being used.
+RUN: while kill -0 ${FUZZER_PID}; do : ; done
+RUN: rm -f fuzz-{0,1}.log
+RUN: rm -rf FuzzerJobsTestCORPUS
+RUN: FileCheck -input-file=%t-fuzzer-jobs-test.log %s
+RUN: rm %t-fuzzer-jobs-test.log
+RUN: cd ../
+
+CHECK-DAG: Job 0 exited with exit code 0
+CHECK-DAG: Job 1 exited with exit code 0
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-leak.test b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-leak.test
new file mode 100644
index 0000000000..9cf5c743ff
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-leak.test
@@ -0,0 +1,35 @@
+REQUIRES: lsan
+RUN: not LLVMFuzzer-LeakTest -runs=100000 -detect_leaks=1 2>&1 | FileCheck %s --check-prefix=LEAK_DURING
+LEAK_DURING: ERROR: LeakSanitizer: detected memory leaks
+LEAK_DURING: Direct leak of 4 byte(s) in 1 object(s) allocated from:
+LEAK_DURING: INFO: to ignore leaks on libFuzzer side use -detect_leaks=0
+LEAK_DURING: Test unit written to ./leak-
+LEAK_DURING-NOT: DONE
+LEAK_DURING-NOT: Done
+
+RUN: not LLVMFuzzer-LeakTest -runs=0 -detect_leaks=1 %S 2>&1 | FileCheck %s --check-prefix=LEAK_IN_CORPUS
+LEAK_IN_CORPUS: ERROR: LeakSanitizer: detected memory leaks
+LEAK_IN_CORPUS: INFO: a leak has been found in the initial corpus.
+
+RUN: not LLVMFuzzer-LeakTest -runs=100000000 %S/hi.txt 2>&1 | FileCheck %s --check-prefix=MULTI_RUN_LEAK
+MULTI_RUN_LEAK-NOT: pulse
+MULTI_RUN_LEAK: LeakSanitizer: detected memory leaks
+
+RUN: not LLVMFuzzer-LeakTest -runs=100000 -detect_leaks=0 2>&1 | FileCheck %s --check-prefix=LEAK_AFTER
+RUN: not LLVMFuzzer-LeakTest -runs=100000 2>&1 | FileCheck %s --check-prefix=LEAK_DURING
+RUN: not LLVMFuzzer-ThreadedLeakTest -runs=100000 -detect_leaks=0 2>&1 | FileCheck %s --check-prefix=LEAK_AFTER
+RUN: not LLVMFuzzer-ThreadedLeakTest -runs=100000 2>&1 | FileCheck %s --check-prefix=LEAK_DURING
+LEAK_AFTER: Done 100000 runs in
+LEAK_AFTER: ERROR: LeakSanitizer: detected memory leaks
+
+RUN: not LLVMFuzzer-LeakTest -runs=100000 -max_len=1 2>&1 | FileCheck %s --check-prefix=MAX_LEN_1
+MAX_LEN_1: Test unit written to ./leak-7cf184f4c67ad58283ecb19349720b0cae756829
+
+RUN: not LLVMFuzzer-LeakTimeoutTest -timeout=1 2>&1 | FileCheck %s --check-prefix=LEAK_TIMEOUT
+LEAK_TIMEOUT: ERROR: libFuzzer: timeout after
+LEAK_TIMEOUT-NOT: LeakSanitizer
+
+RUN: LLVMFuzzer-AccumulateAllocationsTest -detect_leaks=1 -runs=100000 2>&1 | FileCheck %s --check-prefix=ACCUMULATE_ALLOCS
+ACCUMULATE_ALLOCS: INFO: libFuzzer disabled leak detection after every mutation
+
+RUN: LLVMFuzzer-LeakTest -error_exitcode=0
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-oom-with-profile.test b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-oom-with-profile.test
new file mode 100644
index 0000000000..2b2b0b9d5d
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-oom-with-profile.test
@@ -0,0 +1,6 @@
+REQUIRES: linux
+RUN: not LLVMFuzzer-OutOfMemoryTest -rss_limit_mb=300 2>&1 | FileCheck %s
+CHECK: ERROR: libFuzzer: out-of-memory (used: {{.*}}; limit: 300Mb)
+CHECK: Live Heap Allocations
+CHECK: Test unit written to ./oom-
+SUMMARY: libFuzzer: out-of-memory
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-oom.test b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-oom.test
new file mode 100644
index 0000000000..8caf649e9f
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-oom.test
@@ -0,0 +1,11 @@
+RUN: not LLVMFuzzer-OutOfMemoryTest -rss_limit_mb=300 2>&1 | FileCheck %s
+CHECK: ERROR: libFuzzer: out-of-memory (used: {{.*}}; limit: 300Mb)
+CHECK: Test unit written to ./oom-
+SUMMARY: libFuzzer: out-of-memory
+
+RUN: not LLVMFuzzer-OutOfMemorySingleLargeMallocTest 2>&1 | FileCheck %s --check-prefix=SINGLE_LARGE_MALLOC
+SINGLE_LARGE_MALLOC: libFuzzer: out-of-memory (malloc(42{{.*}}))
+SINGLE_LARGE_MALLOC: in LLVMFuzzerTestOneInput
+
+# Check that -rss_limit_mb=0 means no limit.
+RUN: LLVMFuzzer-AccumulateAllocationsTest -runs=1000 -rss_limit_mb=0
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-printcovpcs.test b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-printcovpcs.test
new file mode 100644
index 0000000000..e4c6f0ed1d
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-printcovpcs.test
@@ -0,0 +1,8 @@
+RUN: LLVMFuzzer-SimpleTest -print_pcs=1 -seed=1 2>&1 | FileCheck %s --check-prefix=PCS
+PCS-NOT: NEW_PC
+PCS:INITED
+PCS:NEW_PC: {{0x[a-f0-9]+}}
+PCS:NEW_PC: {{0x[a-f0-9]+}}
+PCS:NEW
+PCS:BINGO
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-runs.test b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-runs.test
new file mode 100644
index 0000000000..056c44782a
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-runs.test
@@ -0,0 +1,8 @@
+RUN: mkdir -p %t
+RUN: echo abcd > %t/NthRunCrashTest.in
+RUN: LLVMFuzzer-NthRunCrashTest %t/NthRunCrashTest.in
+RUN: LLVMFuzzer-NthRunCrashTest %t/NthRunCrashTest.in -runs=10
+RUN: not LLVMFuzzer-NthRunCrashTest %t/NthRunCrashTest.in -runs=10000 2>&1 | FileCheck %s
+RUN: rm %t/NthRunCrashTest.in
+CHECK: BINGO
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-seed.test b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-seed.test
new file mode 100644
index 0000000000..f1bdf9e4ae
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-seed.test
@@ -0,0 +1,3 @@
+RUN: LLVMFuzzer-SimpleCmpTest -seed=-1 -runs=0 2>&1 | FileCheck %s --check-prefix=CHECK_SEED_MINUS_ONE
+CHECK_SEED_MINUS_ONE: Seed: 4294967295
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-segv.test b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-segv.test
new file mode 100644
index 0000000000..330f03bcc4
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-segv.test
@@ -0,0 +1,5 @@
+RUN: ASAN_OPTIONS=handle_segv=0 not LLVMFuzzer-NullDerefTest 2>&1 | FileCheck %s --check-prefix=LIBFUZZER_OWN_SEGV_HANDLER
+LIBFUZZER_OWN_SEGV_HANDLER: == ERROR: libFuzzer: deadly signal
+LIBFUZZER_OWN_SEGV_HANDLER: SUMMARY: libFuzzer: deadly signal
+LIBFUZZER_OWN_SEGV_HANDLER: Test unit written to ./crash-
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-singleinputs.test b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-singleinputs.test
new file mode 100644
index 0000000000..ca8403bff8
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-singleinputs.test
@@ -0,0 +1,16 @@
+RUN: not LLVMFuzzer-NullDerefTest %S/hi.txt 2>&1 | FileCheck %s --check-prefix=SingleInput
+SingleInput-NOT: Test unit written to ./crash-
+
+RUN: rm -rf %tmp/SINGLE_INPUTS
+RUN: mkdir -p %tmp/SINGLE_INPUTS
+RUN: echo aaa > %tmp/SINGLE_INPUTS/aaa
+RUN: echo bbb > %tmp/SINGLE_INPUTS/bbb
+RUN: LLVMFuzzer-SimpleTest %tmp/SINGLE_INPUTS/aaa %tmp/SINGLE_INPUTS/bbb 2>&1 | FileCheck %s --check-prefix=SINGLE_INPUTS
+RUN: LLVMFuzzer-SimpleTest -max_len=2 %tmp/SINGLE_INPUTS/aaa %tmp/SINGLE_INPUTS/bbb 2>&1 | FileCheck %s --check-prefix=SINGLE_INPUTS
+RUN: rm -rf %tmp/SINGLE_INPUTS
+SINGLE_INPUTS: LLVMFuzzer-SimpleTest: Running 2 inputs 1 time(s) each.
+SINGLE_INPUTS: aaa in
+SINGLE_INPUTS: bbb in
+SINGLE_INPUTS: NOTE: fuzzing was not performed, you have only
+SINGLE_INPUTS: executed the target code on a fixed set of inputs.
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-threaded.test b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-threaded.test
new file mode 100644
index 0000000000..c58a33456c
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-threaded.test
@@ -0,0 +1,7 @@
+CHECK: Done 1000 runs in
+
+RUN: LLVMFuzzer-ThreadedTest -use_traces=1 -runs=1000 2>&1 | FileCheck %s
+RUN: LLVMFuzzer-ThreadedTest -use_traces=1 -runs=1000 2>&1 | FileCheck %s
+RUN: LLVMFuzzer-ThreadedTest -use_traces=1 -runs=1000 2>&1 | FileCheck %s
+RUN: LLVMFuzzer-ThreadedTest -use_traces=1 -runs=1000 2>&1 | FileCheck %s
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-timeout.test b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-timeout.test
new file mode 100644
index 0000000000..beb0867118
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-timeout.test
@@ -0,0 +1,19 @@
+RUN: not LLVMFuzzer-TimeoutTest -timeout=1 2>&1 | FileCheck %s --check-prefix=TimeoutTest
+TimeoutTest: ALARM: working on the last Unit for
+TimeoutTest: Test unit written to ./timeout-
+TimeoutTest: == ERROR: libFuzzer: timeout after
+TimeoutTest: #0
+TimeoutTest: #1
+TimeoutTest: #2
+TimeoutTest: SUMMARY: libFuzzer: timeout
+
+RUN: not LLVMFuzzer-TimeoutTest -timeout=1 %S/hi.txt 2>&1 | FileCheck %s --check-prefix=SingleInputTimeoutTest
+SingleInputTimeoutTest: ALARM: working on the last Unit for {{[1-3]}} seconds
+SingleInputTimeoutTest-NOT: Test unit written to ./timeout-
+
+RUN: LLVMFuzzer-TimeoutTest -timeout=1 -timeout_exitcode=0
+
+RUN: not LLVMFuzzer-TimeoutEmptyTest -timeout=1 2>&1 | FileCheck %s --check-prefix=TimeoutEmptyTest
+TimeoutEmptyTest: ALARM: working on the last Unit for
+TimeoutEmptyTest: == ERROR: libFuzzer: timeout after
+TimeoutEmptyTest: SUMMARY: libFuzzer: timeout
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-traces-hooks.test b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-traces-hooks.test
new file mode 100644
index 0000000000..71fe6f2daf
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-traces-hooks.test
@@ -0,0 +1,25 @@
+// FIXME: Support sanitizer hooks for memcmp and strcmp need
+// to be implemented in the sanitizer runtime for platforms other
+// than linux
+REQUIRES: linux
+CHECK: BINGO
+Done1000000: Done 1000000 runs in
+
+RUN: not LLVMFuzzer-MemcmpTest -seed=4294967295 -runs=100000 2>&1 | FileCheck %s
+RUN: LLVMFuzzer-MemcmpTest -use_memcmp=0 -seed=4294967295 -runs=1000000 2>&1 | FileCheck %s --check-prefix=Done1000000
+
+RUN: not LLVMFuzzer-StrncmpTest -seed=2 -runs=100000 2>&1 | FileCheck %s
+RUN: LLVMFuzzer-StrncmpTest -use_memcmp=0 -seed=3 -runs=1000000 2>&1 | FileCheck %s --check-prefix=Done1000000
+
+RUN: not LLVMFuzzer-StrcmpTest -seed=4 -runs=200000 2>&1 | FileCheck %s
+RUN: LLVMFuzzer-StrcmpTest -use_memcmp=0 -seed=5 -runs=1000000 2>&1 | FileCheck %s --check-prefix=Done1000000
+
+RUN: not LLVMFuzzer-StrstrTest -seed=6 -runs=200000 2>&1 | FileCheck %s
+RUN: LLVMFuzzer-StrstrTest -use_memmem=0 -seed=7 -runs=1000000 2>&1 | FileCheck %s --check-prefix=Done1000000
+
+RUN: LLVMFuzzer-RepeatedMemcmp -seed=10 -runs=100000 2>&1 | FileCheck %s --check-prefix=RECOMMENDED_DICT
+RECOMMENDED_DICT:###### Recommended dictionary. ######
+RECOMMENDED_DICT-DAG: "foo"
+RECOMMENDED_DICT-DAG: "bar"
+RECOMMENDED_DICT:###### End of recommended dictionary. ######
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-ubsan.test b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-ubsan.test
new file mode 100644
index 0000000000..0e8ad6c94a
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer-ubsan.test
@@ -0,0 +1,4 @@
+RUN: not LLVMFuzzer-SignedIntOverflowTest-Ubsan 2>&1 | FileCheck %s
+CHECK: runtime error: signed integer overflow: 2147483647 + 1 cannot be represented in type 'int'
+CHECK: Test unit written to ./crash-
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer.test b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer.test
new file mode 100644
index 0000000000..2f91c2195c
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/fuzzer.test
@@ -0,0 +1,57 @@
+CHECK: BINGO
+Done1000000: Done 1000000 runs in
+
+RUN: LLVMFuzzer-SimpleTest 2>&1 | FileCheck %s
+
+# only_ascii mode. Will perform some minimal self-validation.
+RUN: LLVMFuzzer-SimpleTest -only_ascii=1 2>&1
+
+RUN: LLVMFuzzer-SimpleCmpTest -max_total_time=1 -use_cmp=0 2>&1 | FileCheck %s --check-prefix=MaxTotalTime
+MaxTotalTime: Done {{.*}} runs in {{.}} second(s)
+
+RUN: not LLVMFuzzer-NullDerefTest 2>&1 | FileCheck %s --check-prefix=NullDerefTest
+RUN: not LLVMFuzzer-NullDerefTest -close_fd_mask=3 2>&1 | FileCheck %s --check-prefix=NullDerefTest
+NullDerefTest: ERROR: AddressSanitizer: SEGV on unknown address
+NullDerefTest: Test unit written to ./crash-
+RUN: not LLVMFuzzer-NullDerefTest -artifact_prefix=ZZZ 2>&1 | FileCheck %s --check-prefix=NullDerefTestPrefix
+NullDerefTestPrefix: Test unit written to ZZZcrash-
+RUN: not LLVMFuzzer-NullDerefTest -artifact_prefix=ZZZ -exact_artifact_path=FOOBAR 2>&1 | FileCheck %s --check-prefix=NullDerefTestExactPath
+NullDerefTestExactPath: Test unit written to FOOBAR
+
+RUN: not LLVMFuzzer-NullDerefOnEmptyTest -print_final_stats=1 2>&1 | FileCheck %s --check-prefix=NULL_DEREF_ON_EMPTY
+NULL_DEREF_ON_EMPTY: stat::number_of_executed_units:
+
+#not LLVMFuzzer-FullCoverageSetTest -timeout=15 -seed=1 -mutate_depth=2 -use_full_coverage_set=1 2>&1 | FileCheck %s
+
+RUN: not LLVMFuzzer-CounterTest -max_len=6 -seed=1 -timeout=15 2>&1 | FileCheck %s --check-prefix=COUNTERS
+
+COUNTERS: INITED {{.*}} {{bits:|ft:}}
+COUNTERS: NEW {{.*}} {{bits:|ft:}} {{[1-9]*}}
+COUNTERS: NEW {{.*}} {{bits:|ft:}} {{[1-9]*}}
+COUNTERS: BINGO
+
+# Don't run UninstrumentedTest for now since we build libFuzzer itself with asan.
+DISABLED: not LLVMFuzzer-UninstrumentedTest-Uninstrumented 2>&1 | FileCheck %s --check-prefix=UNINSTRUMENTED
+UNINSTRUMENTED: ERROR: __sanitizer_set_death_callback is not defined. Exiting.
+
+RUN: not LLVMFuzzer-UninstrumentedTest-NoCoverage 2>&1 | FileCheck %s --check-prefix=NO_COVERAGE
+NO_COVERAGE: ERROR: no interesting inputs were found. Is the code instrumented for coverage? Exiting
+
+RUN: not LLVMFuzzer-BufferOverflowOnInput 2>&1 | FileCheck %s --check-prefix=OOB
+OOB: AddressSanitizer: heap-buffer-overflow
+OOB: is located 0 bytes to the right of 3-byte region
+
+RUN: not LLVMFuzzer-InitializeTest -use_value_profile=1 2>&1 | FileCheck %s
+
+RUN: not LLVMFuzzer-DSOTest 2>&1 | FileCheck %s --check-prefix=DSO
+DSO: INFO: Loaded 3 modules
+DSO: BINGO
+
+RUN: LLVMFuzzer-SimpleTest -exit_on_src_pos=SimpleTest.cpp:17 2>&1 | FileCheck %s --check-prefix=EXIT_ON_SRC_POS
+RUN: LLVMFuzzer-ShrinkControlFlowTest -exit_on_src_pos=ShrinkControlFlowTest.cpp:23 2>&1 | FileCheck %s --check-prefix=EXIT_ON_SRC_POS
+EXIT_ON_SRC_POS: INFO: found line matching '{{.*}}', exiting.
+
+RUN: ASAN_OPTIONS=strict_string_checks=1 not LLVMFuzzer-StrncmpOOBTest -seed=1 -runs=1000000 2>&1 | FileCheck %s --check-prefix=STRNCMP
+STRNCMP: AddressSanitizer: heap-buffer-overflow
+STRNCMP-NOT: __sanitizer_weak_hook_strncmp
+STRNCMP: in LLVMFuzzerTestOneInput
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/hi.txt b/json4cpp/tests/thirdparty/Fuzzer/test/hi.txt
new file mode 100644
index 0000000000..2f9031f0ec
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/hi.txt
@@ -0,0 +1 @@
+Hi! \ No newline at end of file
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/lit.cfg b/json4cpp/tests/thirdparty/Fuzzer/test/lit.cfg
new file mode 100644
index 0000000000..745af0c382
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/lit.cfg
@@ -0,0 +1,29 @@
+import lit.formats
+import sys
+
+config.name = "LLVMFuzzer"
+config.test_format = lit.formats.ShTest(True)
+config.suffixes = ['.test']
+config.test_source_root = os.path.dirname(__file__)
+
+# Tweak PATH to include llvm tools dir and current exec dir.
+llvm_tools_dir = getattr(config, 'llvm_tools_dir', None)
+if (not llvm_tools_dir) or (not os.path.exists(llvm_tools_dir)):
+ lit_config.fatal("Invalid llvm_tools_dir config attribute: %r" % llvm_tools_dir)
+path = os.path.pathsep.join((llvm_tools_dir, config.test_exec_root,
+ config.environment['PATH']))
+config.environment['PATH'] = path
+
+if config.has_lsan:
+ lit_config.note('lsan feature available')
+ config.available_features.add('lsan')
+else:
+ lit_config.note('lsan feature unavailable')
+
+if sys.platform.startswith('linux'):
+ # Note the value of ``sys.platform`` is not consistent
+ # between python 2 and 3, hence the use of ``.startswith()``.
+ lit_config.note('linux feature available')
+ config.available_features.add('linux')
+else:
+ lit_config.note('linux feature unavailable')
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/lit.site.cfg.in b/json4cpp/tests/thirdparty/Fuzzer/test/lit.site.cfg.in
new file mode 100644
index 0000000000..03e86c487c
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/lit.site.cfg.in
@@ -0,0 +1,4 @@
+config.test_exec_root = "@CMAKE_CURRENT_BINARY_DIR@"
+config.llvm_tools_dir = "@LLVM_TOOLS_DIR@"
+config.has_lsan = True if @HAS_LSAN@ == 1 else False
+lit_config.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/lit.cfg")
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/merge.test b/json4cpp/tests/thirdparty/Fuzzer/test/merge.test
new file mode 100644
index 0000000000..1f1810eb01
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/merge.test
@@ -0,0 +1,46 @@
+CHECK: BINGO
+
+RUN: rm -rf %tmp/T1 %tmp/T2
+RUN: mkdir -p %tmp/T1 %tmp/T2
+RUN: echo F..... > %tmp/T1/1
+RUN: echo .U.... > %tmp/T1/2
+RUN: echo ..Z... > %tmp/T1/3
+
+# T1 has 3 elements, T2 is empty.
+RUN: LLVMFuzzer-FullCoverageSetTest -merge=1 %tmp/T1 %tmp/T2 2>&1 | FileCheck %s --check-prefix=CHECK1
+CHECK1: MERGE-OUTER: 3 files, 3 in the initial corpus
+CHECK1: MERGE-OUTER: 0 new files with 0 new features added
+
+RUN: echo ...Z.. > %tmp/T2/1
+RUN: echo ....E. > %tmp/T2/2
+RUN: echo .....R > %tmp/T2/3
+RUN: echo F..... > %tmp/T2/a
+RUN: echo .U.... > %tmp/T2/b
+RUN: echo ..Z... > %tmp/T2/c
+
+# T1 has 3 elements, T2 has 6 elements, only 3 are new.
+RUN: LLVMFuzzer-FullCoverageSetTest -merge=1 %tmp/T1 %tmp/T2 2>&1 | FileCheck %s --check-prefix=CHECK2
+CHECK2: MERGE-OUTER: 9 files, 3 in the initial corpus
+CHECK2: MERGE-OUTER: 3 new files with 3 new features added
+
+# Now, T1 has 6 units and T2 has no new interesting units.
+RUN: LLVMFuzzer-FullCoverageSetTest -merge=1 %tmp/T1 %tmp/T2 2>&1 | FileCheck %s --check-prefix=CHECK3
+CHECK3: MERGE-OUTER: 12 files, 6 in the initial corpus
+CHECK3: MERGE-OUTER: 0 new files with 0 new features added
+
+# Check that we respect max_len during the merge and don't crash.
+RUN: rm %tmp/T1/??*
+RUN: echo looooooooong > %tmp/T2/looooooooong
+RUN: LLVMFuzzer-FullCoverageSetTest -merge=1 %tmp/T1 %tmp/T2 -max_len=6 2>&1 | FileCheck %s --check-prefix=MAX_LEN
+MAX_LEN: MERGE-OUTER: 3 new files
+
+# Check that merge tolerates failures.
+RUN: rm %tmp/T1/??*
+RUN: echo 'FUZZER' > %tmp/T2/FUZZER
+RUN: LLVMFuzzer-FullCoverageSetTest -merge=1 %tmp/T1 %tmp/T2 2>&1 | FileCheck %s --check-prefix=MERGE_WITH_CRASH
+MERGE_WITH_CRASH: MERGE-OUTER: succesfull in 2 attempt(s)
+MERGE_WITH_CRASH: MERGE-OUTER: 3 new files
+
+# Check that we actually limit the size with max_len
+RUN: LLVMFuzzer-FullCoverageSetTest -merge=1 %tmp/T1 %tmp/T2 -max_len=5 2>&1 | FileCheck %s --check-prefix=MERGE_LEN5
+MERGE_LEN5: MERGE-OUTER: succesfull in 1 attempt(s)
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/minimize_crash.test b/json4cpp/tests/thirdparty/Fuzzer/test/minimize_crash.test
new file mode 100644
index 0000000000..7e5406598e
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/minimize_crash.test
@@ -0,0 +1,6 @@
+RUN: echo 'Hi!rv349f34t3gg' > not_minimal_crash
+RUN: LLVMFuzzer-NullDerefTest -minimize_crash=1 not_minimal_crash -max_total_time=2 2>&1 | FileCheck %s
+CHECK: CRASH_MIN: failed to minimize beyond minimized-from-{{.*}} (3 bytes), exiting
+RUN: LLVMFuzzer-NullDerefTest -minimize_crash=1 not_minimal_crash -max_total_time=2 -exact_artifact_path=exact_minimized_path 2>&1 | FileCheck %s --check-prefix=CHECK_EXACT
+CHECK_EXACT: CRASH_MIN: failed to minimize beyond exact_minimized_path (3 bytes), exiting
+RUN: rm not_minimal_crash minimized-from-* exact_minimized_path
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/no-coverage/CMakeLists.txt b/json4cpp/tests/thirdparty/Fuzzer/test/no-coverage/CMakeLists.txt
new file mode 100644
index 0000000000..d2f6f438ad
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/no-coverage/CMakeLists.txt
@@ -0,0 +1,29 @@
+# These tests are not instrumented with coverage,
+# but have coverage rt in the binary.
+
+set(CMAKE_CXX_FLAGS
+ "${LIBFUZZER_FLAGS_BASE} -fno-sanitize-coverage=edge,trace-cmp,indirect-calls,8bit-counters,trace-pc-guard")
+
+set(NoCoverageTests
+ UninstrumentedTest
+ )
+
+foreach(Test ${NoCoverageTests})
+ add_libfuzzer_test(${Test}-NoCoverage SOURCES ../${Test}.cpp)
+endforeach()
+
+
+###############################################################################
+# AFL Driver test
+###############################################################################
+
+add_executable(AFLDriverTest
+ ../AFLDriverTest.cpp ../../afl/afl_driver.cpp)
+
+set_target_properties(AFLDriverTest
+ PROPERTIES RUNTIME_OUTPUT_DIRECTORY
+ "${CMAKE_BINARY_DIR}/lib/Fuzzer/test"
+ )
+
+# Propagate value into parent directory
+set(TestBinaries ${TestBinaries} AFLDriverTest PARENT_SCOPE)
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/repeated-bytes.test b/json4cpp/tests/thirdparty/Fuzzer/test/repeated-bytes.test
new file mode 100644
index 0000000000..7139408793
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/repeated-bytes.test
@@ -0,0 +1,2 @@
+CHECK: BINGO
+RUN: LLVMFuzzer-RepeatedBytesTest -seed=1 -runs=1000000 2>&1 | FileCheck %s
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/shrink.test b/json4cpp/tests/thirdparty/Fuzzer/test/shrink.test
new file mode 100644
index 0000000000..edb86cb193
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/shrink.test
@@ -0,0 +1,7 @@
+RUN: LLVMFuzzer-ShrinkControlFlowTest -seed=1 -exit_on_item=0eb8e4ed029b774d80f2b66408203801cb982a60 -runs=1000000 -shrink=1 2>&1 | FileCheck %s --check-prefix=SHRINK1
+RUN: LLVMFuzzer-ShrinkControlFlowTest -seed=1 -exit_on_item=0eb8e4ed029b774d80f2b66408203801cb982a60 -runs=1000000 -shrink=0 2>&1 | FileCheck %s --check-prefix=SHRINK0
+RUN: LLVMFuzzer-ShrinkValueProfileTest -seed=1 -exit_on_item=aea2e3923af219a8956f626558ef32f30a914ebc -runs=100000 -shrink=1 -use_value_profile=1 2>&1 | FileCheck %s --check-prefix=SHRINK1_VP
+
+SHRINK0: Done 1000000 runs in
+SHRINK1: INFO: found item with checksum '0eb8e4ed029b774d80f2b66408203801cb982a60', exiting.
+SHRINK1_VP: INFO: found item with checksum 'aea2e3923af219a8956f626558ef32f30a914ebc', exiting
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/simple-cmp.test b/json4cpp/tests/thirdparty/Fuzzer/test/simple-cmp.test
new file mode 100644
index 0000000000..145a036652
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/simple-cmp.test
@@ -0,0 +1,2 @@
+CHECK: BINGO
+RUN: not LLVMFuzzer-SimpleCmpTest -seed=1 -runs=100000000 2>&1 | FileCheck %s
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/standalone.test b/json4cpp/tests/thirdparty/Fuzzer/test/standalone.test
new file mode 100644
index 0000000000..3097b3ff35
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/standalone.test
@@ -0,0 +1,4 @@
+RUN: LLVMFuzzer-StandaloneInitializeTest %S/hi.txt %S/dict1.txt 2>&1 | FileCheck %s
+CHECK: StandaloneFuzzTargetMain: running 2 inputs
+CHECK: Done: {{.*}}hi.txt: (3 bytes)
+CHECK: Done: {{.*}}dict1.txt: (61 bytes)
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/swap-cmp.test b/json4cpp/tests/thirdparty/Fuzzer/test/swap-cmp.test
new file mode 100644
index 0000000000..908b798664
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/swap-cmp.test
@@ -0,0 +1,2 @@
+CHECK: BINGO
+RUN: not LLVMFuzzer-SwapCmpTest -seed=1 -runs=10000000 2>&1 | FileCheck %s
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/trace-malloc.test b/json4cpp/tests/thirdparty/Fuzzer/test/trace-malloc.test
new file mode 100644
index 0000000000..c95147904d
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/trace-malloc.test
@@ -0,0 +1,10 @@
+RUN: LLVMFuzzer-TraceMallocTest -seed=1 -trace_malloc=1 -runs=10000 2>&1 | FileCheck %s
+CHECK-DAG: MallocFreeTracer: STOP 0 0 (same)
+CHECK-DAG: MallocFreeTracer: STOP 0 1 (DIFFERENT)
+CHECK-DAG: MallocFreeTracer: STOP 1 0 (DIFFERENT)
+CHECK-DAG: MallocFreeTracer: STOP 1 1 (same)
+
+RUN: LLVMFuzzer-TraceMallocTest -seed=1 -trace_malloc=2 -runs=1000 2>&1 | FileCheck %s --check-prefix=TRACE2
+TRACE2-DAG: FREE[0]
+TRACE2-DAG: MALLOC[0]
+TRACE2-DAG: in LLVMFuzzerTestOneInput
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/ubsan/CMakeLists.txt b/json4cpp/tests/thirdparty/Fuzzer/test/ubsan/CMakeLists.txt
new file mode 100644
index 0000000000..7a9eacdbe7
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/ubsan/CMakeLists.txt
@@ -0,0 +1,15 @@
+# These tests are instrumented with ubsan in non-recovery mode.
+
+set(CMAKE_CXX_FLAGS
+ "${LIBFUZZER_FLAGS_BASE} -fsanitize=undefined -fno-sanitize-recover=all")
+
+set(UbsanTests
+ SignedIntOverflowTest
+ )
+
+foreach(Test ${UbsanTests})
+ add_libfuzzer_test(${Test}-Ubsan SOURCES ../${Test}.cpp)
+endforeach()
+
+# Propagate value into parent directory
+set(TestBinaries ${TestBinaries} PARENT_SCOPE)
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/ulimit.test b/json4cpp/tests/thirdparty/Fuzzer/test/ulimit.test
new file mode 100644
index 0000000000..a60636c351
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/ulimit.test
@@ -0,0 +1,2 @@
+RUN: ulimit -s 1000
+RUN: LLVMFuzzer-SimpleTest
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/uninstrumented/CMakeLists.txt b/json4cpp/tests/thirdparty/Fuzzer/test/uninstrumented/CMakeLists.txt
new file mode 100644
index 0000000000..29b66e6e58
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/uninstrumented/CMakeLists.txt
@@ -0,0 +1,16 @@
+# These tests are not instrumented with coverage and don't
+# have coverage rt in the binary.
+
+set(CMAKE_CXX_FLAGS
+ "${LIBFUZZER_FLAGS_BASE} -fno-sanitize=all -fno-sanitize-coverage=edge,trace-cmp,indirect-calls,8bit-counters,trace-pc-guard")
+
+set(UninstrumentedTests
+ UninstrumentedTest
+ )
+
+foreach(Test ${UninstrumentedTests})
+ add_libfuzzer_test(${Test}-Uninstrumented SOURCES ../${Test}.cpp)
+endforeach()
+
+# Propagate value into parent directory
+set(TestBinaries ${TestBinaries} PARENT_SCOPE)
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/unit/lit.cfg b/json4cpp/tests/thirdparty/Fuzzer/test/unit/lit.cfg
new file mode 100644
index 0000000000..0cc31939c5
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/unit/lit.cfg
@@ -0,0 +1,7 @@
+import lit.formats
+
+config.name = "LLVMFuzzer-Unittest"
+print config.test_exec_root
+config.test_format = lit.formats.GoogleTest(".", "Unittest")
+config.suffixes = []
+config.test_source_root = config.test_exec_root
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/unit/lit.site.cfg.in b/json4cpp/tests/thirdparty/Fuzzer/test/unit/lit.site.cfg.in
new file mode 100644
index 0000000000..114daf474b
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/unit/lit.site.cfg.in
@@ -0,0 +1,2 @@
+config.test_exec_root = "@CMAKE_CURRENT_BINARY_DIR@"
+lit_config.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/unit/lit.cfg")
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/value-profile-cmp.test b/json4cpp/tests/thirdparty/Fuzzer/test/value-profile-cmp.test
new file mode 100644
index 0000000000..48edba4f5c
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/value-profile-cmp.test
@@ -0,0 +1,2 @@
+CHECK: BINGO
+RUN: not LLVMFuzzer-SimpleCmpTest -seed=1 -use_cmp=0 -use_value_profile=1 -runs=100000000 2>&1 | FileCheck %s
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/value-profile-cmp2.test b/json4cpp/tests/thirdparty/Fuzzer/test/value-profile-cmp2.test
new file mode 100644
index 0000000000..43d62400d9
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/value-profile-cmp2.test
@@ -0,0 +1,2 @@
+CHECK: BINGO
+RUN: not LLVMFuzzer-SimpleHashTest -seed=1 -use_cmp=0 -use_value_profile=1 -runs=100000000 2>&1 | FileCheck %s
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/value-profile-cmp3.test b/json4cpp/tests/thirdparty/Fuzzer/test/value-profile-cmp3.test
new file mode 100644
index 0000000000..8a962763f3
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/value-profile-cmp3.test
@@ -0,0 +1,2 @@
+CHECK: BINGO
+RUN: not LLVMFuzzer-AbsNegAndConstantTest -seed=1 -use_cmp=0 -use_value_profile=1 -runs=100000000 2>&1 | FileCheck %s
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/value-profile-cmp4.test b/json4cpp/tests/thirdparty/Fuzzer/test/value-profile-cmp4.test
new file mode 100644
index 0000000000..1e7131e5b5
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/value-profile-cmp4.test
@@ -0,0 +1,2 @@
+CHECK: BINGO
+RUN: not LLVMFuzzer-AbsNegAndConstant64Test -seed=1 -use_cmp=0 -use_value_profile=1 -runs=100000000 2>&1 | FileCheck %s
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/value-profile-div.test b/json4cpp/tests/thirdparty/Fuzzer/test/value-profile-div.test
new file mode 100644
index 0000000000..ba45e4129d
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/value-profile-div.test
@@ -0,0 +1,3 @@
+CHECK: AddressSanitizer: FPE
+RUN: not LLVMFuzzer-DivTest -seed=1 -use_value_profile=1 -runs=10000000 2>&1 | FileCheck %s
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/value-profile-load.test b/json4cpp/tests/thirdparty/Fuzzer/test/value-profile-load.test
new file mode 100644
index 0000000000..14d3109a24
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/value-profile-load.test
@@ -0,0 +1,3 @@
+CHECK: AddressSanitizer: global-buffer-overflow
+RUN: not LLVMFuzzer-LoadTest -seed=1 -use_cmp=0 -use_value_profile=1 -runs=10000000 2>&1 | FileCheck %s
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/value-profile-mem.test b/json4cpp/tests/thirdparty/Fuzzer/test/value-profile-mem.test
new file mode 100644
index 0000000000..09d737dbe7
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/value-profile-mem.test
@@ -0,0 +1,2 @@
+CHECK: BINGO
+RUN: not LLVMFuzzer-SingleMemcmpTest -seed=1 -use_cmp=0 -use_memcmp=0 -use_value_profile=1 -runs=10000000 2>&1 | FileCheck %s
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/value-profile-set.test b/json4cpp/tests/thirdparty/Fuzzer/test/value-profile-set.test
new file mode 100644
index 0000000000..9d06c36563
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/value-profile-set.test
@@ -0,0 +1,3 @@
+CHECK: BINGO
+RUN: not LLVMFuzzer-FourIndependentBranchesTest -seed=1 -use_cmp=0 -use_value_profile=1 -runs=100000000 2>&1 | FileCheck %s
+
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/value-profile-strcmp.test b/json4cpp/tests/thirdparty/Fuzzer/test/value-profile-strcmp.test
new file mode 100644
index 0000000000..1e7ef9b45e
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/value-profile-strcmp.test
@@ -0,0 +1,2 @@
+CHECK: BINGO
+RUN: not LLVMFuzzer-SingleStrcmpTest -seed=1 -use_cmp=0 -use_memcmp=0 -use_value_profile=1 -runs=10000000 2>&1 | FileCheck %s
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/value-profile-strncmp.test b/json4cpp/tests/thirdparty/Fuzzer/test/value-profile-strncmp.test
new file mode 100644
index 0000000000..650973180c
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/value-profile-strncmp.test
@@ -0,0 +1,2 @@
+CHECK: BINGO
+RUN: not LLVMFuzzer-SingleStrncmpTest -seed=1 -use_cmp=0 -use_memcmp=0 -use_value_profile=1 -runs=10000000 2>&1 | FileCheck %s
diff --git a/json4cpp/tests/thirdparty/Fuzzer/test/value-profile-switch.test b/json4cpp/tests/thirdparty/Fuzzer/test/value-profile-switch.test
new file mode 100644
index 0000000000..1947f56830
--- /dev/null
+++ b/json4cpp/tests/thirdparty/Fuzzer/test/value-profile-switch.test
@@ -0,0 +1,3 @@
+CHECK: BINGO
+RUN: not LLVMFuzzer-SwitchTest -use_cmp=0 -use_value_profile=1 -runs=100000000 -seed=1 2>&1 | FileCheck %s
+RUN: not LLVMFuzzer-Switch2Test -use_cmp=0 -use_value_profile=1 -runs=100000000 -seed=1 2>&1 | FileCheck %s
diff --git a/json4cpp/tests/thirdparty/doctest/doctest.h b/json4cpp/tests/thirdparty/doctest/doctest.h
new file mode 100644
index 0000000000..2a29eb40bc
--- /dev/null
+++ b/json4cpp/tests/thirdparty/doctest/doctest.h
@@ -0,0 +1,7135 @@
+// ====================================================================== lgtm [cpp/missing-header-guard]
+// == DO NOT MODIFY THIS FILE BY HAND - IT IS AUTO GENERATED BY CMAKE! ==
+// ======================================================================
+//
+// doctest.h - the lightest feature-rich C++ single-header testing framework for unit tests and TDD
+//
+// Copyright (c) 2016-2023 Viktor Kirilov
+//
+// Distributed under the MIT Software License
+// See accompanying file LICENSE.txt or copy at
+// https://opensource.org/licenses/MIT
+//
+// The documentation can be found at the library's page:
+// https://github.com/doctest/doctest/blob/master/doc/markdown/readme.md
+//
+// =================================================================================================
+// =================================================================================================
+// =================================================================================================
+//
+// The library is heavily influenced by Catch - https://github.com/catchorg/Catch2
+// which uses the Boost Software License - Version 1.0
+// see here - https://github.com/catchorg/Catch2/blob/master/LICENSE.txt
+//
+// The concept of subcases (sections in Catch) and expression decomposition are from there.
+// Some parts of the code are taken directly:
+// - stringification - the detection of "ostream& operator<<(ostream&, const T&)" and StringMaker<>
+// - the Approx() helper class for floating point comparison
+// - colors in the console
+// - breaking into a debugger
+// - signal / SEH handling
+// - timer
+// - XmlWriter class - thanks to Phil Nash for allowing the direct reuse (AKA copy/paste)
+//
+// The expression decomposing templates are taken from lest - https://github.com/martinmoene/lest
+// which uses the Boost Software License - Version 1.0
+// see here - https://github.com/martinmoene/lest/blob/master/LICENSE.txt
+//
+// =================================================================================================
+// =================================================================================================
+// =================================================================================================
+
+#ifndef DOCTEST_LIBRARY_INCLUDED
+#define DOCTEST_LIBRARY_INCLUDED
+
+// =================================================================================================
+// == VERSION ======================================================================================
+// =================================================================================================
+
+#define DOCTEST_VERSION_MAJOR 2
+#define DOCTEST_VERSION_MINOR 4
+#define DOCTEST_VERSION_PATCH 12
+
+// util we need here
+#define DOCTEST_TOSTR_IMPL(x) #x
+#define DOCTEST_TOSTR(x) DOCTEST_TOSTR_IMPL(x)
+
+#define DOCTEST_VERSION_STR \
+ DOCTEST_TOSTR(DOCTEST_VERSION_MAJOR) "." \
+ DOCTEST_TOSTR(DOCTEST_VERSION_MINOR) "." \
+ DOCTEST_TOSTR(DOCTEST_VERSION_PATCH)
+
+#define DOCTEST_VERSION \
+ (DOCTEST_VERSION_MAJOR * 10000 + DOCTEST_VERSION_MINOR * 100 + DOCTEST_VERSION_PATCH)
+
+// =================================================================================================
+// == COMPILER VERSION =============================================================================
+// =================================================================================================
+
+// ideas for the version stuff are taken from here: https://github.com/cxxstuff/cxx_detect
+
+#ifdef _MSC_VER
+#define DOCTEST_CPLUSPLUS _MSVC_LANG
+#else
+#define DOCTEST_CPLUSPLUS __cplusplus
+#endif
+
+#define DOCTEST_COMPILER(MAJOR, MINOR, PATCH) ((MAJOR)*10000000 + (MINOR)*100000 + (PATCH))
+
+// GCC/Clang and GCC/MSVC are mutually exclusive, but Clang/MSVC are not because of clang-cl...
+#if defined(_MSC_VER) && defined(_MSC_FULL_VER)
+#if _MSC_VER == _MSC_FULL_VER / 10000
+#define DOCTEST_MSVC DOCTEST_COMPILER(_MSC_VER / 100, _MSC_VER % 100, _MSC_FULL_VER % 10000)
+#else // MSVC
+#define DOCTEST_MSVC \
+ DOCTEST_COMPILER(_MSC_VER / 100, (_MSC_FULL_VER / 100000) % 100, _MSC_FULL_VER % 100000)
+#endif // MSVC
+#endif // MSVC
+#if defined(__clang__) && defined(__clang_minor__) && defined(__clang_patchlevel__)
+#define DOCTEST_CLANG DOCTEST_COMPILER(__clang_major__, __clang_minor__, __clang_patchlevel__)
+#elif defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) && \
+ !defined(__INTEL_COMPILER)
+#define DOCTEST_GCC DOCTEST_COMPILER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
+#endif // GCC
+#if defined(__INTEL_COMPILER)
+#define DOCTEST_ICC DOCTEST_COMPILER(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0)
+#endif // ICC
+
+#ifndef DOCTEST_MSVC
+#define DOCTEST_MSVC 0
+#endif // DOCTEST_MSVC
+#ifndef DOCTEST_CLANG
+#define DOCTEST_CLANG 0
+#endif // DOCTEST_CLANG
+#ifndef DOCTEST_GCC
+#define DOCTEST_GCC 0
+#endif // DOCTEST_GCC
+#ifndef DOCTEST_ICC
+#define DOCTEST_ICC 0
+#endif // DOCTEST_ICC
+
+// =================================================================================================
+// == COMPILER WARNINGS HELPERS ====================================================================
+// =================================================================================================
+
+#if DOCTEST_CLANG && !DOCTEST_ICC
+#define DOCTEST_PRAGMA_TO_STR(x) _Pragma(#x)
+#define DOCTEST_CLANG_SUPPRESS_WARNING_PUSH _Pragma("clang diagnostic push")
+#define DOCTEST_CLANG_SUPPRESS_WARNING(w) DOCTEST_PRAGMA_TO_STR(clang diagnostic ignored w)
+#define DOCTEST_CLANG_SUPPRESS_WARNING_POP _Pragma("clang diagnostic pop")
+#define DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH(w) \
+ DOCTEST_CLANG_SUPPRESS_WARNING_PUSH DOCTEST_CLANG_SUPPRESS_WARNING(w)
+#else // DOCTEST_CLANG
+#define DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
+#define DOCTEST_CLANG_SUPPRESS_WARNING(w)
+#define DOCTEST_CLANG_SUPPRESS_WARNING_POP
+#define DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH(w)
+#endif // DOCTEST_CLANG
+
+#if DOCTEST_GCC
+#define DOCTEST_PRAGMA_TO_STR(x) _Pragma(#x)
+#define DOCTEST_GCC_SUPPRESS_WARNING_PUSH _Pragma("GCC diagnostic push")
+#define DOCTEST_GCC_SUPPRESS_WARNING(w) DOCTEST_PRAGMA_TO_STR(GCC diagnostic ignored w)
+#define DOCTEST_GCC_SUPPRESS_WARNING_POP _Pragma("GCC diagnostic pop")
+#define DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH(w) \
+ DOCTEST_GCC_SUPPRESS_WARNING_PUSH DOCTEST_GCC_SUPPRESS_WARNING(w)
+#else // DOCTEST_GCC
+#define DOCTEST_GCC_SUPPRESS_WARNING_PUSH
+#define DOCTEST_GCC_SUPPRESS_WARNING(w)
+#define DOCTEST_GCC_SUPPRESS_WARNING_POP
+#define DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH(w)
+#endif // DOCTEST_GCC
+
+#if DOCTEST_MSVC
+#define DOCTEST_MSVC_SUPPRESS_WARNING_PUSH __pragma(warning(push))
+#define DOCTEST_MSVC_SUPPRESS_WARNING(w) __pragma(warning(disable : w))
+#define DOCTEST_MSVC_SUPPRESS_WARNING_POP __pragma(warning(pop))
+#define DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(w) \
+ DOCTEST_MSVC_SUPPRESS_WARNING_PUSH DOCTEST_MSVC_SUPPRESS_WARNING(w)
+#else // DOCTEST_MSVC
+#define DOCTEST_MSVC_SUPPRESS_WARNING_PUSH
+#define DOCTEST_MSVC_SUPPRESS_WARNING(w)
+#define DOCTEST_MSVC_SUPPRESS_WARNING_POP
+#define DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(w)
+#endif // DOCTEST_MSVC
+
+// =================================================================================================
+// == COMPILER WARNINGS ============================================================================
+// =================================================================================================
+
+// both the header and the implementation suppress all of these,
+// so it only makes sense to aggregate them like so
+#define DOCTEST_SUPPRESS_COMMON_WARNINGS_PUSH \
+ DOCTEST_CLANG_SUPPRESS_WARNING_PUSH \
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wunknown-pragmas") \
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wweak-vtables") \
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wpadded") \
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-prototypes") \
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat") \
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic") \
+ \
+ DOCTEST_GCC_SUPPRESS_WARNING_PUSH \
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wunknown-pragmas") \
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wpragmas") \
+ DOCTEST_GCC_SUPPRESS_WARNING("-Weffc++") \
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-overflow") \
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-aliasing") \
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-declarations") \
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wuseless-cast") \
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wnoexcept") \
+ \
+ DOCTEST_MSVC_SUPPRESS_WARNING_PUSH \
+ /* these 4 also disabled globally via cmake: */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4514) /* unreferenced inline function has been removed */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4571) /* SEH related */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4710) /* function not inlined */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4711) /* function selected for inline expansion*/ \
+ /* common ones */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4616) /* invalid compiler warning */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4619) /* invalid compiler warning */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4996) /* The compiler encountered a deprecated declaration */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4706) /* assignment within conditional expression */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4512) /* 'class' : assignment operator could not be generated */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4127) /* conditional expression is constant */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4820) /* padding */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4625) /* copy constructor was implicitly deleted */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4626) /* assignment operator was implicitly deleted */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5027) /* move assignment operator implicitly deleted */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5026) /* move constructor was implicitly deleted */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4640) /* construction of local static object not thread-safe */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5045) /* Spectre mitigation for memory load */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5264) /* 'variable-name': 'const' variable is not used */ \
+ /* static analysis */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(26439) /* Function may not throw. Declare it 'noexcept' */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(26495) /* Always initialize a member variable */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(26451) /* Arithmetic overflow ... */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(26444) /* Avoid unnamed objects with custom ctor and dtor... */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(26812) /* Prefer 'enum class' over 'enum' */
+
+#define DOCTEST_SUPPRESS_COMMON_WARNINGS_POP \
+ DOCTEST_CLANG_SUPPRESS_WARNING_POP \
+ DOCTEST_GCC_SUPPRESS_WARNING_POP \
+ DOCTEST_MSVC_SUPPRESS_WARNING_POP
+
+DOCTEST_SUPPRESS_COMMON_WARNINGS_PUSH
+
+DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
+DOCTEST_CLANG_SUPPRESS_WARNING("-Wnon-virtual-dtor")
+DOCTEST_CLANG_SUPPRESS_WARNING("-Wdeprecated")
+
+DOCTEST_GCC_SUPPRESS_WARNING_PUSH
+DOCTEST_GCC_SUPPRESS_WARNING("-Wctor-dtor-privacy")
+DOCTEST_GCC_SUPPRESS_WARNING("-Wnon-virtual-dtor")
+DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-promo")
+
+DOCTEST_MSVC_SUPPRESS_WARNING_PUSH
+DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly defined as deleted
+
+#define DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN \
+ DOCTEST_MSVC_SUPPRESS_WARNING_PUSH \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4548) /* before comma no effect; expected side - effect */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4265) /* virtual functions, but destructor is not virtual */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4986) /* exception specification does not match previous */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4350) /* 'member1' called instead of 'member2' */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4668) /* not defined as a preprocessor macro */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4365) /* signed/unsigned mismatch */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4774) /* format string not a string literal */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4820) /* padding */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4625) /* copy constructor was implicitly deleted */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4626) /* assignment operator was implicitly deleted */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5027) /* move assignment operator implicitly deleted */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5026) /* move constructor was implicitly deleted */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4623) /* default constructor was implicitly deleted */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5039) /* pointer to pot. throwing function passed to extern C */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5045) /* Spectre mitigation for memory load */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5105) /* macro producing 'defined' has undefined behavior */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4738) /* storing float result in memory, loss of performance */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5262) /* implicit fall-through */
+
+#define DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END DOCTEST_MSVC_SUPPRESS_WARNING_POP
+
+// =================================================================================================
+// == FEATURE DETECTION ============================================================================
+// =================================================================================================
+
+// general compiler feature support table: https://en.cppreference.com/w/cpp/compiler_support
+// MSVC C++11 feature support table: https://msdn.microsoft.com/en-us/library/hh567368.aspx
+// GCC C++11 feature support table: https://gcc.gnu.org/projects/cxx-status.html
+// MSVC version table:
+// https://en.wikipedia.org/wiki/Microsoft_Visual_C%2B%2B#Internal_version_numbering
+// MSVC++ 14.3 (17) _MSC_VER == 1930 (Visual Studio 2022)
+// MSVC++ 14.2 (16) _MSC_VER == 1920 (Visual Studio 2019)
+// MSVC++ 14.1 (15) _MSC_VER == 1910 (Visual Studio 2017)
+// MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015)
+// MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013)
+// MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012)
+// MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010)
+// MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008)
+// MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005)
+
+// Universal Windows Platform support
+#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
+#define DOCTEST_CONFIG_NO_WINDOWS_SEH
+#endif // WINAPI_FAMILY
+#if DOCTEST_MSVC && !defined(DOCTEST_CONFIG_WINDOWS_SEH)
+#define DOCTEST_CONFIG_WINDOWS_SEH
+#endif // MSVC
+#if defined(DOCTEST_CONFIG_NO_WINDOWS_SEH) && defined(DOCTEST_CONFIG_WINDOWS_SEH)
+#undef DOCTEST_CONFIG_WINDOWS_SEH
+#endif // DOCTEST_CONFIG_NO_WINDOWS_SEH
+
+#if !defined(_WIN32) && !defined(__QNX__) && !defined(DOCTEST_CONFIG_POSIX_SIGNALS) && \
+ !defined(__EMSCRIPTEN__) && !defined(__wasi__)
+#define DOCTEST_CONFIG_POSIX_SIGNALS
+#endif // _WIN32
+#if defined(DOCTEST_CONFIG_NO_POSIX_SIGNALS) && defined(DOCTEST_CONFIG_POSIX_SIGNALS)
+#undef DOCTEST_CONFIG_POSIX_SIGNALS
+#endif // DOCTEST_CONFIG_NO_POSIX_SIGNALS
+
+#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
+#if !defined(__cpp_exceptions) && !defined(__EXCEPTIONS) && !defined(_CPPUNWIND) \
+ || defined(__wasi__)
+#define DOCTEST_CONFIG_NO_EXCEPTIONS
+#endif // no exceptions
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
+
+#ifdef DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS
+#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
+#define DOCTEST_CONFIG_NO_EXCEPTIONS
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS
+
+#if defined(DOCTEST_CONFIG_NO_EXCEPTIONS) && !defined(DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS)
+#define DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS && !DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS
+
+#ifdef __wasi__
+#define DOCTEST_CONFIG_NO_MULTITHREADING
+#endif
+
+#if defined(DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN) && !defined(DOCTEST_CONFIG_IMPLEMENT)
+#define DOCTEST_CONFIG_IMPLEMENT
+#endif // DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
+
+#if defined(_WIN32) || defined(__CYGWIN__)
+#if DOCTEST_MSVC
+#define DOCTEST_SYMBOL_EXPORT __declspec(dllexport)
+#define DOCTEST_SYMBOL_IMPORT __declspec(dllimport)
+#else // MSVC
+#define DOCTEST_SYMBOL_EXPORT __attribute__((dllexport))
+#define DOCTEST_SYMBOL_IMPORT __attribute__((dllimport))
+#endif // MSVC
+#else // _WIN32
+#define DOCTEST_SYMBOL_EXPORT __attribute__((visibility("default")))
+#define DOCTEST_SYMBOL_IMPORT
+#endif // _WIN32
+
+#ifdef DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL
+#ifdef DOCTEST_CONFIG_IMPLEMENT
+#define DOCTEST_INTERFACE DOCTEST_SYMBOL_EXPORT
+#else // DOCTEST_CONFIG_IMPLEMENT
+#define DOCTEST_INTERFACE DOCTEST_SYMBOL_IMPORT
+#endif // DOCTEST_CONFIG_IMPLEMENT
+#else // DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL
+#define DOCTEST_INTERFACE
+#endif // DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL
+
+// needed for extern template instantiations
+// see https://github.com/fmtlib/fmt/issues/2228
+#if DOCTEST_MSVC
+#define DOCTEST_INTERFACE_DECL
+#define DOCTEST_INTERFACE_DEF DOCTEST_INTERFACE
+#else // DOCTEST_MSVC
+#define DOCTEST_INTERFACE_DECL DOCTEST_INTERFACE
+#define DOCTEST_INTERFACE_DEF
+#endif // DOCTEST_MSVC
+
+#define DOCTEST_EMPTY
+
+#if DOCTEST_MSVC
+#define DOCTEST_NOINLINE __declspec(noinline)
+#define DOCTEST_UNUSED
+#define DOCTEST_ALIGNMENT(x)
+#elif DOCTEST_CLANG && DOCTEST_CLANG < DOCTEST_COMPILER(3, 5, 0)
+#define DOCTEST_NOINLINE
+#define DOCTEST_UNUSED
+#define DOCTEST_ALIGNMENT(x)
+#else
+#define DOCTEST_NOINLINE __attribute__((noinline))
+#define DOCTEST_UNUSED __attribute__((unused))
+#define DOCTEST_ALIGNMENT(x) __attribute__((aligned(x)))
+#endif
+
+#ifdef DOCTEST_CONFIG_NO_CONTRADICTING_INLINE
+#define DOCTEST_INLINE_NOINLINE inline
+#else
+#define DOCTEST_INLINE_NOINLINE inline DOCTEST_NOINLINE
+#endif
+
+#ifndef DOCTEST_NORETURN
+#if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0))
+#define DOCTEST_NORETURN
+#else // DOCTEST_MSVC
+#define DOCTEST_NORETURN [[noreturn]]
+#endif // DOCTEST_MSVC
+#endif // DOCTEST_NORETURN
+
+#ifndef DOCTEST_NOEXCEPT
+#if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0))
+#define DOCTEST_NOEXCEPT
+#else // DOCTEST_MSVC
+#define DOCTEST_NOEXCEPT noexcept
+#endif // DOCTEST_MSVC
+#endif // DOCTEST_NOEXCEPT
+
+#ifndef DOCTEST_CONSTEXPR
+#if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0))
+#define DOCTEST_CONSTEXPR const
+#define DOCTEST_CONSTEXPR_FUNC inline
+#else // DOCTEST_MSVC
+#define DOCTEST_CONSTEXPR constexpr
+#define DOCTEST_CONSTEXPR_FUNC constexpr
+#endif // DOCTEST_MSVC
+#endif // DOCTEST_CONSTEXPR
+
+#ifndef DOCTEST_NO_SANITIZE_INTEGER
+#if DOCTEST_CLANG >= DOCTEST_COMPILER(3, 7, 0)
+#define DOCTEST_NO_SANITIZE_INTEGER __attribute__((no_sanitize("integer")))
+#else
+#define DOCTEST_NO_SANITIZE_INTEGER
+#endif
+#endif // DOCTEST_NO_SANITIZE_INTEGER
+
+// =================================================================================================
+// == FEATURE DETECTION END ========================================================================
+// =================================================================================================
+
+#define DOCTEST_DECLARE_INTERFACE(name) \
+ virtual ~name(); \
+ name() = default; \
+ name(const name&) = delete; \
+ name(name&&) = delete; \
+ name& operator=(const name&) = delete; \
+ name& operator=(name&&) = delete;
+
+#define DOCTEST_DEFINE_INTERFACE(name) \
+ name::~name() = default;
+
+// internal macros for string concatenation and anonymous variable name generation
+#define DOCTEST_CAT_IMPL(s1, s2) s1##s2
+#define DOCTEST_CAT(s1, s2) DOCTEST_CAT_IMPL(s1, s2)
+#ifdef __COUNTER__ // not standard and may be missing for some compilers
+#define DOCTEST_ANONYMOUS(x) DOCTEST_CAT(x, __COUNTER__)
+#else // __COUNTER__
+#define DOCTEST_ANONYMOUS(x) DOCTEST_CAT(x, __LINE__)
+#endif // __COUNTER__
+
+#ifndef DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE
+#define DOCTEST_REF_WRAP(x) x&
+#else // DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE
+#define DOCTEST_REF_WRAP(x) x
+#endif // DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE
+
+// not using __APPLE__ because... this is how Catch does it
+#ifdef __MAC_OS_X_VERSION_MIN_REQUIRED
+#define DOCTEST_PLATFORM_MAC
+#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
+#define DOCTEST_PLATFORM_IPHONE
+#elif defined(_WIN32)
+#define DOCTEST_PLATFORM_WINDOWS
+#elif defined(__wasi__)
+#define DOCTEST_PLATFORM_WASI
+#else // DOCTEST_PLATFORM
+#define DOCTEST_PLATFORM_LINUX
+#endif // DOCTEST_PLATFORM
+
+namespace doctest { namespace detail {
+ static DOCTEST_CONSTEXPR int consume(const int*, int) noexcept { return 0; }
+}}
+
+#define DOCTEST_GLOBAL_NO_WARNINGS(var, ...) \
+ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wglobal-constructors") \
+ static const int var = doctest::detail::consume(&var, __VA_ARGS__); \
+ DOCTEST_CLANG_SUPPRESS_WARNING_POP
+
+#ifndef DOCTEST_BREAK_INTO_DEBUGGER
+// should probably take a look at https://github.com/scottt/debugbreak
+#ifdef DOCTEST_PLATFORM_LINUX
+#if defined(__GNUC__) && (defined(__i386) || defined(__x86_64))
+// Break at the location of the failing check if possible
+#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) // NOLINT(hicpp-no-assembler)
+#else
+#include <signal.h>
+#define DOCTEST_BREAK_INTO_DEBUGGER() raise(SIGTRAP)
+#endif
+#elif defined(DOCTEST_PLATFORM_MAC)
+#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(__i386)
+#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) // NOLINT(hicpp-no-assembler)
+#elif defined(__ppc__) || defined(__ppc64__)
+// https://www.cocoawithlove.com/2008/03/break-into-debugger.html
+#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n": : : "memory","r0","r3","r4") // NOLINT(hicpp-no-assembler)
+#else
+#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("brk #0"); // NOLINT(hicpp-no-assembler)
+#endif
+#elif DOCTEST_MSVC
+#define DOCTEST_BREAK_INTO_DEBUGGER() __debugbreak()
+#elif defined(__MINGW32__)
+DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wredundant-decls")
+extern "C" __declspec(dllimport) void __stdcall DebugBreak();
+DOCTEST_GCC_SUPPRESS_WARNING_POP
+#define DOCTEST_BREAK_INTO_DEBUGGER() ::DebugBreak()
+#else // linux
+#define DOCTEST_BREAK_INTO_DEBUGGER() (static_cast<void>(0))
+#endif // linux
+#endif // DOCTEST_BREAK_INTO_DEBUGGER
+
+// this is kept here for backwards compatibility since the config option was changed
+#ifdef DOCTEST_CONFIG_USE_IOSFWD
+#ifndef DOCTEST_CONFIG_USE_STD_HEADERS
+#define DOCTEST_CONFIG_USE_STD_HEADERS
+#endif
+#endif // DOCTEST_CONFIG_USE_IOSFWD
+
+// for clang - always include ciso646 (which drags some std stuff) because
+// we want to check if we are using libc++ with the _LIBCPP_VERSION macro in
+// which case we don't want to forward declare stuff from std - for reference:
+// https://github.com/doctest/doctest/issues/126
+// https://github.com/doctest/doctest/issues/356
+#if DOCTEST_CLANG
+#include <ciso646>
+#endif // clang
+
+#ifdef _LIBCPP_VERSION
+#ifndef DOCTEST_CONFIG_USE_STD_HEADERS
+#define DOCTEST_CONFIG_USE_STD_HEADERS
+#endif
+#endif // _LIBCPP_VERSION
+
+#ifdef DOCTEST_CONFIG_USE_STD_HEADERS
+#ifndef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+#define DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN
+#include <cstddef>
+#include <ostream>
+#include <istream>
+DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END
+#else // DOCTEST_CONFIG_USE_STD_HEADERS
+
+// Forward declaring 'X' in namespace std is not permitted by the C++ Standard.
+DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4643)
+
+namespace std { // NOLINT(cert-dcl58-cpp)
+typedef decltype(nullptr) nullptr_t; // NOLINT(modernize-use-using)
+typedef decltype(sizeof(void*)) size_t; // NOLINT(modernize-use-using)
+template <class charT>
+struct char_traits;
+template <>
+struct char_traits<char>;
+template <class charT, class traits>
+class basic_ostream; // NOLINT(fuchsia-virtual-inheritance)
+typedef basic_ostream<char, char_traits<char>> ostream; // NOLINT(modernize-use-using)
+template<class traits>
+// NOLINTNEXTLINE
+basic_ostream<char, traits>& operator<<(basic_ostream<char, traits>&, const char*);
+template <class charT, class traits>
+class basic_istream;
+typedef basic_istream<char, char_traits<char>> istream; // NOLINT(modernize-use-using)
+template <class... Types>
+class tuple;
+#if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0)
+// see this issue on why this is needed: https://github.com/doctest/doctest/issues/183
+template <class Ty>
+class allocator;
+template <class Elem, class Traits, class Alloc>
+class basic_string;
+using string = basic_string<char, char_traits<char>, allocator<char>>;
+#endif // VS 2019
+} // namespace std
+
+DOCTEST_MSVC_SUPPRESS_WARNING_POP
+
+#endif // DOCTEST_CONFIG_USE_STD_HEADERS
+
+#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+#include <type_traits>
+#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+
+namespace doctest {
+
+using std::size_t;
+
+DOCTEST_INTERFACE extern bool is_running_in_test;
+
+#ifndef DOCTEST_CONFIG_STRING_SIZE_TYPE
+#define DOCTEST_CONFIG_STRING_SIZE_TYPE unsigned
+#endif
+
+// A 24 byte string class (can be as small as 17 for x64 and 13 for x86) that can hold strings with length
+// of up to 23 chars on the stack before going on the heap - the last byte of the buffer is used for:
+// - "is small" bit - the highest bit - if "0" then it is small - otherwise its "1" (128)
+// - if small - capacity left before going on the heap - using the lowest 5 bits
+// - if small - 2 bits are left unused - the second and third highest ones
+// - if small - acts as a null terminator if strlen() is 23 (24 including the null terminator)
+// and the "is small" bit remains "0" ("as well as the capacity left") so its OK
+// Idea taken from this lecture about the string implementation of facebook/folly - fbstring
+// https://www.youtube.com/watch?v=kPR8h4-qZdk
+// TODO:
+// - optimizations - like not deleting memory unnecessarily in operator= and etc.
+// - resize/reserve/clear
+// - replace
+// - back/front
+// - iterator stuff
+// - find & friends
+// - push_back/pop_back
+// - assign/insert/erase
+// - relational operators as free functions - taking const char* as one of the params
+class DOCTEST_INTERFACE String
+{
+public:
+ using size_type = DOCTEST_CONFIG_STRING_SIZE_TYPE;
+
+private:
+ static DOCTEST_CONSTEXPR size_type len = 24; //!OCLINT avoid private static members
+ static DOCTEST_CONSTEXPR size_type last = len - 1; //!OCLINT avoid private static members
+
+ struct view // len should be more than sizeof(view) - because of the final byte for flags
+ {
+ char* ptr;
+ size_type size;
+ size_type capacity;
+ };
+
+ union
+ {
+ char buf[len]; // NOLINT(*-avoid-c-arrays)
+ view data;
+ };
+
+ char* allocate(size_type sz);
+
+ bool isOnStack() const noexcept { return (buf[last] & 128) == 0; }
+ void setOnHeap() noexcept;
+ void setLast(size_type in = last) noexcept;
+ void setSize(size_type sz) noexcept;
+
+ void copy(const String& other);
+
+public:
+ static DOCTEST_CONSTEXPR size_type npos = static_cast<size_type>(-1);
+
+ String() noexcept;
+ ~String();
+
+ // cppcheck-suppress noExplicitConstructor
+ String(const char* in);
+ String(const char* in, size_type in_size);
+
+ String(std::istream& in, size_type in_size);
+
+ String(const String& other);
+ String& operator=(const String& other);
+
+ String& operator+=(const String& other);
+
+ String(String&& other) noexcept;
+ String& operator=(String&& other) noexcept;
+
+ char operator[](size_type i) const;
+ char& operator[](size_type i);
+
+ // the only functions I'm willing to leave in the interface - available for inlining
+ const char* c_str() const { return const_cast<String*>(this)->c_str(); } // NOLINT
+ char* c_str() {
+ if (isOnStack()) {
+ return reinterpret_cast<char*>(buf);
+ }
+ return data.ptr;
+ }
+
+ size_type size() const;
+ size_type capacity() const;
+
+ String substr(size_type pos, size_type cnt = npos) &&;
+ String substr(size_type pos, size_type cnt = npos) const &;
+
+ size_type find(char ch, size_type pos = 0) const;
+ size_type rfind(char ch, size_type pos = npos) const;
+
+ int compare(const char* other, bool no_case = false) const;
+ int compare(const String& other, bool no_case = false) const;
+
+friend DOCTEST_INTERFACE std::ostream& operator<<(std::ostream& s, const String& in);
+};
+
+DOCTEST_INTERFACE String operator+(const String& lhs, const String& rhs);
+
+DOCTEST_INTERFACE bool operator==(const String& lhs, const String& rhs);
+DOCTEST_INTERFACE bool operator!=(const String& lhs, const String& rhs);
+DOCTEST_INTERFACE bool operator<(const String& lhs, const String& rhs);
+DOCTEST_INTERFACE bool operator>(const String& lhs, const String& rhs);
+DOCTEST_INTERFACE bool operator<=(const String& lhs, const String& rhs);
+DOCTEST_INTERFACE bool operator>=(const String& lhs, const String& rhs);
+
+class DOCTEST_INTERFACE Contains {
+public:
+ explicit Contains(const String& string);
+
+ bool checkWith(const String& other) const;
+
+ String string;
+};
+
+DOCTEST_INTERFACE String toString(const Contains& in);
+
+DOCTEST_INTERFACE bool operator==(const String& lhs, const Contains& rhs);
+DOCTEST_INTERFACE bool operator==(const Contains& lhs, const String& rhs);
+DOCTEST_INTERFACE bool operator!=(const String& lhs, const Contains& rhs);
+DOCTEST_INTERFACE bool operator!=(const Contains& lhs, const String& rhs);
+
+namespace Color {
+ enum Enum
+ {
+ None = 0,
+ White,
+ Red,
+ Green,
+ Blue,
+ Cyan,
+ Yellow,
+ Grey,
+
+ Bright = 0x10,
+
+ BrightRed = Bright | Red,
+ BrightGreen = Bright | Green,
+ LightGrey = Bright | Grey,
+ BrightWhite = Bright | White
+ };
+
+ DOCTEST_INTERFACE std::ostream& operator<<(std::ostream& s, Color::Enum code);
+} // namespace Color
+
+namespace assertType {
+ enum Enum
+ {
+ // macro traits
+
+ is_warn = 1,
+ is_check = 2 * is_warn,
+ is_require = 2 * is_check,
+
+ is_normal = 2 * is_require,
+ is_throws = 2 * is_normal,
+ is_throws_as = 2 * is_throws,
+ is_throws_with = 2 * is_throws_as,
+ is_nothrow = 2 * is_throws_with,
+
+ is_false = 2 * is_nothrow,
+ is_unary = 2 * is_false, // not checked anywhere - used just to distinguish the types
+
+ is_eq = 2 * is_unary,
+ is_ne = 2 * is_eq,
+
+ is_lt = 2 * is_ne,
+ is_gt = 2 * is_lt,
+
+ is_ge = 2 * is_gt,
+ is_le = 2 * is_ge,
+
+ // macro types
+
+ DT_WARN = is_normal | is_warn,
+ DT_CHECK = is_normal | is_check,
+ DT_REQUIRE = is_normal | is_require,
+
+ DT_WARN_FALSE = is_normal | is_false | is_warn,
+ DT_CHECK_FALSE = is_normal | is_false | is_check,
+ DT_REQUIRE_FALSE = is_normal | is_false | is_require,
+
+ DT_WARN_THROWS = is_throws | is_warn,
+ DT_CHECK_THROWS = is_throws | is_check,
+ DT_REQUIRE_THROWS = is_throws | is_require,
+
+ DT_WARN_THROWS_AS = is_throws_as | is_warn,
+ DT_CHECK_THROWS_AS = is_throws_as | is_check,
+ DT_REQUIRE_THROWS_AS = is_throws_as | is_require,
+
+ DT_WARN_THROWS_WITH = is_throws_with | is_warn,
+ DT_CHECK_THROWS_WITH = is_throws_with | is_check,
+ DT_REQUIRE_THROWS_WITH = is_throws_with | is_require,
+
+ DT_WARN_THROWS_WITH_AS = is_throws_with | is_throws_as | is_warn,
+ DT_CHECK_THROWS_WITH_AS = is_throws_with | is_throws_as | is_check,
+ DT_REQUIRE_THROWS_WITH_AS = is_throws_with | is_throws_as | is_require,
+
+ DT_WARN_NOTHROW = is_nothrow | is_warn,
+ DT_CHECK_NOTHROW = is_nothrow | is_check,
+ DT_REQUIRE_NOTHROW = is_nothrow | is_require,
+
+ DT_WARN_EQ = is_normal | is_eq | is_warn,
+ DT_CHECK_EQ = is_normal | is_eq | is_check,
+ DT_REQUIRE_EQ = is_normal | is_eq | is_require,
+
+ DT_WARN_NE = is_normal | is_ne | is_warn,
+ DT_CHECK_NE = is_normal | is_ne | is_check,
+ DT_REQUIRE_NE = is_normal | is_ne | is_require,
+
+ DT_WARN_GT = is_normal | is_gt | is_warn,
+ DT_CHECK_GT = is_normal | is_gt | is_check,
+ DT_REQUIRE_GT = is_normal | is_gt | is_require,
+
+ DT_WARN_LT = is_normal | is_lt | is_warn,
+ DT_CHECK_LT = is_normal | is_lt | is_check,
+ DT_REQUIRE_LT = is_normal | is_lt | is_require,
+
+ DT_WARN_GE = is_normal | is_ge | is_warn,
+ DT_CHECK_GE = is_normal | is_ge | is_check,
+ DT_REQUIRE_GE = is_normal | is_ge | is_require,
+
+ DT_WARN_LE = is_normal | is_le | is_warn,
+ DT_CHECK_LE = is_normal | is_le | is_check,
+ DT_REQUIRE_LE = is_normal | is_le | is_require,
+
+ DT_WARN_UNARY = is_normal | is_unary | is_warn,
+ DT_CHECK_UNARY = is_normal | is_unary | is_check,
+ DT_REQUIRE_UNARY = is_normal | is_unary | is_require,
+
+ DT_WARN_UNARY_FALSE = is_normal | is_false | is_unary | is_warn,
+ DT_CHECK_UNARY_FALSE = is_normal | is_false | is_unary | is_check,
+ DT_REQUIRE_UNARY_FALSE = is_normal | is_false | is_unary | is_require,
+ };
+} // namespace assertType
+
+DOCTEST_INTERFACE const char* assertString(assertType::Enum at);
+DOCTEST_INTERFACE const char* failureString(assertType::Enum at);
+DOCTEST_INTERFACE const char* skipPathFromFilename(const char* file);
+
+struct DOCTEST_INTERFACE TestCaseData
+{
+ String m_file; // the file in which the test was registered (using String - see #350)
+ unsigned m_line; // the line where the test was registered
+ const char* m_name; // name of the test case
+ const char* m_test_suite; // the test suite in which the test was added
+ const char* m_description;
+ bool m_skip;
+ bool m_no_breaks;
+ bool m_no_output;
+ bool m_may_fail;
+ bool m_should_fail;
+ int m_expected_failures;
+ double m_timeout;
+};
+
+struct DOCTEST_INTERFACE AssertData
+{
+ // common - for all asserts
+ const TestCaseData* m_test_case;
+ assertType::Enum m_at;
+ const char* m_file;
+ int m_line;
+ const char* m_expr;
+ bool m_failed;
+
+ // exception-related - for all asserts
+ bool m_threw;
+ String m_exception;
+
+ // for normal asserts
+ String m_decomp;
+
+ // for specific exception-related asserts
+ bool m_threw_as;
+ const char* m_exception_type;
+
+ class DOCTEST_INTERFACE StringContains {
+ private:
+ Contains content;
+ bool isContains;
+
+ public:
+ StringContains(const String& str) : content(str), isContains(false) { }
+ StringContains(Contains cntn) : content(static_cast<Contains&&>(cntn)), isContains(true) { }
+
+ bool check(const String& str) { return isContains ? (content == str) : (content.string == str); }
+
+ operator const String&() const { return content.string; }
+
+ const char* c_str() const { return content.string.c_str(); }
+ } m_exception_string;
+
+ AssertData(assertType::Enum at, const char* file, int line, const char* expr,
+ const char* exception_type, const StringContains& exception_string);
+};
+
+struct DOCTEST_INTERFACE MessageData
+{
+ String m_string;
+ const char* m_file;
+ int m_line;
+ assertType::Enum m_severity;
+};
+
+struct DOCTEST_INTERFACE SubcaseSignature
+{
+ String m_name;
+ const char* m_file;
+ int m_line;
+
+ bool operator==(const SubcaseSignature& other) const;
+ bool operator<(const SubcaseSignature& other) const;
+};
+
+struct DOCTEST_INTERFACE IContextScope
+{
+ DOCTEST_DECLARE_INTERFACE(IContextScope)
+ virtual void stringify(std::ostream*) const = 0;
+};
+
+namespace detail {
+ struct DOCTEST_INTERFACE TestCase;
+} // namespace detail
+
+struct ContextOptions //!OCLINT too many fields
+{
+ std::ostream* cout = nullptr; // stdout stream
+ String binary_name; // the test binary name
+
+ const detail::TestCase* currentTest = nullptr;
+
+ // == parameters from the command line
+ String out; // output filename
+ String order_by; // how tests should be ordered
+ unsigned rand_seed; // the seed for rand ordering
+
+ unsigned first; // the first (matching) test to be executed
+ unsigned last; // the last (matching) test to be executed
+
+ int abort_after; // stop tests after this many failed assertions
+ int subcase_filter_levels; // apply the subcase filters for the first N levels
+
+ bool success; // include successful assertions in output
+ bool case_sensitive; // if filtering should be case sensitive
+ bool exit; // if the program should be exited after the tests are ran/whatever
+ bool duration; // print the time duration of each test case
+ bool minimal; // minimal console output (only test failures)
+ bool quiet; // no console output
+ bool no_throw; // to skip exceptions-related assertion macros
+ bool no_exitcode; // if the framework should return 0 as the exitcode
+ bool no_run; // to not run the tests at all (can be done with an "*" exclude)
+ bool no_intro; // to not print the intro of the framework
+ bool no_version; // to not print the version of the framework
+ bool no_colors; // if output to the console should be colorized
+ bool force_colors; // forces the use of colors even when a tty cannot be detected
+ bool no_breaks; // to not break into the debugger
+ bool no_skip; // don't skip test cases which are marked to be skipped
+ bool gnu_file_line; // if line numbers should be surrounded with :x: and not (x):
+ bool no_path_in_filenames; // if the path to files should be removed from the output
+ String strip_file_prefixes;// remove the longest matching one of these prefixes from any file paths in the output
+ bool no_line_numbers; // if source code line numbers should be omitted from the output
+ bool no_debug_output; // no output in the debug console when a debugger is attached
+ bool no_skipped_summary; // don't print "skipped" in the summary !!! UNDOCUMENTED !!!
+ bool no_time_in_output; // omit any time/timestamps from output !!! UNDOCUMENTED !!!
+
+ bool help; // to print the help
+ bool version; // to print the version
+ bool count; // if only the count of matching tests is to be retrieved
+ bool list_test_cases; // to list all tests matching the filters
+ bool list_test_suites; // to list all suites matching the filters
+ bool list_reporters; // lists all registered reporters
+};
+
+namespace detail {
+ namespace types {
+#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+ using namespace std;
+#else
+ template <bool COND, typename T = void>
+ struct enable_if { };
+
+ template <typename T>
+ struct enable_if<true, T> { using type = T; };
+
+ struct true_type { static DOCTEST_CONSTEXPR bool value = true; };
+ struct false_type { static DOCTEST_CONSTEXPR bool value = false; };
+
+ template <typename T> struct remove_reference { using type = T; };
+ template <typename T> struct remove_reference<T&> { using type = T; };
+ template <typename T> struct remove_reference<T&&> { using type = T; };
+
+ template <typename T> struct is_rvalue_reference : false_type { };
+ template <typename T> struct is_rvalue_reference<T&&> : true_type { };
+
+ template<typename T> struct remove_const { using type = T; };
+ template <typename T> struct remove_const<const T> { using type = T; };
+
+ // Compiler intrinsics
+ template <typename T> struct is_enum { static DOCTEST_CONSTEXPR bool value = __is_enum(T); };
+ template <typename T> struct underlying_type { using type = __underlying_type(T); };
+
+ template <typename T> struct is_pointer : false_type { };
+ template <typename T> struct is_pointer<T*> : true_type { };
+
+ template <typename T> struct is_array : false_type { };
+ // NOLINTNEXTLINE(*-avoid-c-arrays)
+ template <typename T, size_t SIZE> struct is_array<T[SIZE]> : true_type { };
+#endif
+ }
+
+ // <utility>
+ template <typename T>
+ T&& declval();
+
+ template <class T>
+ DOCTEST_CONSTEXPR_FUNC T&& forward(typename types::remove_reference<T>::type& t) DOCTEST_NOEXCEPT {
+ return static_cast<T&&>(t);
+ }
+
+ template <class T>
+ DOCTEST_CONSTEXPR_FUNC T&& forward(typename types::remove_reference<T>::type&& t) DOCTEST_NOEXCEPT {
+ return static_cast<T&&>(t);
+ }
+
+ template <typename T>
+ struct deferred_false : types::false_type { };
+
+// MSVS 2015 :(
+#if !DOCTEST_CLANG && defined(_MSC_VER) && _MSC_VER <= 1900
+ template <typename T, typename = void>
+ struct has_global_insertion_operator : types::false_type { };
+
+ template <typename T>
+ struct has_global_insertion_operator<T, decltype(::operator<<(declval<std::ostream&>(), declval<const T&>()), void())> : types::true_type { };
+
+ template <typename T, typename = void>
+ struct has_insertion_operator { static DOCTEST_CONSTEXPR bool value = has_global_insertion_operator<T>::value; };
+
+ template <typename T, bool global>
+ struct insert_hack;
+
+ template <typename T>
+ struct insert_hack<T, true> {
+ static void insert(std::ostream& os, const T& t) { ::operator<<(os, t); }
+ };
+
+ template <typename T>
+ struct insert_hack<T, false> {
+ static void insert(std::ostream& os, const T& t) { operator<<(os, t); }
+ };
+
+ template <typename T>
+ using insert_hack_t = insert_hack<T, has_global_insertion_operator<T>::value>;
+#else
+ template <typename T, typename = void>
+ struct has_insertion_operator : types::false_type { };
+#endif
+
+ template <typename T>
+ struct has_insertion_operator<T, decltype(operator<<(declval<std::ostream&>(), declval<const T&>()), void())> : types::true_type { };
+
+ template <typename T>
+ struct should_stringify_as_underlying_type {
+ static DOCTEST_CONSTEXPR bool value = detail::types::is_enum<T>::value && !doctest::detail::has_insertion_operator<T>::value;
+ };
+
+ DOCTEST_INTERFACE std::ostream* tlssPush();
+ DOCTEST_INTERFACE String tlssPop();
+
+ template <bool C>
+ struct StringMakerBase {
+ template <typename T>
+ static String convert(const DOCTEST_REF_WRAP(T)) {
+#ifdef DOCTEST_CONFIG_REQUIRE_STRINGIFICATION_FOR_ALL_USED_TYPES
+ static_assert(deferred_false<T>::value, "No stringification detected for type T. See string conversion manual");
+#endif
+ return "{?}";
+ }
+ };
+
+ template <typename T>
+ struct filldata;
+
+ template <typename T>
+ void filloss(std::ostream* stream, const T& in) {
+ filldata<T>::fill(stream, in);
+ }
+
+ template <typename T, size_t N>
+ 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<typename types::remove_reference<decltype(in)>::type>(stream, in);
+ }
+
+ template <typename T>
+ String toStream(const T& in) {
+ std::ostream* stream = tlssPush();
+ filloss(stream, in);
+ return tlssPop();
+ }
+
+ template <>
+ struct StringMakerBase<true> {
+ template <typename T>
+ static String convert(const DOCTEST_REF_WRAP(T) in) {
+ return toStream(in);
+ }
+ };
+} // namespace detail
+
+template <typename T>
+struct StringMaker : public detail::StringMakerBase<
+ detail::has_insertion_operator<T>::value || detail::types::is_pointer<T>::value || detail::types::is_array<T>::value>
+{};
+
+#ifndef DOCTEST_STRINGIFY
+#ifdef DOCTEST_CONFIG_DOUBLE_STRINGIFY
+#define DOCTEST_STRINGIFY(...) toString(toString(__VA_ARGS__))
+#else
+#define DOCTEST_STRINGIFY(...) toString(__VA_ARGS__)
+#endif
+#endif
+
+template <typename T>
+String toString() {
+#if DOCTEST_CLANG == 0 && DOCTEST_GCC == 0 && DOCTEST_ICC == 0
+ String ret = __FUNCSIG__; // class doctest::String __cdecl doctest::toString<TYPE>(void)
+ String::size_type beginPos = ret.find('<');
+ return ret.substr(beginPos + 1, ret.size() - beginPos - static_cast<String::size_type>(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 <typename T, typename detail::types::enable_if<!detail::should_stringify_as_underlying_type<T>::value, bool>::type = true>
+String toString(const DOCTEST_REF_WRAP(T) value) {
+ return StringMaker<T>::convert(value);
+}
+
+#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+DOCTEST_INTERFACE String toString(const char* in);
+#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+
+#if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0)
+// see this issue on why this is needed: https://github.com/doctest/doctest/issues/183
+DOCTEST_INTERFACE String toString(const std::string& in);
+#endif // VS 2019
+
+DOCTEST_INTERFACE String toString(String in);
+
+DOCTEST_INTERFACE String toString(std::nullptr_t);
+
+DOCTEST_INTERFACE String toString(bool in);
+
+DOCTEST_INTERFACE String toString(float in);
+DOCTEST_INTERFACE String toString(double in);
+DOCTEST_INTERFACE String toString(double long in);
+
+DOCTEST_INTERFACE String toString(char in);
+DOCTEST_INTERFACE String toString(char signed in);
+DOCTEST_INTERFACE String toString(char unsigned in);
+DOCTEST_INTERFACE String toString(short in);
+DOCTEST_INTERFACE String toString(short unsigned in);
+DOCTEST_INTERFACE String toString(signed in);
+DOCTEST_INTERFACE String toString(unsigned in);
+DOCTEST_INTERFACE String toString(long in);
+DOCTEST_INTERFACE String toString(long unsigned in);
+DOCTEST_INTERFACE String toString(long long in);
+DOCTEST_INTERFACE String toString(long long unsigned in);
+
+template <typename T, typename detail::types::enable_if<detail::should_stringify_as_underlying_type<T>::value, bool>::type = true>
+String toString(const DOCTEST_REF_WRAP(T) value) {
+ using UT = typename detail::types::underlying_type<T>::type;
+ return (DOCTEST_STRINGIFY(static_cast<UT>(value)));
+}
+
+namespace detail {
+ template <typename T>
+ struct filldata
+ {
+ static void fill(std::ostream* stream, const T& in) {
+#if defined(_MSC_VER) && _MSC_VER <= 1900
+ insert_hack_t<T>::insert(*stream, in);
+#else
+ operator<<(*stream, in);
+#endif
+ }
+ };
+
+DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4866)
+// NOLINTBEGIN(*-avoid-c-arrays)
+ template <typename T, size_t N>
+ struct filldata<T[N]> {
+ 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 <size_t N>
+ struct filldata<const char[N]> {
+ 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<const void*> {
+ static void fill(std::ostream* stream, const void* in);
+ };
+
+ template <typename T>
+ struct filldata<T*> {
+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<const void*>::fill(stream,
+#if DOCTEST_GCC == 0 || DOCTEST_GCC >= DOCTEST_COMPILER(4, 9, 0)
+ reinterpret_cast<const void*>(in)
+#else
+ *reinterpret_cast<const void* const*>(&in)
+#endif
+ );
+DOCTEST_CLANG_SUPPRESS_WARNING_POP
+ }
+ };
+}
+
+struct DOCTEST_INTERFACE Approx
+{
+ Approx(double value);
+
+ Approx operator()(double value) const;
+
+#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+ template <typename T>
+ explicit Approx(const T& value,
+ typename detail::types::enable_if<std::is_constructible<double, T>::value>::type* =
+ static_cast<T*>(nullptr)) {
+ *this = static_cast<double>(value);
+ }
+#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+
+ Approx& epsilon(double newEpsilon);
+
+#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+ template <typename T>
+ typename std::enable_if<std::is_constructible<double, T>::value, Approx&>::type epsilon(
+ const T& newEpsilon) {
+ m_epsilon = static_cast<double>(newEpsilon);
+ return *this;
+ }
+#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+
+ Approx& scale(double newScale);
+
+#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+ template <typename T>
+ typename std::enable_if<std::is_constructible<double, T>::value, Approx&>::type scale(
+ const T& newScale) {
+ m_scale = static_cast<double>(newScale);
+ return *this;
+ }
+#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+
+ // clang-format off
+ DOCTEST_INTERFACE friend bool operator==(double lhs, const Approx & rhs);
+ DOCTEST_INTERFACE friend bool operator==(const Approx & lhs, double rhs);
+ DOCTEST_INTERFACE friend bool operator!=(double lhs, const Approx & rhs);
+ DOCTEST_INTERFACE friend bool operator!=(const Approx & lhs, double rhs);
+ DOCTEST_INTERFACE friend bool operator<=(double lhs, const Approx & rhs);
+ DOCTEST_INTERFACE friend bool operator<=(const Approx & lhs, double rhs);
+ DOCTEST_INTERFACE friend bool operator>=(double lhs, const Approx & rhs);
+ DOCTEST_INTERFACE friend bool operator>=(const Approx & lhs, double rhs);
+ DOCTEST_INTERFACE friend bool operator< (double lhs, const Approx & rhs);
+ DOCTEST_INTERFACE friend bool operator< (const Approx & lhs, double rhs);
+ DOCTEST_INTERFACE friend bool operator> (double lhs, const Approx & rhs);
+ DOCTEST_INTERFACE friend bool operator> (const Approx & lhs, double rhs);
+
+#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+#define DOCTEST_APPROX_PREFIX \
+ template <typename T> friend typename std::enable_if<std::is_constructible<double, T>::value, bool>::type
+
+ DOCTEST_APPROX_PREFIX operator==(const T& lhs, const Approx& rhs) { return operator==(static_cast<double>(lhs), rhs); }
+ DOCTEST_APPROX_PREFIX operator==(const Approx& lhs, const T& rhs) { return operator==(rhs, lhs); }
+ DOCTEST_APPROX_PREFIX operator!=(const T& lhs, const Approx& rhs) { return !operator==(lhs, rhs); }
+ DOCTEST_APPROX_PREFIX operator!=(const Approx& lhs, const T& rhs) { return !operator==(rhs, lhs); }
+ DOCTEST_APPROX_PREFIX operator<=(const T& lhs, const Approx& rhs) { return static_cast<double>(lhs) < rhs.m_value || lhs == rhs; }
+ DOCTEST_APPROX_PREFIX operator<=(const Approx& lhs, const T& rhs) { return lhs.m_value < static_cast<double>(rhs) || lhs == rhs; }
+ DOCTEST_APPROX_PREFIX operator>=(const T& lhs, const Approx& rhs) { return static_cast<double>(lhs) > rhs.m_value || lhs == rhs; }
+ DOCTEST_APPROX_PREFIX operator>=(const Approx& lhs, const T& rhs) { return lhs.m_value > static_cast<double>(rhs) || lhs == rhs; }
+ DOCTEST_APPROX_PREFIX operator< (const T& lhs, const Approx& rhs) { return static_cast<double>(lhs) < rhs.m_value && lhs != rhs; }
+ DOCTEST_APPROX_PREFIX operator< (const Approx& lhs, const T& rhs) { return lhs.m_value < static_cast<double>(rhs) && lhs != rhs; }
+ DOCTEST_APPROX_PREFIX operator> (const T& lhs, const Approx& rhs) { return static_cast<double>(lhs) > rhs.m_value && lhs != rhs; }
+ DOCTEST_APPROX_PREFIX operator> (const Approx& lhs, const T& rhs) { return lhs.m_value > static_cast<double>(rhs) && lhs != rhs; }
+#undef DOCTEST_APPROX_PREFIX
+#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+
+ // clang-format on
+
+ double m_epsilon;
+ double m_scale;
+ double m_value;
+};
+
+DOCTEST_INTERFACE String toString(const Approx& in);
+
+DOCTEST_INTERFACE const ContextOptions* getContextOptions();
+
+template <typename F>
+struct DOCTEST_INTERFACE_DECL IsNaN
+{
+ F value; bool flipped;
+ IsNaN(F f, bool flip = false) : value(f), flipped(flip) { }
+ IsNaN<F> operator!() const { return { value, !flipped }; }
+ operator bool() const;
+};
+#ifndef __MINGW32__
+extern template struct DOCTEST_INTERFACE_DECL IsNaN<float>;
+extern template struct DOCTEST_INTERFACE_DECL IsNaN<double>;
+extern template struct DOCTEST_INTERFACE_DECL IsNaN<long double>;
+#endif
+DOCTEST_INTERFACE String toString(IsNaN<float> in);
+DOCTEST_INTERFACE String toString(IsNaN<double> in);
+DOCTEST_INTERFACE String toString(IsNaN<double long> in);
+
+#ifndef DOCTEST_CONFIG_DISABLE
+
+namespace detail {
+ // clang-format off
+#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+ template<class T> struct decay_array { using type = T; };
+ template<class T, unsigned N> struct decay_array<T[N]> { using type = T*; };
+ template<class T> struct decay_array<T[]> { using type = T*; };
+
+ template<class T> struct not_char_pointer { static DOCTEST_CONSTEXPR int value = 1; };
+ template<> struct not_char_pointer<char*> { static DOCTEST_CONSTEXPR int value = 0; };
+ template<> struct not_char_pointer<const char*> { static DOCTEST_CONSTEXPR int value = 0; };
+
+ template<class T> struct can_use_op : public not_char_pointer<typename decay_array<T>::type> {};
+#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+ // clang-format on
+
+ struct DOCTEST_INTERFACE TestFailureException
+ {
+ };
+
+ DOCTEST_INTERFACE bool checkIfShouldThrow(assertType::Enum at);
+
+#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
+ DOCTEST_NORETURN
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
+ DOCTEST_INTERFACE void throwException();
+
+ struct DOCTEST_INTERFACE Subcase
+ {
+ SubcaseSignature m_signature;
+ bool m_entered = false;
+
+ Subcase(const String& name, const char* file, int line);
+ Subcase(const Subcase&) = delete;
+ Subcase(Subcase&&) = delete;
+ Subcase& operator=(const Subcase&) = delete;
+ Subcase& operator=(Subcase&&) = delete;
+ ~Subcase();
+
+ operator bool() const;
+
+ private:
+ bool checkFilters();
+ };
+
+ template <typename L, typename R>
+ String stringifyBinaryExpr(const DOCTEST_REF_WRAP(L) lhs, const char* op,
+ const DOCTEST_REF_WRAP(R) rhs) {
+ return (DOCTEST_STRINGIFY(lhs)) + op + (DOCTEST_STRINGIFY(rhs));
+ }
+
+#if DOCTEST_CLANG && DOCTEST_CLANG < DOCTEST_COMPILER(3, 6, 0)
+DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wunused-comparison")
+#endif
+
+// This will check if there is any way it could find a operator like member or friend and uses it.
+// If not it doesn't find the operator or if the operator at global scope is defined after
+// this template, the template won't be instantiated due to SFINAE. Once the template is not
+// instantiated it can look for global operator using normal conversions.
+#ifdef __NVCC__
+#define SFINAE_OP(ret,op) ret
+#else
+#define SFINAE_OP(ret,op) decltype((void)(doctest::detail::declval<L>() op doctest::detail::declval<R>()),ret{})
+#endif
+
+#define DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(op, op_str, op_macro) \
+ template <typename R> \
+ DOCTEST_NOINLINE SFINAE_OP(Result,op) operator op(R&& rhs) { \
+ bool res = op_macro(doctest::detail::forward<const L>(lhs), doctest::detail::forward<R>(rhs)); \
+ if(m_at & assertType::is_false) \
+ res = !res; \
+ if(!res || doctest::getContextOptions()->success) \
+ return Result(res, stringifyBinaryExpr(lhs, op_str, rhs)); \
+ return Result(res); \
+ }
+
+ // more checks could be added - like in Catch:
+ // https://github.com/catchorg/Catch2/pull/1480/files
+ // https://github.com/catchorg/Catch2/pull/1481/files
+#define DOCTEST_FORBIT_EXPRESSION(rt, op) \
+ template <typename R> \
+ rt& operator op(const R&) { \
+ static_assert(deferred_false<R>::value, \
+ "Expression Too Complex Please Rewrite As Binary Comparison!"); \
+ return *this; \
+ }
+
+ struct DOCTEST_INTERFACE Result // NOLINT(*-member-init)
+ {
+ bool m_passed;
+ String m_decomp;
+
+ Result() = default; // TODO: Why do we need this? (To remove NOLINT)
+ Result(bool passed, const String& decomposition = String());
+
+ // forbidding some expressions based on this table: https://en.cppreference.com/w/cpp/language/operator_precedence
+ DOCTEST_FORBIT_EXPRESSION(Result, &)
+ DOCTEST_FORBIT_EXPRESSION(Result, ^)
+ DOCTEST_FORBIT_EXPRESSION(Result, |)
+ DOCTEST_FORBIT_EXPRESSION(Result, &&)
+ DOCTEST_FORBIT_EXPRESSION(Result, ||)
+ DOCTEST_FORBIT_EXPRESSION(Result, ==)
+ DOCTEST_FORBIT_EXPRESSION(Result, !=)
+ DOCTEST_FORBIT_EXPRESSION(Result, <)
+ DOCTEST_FORBIT_EXPRESSION(Result, >)
+ DOCTEST_FORBIT_EXPRESSION(Result, <=)
+ DOCTEST_FORBIT_EXPRESSION(Result, >=)
+ DOCTEST_FORBIT_EXPRESSION(Result, =)
+ DOCTEST_FORBIT_EXPRESSION(Result, +=)
+ DOCTEST_FORBIT_EXPRESSION(Result, -=)
+ DOCTEST_FORBIT_EXPRESSION(Result, *=)
+ DOCTEST_FORBIT_EXPRESSION(Result, /=)
+ DOCTEST_FORBIT_EXPRESSION(Result, %=)
+ DOCTEST_FORBIT_EXPRESSION(Result, <<=)
+ DOCTEST_FORBIT_EXPRESSION(Result, >>=)
+ DOCTEST_FORBIT_EXPRESSION(Result, &=)
+ DOCTEST_FORBIT_EXPRESSION(Result, ^=)
+ DOCTEST_FORBIT_EXPRESSION(Result, |=)
+ };
+
+#ifndef DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION
+
+ DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wsign-conversion")
+ DOCTEST_CLANG_SUPPRESS_WARNING("-Wsign-compare")
+ //DOCTEST_CLANG_SUPPRESS_WARNING("-Wdouble-promotion")
+ //DOCTEST_CLANG_SUPPRESS_WARNING("-Wconversion")
+ //DOCTEST_CLANG_SUPPRESS_WARNING("-Wfloat-equal")
+
+ DOCTEST_GCC_SUPPRESS_WARNING_PUSH
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-conversion")
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-compare")
+ //DOCTEST_GCC_SUPPRESS_WARNING("-Wdouble-promotion")
+ //DOCTEST_GCC_SUPPRESS_WARNING("-Wconversion")
+ //DOCTEST_GCC_SUPPRESS_WARNING("-Wfloat-equal")
+
+ DOCTEST_MSVC_SUPPRESS_WARNING_PUSH
+ // https://stackoverflow.com/questions/39479163 what's the difference between 4018 and 4389
+ DOCTEST_MSVC_SUPPRESS_WARNING(4388) // signed/unsigned mismatch
+ DOCTEST_MSVC_SUPPRESS_WARNING(4389) // 'operator' : signed/unsigned mismatch
+ DOCTEST_MSVC_SUPPRESS_WARNING(4018) // 'expression' : signed/unsigned mismatch
+ //DOCTEST_MSVC_SUPPRESS_WARNING(4805) // 'operation' : unsafe mix of type 'type' and type 'type' in operation
+
+#endif // DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION
+
+ // clang-format off
+#ifndef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+#define DOCTEST_COMPARISON_RETURN_TYPE bool
+#else // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+#define DOCTEST_COMPARISON_RETURN_TYPE typename types::enable_if<can_use_op<L>::value || can_use_op<R>::value, bool>::type
+ inline bool eq(const char* lhs, const char* rhs) { return String(lhs) == String(rhs); }
+ inline bool ne(const char* lhs, const char* rhs) { return String(lhs) != String(rhs); }
+ inline bool lt(const char* lhs, const char* rhs) { return String(lhs) < String(rhs); }
+ inline bool gt(const char* lhs, const char* rhs) { return String(lhs) > String(rhs); }
+ inline bool le(const char* lhs, const char* rhs) { return String(lhs) <= String(rhs); }
+ inline bool ge(const char* lhs, const char* rhs) { return String(lhs) >= String(rhs); }
+#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+ // clang-format on
+
+#define DOCTEST_RELATIONAL_OP(name, op) \
+ template <typename L, typename R> \
+ DOCTEST_COMPARISON_RETURN_TYPE name(const DOCTEST_REF_WRAP(L) lhs, \
+ const DOCTEST_REF_WRAP(R) rhs) { \
+ return lhs op rhs; \
+ }
+
+ DOCTEST_RELATIONAL_OP(eq, ==)
+ DOCTEST_RELATIONAL_OP(ne, !=)
+ DOCTEST_RELATIONAL_OP(lt, <)
+ DOCTEST_RELATIONAL_OP(gt, >)
+ DOCTEST_RELATIONAL_OP(le, <=)
+ DOCTEST_RELATIONAL_OP(ge, >=)
+
+#ifndef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+#define DOCTEST_CMP_EQ(l, r) l == r
+#define DOCTEST_CMP_NE(l, r) l != r
+#define DOCTEST_CMP_GT(l, r) l > r
+#define DOCTEST_CMP_LT(l, r) l < r
+#define DOCTEST_CMP_GE(l, r) l >= r
+#define DOCTEST_CMP_LE(l, r) l <= r
+#else // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+#define DOCTEST_CMP_EQ(l, r) eq(l, r)
+#define DOCTEST_CMP_NE(l, r) ne(l, r)
+#define DOCTEST_CMP_GT(l, r) gt(l, r)
+#define DOCTEST_CMP_LT(l, r) lt(l, r)
+#define DOCTEST_CMP_GE(l, r) ge(l, r)
+#define DOCTEST_CMP_LE(l, r) le(l, r)
+#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING
+
+ template <typename L>
+ // cppcheck-suppress copyCtorAndEqOperator
+ struct Expression_lhs
+ {
+ L lhs;
+ assertType::Enum m_at;
+
+ explicit Expression_lhs(L&& in, assertType::Enum at)
+ : lhs(static_cast<L&&>(in))
+ , m_at(at) {}
+
+ DOCTEST_NOINLINE operator Result() {
+// this is needed only for MSVC 2015
+DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4800) // 'int': forcing value to bool
+ bool res = static_cast<bool>(lhs);
+DOCTEST_MSVC_SUPPRESS_WARNING_POP
+ if(m_at & assertType::is_false) { //!OCLINT bitwise operator in conditional
+ res = !res;
+ }
+
+ if(!res || getContextOptions()->success) {
+ return { res, (DOCTEST_STRINGIFY(lhs)) };
+ }
+ return { res };
+ }
+
+ /* This is required for user-defined conversions from Expression_lhs to L */
+ operator L() const { return lhs; }
+
+ // clang-format off
+ DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(==, " == ", DOCTEST_CMP_EQ) //!OCLINT bitwise operator in conditional
+ DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(!=, " != ", DOCTEST_CMP_NE) //!OCLINT bitwise operator in conditional
+ DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(>, " > ", DOCTEST_CMP_GT) //!OCLINT bitwise operator in conditional
+ DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(<, " < ", DOCTEST_CMP_LT) //!OCLINT bitwise operator in conditional
+ DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(>=, " >= ", DOCTEST_CMP_GE) //!OCLINT bitwise operator in conditional
+ DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(<=, " <= ", DOCTEST_CMP_LE) //!OCLINT bitwise operator in conditional
+ // clang-format on
+
+ // forbidding some expressions based on this table: https://en.cppreference.com/w/cpp/language/operator_precedence
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, &)
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, ^)
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, |)
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, &&)
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, ||)
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, =)
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, +=)
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, -=)
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, *=)
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, /=)
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, %=)
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, <<=)
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, >>=)
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, &=)
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, ^=)
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, |=)
+ // these 2 are unfortunate because they should be allowed - they have higher precedence over the comparisons, but the
+ // ExpressionDecomposer class uses the left shift operator to capture the left operand of the binary expression...
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, <<)
+ DOCTEST_FORBIT_EXPRESSION(Expression_lhs, >>)
+ };
+
+#ifndef DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION
+
+ DOCTEST_CLANG_SUPPRESS_WARNING_POP
+ DOCTEST_MSVC_SUPPRESS_WARNING_POP
+ DOCTEST_GCC_SUPPRESS_WARNING_POP
+
+#endif // DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION
+
+#if DOCTEST_CLANG && DOCTEST_CLANG < DOCTEST_COMPILER(3, 6, 0)
+DOCTEST_CLANG_SUPPRESS_WARNING_POP
+#endif
+
+ struct DOCTEST_INTERFACE ExpressionDecomposer
+ {
+ assertType::Enum m_at;
+
+ ExpressionDecomposer(assertType::Enum at);
+
+ // The right operator for capturing expressions is "<=" instead of "<<" (based on the operator precedence table)
+ // but then there will be warnings from GCC about "-Wparentheses" and since "_Pragma()" is problematic this will stay for now...
+ // https://github.com/catchorg/Catch2/issues/870
+ // https://github.com/catchorg/Catch2/issues/565
+ template <typename L>
+ Expression_lhs<const L&&> operator<<(const L&& operand) { //bitfields bind to universal ref but not const rvalue ref
+ return Expression_lhs<const L&&>(static_cast<const L&&>(operand), m_at);
+ }
+
+ template <typename L,typename types::enable_if<!doctest::detail::types::is_rvalue_reference<L>::value,void >::type* = nullptr>
+ Expression_lhs<const L&> operator<<(const L &operand) {
+ return Expression_lhs<const L&>(operand, m_at);
+ }
+ };
+
+ struct DOCTEST_INTERFACE TestSuite
+ {
+ const char* m_test_suite = nullptr;
+ const char* m_description = nullptr;
+ bool m_skip = false;
+ bool m_no_breaks = false;
+ bool m_no_output = false;
+ bool m_may_fail = false;
+ bool m_should_fail = false;
+ int m_expected_failures = 0;
+ double m_timeout = 0;
+
+ TestSuite& operator*(const char* in);
+
+ template <typename T>
+ TestSuite& operator*(const T& in) {
+ in.fill(*this);
+ return *this;
+ }
+ };
+
+ using funcType = void (*)();
+
+ struct DOCTEST_INTERFACE TestCase : public TestCaseData
+ {
+ funcType m_test; // a function pointer to the test case
+
+ String m_type; // for templated test cases - gets appended to the real name
+ int m_template_id; // an ID used to distinguish between the different versions of a templated test case
+ String m_full_name; // contains the name (only for templated test cases!) + the template type
+
+ TestCase(funcType test, const char* file, unsigned line, const TestSuite& test_suite,
+ const String& type = String(), int template_id = -1);
+
+ TestCase(const TestCase& other);
+ TestCase(TestCase&&) = delete;
+
+ DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(26434) // hides a non-virtual function
+ TestCase& operator=(const TestCase& other);
+ DOCTEST_MSVC_SUPPRESS_WARNING_POP
+
+ TestCase& operator=(TestCase&&) = delete;
+
+ TestCase& operator*(const char* in);
+
+ template <typename T>
+ TestCase& operator*(const T& in) {
+ in.fill(*this);
+ return *this;
+ }
+
+ bool operator<(const TestCase& other) const;
+
+ ~TestCase() = default;
+ };
+
+ // forward declarations of functions used by the macros
+ DOCTEST_INTERFACE int regTest(const TestCase& tc);
+ DOCTEST_INTERFACE int setTestSuite(const TestSuite& ts);
+ DOCTEST_INTERFACE bool isDebuggerActive();
+
+ template<typename T>
+ int instantiationHelper(const T&) { return 0; }
+
+ namespace binaryAssertComparison {
+ enum Enum
+ {
+ eq = 0,
+ ne,
+ gt,
+ lt,
+ ge,
+ le
+ };
+ } // namespace binaryAssertComparison
+
+ // clang-format off
+ template <int, class L, class R> struct RelationalComparator { bool operator()(const DOCTEST_REF_WRAP(L), const DOCTEST_REF_WRAP(R) ) const { return false; } };
+
+#define DOCTEST_BINARY_RELATIONAL_OP(n, op) \
+ template <class L, class R> struct RelationalComparator<n, L, R> { bool operator()(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) const { return op(lhs, rhs); } };
+ // clang-format on
+
+ DOCTEST_BINARY_RELATIONAL_OP(0, doctest::detail::eq)
+ DOCTEST_BINARY_RELATIONAL_OP(1, doctest::detail::ne)
+ DOCTEST_BINARY_RELATIONAL_OP(2, doctest::detail::gt)
+ DOCTEST_BINARY_RELATIONAL_OP(3, doctest::detail::lt)
+ DOCTEST_BINARY_RELATIONAL_OP(4, doctest::detail::ge)
+ DOCTEST_BINARY_RELATIONAL_OP(5, doctest::detail::le)
+
+ struct DOCTEST_INTERFACE ResultBuilder : public AssertData
+ {
+ ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr,
+ const char* exception_type = "", const String& exception_string = "");
+
+ ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr,
+ const char* exception_type, const Contains& exception_string);
+
+ void setResult(const Result& res);
+
+ template <int comparison, typename L, typename R>
+ DOCTEST_NOINLINE bool binary_assert(const DOCTEST_REF_WRAP(L) lhs,
+ const DOCTEST_REF_WRAP(R) rhs) {
+ m_failed = !RelationalComparator<comparison, L, R>()(lhs, rhs);
+ if (m_failed || getContextOptions()->success) {
+ m_decomp = stringifyBinaryExpr(lhs, ", ", rhs);
+ }
+ return !m_failed;
+ }
+
+ template <typename L>
+ DOCTEST_NOINLINE bool unary_assert(const DOCTEST_REF_WRAP(L) val) {
+ m_failed = !val;
+
+ if (m_at & assertType::is_false) { //!OCLINT bitwise operator in conditional
+ m_failed = !m_failed;
+ }
+
+ if (m_failed || getContextOptions()->success) {
+ m_decomp = (DOCTEST_STRINGIFY(val));
+ }
+
+ return !m_failed;
+ }
+
+ void translateException();
+
+ bool log();
+ void react() const;
+ };
+
+ namespace assertAction {
+ enum Enum
+ {
+ nothing = 0,
+ dbgbreak = 1,
+ shouldthrow = 2
+ };
+ } // namespace assertAction
+
+ DOCTEST_INTERFACE void failed_out_of_a_testing_context(const AssertData& ad);
+
+ DOCTEST_INTERFACE bool decomp_assert(assertType::Enum at, const char* file, int line,
+ const char* expr, const Result& result);
+
+#define DOCTEST_ASSERT_OUT_OF_TESTS(decomp) \
+ do { \
+ if(!is_running_in_test) { \
+ if(failed) { \
+ ResultBuilder rb(at, file, line, expr); \
+ rb.m_failed = failed; \
+ rb.m_decomp = decomp; \
+ failed_out_of_a_testing_context(rb); \
+ if(isDebuggerActive() && !getContextOptions()->no_breaks) \
+ DOCTEST_BREAK_INTO_DEBUGGER(); \
+ if(checkIfShouldThrow(at)) \
+ throwException(); \
+ } \
+ return !failed; \
+ } \
+ } while(false)
+
+#define DOCTEST_ASSERT_IN_TESTS(decomp) \
+ ResultBuilder rb(at, file, line, expr); \
+ rb.m_failed = failed; \
+ if(rb.m_failed || getContextOptions()->success) \
+ rb.m_decomp = decomp; \
+ if(rb.log()) \
+ DOCTEST_BREAK_INTO_DEBUGGER(); \
+ if(rb.m_failed && checkIfShouldThrow(at)) \
+ throwException()
+
+ template <int comparison, typename L, typename R>
+ 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<comparison, L, R>()(lhs, rhs);
+
+ // ###################################################################################
+ // IF THE DEBUGGER BREAKS HERE - GO 1 LEVEL UP IN THE CALLSTACK FOR THE FAILING ASSERT
+ // THIS IS THE EFFECT OF HAVING 'DOCTEST_CONFIG_SUPER_FAST_ASSERTS' DEFINED
+ // ###################################################################################
+ DOCTEST_ASSERT_OUT_OF_TESTS(stringifyBinaryExpr(lhs, ", ", rhs));
+ DOCTEST_ASSERT_IN_TESTS(stringifyBinaryExpr(lhs, ", ", rhs));
+ return !failed;
+ }
+
+ template <typename L>
+ DOCTEST_NOINLINE bool unary_assert(assertType::Enum at, const char* file, int line,
+ const char* expr, const DOCTEST_REF_WRAP(L) val) {
+ bool failed = !val;
+
+ if(at & assertType::is_false) //!OCLINT bitwise operator in conditional
+ failed = !failed;
+
+ // ###################################################################################
+ // IF THE DEBUGGER BREAKS HERE - GO 1 LEVEL UP IN THE CALLSTACK FOR THE FAILING ASSERT
+ // THIS IS THE EFFECT OF HAVING 'DOCTEST_CONFIG_SUPER_FAST_ASSERTS' DEFINED
+ // ###################################################################################
+ DOCTEST_ASSERT_OUT_OF_TESTS((DOCTEST_STRINGIFY(val)));
+ DOCTEST_ASSERT_IN_TESTS((DOCTEST_STRINGIFY(val)));
+ return !failed;
+ }
+
+ struct DOCTEST_INTERFACE IExceptionTranslator
+ {
+ DOCTEST_DECLARE_INTERFACE(IExceptionTranslator)
+ virtual bool translate(String&) const = 0;
+ };
+
+ template <typename T>
+ class ExceptionTranslator : public IExceptionTranslator //!OCLINT destructor of virtual class
+ {
+ public:
+ explicit ExceptionTranslator(String (*translateFunction)(T))
+ : m_translateFunction(translateFunction) {}
+
+ bool translate(String& res) const override {
+#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
+ try {
+ throw; // lgtm [cpp/rethrow-no-exception]
+ // cppcheck-suppress catchExceptionByValue
+ } catch(const T& ex) {
+ res = m_translateFunction(ex); //!OCLINT parameter reassignment
+ return true;
+ } catch(...) {} //!OCLINT - empty catch statement
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
+ static_cast<void>(res); // to silence -Wunused-parameter
+ return false;
+ }
+
+ private:
+ String (*m_translateFunction)(T);
+ };
+
+ DOCTEST_INTERFACE void registerExceptionTranslatorImpl(const IExceptionTranslator* et);
+
+ // ContextScope base class used to allow implementing methods of ContextScope
+ // that don't depend on the template parameter in doctest.cpp.
+ struct DOCTEST_INTERFACE ContextScopeBase : public IContextScope {
+ ContextScopeBase(const ContextScopeBase&) = delete;
+
+ ContextScopeBase& operator=(const ContextScopeBase&) = delete;
+ ContextScopeBase& operator=(ContextScopeBase&&) = delete;
+
+ ~ContextScopeBase() override = default;
+
+ protected:
+ ContextScopeBase();
+ ContextScopeBase(ContextScopeBase&& other) noexcept;
+
+ void destroy();
+ bool need_to_destroy{true};
+ };
+
+ template <typename L> class ContextScope : public ContextScopeBase
+ {
+ L lambda_;
+
+ public:
+ explicit ContextScope(const L &lambda) : lambda_(lambda) {}
+ explicit ContextScope(L&& lambda) : lambda_(static_cast<L&&>(lambda)) { }
+
+ ContextScope(const ContextScope&) = delete;
+ ContextScope(ContextScope&&) noexcept = default;
+
+ ContextScope& operator=(const ContextScope&) = delete;
+ ContextScope& operator=(ContextScope&&) = delete;
+
+ void stringify(std::ostream* s) const override { lambda_(s); }
+
+ ~ContextScope() override {
+ if (need_to_destroy) {
+ destroy();
+ }
+ }
+ };
+
+ struct DOCTEST_INTERFACE MessageBuilder : public MessageData
+ {
+ std::ostream* m_stream;
+ bool logged = false;
+
+ MessageBuilder(const char* file, int line, assertType::Enum severity);
+
+ MessageBuilder(const MessageBuilder&) = delete;
+ MessageBuilder(MessageBuilder&&) = delete;
+
+ MessageBuilder& operator=(const MessageBuilder&) = delete;
+ MessageBuilder& operator=(MessageBuilder&&) = delete;
+
+ ~MessageBuilder();
+
+ // the preferred way of chaining parameters for stringification
+DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4866)
+ template <typename T>
+ 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 <typename T>
+ 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 <typename T>
+ MessageBuilder& operator*(const T& in) { return this->operator,(in); }
+
+ bool log();
+ void react();
+ };
+
+ template <typename L>
+ ContextScope<L> MakeContextScope(const L &lambda) {
+ return ContextScope<L>(lambda);
+ }
+} // namespace detail
+
+#define DOCTEST_DEFINE_DECORATOR(name, type, def) \
+ struct name \
+ { \
+ type data; \
+ name(type in = def) \
+ : data(in) {} \
+ void fill(detail::TestCase& state) const { state.DOCTEST_CAT(m_, name) = data; } \
+ void fill(detail::TestSuite& state) const { state.DOCTEST_CAT(m_, name) = data; } \
+ }
+
+DOCTEST_DEFINE_DECORATOR(test_suite, const char*, "");
+DOCTEST_DEFINE_DECORATOR(description, const char*, "");
+DOCTEST_DEFINE_DECORATOR(skip, bool, true);
+DOCTEST_DEFINE_DECORATOR(no_breaks, bool, true);
+DOCTEST_DEFINE_DECORATOR(no_output, bool, true);
+DOCTEST_DEFINE_DECORATOR(timeout, double, 0);
+DOCTEST_DEFINE_DECORATOR(may_fail, bool, true);
+DOCTEST_DEFINE_DECORATOR(should_fail, bool, true);
+DOCTEST_DEFINE_DECORATOR(expected_failures, int, 0);
+
+template <typename T>
+int registerExceptionTranslator(String (*translateFunction)(T)) {
+ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wexit-time-destructors")
+ static detail::ExceptionTranslator<T> exceptionTranslator(translateFunction);
+ DOCTEST_CLANG_SUPPRESS_WARNING_POP
+ detail::registerExceptionTranslatorImpl(&exceptionTranslator);
+ return 0;
+}
+
+} // namespace doctest
+
+// in a separate namespace outside of doctest because the DOCTEST_TEST_SUITE macro
+// introduces an anonymous namespace in which getCurrentTestSuite gets overridden
+namespace doctest_detail_test_suite_ns {
+DOCTEST_INTERFACE doctest::detail::TestSuite& getCurrentTestSuite();
+} // namespace doctest_detail_test_suite_ns
+
+namespace doctest {
+#else // DOCTEST_CONFIG_DISABLE
+template <typename T>
+int registerExceptionTranslator(String (*)(T)) {
+ return 0;
+}
+#endif // DOCTEST_CONFIG_DISABLE
+
+namespace detail {
+ using assert_handler = void (*)(const AssertData&);
+ struct ContextState;
+} // namespace detail
+
+class DOCTEST_INTERFACE Context
+{
+ detail::ContextState* p;
+
+ void parseArgs(int argc, const char* const* argv, bool withDefaults = false);
+
+public:
+ explicit Context(int argc = 0, const char* const* argv = nullptr);
+
+ Context(const Context&) = delete;
+ Context(Context&&) = delete;
+
+ Context& operator=(const Context&) = delete;
+ Context& operator=(Context&&) = delete;
+
+ ~Context(); // NOLINT(performance-trivially-destructible)
+
+ void applyCommandLine(int argc, const char* const* argv);
+
+ void addFilter(const char* filter, const char* value);
+ void clearFilters();
+ void setOption(const char* option, bool value);
+ void setOption(const char* option, int value);
+ void setOption(const char* option, const char* value);
+
+ bool shouldExit();
+
+ void setAsDefaultForAssertsOutOfTestCases();
+
+ void setAssertHandler(detail::assert_handler ah);
+
+ void setCout(std::ostream* out);
+
+ int run();
+};
+
+namespace TestCaseFailureReason {
+ enum Enum
+ {
+ None = 0,
+ AssertFailure = 1, // an assertion has failed in the test case
+ Exception = 2, // test case threw an exception
+ Crash = 4, // a crash...
+ TooManyFailedAsserts = 8, // the abort-after option
+ Timeout = 16, // see the timeout decorator
+ ShouldHaveFailedButDidnt = 32, // see the should_fail decorator
+ ShouldHaveFailedAndDid = 64, // see the should_fail decorator
+ DidntFailExactlyNumTimes = 128, // see the expected_failures decorator
+ FailedExactlyNumTimes = 256, // see the expected_failures decorator
+ CouldHaveFailedAndDid = 512 // see the may_fail decorator
+ };
+} // namespace TestCaseFailureReason
+
+struct DOCTEST_INTERFACE CurrentTestCaseStats
+{
+ int numAssertsCurrentTest;
+ int numAssertsFailedCurrentTest;
+ double seconds;
+ int failure_flags; // use TestCaseFailureReason::Enum
+ bool testCaseSuccess;
+};
+
+struct DOCTEST_INTERFACE TestCaseException
+{
+ String error_string;
+ bool is_crash;
+};
+
+struct DOCTEST_INTERFACE TestRunStats
+{
+ unsigned numTestCases;
+ unsigned numTestCasesPassingFilters;
+ unsigned numTestSuitesPassingFilters;
+ unsigned numTestCasesFailed;
+ int numAsserts;
+ int numAssertsFailed;
+};
+
+struct QueryData
+{
+ const TestRunStats* run_stats = nullptr;
+ const TestCaseData** data = nullptr;
+ unsigned num_data = 0;
+};
+
+struct DOCTEST_INTERFACE IReporter
+{
+ // The constructor has to accept "const ContextOptions&" as a single argument
+ // which has most of the options for the run + a pointer to the stdout stream
+ // Reporter(const ContextOptions& in)
+
+ // called when a query should be reported (listing test cases, printing the version, etc.)
+ virtual void report_query(const QueryData&) = 0;
+
+ // called when the whole test run starts
+ virtual void test_run_start() = 0;
+ // called when the whole test run ends (caching a pointer to the input doesn't make sense here)
+ virtual void test_run_end(const TestRunStats&) = 0;
+
+ // called when a test case is started (safe to cache a pointer to the input)
+ virtual void test_case_start(const TestCaseData&) = 0;
+ // called when a test case is reentered because of unfinished subcases (safe to cache a pointer to the input)
+ virtual void test_case_reenter(const TestCaseData&) = 0;
+ // called when a test case has ended
+ virtual void test_case_end(const CurrentTestCaseStats&) = 0;
+
+ // called when an exception is thrown from the test case (or it crashes)
+ virtual void test_case_exception(const TestCaseException&) = 0;
+
+ // called whenever a subcase is entered (don't cache pointers to the input)
+ virtual void subcase_start(const SubcaseSignature&) = 0;
+ // called whenever a subcase is exited (don't cache pointers to the input)
+ virtual void subcase_end() = 0;
+
+ // called for each assert (don't cache pointers to the input)
+ virtual void log_assert(const AssertData&) = 0;
+ // called for each message (don't cache pointers to the input)
+ virtual void log_message(const MessageData&) = 0;
+
+ // called when a test case is skipped either because it doesn't pass the filters, has a skip decorator
+ // or isn't in the execution range (between first and last) (safe to cache a pointer to the input)
+ virtual void test_case_skipped(const TestCaseData&) = 0;
+
+ DOCTEST_DECLARE_INTERFACE(IReporter)
+
+ // can obtain all currently active contexts and stringify them if one wishes to do so
+ static int get_num_active_contexts();
+ static const IContextScope* const* get_active_contexts();
+
+ // can iterate through contexts which have been stringified automatically in their destructors when an exception has been thrown
+ static int get_num_stringified_contexts();
+ static const String* get_stringified_contexts();
+};
+
+namespace detail {
+ using reporterCreatorFunc = IReporter* (*)(const ContextOptions&);
+
+ DOCTEST_INTERFACE void registerReporterImpl(const char* name, int prio, reporterCreatorFunc c, bool isReporter);
+
+ template <typename Reporter>
+ IReporter* reporterCreator(const ContextOptions& o) {
+ return new Reporter(o);
+ }
+} // namespace detail
+
+template <typename Reporter>
+int registerReporter(const char* name, int priority, bool isReporter) {
+ detail::registerReporterImpl(name, priority, detail::reporterCreator<Reporter>, isReporter);
+ return 0;
+}
+} // namespace doctest
+
+#ifdef DOCTEST_CONFIG_ASSERTS_RETURN_VALUES
+#define DOCTEST_FUNC_EMPTY [] { return false; }()
+#else
+#define DOCTEST_FUNC_EMPTY (void)0
+#endif
+
+// if registering is not disabled
+#ifndef DOCTEST_CONFIG_DISABLE
+
+#ifdef DOCTEST_CONFIG_ASSERTS_RETURN_VALUES
+#define DOCTEST_FUNC_SCOPE_BEGIN [&]
+#define DOCTEST_FUNC_SCOPE_END ()
+#define DOCTEST_FUNC_SCOPE_RET(v) return v
+#else
+#define DOCTEST_FUNC_SCOPE_BEGIN do
+#define DOCTEST_FUNC_SCOPE_END while(false)
+#define DOCTEST_FUNC_SCOPE_RET(v) (void)0
+#endif
+
+// common code in asserts - for convenience
+#define DOCTEST_ASSERT_LOG_REACT_RETURN(b) \
+ if(b.log()) DOCTEST_BREAK_INTO_DEBUGGER(); \
+ b.react(); \
+ DOCTEST_FUNC_SCOPE_RET(!b.m_failed)
+
+#ifdef DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS
+#define DOCTEST_WRAP_IN_TRY(x) x;
+#else // DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS
+#define DOCTEST_WRAP_IN_TRY(x) \
+ try { \
+ x; \
+ } catch(...) { DOCTEST_RB.translateException(); }
+#endif // DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS
+
+#ifdef DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS
+#define DOCTEST_CAST_TO_VOID(...) \
+ DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wuseless-cast") \
+ static_cast<void>(__VA_ARGS__); \
+ DOCTEST_GCC_SUPPRESS_WARNING_POP
+#else // DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS
+#define DOCTEST_CAST_TO_VOID(...) __VA_ARGS__;
+#endif // DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS
+
+// registers the test by initializing a dummy var with a function
+#define DOCTEST_REGISTER_FUNCTION(global_prefix, f, decorators) \
+ global_prefix DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_VAR_), /* NOLINT */ \
+ doctest::detail::regTest( \
+ doctest::detail::TestCase( \
+ f, __FILE__, __LINE__, \
+ doctest_detail_test_suite_ns::getCurrentTestSuite()) * \
+ decorators))
+
+#define DOCTEST_IMPLEMENT_FIXTURE(der, base, func, decorators) \
+ namespace { /* NOLINT */ \
+ struct der : public base \
+ { \
+ void f(); \
+ }; \
+ static DOCTEST_INLINE_NOINLINE void func() { \
+ der v; \
+ v.f(); \
+ } \
+ DOCTEST_REGISTER_FUNCTION(DOCTEST_EMPTY, func, decorators) \
+ } \
+ DOCTEST_INLINE_NOINLINE void der::f() // NOLINT(misc-definitions-in-headers)
+
+#define DOCTEST_CREATE_AND_REGISTER_FUNCTION(f, decorators) \
+ static void f(); \
+ DOCTEST_REGISTER_FUNCTION(DOCTEST_EMPTY, f, decorators) \
+ static void f()
+
+#define DOCTEST_CREATE_AND_REGISTER_FUNCTION_IN_CLASS(f, proxy, decorators) \
+ static doctest::detail::funcType proxy() { return f; } \
+ DOCTEST_REGISTER_FUNCTION(inline, proxy(), decorators) \
+ static void f()
+
+// for registering tests
+#define DOCTEST_TEST_CASE(decorators) \
+ DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), decorators)
+
+// for registering tests in classes - requires C++17 for inline variables!
+#if DOCTEST_CPLUSPLUS >= 201703L
+#define DOCTEST_TEST_CASE_CLASS(decorators) \
+ DOCTEST_CREATE_AND_REGISTER_FUNCTION_IN_CLASS(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), \
+ DOCTEST_ANONYMOUS(DOCTEST_ANON_PROXY_), \
+ decorators)
+#else // DOCTEST_TEST_CASE_CLASS
+#define DOCTEST_TEST_CASE_CLASS(...) \
+ TEST_CASES_CAN_BE_REGISTERED_IN_CLASSES_ONLY_IN_CPP17_MODE_OR_WITH_VS_2017_OR_NEWER
+#endif // DOCTEST_TEST_CASE_CLASS
+
+// for registering tests with a fixture
+#define DOCTEST_TEST_CASE_FIXTURE(c, decorators) \
+ DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(DOCTEST_ANON_CLASS_), c, \
+ DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), decorators)
+
+// for converting types to strings without the <typeinfo> header and demangling
+#define DOCTEST_TYPE_TO_STRING_AS(str, ...) \
+ namespace doctest { \
+ template <> \
+ inline String toString<__VA_ARGS__>() { \
+ return str; \
+ } \
+ } \
+ static_assert(true, "")
+
+#define DOCTEST_TYPE_TO_STRING(...) DOCTEST_TYPE_TO_STRING_AS(#__VA_ARGS__, __VA_ARGS__)
+
+#define DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(dec, T, iter, func) \
+ template <typename T> \
+ static void func(); \
+ namespace { /* NOLINT */ \
+ template <typename Tuple> \
+ struct iter; \
+ template <typename Type, typename... Rest> \
+ struct iter<std::tuple<Type, Rest...>> \
+ { \
+ iter(const char* file, unsigned line, int index) { \
+ doctest::detail::regTest(doctest::detail::TestCase(func<Type>, file, line, \
+ doctest_detail_test_suite_ns::getCurrentTestSuite(), \
+ doctest::toString<Type>(), \
+ int(line) * 1000 + index) \
+ * dec); \
+ iter<std::tuple<Rest...>>(file, line, index + 1); \
+ } \
+ }; \
+ template <> \
+ struct iter<std::tuple<>> \
+ { \
+ iter(const char*, unsigned, int) {} \
+ }; \
+ } \
+ template <typename T> \
+ static void func()
+
+#define DOCTEST_TEST_CASE_TEMPLATE_DEFINE(dec, T, id) \
+ DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(dec, T, DOCTEST_CAT(id, ITERATOR), \
+ DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_))
+
+#define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, anon, ...) \
+ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_CAT(anon, DUMMY), /* NOLINT(cert-err58-cpp, fuchsia-statically-constructed-objects) */ \
+ doctest::detail::instantiationHelper( \
+ DOCTEST_CAT(id, ITERATOR)<__VA_ARGS__>(__FILE__, __LINE__, 0)))
+
+#define DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id, ...) \
+ DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_), std::tuple<__VA_ARGS__>) \
+ static_assert(true, "")
+
+#define DOCTEST_TEST_CASE_TEMPLATE_APPLY(id, ...) \
+ DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_), __VA_ARGS__) \
+ static_assert(true, "")
+
+#define DOCTEST_TEST_CASE_TEMPLATE_IMPL(dec, T, anon, ...) \
+ DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(dec, T, DOCTEST_CAT(anon, ITERATOR), anon); \
+ DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(anon, anon, std::tuple<__VA_ARGS__>) \
+ template <typename T> \
+ static void anon()
+
+#define DOCTEST_TEST_CASE_TEMPLATE(dec, T, ...) \
+ DOCTEST_TEST_CASE_TEMPLATE_IMPL(dec, T, DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_), __VA_ARGS__)
+
+// for subcases
+#define DOCTEST_SUBCASE(name) \
+ if(const doctest::detail::Subcase & DOCTEST_ANONYMOUS(DOCTEST_ANON_SUBCASE_) DOCTEST_UNUSED = \
+ doctest::detail::Subcase(name, __FILE__, __LINE__))
+
+// for grouping tests in test suites by using code blocks
+#define DOCTEST_TEST_SUITE_IMPL(decorators, ns_name) \
+ namespace ns_name { namespace doctest_detail_test_suite_ns { \
+ static DOCTEST_NOINLINE doctest::detail::TestSuite& getCurrentTestSuite() noexcept { \
+ DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4640) \
+ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wexit-time-destructors") \
+ DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wmissing-field-initializers") \
+ static doctest::detail::TestSuite data{}; \
+ static bool inited = false; \
+ DOCTEST_MSVC_SUPPRESS_WARNING_POP \
+ DOCTEST_CLANG_SUPPRESS_WARNING_POP \
+ DOCTEST_GCC_SUPPRESS_WARNING_POP \
+ if(!inited) { \
+ data* decorators; \
+ inited = true; \
+ } \
+ return data; \
+ } \
+ } \
+ } \
+ namespace ns_name
+
+#define DOCTEST_TEST_SUITE(decorators) \
+ DOCTEST_TEST_SUITE_IMPL(decorators, DOCTEST_ANONYMOUS(DOCTEST_ANON_SUITE_))
+
+// for starting a testsuite block
+#define DOCTEST_TEST_SUITE_BEGIN(decorators) \
+ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_VAR_), /* NOLINT(cert-err58-cpp) */ \
+ doctest::detail::setTestSuite(doctest::detail::TestSuite() * decorators)) \
+ static_assert(true, "")
+
+// for ending a testsuite block
+#define DOCTEST_TEST_SUITE_END \
+ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_VAR_), /* NOLINT(cert-err58-cpp) */ \
+ doctest::detail::setTestSuite(doctest::detail::TestSuite() * "")) \
+ using DOCTEST_ANONYMOUS(DOCTEST_ANON_FOR_SEMICOLON_) = int
+
+// for registering exception translators
+#define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR_IMPL(translatorName, signature) \
+ inline doctest::String translatorName(signature); \
+ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_TRANSLATOR_), /* NOLINT(cert-err58-cpp) */ \
+ doctest::registerExceptionTranslator(translatorName)) \
+ doctest::String translatorName(signature)
+
+#define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature) \
+ DOCTEST_REGISTER_EXCEPTION_TRANSLATOR_IMPL(DOCTEST_ANONYMOUS(DOCTEST_ANON_TRANSLATOR_), \
+ signature)
+
+// for registering reporters
+#define DOCTEST_REGISTER_REPORTER(name, priority, reporter) \
+ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_REPORTER_), /* NOLINT(cert-err58-cpp) */ \
+ doctest::registerReporter<reporter>(name, priority, true)) \
+ static_assert(true, "")
+
+// for registering listeners
+#define DOCTEST_REGISTER_LISTENER(name, priority, reporter) \
+ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(DOCTEST_ANON_REPORTER_), /* NOLINT(cert-err58-cpp) */ \
+ doctest::registerReporter<reporter>(name, priority, false)) \
+ static_assert(true, "")
+
+// clang-format off
+// for logging - disabling formatting because it's important to have these on 2 separate lines - see PR #557
+#define DOCTEST_INFO(...) \
+ DOCTEST_INFO_IMPL(DOCTEST_ANONYMOUS(DOCTEST_CAPTURE_), \
+ DOCTEST_ANONYMOUS(DOCTEST_CAPTURE_OTHER_), \
+ __VA_ARGS__)
+// clang-format on
+
+#define DOCTEST_INFO_IMPL(mb_name, s_name, ...) \
+ auto DOCTEST_ANONYMOUS(DOCTEST_CAPTURE_) = doctest::detail::MakeContextScope( \
+ [&](std::ostream* s_name) { \
+ doctest::detail::MessageBuilder mb_name(__FILE__, __LINE__, doctest::assertType::is_warn); \
+ mb_name.m_stream = s_name; \
+ mb_name * __VA_ARGS__; \
+ })
+
+#define DOCTEST_CAPTURE(x) DOCTEST_INFO(#x " := ", x)
+
+#define DOCTEST_ADD_AT_IMPL(type, file, line, mb, ...) \
+ DOCTEST_FUNC_SCOPE_BEGIN { \
+ doctest::detail::MessageBuilder mb(file, line, doctest::assertType::type); \
+ mb * __VA_ARGS__; \
+ if(mb.log()) \
+ DOCTEST_BREAK_INTO_DEBUGGER(); \
+ mb.react(); \
+ } DOCTEST_FUNC_SCOPE_END
+
+// clang-format off
+#define DOCTEST_ADD_MESSAGE_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_warn, file, line, DOCTEST_ANONYMOUS(DOCTEST_MESSAGE_), __VA_ARGS__)
+#define DOCTEST_ADD_FAIL_CHECK_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_check, file, line, DOCTEST_ANONYMOUS(DOCTEST_MESSAGE_), __VA_ARGS__)
+#define DOCTEST_ADD_FAIL_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_require, file, line, DOCTEST_ANONYMOUS(DOCTEST_MESSAGE_), __VA_ARGS__)
+// clang-format on
+
+#define DOCTEST_MESSAGE(...) DOCTEST_ADD_MESSAGE_AT(__FILE__, __LINE__, __VA_ARGS__)
+#define DOCTEST_FAIL_CHECK(...) DOCTEST_ADD_FAIL_CHECK_AT(__FILE__, __LINE__, __VA_ARGS__)
+#define DOCTEST_FAIL(...) DOCTEST_ADD_FAIL_AT(__FILE__, __LINE__, __VA_ARGS__)
+
+#define DOCTEST_TO_LVALUE(...) __VA_ARGS__ // Not removed to keep backwards compatibility.
+
+#ifndef DOCTEST_CONFIG_SUPER_FAST_ASSERTS
+
+#define DOCTEST_ASSERT_IMPLEMENT_2(assert_type, ...) \
+ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Woverloaded-shift-op-parentheses") \
+ /* NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) */ \
+ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
+ __LINE__, #__VA_ARGS__); \
+ DOCTEST_WRAP_IN_TRY(DOCTEST_RB.setResult( \
+ doctest::detail::ExpressionDecomposer(doctest::assertType::assert_type) \
+ << __VA_ARGS__)) /* NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) */ \
+ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB) \
+ DOCTEST_CLANG_SUPPRESS_WARNING_POP
+
+#define DOCTEST_ASSERT_IMPLEMENT_1(assert_type, ...) \
+ DOCTEST_FUNC_SCOPE_BEGIN { \
+ DOCTEST_ASSERT_IMPLEMENT_2(assert_type, __VA_ARGS__); \
+ } DOCTEST_FUNC_SCOPE_END // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks)
+
+#define DOCTEST_BINARY_ASSERT(assert_type, comp, ...) \
+ DOCTEST_FUNC_SCOPE_BEGIN { \
+ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
+ __LINE__, #__VA_ARGS__); \
+ DOCTEST_WRAP_IN_TRY( \
+ DOCTEST_RB.binary_assert<doctest::detail::binaryAssertComparison::comp>( \
+ __VA_ARGS__)) \
+ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \
+ } DOCTEST_FUNC_SCOPE_END
+
+#define DOCTEST_UNARY_ASSERT(assert_type, ...) \
+ DOCTEST_FUNC_SCOPE_BEGIN { \
+ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
+ __LINE__, #__VA_ARGS__); \
+ DOCTEST_WRAP_IN_TRY(DOCTEST_RB.unary_assert(__VA_ARGS__)) \
+ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \
+ } DOCTEST_FUNC_SCOPE_END
+
+#else // DOCTEST_CONFIG_SUPER_FAST_ASSERTS
+
+// necessary for <ASSERT>_MESSAGE
+#define DOCTEST_ASSERT_IMPLEMENT_2 DOCTEST_ASSERT_IMPLEMENT_1
+
+#define DOCTEST_ASSERT_IMPLEMENT_1(assert_type, ...) \
+ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Woverloaded-shift-op-parentheses") \
+ doctest::detail::decomp_assert( \
+ doctest::assertType::assert_type, __FILE__, __LINE__, #__VA_ARGS__, \
+ doctest::detail::ExpressionDecomposer(doctest::assertType::assert_type) \
+ << __VA_ARGS__) DOCTEST_CLANG_SUPPRESS_WARNING_POP
+
+#define DOCTEST_BINARY_ASSERT(assert_type, comparison, ...) \
+ doctest::detail::binary_assert<doctest::detail::binaryAssertComparison::comparison>( \
+ doctest::assertType::assert_type, __FILE__, __LINE__, #__VA_ARGS__, __VA_ARGS__)
+
+#define DOCTEST_UNARY_ASSERT(assert_type, ...) \
+ doctest::detail::unary_assert(doctest::assertType::assert_type, __FILE__, __LINE__, \
+ #__VA_ARGS__, __VA_ARGS__)
+
+#endif // DOCTEST_CONFIG_SUPER_FAST_ASSERTS
+
+#define DOCTEST_WARN(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_WARN, __VA_ARGS__)
+#define DOCTEST_CHECK(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_CHECK, __VA_ARGS__)
+#define DOCTEST_REQUIRE(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_REQUIRE, __VA_ARGS__)
+#define DOCTEST_WARN_FALSE(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_WARN_FALSE, __VA_ARGS__)
+#define DOCTEST_CHECK_FALSE(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_CHECK_FALSE, __VA_ARGS__)
+#define DOCTEST_REQUIRE_FALSE(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_REQUIRE_FALSE, __VA_ARGS__)
+
+// clang-format off
+#define DOCTEST_WARN_MESSAGE(cond, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN, cond); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_CHECK_MESSAGE(cond, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK, cond); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_REQUIRE_MESSAGE(cond, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE, cond); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_WARN_FALSE_MESSAGE(cond, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN_FALSE, cond); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_CHECK_FALSE_MESSAGE(cond, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK_FALSE, cond); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE_FALSE, cond); } DOCTEST_FUNC_SCOPE_END
+// clang-format on
+
+#define DOCTEST_WARN_EQ(...) DOCTEST_BINARY_ASSERT(DT_WARN_EQ, eq, __VA_ARGS__)
+#define DOCTEST_CHECK_EQ(...) DOCTEST_BINARY_ASSERT(DT_CHECK_EQ, eq, __VA_ARGS__)
+#define DOCTEST_REQUIRE_EQ(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_EQ, eq, __VA_ARGS__)
+#define DOCTEST_WARN_NE(...) DOCTEST_BINARY_ASSERT(DT_WARN_NE, ne, __VA_ARGS__)
+#define DOCTEST_CHECK_NE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_NE, ne, __VA_ARGS__)
+#define DOCTEST_REQUIRE_NE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_NE, ne, __VA_ARGS__)
+#define DOCTEST_WARN_GT(...) DOCTEST_BINARY_ASSERT(DT_WARN_GT, gt, __VA_ARGS__)
+#define DOCTEST_CHECK_GT(...) DOCTEST_BINARY_ASSERT(DT_CHECK_GT, gt, __VA_ARGS__)
+#define DOCTEST_REQUIRE_GT(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_GT, gt, __VA_ARGS__)
+#define DOCTEST_WARN_LT(...) DOCTEST_BINARY_ASSERT(DT_WARN_LT, lt, __VA_ARGS__)
+#define DOCTEST_CHECK_LT(...) DOCTEST_BINARY_ASSERT(DT_CHECK_LT, lt, __VA_ARGS__)
+#define DOCTEST_REQUIRE_LT(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_LT, lt, __VA_ARGS__)
+#define DOCTEST_WARN_GE(...) DOCTEST_BINARY_ASSERT(DT_WARN_GE, ge, __VA_ARGS__)
+#define DOCTEST_CHECK_GE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_GE, ge, __VA_ARGS__)
+#define DOCTEST_REQUIRE_GE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_GE, ge, __VA_ARGS__)
+#define DOCTEST_WARN_LE(...) DOCTEST_BINARY_ASSERT(DT_WARN_LE, le, __VA_ARGS__)
+#define DOCTEST_CHECK_LE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_LE, le, __VA_ARGS__)
+#define DOCTEST_REQUIRE_LE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_LE, le, __VA_ARGS__)
+
+#define DOCTEST_WARN_UNARY(...) DOCTEST_UNARY_ASSERT(DT_WARN_UNARY, __VA_ARGS__)
+#define DOCTEST_CHECK_UNARY(...) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY, __VA_ARGS__)
+#define DOCTEST_REQUIRE_UNARY(...) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY, __VA_ARGS__)
+#define DOCTEST_WARN_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_WARN_UNARY_FALSE, __VA_ARGS__)
+#define DOCTEST_CHECK_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY_FALSE, __VA_ARGS__)
+#define DOCTEST_REQUIRE_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY_FALSE, __VA_ARGS__)
+
+#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
+
+#define DOCTEST_ASSERT_THROWS_AS(expr, assert_type, message, ...) \
+ DOCTEST_FUNC_SCOPE_BEGIN { \
+ if(!doctest::getContextOptions()->no_throw) { \
+ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
+ __LINE__, #expr, #__VA_ARGS__, message); \
+ try { \
+ DOCTEST_CAST_TO_VOID(expr) \
+ } catch(const typename doctest::detail::types::remove_const< \
+ typename doctest::detail::types::remove_reference<__VA_ARGS__>::type>::type&) {\
+ DOCTEST_RB.translateException(); \
+ DOCTEST_RB.m_threw_as = true; \
+ } catch(...) { DOCTEST_RB.translateException(); } \
+ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \
+ } else { /* NOLINT(*-else-after-return) */ \
+ DOCTEST_FUNC_SCOPE_RET(false); \
+ } \
+ } DOCTEST_FUNC_SCOPE_END
+
+#define DOCTEST_ASSERT_THROWS_WITH(expr, expr_str, assert_type, ...) \
+ DOCTEST_FUNC_SCOPE_BEGIN { \
+ if(!doctest::getContextOptions()->no_throw) { \
+ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
+ __LINE__, expr_str, "", __VA_ARGS__); \
+ try { \
+ DOCTEST_CAST_TO_VOID(expr) \
+ } catch(...) { DOCTEST_RB.translateException(); } \
+ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \
+ } else { /* NOLINT(*-else-after-return) */ \
+ DOCTEST_FUNC_SCOPE_RET(false); \
+ } \
+ } DOCTEST_FUNC_SCOPE_END
+
+#define DOCTEST_ASSERT_NOTHROW(assert_type, ...) \
+ DOCTEST_FUNC_SCOPE_BEGIN { \
+ doctest::detail::ResultBuilder DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \
+ __LINE__, #__VA_ARGS__); \
+ try { \
+ DOCTEST_CAST_TO_VOID(__VA_ARGS__) \
+ } catch(...) { DOCTEST_RB.translateException(); } \
+ DOCTEST_ASSERT_LOG_REACT_RETURN(DOCTEST_RB); \
+ } DOCTEST_FUNC_SCOPE_END
+
+// clang-format off
+#define DOCTEST_WARN_THROWS(...) DOCTEST_ASSERT_THROWS_WITH((__VA_ARGS__), #__VA_ARGS__, DT_WARN_THROWS, "")
+#define DOCTEST_CHECK_THROWS(...) DOCTEST_ASSERT_THROWS_WITH((__VA_ARGS__), #__VA_ARGS__, DT_CHECK_THROWS, "")
+#define DOCTEST_REQUIRE_THROWS(...) DOCTEST_ASSERT_THROWS_WITH((__VA_ARGS__), #__VA_ARGS__, DT_REQUIRE_THROWS, "")
+
+#define DOCTEST_WARN_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_WARN_THROWS_AS, "", __VA_ARGS__)
+#define DOCTEST_CHECK_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_CHECK_THROWS_AS, "", __VA_ARGS__)
+#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_REQUIRE_THROWS_AS, "", __VA_ARGS__)
+
+#define DOCTEST_WARN_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, #expr, DT_WARN_THROWS_WITH, __VA_ARGS__)
+#define DOCTEST_CHECK_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, #expr, DT_CHECK_THROWS_WITH, __VA_ARGS__)
+#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, #expr, DT_REQUIRE_THROWS_WITH, __VA_ARGS__)
+
+#define DOCTEST_WARN_THROWS_WITH_AS(expr, message, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_WARN_THROWS_WITH_AS, message, __VA_ARGS__)
+#define DOCTEST_CHECK_THROWS_WITH_AS(expr, message, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_CHECK_THROWS_WITH_AS, message, __VA_ARGS__)
+#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, message, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_REQUIRE_THROWS_WITH_AS, message, __VA_ARGS__)
+
+#define DOCTEST_WARN_NOTHROW(...) DOCTEST_ASSERT_NOTHROW(DT_WARN_NOTHROW, __VA_ARGS__)
+#define DOCTEST_CHECK_NOTHROW(...) DOCTEST_ASSERT_NOTHROW(DT_CHECK_NOTHROW, __VA_ARGS__)
+#define DOCTEST_REQUIRE_NOTHROW(...) DOCTEST_ASSERT_NOTHROW(DT_REQUIRE_NOTHROW, __VA_ARGS__)
+
+#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS(expr); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS(expr); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS(expr); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_AS(expr, ex); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_AS(expr, ex); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_AS(expr, ex); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_WITH(expr, with); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_WITH(expr, with); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_WITH(expr, with); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_WITH_AS(expr, with, ex); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ex); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ex); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_NOTHROW(expr); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_NOTHROW(expr); } DOCTEST_FUNC_SCOPE_END
+#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) DOCTEST_FUNC_SCOPE_BEGIN { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_NOTHROW(expr); } DOCTEST_FUNC_SCOPE_END
+// clang-format on
+
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
+
+// =================================================================================================
+// == WHAT FOLLOWS IS VERSIONS OF THE MACROS THAT DO NOT DO ANY REGISTERING! ==
+// == THIS CAN BE ENABLED BY DEFINING DOCTEST_CONFIG_DISABLE GLOBALLY! ==
+// =================================================================================================
+#else // DOCTEST_CONFIG_DISABLE
+
+#define DOCTEST_IMPLEMENT_FIXTURE(der, base, func, name) \
+ namespace /* NOLINT */ { \
+ template <typename DOCTEST_UNUSED_TEMPLATE_TYPE> \
+ struct der : public base \
+ { void f(); }; \
+ } \
+ template <typename DOCTEST_UNUSED_TEMPLATE_TYPE> \
+ inline void der<DOCTEST_UNUSED_TEMPLATE_TYPE>::f()
+
+#define DOCTEST_CREATE_AND_REGISTER_FUNCTION(f, name) \
+ template <typename DOCTEST_UNUSED_TEMPLATE_TYPE> \
+ static inline void f()
+
+// for registering tests
+#define DOCTEST_TEST_CASE(name) \
+ DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), name)
+
+// for registering tests in classes
+#define DOCTEST_TEST_CASE_CLASS(name) \
+ DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), name)
+
+// for registering tests with a fixture
+#define DOCTEST_TEST_CASE_FIXTURE(x, name) \
+ DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(DOCTEST_ANON_CLASS_), x, \
+ DOCTEST_ANONYMOUS(DOCTEST_ANON_FUNC_), name)
+
+// for converting types to strings without the <typeinfo> header and demangling
+#define DOCTEST_TYPE_TO_STRING_AS(str, ...) static_assert(true, "")
+#define DOCTEST_TYPE_TO_STRING(...) static_assert(true, "")
+
+// for typed tests
+#define DOCTEST_TEST_CASE_TEMPLATE(name, type, ...) \
+ template <typename type> \
+ inline void DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_)()
+
+#define DOCTEST_TEST_CASE_TEMPLATE_DEFINE(name, type, id) \
+ template <typename type> \
+ inline void DOCTEST_ANONYMOUS(DOCTEST_ANON_TMP_)()
+
+#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 // NOLINT
+
+// for starting a testsuite block
+#define DOCTEST_TEST_SUITE_BEGIN(name) static_assert(true, "")
+
+// for ending a testsuite block
+#define DOCTEST_TEST_SUITE_END using DOCTEST_ANONYMOUS(DOCTEST_ANON_FOR_SEMICOLON_) = int
+
+#define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature) \
+ template <typename DOCTEST_UNUSED_TEMPLATE_TYPE> \
+ 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(...) (static_cast<void>(0))
+#define DOCTEST_CAPTURE(x) (static_cast<void>(0))
+#define DOCTEST_ADD_MESSAGE_AT(file, line, ...) (static_cast<void>(0))
+#define DOCTEST_ADD_FAIL_CHECK_AT(file, line, ...) (static_cast<void>(0))
+#define DOCTEST_ADD_FAIL_AT(file, line, ...) (static_cast<void>(0))
+#define DOCTEST_MESSAGE(...) (static_cast<void>(0))
+#define DOCTEST_FAIL_CHECK(...) (static_cast<void>(0))
+#define DOCTEST_FAIL(...) (static_cast<void>(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 <typename L, typename R> \
+ 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
+#define DOCTEST_FAST_CHECK_EQ DOCTEST_CHECK_EQ
+#define DOCTEST_FAST_REQUIRE_EQ DOCTEST_REQUIRE_EQ
+#define DOCTEST_FAST_WARN_NE DOCTEST_WARN_NE
+#define DOCTEST_FAST_CHECK_NE DOCTEST_CHECK_NE
+#define DOCTEST_FAST_REQUIRE_NE DOCTEST_REQUIRE_NE
+#define DOCTEST_FAST_WARN_GT DOCTEST_WARN_GT
+#define DOCTEST_FAST_CHECK_GT DOCTEST_CHECK_GT
+#define DOCTEST_FAST_REQUIRE_GT DOCTEST_REQUIRE_GT
+#define DOCTEST_FAST_WARN_LT DOCTEST_WARN_LT
+#define DOCTEST_FAST_CHECK_LT DOCTEST_CHECK_LT
+#define DOCTEST_FAST_REQUIRE_LT DOCTEST_REQUIRE_LT
+#define DOCTEST_FAST_WARN_GE DOCTEST_WARN_GE
+#define DOCTEST_FAST_CHECK_GE DOCTEST_CHECK_GE
+#define DOCTEST_FAST_REQUIRE_GE DOCTEST_REQUIRE_GE
+#define DOCTEST_FAST_WARN_LE DOCTEST_WARN_LE
+#define DOCTEST_FAST_CHECK_LE DOCTEST_CHECK_LE
+#define DOCTEST_FAST_REQUIRE_LE DOCTEST_REQUIRE_LE
+
+#define DOCTEST_FAST_WARN_UNARY DOCTEST_WARN_UNARY
+#define DOCTEST_FAST_CHECK_UNARY DOCTEST_CHECK_UNARY
+#define DOCTEST_FAST_REQUIRE_UNARY DOCTEST_REQUIRE_UNARY
+#define DOCTEST_FAST_WARN_UNARY_FALSE DOCTEST_WARN_UNARY_FALSE
+#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(id, ...) DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id,__VA_ARGS__)
+// clang-format on
+
+// BDD style macros
+// clang-format off
+#define DOCTEST_SCENARIO(name) DOCTEST_TEST_CASE(" Scenario: " name)
+#define DOCTEST_SCENARIO_CLASS(name) DOCTEST_TEST_CASE_CLASS(" Scenario: " name)
+#define DOCTEST_SCENARIO_TEMPLATE(name, T, ...) DOCTEST_TEST_CASE_TEMPLATE(" Scenario: " name, T, __VA_ARGS__)
+#define DOCTEST_SCENARIO_TEMPLATE_DEFINE(name, T, id) DOCTEST_TEST_CASE_TEMPLATE_DEFINE(" Scenario: " name, T, id)
+
+#define DOCTEST_GIVEN(name) DOCTEST_SUBCASE(" Given: " name)
+#define DOCTEST_WHEN(name) DOCTEST_SUBCASE(" When: " name)
+#define DOCTEST_AND_WHEN(name) DOCTEST_SUBCASE("And when: " name)
+#define DOCTEST_THEN(name) DOCTEST_SUBCASE(" Then: " name)
+#define DOCTEST_AND_THEN(name) DOCTEST_SUBCASE(" And: " name)
+// clang-format on
+
+// == SHORT VERSIONS OF THE MACROS
+#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(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(__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
+
+#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();
+
+#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
+#define DOCTEST_SINGLE_HEADER
+#endif // DOCTEST_SINGLE_HEADER
+
+#if defined(DOCTEST_CONFIG_IMPLEMENT) || !defined(DOCTEST_SINGLE_HEADER)
+
+#ifndef DOCTEST_SINGLE_HEADER
+#include "doctest_fwd.h"
+#endif // DOCTEST_SINGLE_HEADER
+
+DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wunused-macros")
+
+#ifndef DOCTEST_LIBRARY_IMPLEMENTATION
+#define DOCTEST_LIBRARY_IMPLEMENTATION
+
+DOCTEST_CLANG_SUPPRESS_WARNING_POP
+
+DOCTEST_SUPPRESS_COMMON_WARNINGS_PUSH
+
+DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
+DOCTEST_CLANG_SUPPRESS_WARNING("-Wglobal-constructors")
+DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors")
+DOCTEST_CLANG_SUPPRESS_WARNING("-Wsign-conversion")
+DOCTEST_CLANG_SUPPRESS_WARNING("-Wshorten-64-to-32")
+DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-variable-declarations")
+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("-Wdisabled-macro-expansion")
+DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-braces")
+DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-field-initializers")
+DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-member-function")
+DOCTEST_CLANG_SUPPRESS_WARNING("-Wnonportable-system-include-path")
+
+DOCTEST_GCC_SUPPRESS_WARNING_PUSH
+DOCTEST_GCC_SUPPRESS_WARNING("-Wconversion")
+DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-conversion")
+DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-field-initializers")
+DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-braces")
+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-function")
+DOCTEST_GCC_SUPPRESS_WARNING("-Wmultiple-inheritance")
+DOCTEST_GCC_SUPPRESS_WARNING("-Wsuggest-attribute")
+
+DOCTEST_MSVC_SUPPRESS_WARNING_PUSH
+DOCTEST_MSVC_SUPPRESS_WARNING(4267) // 'var' : conversion from 'x' to 'y', possible loss of data
+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(5039) // pointer to potentially throwing function passed to extern C
+DOCTEST_MSVC_SUPPRESS_WARNING(4800) // forcing value to bool 'true' or 'false' (performance warning)
+DOCTEST_MSVC_SUPPRESS_WARNING(5245) // unreferenced function with internal linkage has been removed
+
+DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN
+
+// required includes - will go only in one translation unit!
+#include <ctime>
+#include <cmath>
+#include <climits>
+// borland (Embarcadero) compiler requires math.h and not cmath - https://github.com/doctest/doctest/pull/37
+#ifdef __BORLANDC__
+#include <math.h>
+#endif // __BORLANDC__
+#include <new>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <limits>
+#include <utility>
+#include <fstream>
+#include <sstream>
+#ifndef DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM
+#include <iostream>
+#endif // DOCTEST_CONFIG_NO_INCLUDE_IOSTREAM
+#include <algorithm>
+#include <iomanip>
+#include <vector>
+#ifndef DOCTEST_CONFIG_NO_MULTITHREADING
+#include <atomic>
+#include <mutex>
+#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<std::mutex> 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 <set>
+#include <map>
+#include <unordered_set>
+#include <exception>
+#include <stdexcept>
+#include <csignal>
+#include <cfloat>
+#include <cctype>
+#include <cstdint>
+#include <string>
+
+#ifdef DOCTEST_PLATFORM_MAC
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/sysctl.h>
+#endif // DOCTEST_PLATFORM_MAC
+
+#ifdef DOCTEST_PLATFORM_WINDOWS
+
+// 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 <AfxWin.h>
+#else
+#include <windows.h>
+#endif
+#include <io.h>
+
+#else // DOCTEST_PLATFORM_WINDOWS
+
+#include <sys/time.h>
+#include <unistd.h>
+
+#endif // DOCTEST_PLATFORM_WINDOWS
+
+// 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)
+#endif // HAVE_UNISTD_H
+
+DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END
+
+// counts the number of elements in a C array
+#define DOCTEST_COUNTOF(x) (sizeof(x) / sizeof(x[0]))
+
+#ifdef DOCTEST_CONFIG_DISABLE
+#define DOCTEST_BRANCH_ON_DISABLED(if_disabled, if_not_disabled) if_disabled
+#else // DOCTEST_CONFIG_DISABLE
+#define DOCTEST_BRANCH_ON_DISABLED(if_disabled, if_not_disabled) if_not_disabled
+#endif // DOCTEST_CONFIG_DISABLE
+
+#ifndef DOCTEST_CONFIG_OPTIONS_PREFIX
+#define DOCTEST_CONFIG_OPTIONS_PREFIX "dt-"
+#endif
+
+#ifndef DOCTEST_CONFIG_OPTIONS_FILE_PREFIX_SEPARATOR
+#define DOCTEST_CONFIG_OPTIONS_FILE_PREFIX_SEPARATOR ':'
+#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
+#define DOCTEST_OPTIONS_PREFIX_DISPLAY DOCTEST_CONFIG_OPTIONS_PREFIX
+#else
+#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 <typename Ex>
+ 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++) {
+ const int d = tolower(*a) - tolower(*b);
+ if(d != 0 || !*a)
+ return d;
+ }
+ }
+
+ struct Endianness
+ {
+ enum Arch
+ {
+ Big,
+ Little
+ };
+
+ static Arch which() {
+ int x = 1;
+ // casting any data pointer to char* is allowed
+ auto ptr = reinterpret_cast<char*>(&x);
+ if(*ptr)
+ return Little;
+ return Big;
+ }
+ };
+} // namespace
+
+namespace detail {
+ DOCTEST_THREAD_LOCAL class
+ {
+ std::vector<std::streampos> stack;
+ std::stringstream ss;
+
+ public:
+ std::ostream* push() {
+ stack.push_back(ss.tellp());
+ return &ss;
+ }
+
+ String pop() {
+ if (stack.empty())
+ DOCTEST_INTERNAL_ERROR("TLSS was empty when trying to pop!");
+
+ std::streampos pos = stack.back();
+ stack.pop_back();
+ unsigned sz = static_cast<unsigned>(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 tlssPop() {
+ return g_oss.pop();
+ }
+
+#ifndef DOCTEST_CONFIG_DISABLE
+
+namespace timer_large_integer
+{
+
+#if defined(DOCTEST_PLATFORM_WINDOWS)
+ using type = ULONGLONG;
+#else // DOCTEST_PLATFORM_WINDOWS
+ using type = std::uint64_t;
+#endif // DOCTEST_PLATFORM_WINDOWS
+}
+
+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} };
+ if(!hz.QuadPart) {
+ QueryPerformanceFrequency(&hz);
+ QueryPerformanceCounter(&hzo);
+ }
+ LARGE_INTEGER t;
+ QueryPerformanceCounter(&t);
+ return ((t.QuadPart - hzo.QuadPart) * LONGLONG(1000000)) / hz.QuadPart;
+ }
+#else // DOCTEST_PLATFORM_WINDOWS
+ ticks_t getCurrentTicks() {
+ timeval t;
+ gettimeofday(&t, nullptr);
+ return static_cast<ticks_t>(t.tv_sec) * 1000000 + static_cast<ticks_t>(t.tv_usec);
+ }
+#endif // DOCTEST_PLATFORM_WINDOWS
+
+ struct Timer
+ {
+ void start() { m_ticks = getCurrentTicks(); }
+ unsigned int getElapsedMicroseconds() const {
+ return static_cast<unsigned int>(getCurrentTicks() - m_ticks);
+ }
+ //unsigned int getElapsedMilliseconds() const {
+ // return static_cast<unsigned int>(getElapsedMicroseconds() / 1000);
+ //}
+ double getElapsedSeconds() const { return static_cast<double>(getCurrentTicks() - m_ticks) / 1000000.0; }
+
+ private:
+ ticks_t m_ticks = 0;
+ };
+
+#ifdef DOCTEST_CONFIG_NO_MULTITHREADING
+ template <typename T>
+ using Atomic = T;
+#else // DOCTEST_CONFIG_NO_MULTITHREADING
+ template <typename T>
+ using Atomic = std::atomic<T>;
+#endif // DOCTEST_CONFIG_NO_MULTITHREADING
+
+#if defined(DOCTEST_CONFIG_NO_MULTI_LANE_ATOMICS) || defined(DOCTEST_CONFIG_NO_MULTITHREADING)
+ template <typename T>
+ using MultiLaneAtomic = Atomic<T>;
+#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 <typename T>
+ class MultiLaneAtomic
+ {
+ struct CacheLineAlignedAtomic
+ {
+ Atomic<T> atomic{};
+ char padding[DOCTEST_MULTI_LANE_ATOMICS_CACHE_LINE_SIZE - sizeof(Atomic<T>)];
+ };
+ 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<T>& myAtomic() DOCTEST_NOEXCEPT {
+ static Atomic<size_t> 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
+ {
+ MultiLaneAtomic<int> numAssertsCurrentTest_atomic;
+ MultiLaneAtomic<int> numAssertsFailedCurrentTest_atomic;
+
+ std::vector<std::vector<String>> filters = decltype(filters)(9); // 9 different filters
+
+ std::vector<IReporter*> reporters_currently_used;
+
+ assert_handler ah = nullptr;
+
+ Timer timer;
+
+ std::vector<String> stringifiedContexts; // logging from INFO() due to an exception
+
+ // stuff for subcases
+ bool reachedLeaf;
+ std::vector<SubcaseSignature> subcaseStack;
+ std::vector<SubcaseSignature> nextSubcaseStack;
+ std::unordered_set<unsigned long long> fullyTraversedSubcases;
+ size_t currentSubcaseDepth;
+ Atomic<bool> shouldLogCurrentException;
+
+ void resetRunData() {
+ numTestCases = 0;
+ numTestCasesPassingFilters = 0;
+ numTestSuitesPassingFilters = 0;
+ numTestCasesFailed = 0;
+ numAsserts = 0;
+ numAssertsFailed = 0;
+ numAssertsCurrentTest = 0;
+ numAssertsFailedCurrentTest = 0;
+ }
+
+ void finalizeTestCaseData() {
+ seconds = timer.getElapsedSeconds();
+
+ // update the non-atomic counters
+ numAsserts += numAssertsCurrentTest_atomic;
+ numAssertsFailed += numAssertsFailedCurrentTest_atomic;
+ numAssertsCurrentTest = numAssertsCurrentTest_atomic;
+ numAssertsFailedCurrentTest = numAssertsFailedCurrentTest_atomic;
+
+ if(numAssertsFailedCurrentTest)
+ failure_flags |= TestCaseFailureReason::AssertFailure;
+
+ if(Approx(currentTest->m_timeout).epsilon(DBL_EPSILON) != 0 &&
+ Approx(seconds).epsilon(DBL_EPSILON) > currentTest->m_timeout)
+ failure_flags |= TestCaseFailureReason::Timeout;
+
+ if(currentTest->m_should_fail) {
+ if(failure_flags) {
+ failure_flags |= TestCaseFailureReason::ShouldHaveFailedAndDid;
+ } else {
+ failure_flags |= TestCaseFailureReason::ShouldHaveFailedButDidnt;
+ }
+ } else if(failure_flags && currentTest->m_may_fail) {
+ failure_flags |= TestCaseFailureReason::CouldHaveFailedAndDid;
+ } else if(currentTest->m_expected_failures > 0) {
+ if(numAssertsFailedCurrentTest == currentTest->m_expected_failures) {
+ failure_flags |= TestCaseFailureReason::FailedExactlyNumTimes;
+ } else {
+ failure_flags |= TestCaseFailureReason::DidntFailExactlyNumTimes;
+ }
+ }
+
+ bool ok_to_fail = (TestCaseFailureReason::ShouldHaveFailedAndDid & failure_flags) ||
+ (TestCaseFailureReason::CouldHaveFailedAndDid & failure_flags) ||
+ (TestCaseFailureReason::FailedExactlyNumTimes & failure_flags);
+
+ // if any subcase has failed - the whole test case has failed
+ testCaseSuccess = !(failure_flags && !ok_to_fail);
+ if(!testCaseSuccess)
+ numTestCasesFailed++;
+ }
+ };
+
+ ContextState* g_cs = nullptr;
+
+ // used to avoid locks for the debug output
+ // TODO: figure out if this is indeed necessary/correct - seems like either there still
+ // could be a race or that there wouldn't be a race even if using the context directly
+ DOCTEST_THREAD_LOCAL bool g_no_colors;
+
+#endif // DOCTEST_CONFIG_DISABLE
+} // namespace detail
+
+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<unsigned char*>(&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) {
+ if(other.isOnStack()) {
+ memcpy(buf, other.buf, len);
+ } else {
+ memcpy(allocate(other.data.size), other.data.ptr, other.data.size);
+ }
+}
+
+String::String() noexcept {
+ buf[0] = '\0';
+ setLast();
+}
+
+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, 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); }
+
+String& String::operator=(const String& other) {
+ if(this != &other) {
+ if(!isOnStack())
+ delete[] data.ptr;
+
+ copy(other);
+ }
+
+ return *this;
+}
+
+String& String::operator+=(const String& other) {
+ 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
+ char* temp = new char[total_size + 1];
+ // copy current data to new location before writing in the union
+ memcpy(temp, buf, my_old_size); // skip the +1 ('\0') for speed
+ // update data in union
+ setOnHeap();
+ data.size = total_size;
+ data.capacity = data.size + 1;
+ data.ptr = temp;
+ // transfer the rest of the data
+ memcpy(data.ptr + my_old_size, other.c_str(), other_size + 1);
+ }
+ } else {
+ if(data.capacity > total_size) {
+ // append to the current heap block
+ data.size = total_size;
+ memcpy(data.ptr + my_old_size, other.c_str(), other_size + 1);
+ } else {
+ // resize
+ data.capacity *= 2;
+ if(data.capacity <= total_size)
+ data.capacity = total_size + 1;
+ // alloc new chunk
+ char* temp = new char[data.capacity];
+ // copy current data to new location before releasing it
+ memcpy(temp, data.ptr, my_old_size); // skip the +1 ('\0') for speed
+ // release old chunk
+ delete[] data.ptr;
+ // update the rest of the union members
+ data.size = total_size;
+ data.ptr = temp;
+ // transfer the rest of the data
+ memcpy(data.ptr + my_old_size, other.c_str(), other_size + 1);
+ }
+ }
+
+ return *this;
+}
+
+String::String(String&& other) noexcept {
+ memcpy(buf, other.buf, len);
+ other.buf[0] = '\0';
+ other.setLast();
+}
+
+String& String::operator=(String&& other) noexcept {
+ if(this != &other) {
+ if(!isOnStack())
+ delete[] data.ptr;
+ memcpy(buf, other.buf, len);
+ other.buf[0] = '\0';
+ other.setLast();
+ }
+ return *this;
+}
+
+char String::operator[](size_type i) const {
+ return const_cast<String*>(this)->operator[](i);
+}
+
+char& String::operator[](size_type i) {
+ if(isOnStack())
+ return reinterpret_cast<char*>(buf)[i];
+ return data.ptr[i];
+}
+
+DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wmaybe-uninitialized")
+String::size_type String::size() const {
+ if(isOnStack())
+ return last - (size_type(buf[last]) & 31); // using "last" would work only if "len" is 32
+ return data.size;
+}
+DOCTEST_GCC_SUPPRESS_WARNING_POP
+
+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() - 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() - 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<size_type>(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<size_type>(it - begin); }
+ else { return npos; }
+}
+
+int String::compare(const char* other, bool no_case) const {
+ if(no_case)
+ return doctest::stricmp(c_str(), other);
+ return std::strcmp(c_str(), other);
+}
+
+int String::compare(const String& other, bool no_case) const {
+ return compare(other.c_str(), no_case);
+}
+
+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; }
+
+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
+
+namespace Color {
+ std::ostream& operator<<(std::ostream& s, Color::Enum code) {
+ color_to_stream(s, code);
+ return s;
+ }
+} // namespace Color
+
+// 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
+}
+// clang-format on
+
+const char* failureString(assertType::Enum at) {
+ if(at & assertType::is_warn) //!OCLINT bitwise operator in conditional
+ return "WARNING";
+ if(at & assertType::is_check) //!OCLINT bitwise operator in conditional
+ return "ERROR";
+ if(at & assertType::is_require) //!OCLINT bitwise operator in conditional
+ return "FATAL ERROR";
+ return "";
+}
+
+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, '/');
+ if(back || forward) {
+ if(back > forward)
+ forward = back;
+ return forward + 1;
+ }
+ } else {
+ const auto prefixes = getContextOptions()->strip_file_prefixes;
+ const char separator = DOCTEST_CONFIG_OPTIONS_FILE_PREFIX_SEPARATOR;
+ String::size_type longest_match = 0U;
+ for(String::size_type pos = 0U; pos < prefixes.size(); ++pos)
+ {
+ const auto prefix_start = pos;
+ pos = std::min(prefixes.find(separator, prefix_start), prefixes.size());
+
+ const auto prefix_size = pos - prefix_start;
+ if(prefix_size > longest_match)
+ {
+ // TODO under DOCTEST_MSVC: does the comparison need strnicmp() to work with drive letter capitalization?
+ if(0 == std::strncmp(prefixes.c_str() + prefix_start, file, prefix_size))
+ {
+ longest_match = prefix_size;
+ }
+ }
+ }
+ return &file[longest_match];
+ }
+#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;
+ if(std::strcmp(m_file, other.m_file) != 0)
+ return std::strcmp(m_file, other.m_file) < 0;
+ return m_name.compare(other.m_name) < 0;
+}
+
+DOCTEST_DEFINE_INTERFACE(IContextScope)
+
+namespace detail {
+ void filldata<const void*>::fill(std::ostream* stream, const void* in) {
+ if (in) { *stream << in; }
+ else { *stream << "nullptr"; }
+ }
+
+ template <typename T>
+ String toStreamLit(T t) {
+ std::ostream* os = tlssPush();
+ os->operator<<(t);
+ return tlssPop();
+ }
+}
+
+#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/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<signed>(in)); }
+String toString(char signed in) { return toStreamLit(static_cast<signed>(in)); }
+String toString(char unsigned in) { return toStreamLit(static_cast<unsigned>(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<double>(std::numeric_limits<float>::epsilon()) * 100)
+ , m_scale(1.0)
+ , m_value(value) {}
+
+Approx Approx::operator()(double value) const {
+ Approx approx(value);
+ approx.epsilon(m_epsilon);
+ approx.scale(m_scale);
+ return approx;
+}
+
+Approx& Approx::epsilon(double newEpsilon) {
+ m_epsilon = newEpsilon;
+ return *this;
+}
+Approx& Approx::scale(double newScale) {
+ m_scale = newScale;
+ return *this;
+}
+
+bool operator==(double lhs, const Approx& rhs) {
+ // Thanks to Richard Harris for his help refining this formula
+ return std::fabs(lhs - rhs.m_value) <
+ rhs.m_epsilon * (rhs.m_scale + std::max<double>(std::fabs(lhs), std::fabs(rhs.m_value)));
+}
+bool operator==(const Approx& lhs, double rhs) { return operator==(rhs, lhs); }
+bool operator!=(double lhs, const Approx& rhs) { return !operator==(lhs, rhs); }
+bool operator!=(const Approx& lhs, double rhs) { return !operator==(rhs, lhs); }
+bool operator<=(double lhs, const Approx& rhs) { return lhs < rhs.m_value || lhs == rhs; }
+bool operator<=(const Approx& lhs, double rhs) { return lhs.m_value < rhs || lhs == rhs; }
+bool operator>=(double lhs, const Approx& rhs) { return lhs > rhs.m_value || lhs == rhs; }
+bool operator>=(const Approx& lhs, double rhs) { return lhs.m_value > rhs || lhs == rhs; }
+bool operator<(double lhs, const Approx& rhs) { return lhs < rhs.m_value && lhs != rhs; }
+bool operator<(const Approx& lhs, double rhs) { return lhs.m_value < rhs && lhs != rhs; }
+bool operator>(double lhs, const Approx& rhs) { return lhs > rhs.m_value && lhs != rhs; }
+bool operator>(const Approx& lhs, double rhs) { return lhs.m_value > rhs && lhs != rhs; }
+
+String toString(const Approx& in) {
+ 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 <typename F>
+IsNaN<F>::operator bool() const {
+ return std::isnan(value) ^ flipped;
+}
+DOCTEST_MSVC_SUPPRESS_WARNING_POP
+template struct DOCTEST_INTERFACE_DEF IsNaN<float>;
+template struct DOCTEST_INTERFACE_DEF IsNaN<double>;
+template struct DOCTEST_INTERFACE_DEF IsNaN<long double>;
+template <typename F>
+String toString(IsNaN<F> in) { return String(in.flipped ? "! " : "") + "IsNaN( " + doctest::toString(in.value) + " )"; }
+String toString(IsNaN<float> in) { return toString<float>(in); }
+String toString(IsNaN<double> in) { return toString<double>(in); }
+String toString(IsNaN<double long> in) { return toString<double long>(in); }
+
+} // namespace doctest
+
+#ifdef DOCTEST_CONFIG_DISABLE
+namespace doctest {
+Context::Context(int, const char* const*) {}
+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; }
+
+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; }
+const String* IReporter::get_stringified_contexts() { return nullptr; }
+
+int registerReporter(const char*, int, IReporter*) { return 0; }
+
+} // namespace doctest
+#else // DOCTEST_CONFIG_DISABLE
+
+#if !defined(DOCTEST_CONFIG_COLORS_NONE)
+#if !defined(DOCTEST_CONFIG_COLORS_WINDOWS) && !defined(DOCTEST_CONFIG_COLORS_ANSI)
+#ifdef DOCTEST_PLATFORM_WINDOWS
+#define DOCTEST_CONFIG_COLORS_WINDOWS
+#else // linux
+#define DOCTEST_CONFIG_COLORS_ANSI
+#endif // platform
+#endif // DOCTEST_CONFIG_COLORS_WINDOWS && DOCTEST_CONFIG_COLORS_ANSI
+#endif // DOCTEST_CONFIG_COLORS_NONE
+
+namespace doctest_detail_test_suite_ns {
+// holds the current test suite
+doctest::detail::TestSuite& getCurrentTestSuite() {
+ static doctest::detail::TestSuite data{};
+ return data;
+}
+} // namespace doctest_detail_test_suite_ns
+
+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 :|
+ using reporterMap = std::map<std::pair<int, String>, reporterCreatorFunc>;
+
+ reporterMap& getReporters() {
+ static reporterMap data;
+ return data;
+ }
+ reporterMap& getListeners() {
+ static reporterMap data;
+ return data;
+ }
+} // namespace
+namespace detail {
+#define DOCTEST_ITERATE_THROUGH_REPORTERS(function, ...) \
+ for(auto& curr_rep : g_cs->reporters_currently_used) \
+ curr_rep->function(__VA_ARGS__)
+
+ bool checkIfShouldThrow(assertType::Enum at) {
+ if(at & assertType::is_require) //!OCLINT bitwise operator in conditional
+ return true;
+
+ if((at & assertType::is_check) //!OCLINT bitwise operator in conditional
+ && getContextOptions()->abort_after > 0 &&
+ (g_cs->numAssertsFailed + g_cs->numAssertsFailedCurrentTest_atomic) >=
+ getContextOptions()->abort_after)
+ return true;
+
+ return false;
+ }
+
+#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
+ DOCTEST_NORETURN void throwException() {
+ g_cs->shouldLogCurrentException = false;
+ throw TestFailureException(); // NOLINT(hicpp-exception-baseclass)
+ }
+#else // DOCTEST_CONFIG_NO_EXCEPTIONS
+ void throwException() {}
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
+} // namespace detail
+
+namespace {
+ using namespace detail;
+ // matching of a string against a wildcard mask (case sensitivity configurable) taken from
+ // https://www.codeproject.com/Articles/1088/Wildcard-string-compare-globbing
+ int wildcmp(const char* str, const char* wild, bool caseSensitive) {
+ const char* cp = str;
+ const char* mp = wild;
+
+ while((*str) && (*wild != '*')) {
+ if((caseSensitive ? (*wild != *str) : (tolower(*wild) != tolower(*str))) &&
+ (*wild != '?')) {
+ return 0;
+ }
+ wild++;
+ str++;
+ }
+
+ while(*str) {
+ if(*wild == '*') {
+ if(!*++wild) {
+ return 1;
+ }
+ mp = wild;
+ cp = str + 1;
+ } else if((caseSensitive ? (*wild == *str) : (tolower(*wild) == tolower(*str))) ||
+ (*wild == '?')) {
+ wild++;
+ str++;
+ } else {
+ wild = mp; //!OCLINT parameter reassignment
+ str = cp++; //!OCLINT parameter reassignment
+ }
+ }
+
+ while(*wild == '*') {
+ wild++;
+ }
+ return !*wild;
+ }
+
+ // 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<String>& filters, bool matchEmpty,
+ bool caseSensitive) {
+ if (filters.empty() && matchEmpty)
+ return true;
+ for (auto& curr : filters)
+ if (wildcmp(name, curr.c_str(), caseSensitive))
+ return true;
+ return false;
+ }
+
+ DOCTEST_NO_SANITIZE_INTEGER
+ unsigned long long hash(unsigned long long a, unsigned long long b) {
+ return (a << 5) + b;
+ }
+
+ // 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;
+ }
+
+ 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<SubcaseSignature>& 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;
+ }
+
+ unsigned long long hash(const std::vector<SubcaseSignature>& 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;
+ }
+
+ 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_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations")
+
+ Subcase::~Subcase() {
+ 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 && (!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) {
+ 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});
+ g_cs->shouldLogCurrentException = false;
+ }
+
+ DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_end, DOCTEST_EMPTY);
+ }
+ }
+
+ DOCTEST_CLANG_SUPPRESS_WARNING_POP
+ DOCTEST_GCC_SUPPRESS_WARNING_POP
+ DOCTEST_MSVC_SUPPRESS_WARNING_POP
+
+ Subcase::operator bool() const { return m_entered; }
+
+ Result::Result(bool passed, const String& decomposition)
+ : m_passed(passed)
+ , m_decomp(decomposition) {}
+
+ ExpressionDecomposer::ExpressionDecomposer(assertType::Enum at)
+ : m_at(at) {}
+
+ TestSuite& TestSuite::operator*(const char* in) {
+ m_test_suite = in;
+ return *this;
+ }
+
+ TestCase::TestCase(funcType test, const char* file, unsigned line, const TestSuite& test_suite,
+ 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;
+ m_timeout = test_suite.m_timeout;
+
+ m_test = test;
+ m_type = type;
+ m_template_id = template_id;
+ }
+
+ TestCase::TestCase(const TestCase& other)
+ : TestCaseData() {
+ *this = other;
+ }
+
+ DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(26434) // hides a non-virtual function
+ TestCase& TestCase::operator=(const TestCase& other) {
+ TestCaseData::operator=(other);
+ m_test = other.m_test;
+ m_type = other.m_type;
+ m_template_id = other.m_template_id;
+ m_full_name = other.m_full_name;
+
+ if(m_template_id != -1)
+ m_name = m_full_name.c_str();
+ return *this;
+ }
+ DOCTEST_MSVC_SUPPRESS_WARNING_POP
+
+ TestCase& TestCase::operator*(const char* in) {
+ 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 + ">";
+ // redirect the name to point to the newly constructed full name
+ m_name = m_full_name.c_str();
+ }
+ return *this;
+ }
+
+ 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<TestCase>& getRegisteredTests() {
+ static std::set<TestCase> data;
+ return data;
+ }
+} // namespace detail
+namespace {
+ using namespace detail;
+ // for sorting tests by file/line
+ bool fileOrderComparator(const TestCase* lhs, const TestCase* rhs) {
+ // this is needed because MSVC gives different case for drive letters
+ // for __FILE__ when evaluated in a header and a source file
+ const int res = lhs->m_file.compare(rhs->m_file, bool(DOCTEST_MSVC));
+ if(res != 0)
+ return res < 0;
+ if(lhs->m_line != rhs->m_line)
+ return lhs->m_line < rhs->m_line;
+ return lhs->m_template_id < rhs->m_template_id;
+ }
+
+ // for sorting tests by suite/file/line
+ bool suiteOrderComparator(const TestCase* lhs, const TestCase* rhs) {
+ const int res = std::strcmp(lhs->m_test_suite, rhs->m_test_suite);
+ if(res != 0)
+ return res < 0;
+ return fileOrderComparator(lhs, rhs);
+ }
+
+ // for sorting tests by name/suite/file/line
+ bool nameOrderComparator(const TestCase* lhs, const TestCase* rhs) {
+ const int res = std::strcmp(lhs->m_name, rhs->m_name);
+ if(res != 0)
+ return res < 0;
+ return suiteOrderComparator(lhs, rhs);
+ }
+
+ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations")
+ void color_to_stream(std::ostream& s, Color::Enum code) {
+ static_cast<void>(s); // for DOCTEST_CONFIG_COLORS_NONE or DOCTEST_CONFIG_COLORS_WINDOWS
+ static_cast<void>(code); // for DOCTEST_CONFIG_COLORS_NONE
+#ifdef DOCTEST_CONFIG_COLORS_ANSI
+ if(g_no_colors ||
+ (isatty(STDOUT_FILENO) == false && getContextOptions()->force_colors == false))
+ return;
+
+ auto col = "";
+ // clang-format off
+ switch(code) { //!OCLINT missing break in switch statement / unnecessary default statement in covered switch statement
+ case Color::Red: col = "[0;31m"; break;
+ case Color::Green: col = "[0;32m"; break;
+ case Color::Blue: col = "[0;34m"; break;
+ case Color::Cyan: col = "[0;36m"; break;
+ case Color::Yellow: col = "[0;33m"; break;
+ case Color::Grey: col = "[1;30m"; break;
+ case Color::LightGrey: col = "[0;37m"; break;
+ case Color::BrightRed: col = "[1;31m"; break;
+ case Color::BrightGreen: col = "[1;32m"; break;
+ case Color::BrightWhite: col = "[1;37m"; break;
+ case Color::Bright: // invalid
+ case Color::None:
+ case Color::White:
+ default: col = "[0m";
+ }
+ // clang-format on
+ s << "\033" << col;
+#endif // DOCTEST_CONFIG_COLORS_ANSI
+
+#ifdef DOCTEST_CONFIG_COLORS_WINDOWS
+ if(g_no_colors ||
+ (_isatty(_fileno(stdout)) == false && getContextOptions()->force_colors == false))
+ return;
+
+ 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) {
+ case Color::White: DOCTEST_SET_ATTR(FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); break;
+ case Color::Red: DOCTEST_SET_ATTR(FOREGROUND_RED); break;
+ case Color::Green: DOCTEST_SET_ATTR(FOREGROUND_GREEN); break;
+ case Color::Blue: DOCTEST_SET_ATTR(FOREGROUND_BLUE); break;
+ case Color::Cyan: DOCTEST_SET_ATTR(FOREGROUND_BLUE | FOREGROUND_GREEN); break;
+ case Color::Yellow: DOCTEST_SET_ATTR(FOREGROUND_RED | FOREGROUND_GREEN); break;
+ case Color::Grey: DOCTEST_SET_ATTR(0); break;
+ case Color::LightGrey: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY); break;
+ case Color::BrightRed: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_RED); break;
+ case Color::BrightGreen: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_GREEN); break;
+ 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(ch.origFgAttrs);
+ }
+ // clang-format on
+#endif // DOCTEST_CONFIG_COLORS_WINDOWS
+ }
+ DOCTEST_CLANG_SUPPRESS_WARNING_POP
+
+ std::vector<const IExceptionTranslator*>& getExceptionTranslators() {
+ static std::vector<const IExceptionTranslator*> data;
+ return data;
+ }
+
+ String translateActiveException() {
+#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
+ auto& translators = getExceptionTranslators();
+ for(auto& curr : translators) {
+ String res;
+ if (curr->translate(res))
+ return res;
+ }
+ // clang-format off
+ DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wcatch-value")
+ try {
+ throw;
+ } catch(std::exception& ex) {
+ return ex.what();
+ } catch(std::string& msg) {
+ return msg.c_str();
+ } catch(const char* msg) {
+ return msg;
+ } catch(...) {
+ return "unknown exception";
+ }
+ DOCTEST_GCC_SUPPRESS_WARNING_POP
+// clang-format on
+#else // DOCTEST_CONFIG_NO_EXCEPTIONS
+ return "";
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
+ }
+} // namespace
+
+namespace detail {
+ // used by the macros for registering tests
+ int regTest(const TestCase& tc) {
+ getRegisteredTests().insert(tc);
+ return 0;
+ }
+
+ // sets the current test suite
+ int setTestSuite(const TestSuite& ts) {
+ doctest_detail_test_suite_ns::getCurrentTestSuite() = ts;
+ return 0;
+ }
+
+#ifdef DOCTEST_IS_DEBUGGER_ACTIVE
+ bool isDebuggerActive() { return DOCTEST_IS_DEBUGGER_ACTIVE(); }
+#else // DOCTEST_IS_DEBUGGER_ACTIVE
+#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
+ // running under the debugger or has a debugger attached post facto).
+ bool isDebuggerActive() {
+ int mib[4];
+ kinfo_proc info;
+ size_t size;
+ // Initialize the flags so that, if sysctl fails for some bizarre
+ // reason, we get a predictable result.
+ info.kp_proc.p_flag = 0;
+ // Initialize mib, which tells sysctl the info we want, in this case
+ // we're looking for information about a specific process ID.
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_PID;
+ mib[3] = getpid();
+ // Call sysctl.
+ size = sizeof(info);
+ if(sysctl(mib, DOCTEST_COUNTOF(mib), &info, &size, 0, 0) != 0) {
+ std::cerr << "\nCall to sysctl failed - unable to determine if debugger is active **\n";
+ return false;
+ }
+ // We're being debugged if the P_TRACED flag is set.
+ return ((info.kp_proc.p_flag & P_TRACED) != 0);
+ }
+#elif DOCTEST_MSVC || defined(__MINGW32__) || defined(__MINGW64__)
+ bool isDebuggerActive() { return ::IsDebuggerPresent() != 0; }
+#else
+ bool isDebuggerActive() { return false; }
+#endif // Platform
+#endif // DOCTEST_IS_DEBUGGER_ACTIVE
+
+ void registerExceptionTranslatorImpl(const IExceptionTranslator* et) {
+ if(std::find(getExceptionTranslators().begin(), getExceptionTranslators().end(), et) ==
+ getExceptionTranslators().end())
+ getExceptionTranslators().push_back(et);
+ }
+
+ DOCTEST_THREAD_LOCAL std::vector<IContextScope*> g_infoContexts; // for logging with INFO()
+
+ ContextScopeBase::ContextScopeBase() {
+ g_infoContexts.push_back(this);
+ }
+
+ 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 && (!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
+ std::ostringstream s;
+ this->stringify(&s);
+ g_cs->stringifiedContexts.push_back(s.str().c_str());
+ }
+ g_infoContexts.pop_back();
+ }
+
+ DOCTEST_CLANG_SUPPRESS_WARNING_POP
+ DOCTEST_GCC_SUPPRESS_WARNING_POP
+ DOCTEST_MSVC_SUPPRESS_WARNING_POP
+} // namespace detail
+namespace {
+ using namespace detail;
+
+#if !defined(DOCTEST_CONFIG_POSIX_SIGNALS) && !defined(DOCTEST_CONFIG_WINDOWS_SEH)
+ struct FatalConditionHandler
+ {
+ static void reset() {}
+ static void allocateAltStackMem() {}
+ static void freeAltStackMem() {}
+ };
+#else // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH
+
+ void reportFatal(const std::string&);
+
+#ifdef DOCTEST_PLATFORM_WINDOWS
+
+ struct SignalDefs
+ {
+ DWORD id;
+ const char* name;
+ };
+ // There is no 1-1 mapping between signals and windows exceptions.
+ // Windows can easily distinguish between SO and SigSegV,
+ // but SigInt, SigTerm, etc are handled differently.
+ SignalDefs signalDefs[] = {
+ {static_cast<DWORD>(EXCEPTION_ILLEGAL_INSTRUCTION),
+ "SIGILL - Illegal instruction signal"},
+ {static_cast<DWORD>(EXCEPTION_STACK_OVERFLOW), "SIGSEGV - Stack overflow"},
+ {static_cast<DWORD>(EXCEPTION_ACCESS_VIOLATION),
+ "SIGSEGV - Segmentation violation signal"},
+ {static_cast<DWORD>(EXCEPTION_INT_DIVIDE_BY_ZERO), "Divide by zero error"},
+ };
+
+ struct FatalConditionHandler
+ {
+ static LONG CALLBACK handleException(PEXCEPTION_POINTERS ExceptionInfo) {
+ // 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;
+ }
+ std::exit(EXIT_FAILURE);
+ }
+
+ static void allocateAltStackMem() {}
+ static void freeAltStackMem() {}
+
+ FatalConditionHandler() {
+ isSet = true;
+ // 32k seems enough for doctest to handle stack overflow,
+ // but the value was found experimentally, so there is no strong guarantee
+ guaranteeSize = 32 * 1024;
+ // Register an unhandled exception filter
+ 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() {
+ if(isSet) {
+ // Unregister handler and restore the old guarantee
+ SetUnhandledExceptionFilter(previousTop);
+ SetThreadStackGuarantee(&guaranteeSize);
+ 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<void>(_CrtSetReportMode(_CRT_ASSERT, prev_report_mode));
+ static_cast<void>(_CrtSetReportFile(_CRT_ASSERT, prev_report_file));
+ isSet = false;
+ }
+ }
+
+ ~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;
+
+#else // DOCTEST_PLATFORM_WINDOWS
+
+ struct SignalDefs
+ {
+ int id;
+ const char* name;
+ };
+ SignalDefs signalDefs[] = {{SIGINT, "SIGINT - Terminal interrupt signal"},
+ {SIGILL, "SIGILL - Illegal instruction signal"},
+ {SIGFPE, "SIGFPE - Floating point error signal"},
+ {SIGSEGV, "SIGSEGV - Segmentation violation signal"},
+ {SIGTERM, "SIGTERM - Termination request signal"},
+ {SIGABRT, "SIGABRT - Abort (abnormal termination) signal"}};
+
+ struct FatalConditionHandler
+ {
+ static bool isSet;
+ static struct sigaction oldSigActions[DOCTEST_COUNTOF(signalDefs)];
+ static stack_t oldSigStack;
+ static size_t altStackSize;
+ static char* altStackMem;
+
+ static void handleSignal(int sig) {
+ const char* name = "<unknown signal>";
+ for(std::size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) {
+ SignalDefs& def = signalDefs[i];
+ if(sig == def.id) {
+ name = def.name;
+ break;
+ }
+ }
+ reset();
+ reportFatal(name);
+ 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 = altStackSize;
+ sigStack.ss_flags = 0;
+ sigaltstack(&sigStack, &oldSigStack);
+ struct sigaction sa = {};
+ sa.sa_handler = handleSignal;
+ sa.sa_flags = SA_ONSTACK;
+ for(std::size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) {
+ sigaction(signalDefs[i].id, &sa, &oldSigActions[i]);
+ }
+ }
+
+ ~FatalConditionHandler() { reset(); }
+ static void reset() {
+ if(isSet) {
+ // Set signals back to previous values -- hopefully nobody overwrote them in the meantime
+ for(std::size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) {
+ sigaction(signalDefs[i].id, &oldSigActions[i], nullptr);
+ }
+ // Return the old stack
+ sigaltstack(&oldSigStack, nullptr);
+ isSet = false;
+ }
+ }
+ };
+
+ bool FatalConditionHandler::isSet = false;
+ struct sigaction FatalConditionHandler::oldSigActions[DOCTEST_COUNTOF(signalDefs)] = {};
+ 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
+
+} // namespace
+
+namespace {
+ using namespace detail;
+
+#ifdef DOCTEST_PLATFORM_WINDOWS
+#define DOCTEST_OUTPUT_DEBUG_STRING(text) ::OutputDebugStringA(text)
+#else
+ // TODO: integration with XCode and other IDEs
+#define DOCTEST_OUTPUT_DEBUG_STRING(text)
+#endif // Platform
+
+ void addAssert(assertType::Enum at) {
+ if((at & assertType::is_warn) == 0) //!OCLINT bitwise operator in conditional
+ g_cs->numAssertsCurrentTest_atomic++;
+ }
+
+ void addFailedAssert(assertType::Enum at) {
+ if((at & assertType::is_warn) == 0) //!OCLINT bitwise operator in conditional
+ g_cs->numAssertsFailedCurrentTest_atomic++;
+ }
+
+#if defined(DOCTEST_CONFIG_POSIX_SIGNALS) || defined(DOCTEST_CONFIG_WINDOWS_SEH)
+ void reportFatal(const std::string& message) {
+ g_cs->failure_flags |= TestCaseFailureReason::Crash;
+
+ DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_exception, {message.c_str(), true});
+
+ while (g_cs->subcaseStack.size()) {
+ g_cs->subcaseStack.pop_back();
+ DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_end, DOCTEST_EMPTY);
+ }
+
+ g_cs->finalizeTestCaseData();
+
+ DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_end, *g_cs);
+
+ DOCTEST_ITERATE_THROUGH_REPORTERS(test_run_end, *g_cs);
+ }
+#endif // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH
+} // namespace
+
+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;
+#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;
+ m_failed = !res.m_passed;
+ }
+
+ void ResultBuilder::translateException() {
+ m_threw = true;
+ m_exception = translateActiveException();
+ }
+
+ bool ResultBuilder::log() {
+ 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_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_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 = "\"" + m_exception + "\"";
+
+ if(is_running_in_test) {
+ addAssert(m_at);
+ DOCTEST_ITERATE_THROUGH_REPORTERS(log_assert, *this);
+
+ if(m_failed)
+ addFailedAssert(m_at);
+ } else if(m_failed) {
+ failed_out_of_a_testing_context(*this);
+ }
+
+ return m_failed && isDebuggerActive() && !getContextOptions()->no_breaks &&
+ (g_cs->currentTest == nullptr || !g_cs->currentTest->m_no_breaks); // break into debugger
+ }
+
+ void ResultBuilder::react() const {
+ if(m_failed && checkIfShouldThrow(m_at))
+ throwException();
+ }
+
+ void failed_out_of_a_testing_context(const AssertData& ad) {
+ if(g_cs->ah)
+ g_cs->ah(ad);
+ else
+ std::abort();
+ }
+
+ bool decomp_assert(assertType::Enum at, const char* file, int line, const char* expr,
+ const Result& result) {
+ bool failed = !result.m_passed;
+
+ // ###################################################################################
+ // 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(result.m_decomp);
+ DOCTEST_ASSERT_IN_TESTS(result.m_decomp);
+ return !failed;
+ }
+
+ MessageBuilder::MessageBuilder(const char* file, int line, assertType::Enum severity) {
+ m_stream = tlssPush();
+ m_file = file;
+ m_line = line;
+ m_severity = severity;
+ }
+
+ MessageBuilder::~MessageBuilder() {
+ if (!logged)
+ tlssPop();
+ }
+
+ DOCTEST_DEFINE_INTERFACE(IExceptionTranslator)
+
+ bool MessageBuilder::log() {
+ if (!logged) {
+ m_string = tlssPop();
+ logged = true;
+ }
+
+ DOCTEST_ITERATE_THROUGH_REPORTERS(log_message, *this);
+
+ const bool isWarn = m_severity & assertType::is_warn;
+
+ // warn is just a message in this context so we don't treat it as an assert
+ if(!isWarn) {
+ addAssert(m_severity);
+ addFailedAssert(m_severity);
+ }
+
+ 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();
+ }
+} // namespace detail
+namespace {
+ using namespace detail;
+
+ // clang-format off
+
+// =================================================================================================
+// The following code has been taken verbatim from Catch2/include/internal/catch_xmlwriter.h/cpp
+// This is done so cherry-picking bug fixes is trivial - even the style/formatting is untouched.
+// =================================================================================================
+
+ class XmlEncode {
+ public:
+ enum ForWhat { ForTextNodes, ForAttributes };
+
+ XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes );
+
+ void encodeTo( std::ostream& os ) const;
+
+ friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode );
+
+ private:
+ std::string m_str;
+ ForWhat m_forWhat;
+ };
+
+ class XmlWriter {
+ public:
+
+ class ScopedElement {
+ public:
+ ScopedElement( XmlWriter* writer );
+
+ ScopedElement( ScopedElement&& other ) DOCTEST_NOEXCEPT;
+ ScopedElement& operator=( ScopedElement&& other ) DOCTEST_NOEXCEPT;
+
+ ~ScopedElement();
+
+ ScopedElement& writeText( std::string const& text, bool indent = true );
+
+ template<typename T>
+ ScopedElement& writeAttribute( std::string const& name, T const& attribute ) {
+ m_writer->writeAttribute( name, attribute );
+ return *this;
+ }
+
+ private:
+ mutable XmlWriter* m_writer = nullptr;
+ };
+
+#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;
+ XmlWriter& operator=( XmlWriter const& ) = delete;
+
+ XmlWriter& startElement( std::string const& name );
+
+ ScopedElement scopedElement( std::string const& name );
+
+ XmlWriter& endElement();
+
+ XmlWriter& writeAttribute( std::string const& name, std::string const& attribute );
+
+ XmlWriter& writeAttribute( std::string const& name, const char* attribute );
+
+ XmlWriter& writeAttribute( std::string const& name, bool attribute );
+
+ template<typename T>
+ XmlWriter& writeAttribute( std::string const& name, T const& attribute ) {
+ std::stringstream rss;
+ rss << attribute;
+ return writeAttribute( name, rss.str() );
+ }
+
+ XmlWriter& writeText( std::string const& text, bool indent = true );
+
+ //XmlWriter& writeComment( std::string const& text );
+
+ //void writeStylesheetRef( std::string const& url );
+
+ //XmlWriter& writeBlankLine();
+
+ void ensureTagClosed();
+
+ void writeDeclaration();
+
+ private:
+
+ void newlineIfNecessary();
+
+ bool m_tagIsOpen = false;
+ bool m_needsNewline = false;
+ std::vector<std::string> m_tags;
+ std::string m_indent;
+ std::ostream& m_os;
+ };
+
+// =================================================================================================
+// The following code has been taken verbatim from Catch2/include/internal/catch_xmlwriter.h/cpp
+// This is done so cherry-picking bug fixes is trivial - even the style/formatting is untouched.
+// =================================================================================================
+
+using uchar = unsigned char;
+
+namespace {
+
+ size_t trailingBytes(unsigned char c) {
+ if ((c & 0xE0) == 0xC0) {
+ return 2;
+ }
+ if ((c & 0xF0) == 0xE0) {
+ return 3;
+ }
+ if ((c & 0xF8) == 0xF0) {
+ return 4;
+ }
+ DOCTEST_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered");
+ }
+
+ uint32_t headerValue(unsigned char c) {
+ if ((c & 0xE0) == 0xC0) {
+ return c & 0x1F;
+ }
+ if ((c & 0xF0) == 0xE0) {
+ return c & 0x0F;
+ }
+ if ((c & 0xF8) == 0xF0) {
+ return c & 0x07;
+ }
+ DOCTEST_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered");
+ }
+
+ void hexEscapeChar(std::ostream& os, unsigned char c) {
+ std::ios_base::fmtflags f(os.flags());
+ os << "\\x"
+ << std::uppercase << std::hex << std::setfill('0') << std::setw(2)
+ << static_cast<int>(c);
+ os.flags(f);
+ }
+
+} // anonymous namespace
+
+ XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat )
+ : m_str( str ),
+ m_forWhat( forWhat )
+ {}
+
+ void XmlEncode::encodeTo( std::ostream& os ) const {
+ // Apostrophe escaping not necessary if we always use " to write attributes
+ // (see: https://www.w3.org/TR/xml/#syntax)
+
+ for( std::size_t idx = 0; idx < m_str.size(); ++ idx ) {
+ uchar c = m_str[idx];
+ switch (c) {
+ case '<': os << "&lt;"; break;
+ case '&': os << "&amp;"; break;
+
+ case '>':
+ // See: https://www.w3.org/TR/xml/#syntax
+ if (idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']')
+ os << "&gt;";
+ else
+ os << c;
+ break;
+
+ case '\"':
+ if (m_forWhat == ForAttributes)
+ os << "&quot;";
+ else
+ os << c;
+ break;
+
+ default:
+ // Check for control characters and invalid utf-8
+
+ // Escape control characters in standard ascii
+ // see https://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0
+ if (c < 0x09 || (c > 0x0D && c < 0x20) || c == 0x7F) {
+ hexEscapeChar(os, c);
+ break;
+ }
+
+ // Plain ASCII: Write it to stream
+ if (c < 0x7F) {
+ os << c;
+ break;
+ }
+
+ // UTF-8 territory
+ // Check if the encoding is valid and if it is not, hex escape bytes.
+ // Important: We do not check the exact decoded values for validity, only the encoding format
+ // First check that this bytes is a valid lead byte:
+ // This means that it is not encoded as 1111 1XXX
+ // Or as 10XX XXXX
+ if (c < 0xC0 ||
+ c >= 0xF8) {
+ hexEscapeChar(os, c);
+ break;
+ }
+
+ auto encBytes = trailingBytes(c);
+ // Are there enough bytes left to avoid accessing out-of-bounds memory?
+ if (idx + encBytes - 1 >= m_str.size()) {
+ hexEscapeChar(os, c);
+ break;
+ }
+ // The header is valid, check data
+ // The next encBytes bytes must together be a valid utf-8
+ // This means: bitpattern 10XX XXXX and the extracted value is sane (ish)
+ bool valid = true;
+ uint32_t value = headerValue(c);
+ for (std::size_t n = 1; n < encBytes; ++n) {
+ uchar nc = m_str[idx + n];
+ valid &= ((nc & 0xC0) == 0x80);
+ value = (value << 6) | (nc & 0x3F);
+ }
+
+ if (
+ // Wrong bit pattern of following bytes
+ (!valid) ||
+ // Overlong encodings
+ (value < 0x80) ||
+ ( value < 0x800 && encBytes > 2) || // removed "0x80 <= value &&" because redundant
+ (0x800 < value && value < 0x10000 && encBytes > 3) ||
+ // Encoded value out of range
+ (value >= 0x110000)
+ ) {
+ hexEscapeChar(os, c);
+ break;
+ }
+
+ // If we got here, this is in fact a valid(ish) utf-8 sequence
+ for (std::size_t n = 0; n < encBytes; ++n) {
+ os << m_str[idx + n];
+ }
+ idx += encBytes - 1;
+ break;
+ }
+ }
+ }
+
+ std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) {
+ xmlEncode.encodeTo( os );
+ return os;
+ }
+
+ XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer )
+ : m_writer( writer )
+ {}
+
+ XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) DOCTEST_NOEXCEPT
+ : m_writer( other.m_writer ){
+ other.m_writer = nullptr;
+ }
+ XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) DOCTEST_NOEXCEPT {
+ if ( m_writer ) {
+ m_writer->endElement();
+ }
+ m_writer = other.m_writer;
+ other.m_writer = nullptr;
+ return *this;
+ }
+
+
+ XmlWriter::ScopedElement::~ScopedElement() {
+ if( m_writer )
+ m_writer->endElement();
+ }
+
+ XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, bool indent ) {
+ m_writer->writeText( text, indent );
+ return *this;
+ }
+
+ XmlWriter::XmlWriter( std::ostream& os ) : m_os( os )
+ {
+ // writeDeclaration(); // called explicitly by the reporters that use the writer class - see issue #627
+ }
+
+ XmlWriter::~XmlWriter() {
+ while( !m_tags.empty() )
+ endElement();
+ }
+
+ XmlWriter& XmlWriter::startElement( std::string const& name ) {
+ ensureTagClosed();
+ newlineIfNecessary();
+ m_os << m_indent << '<' << name;
+ m_tags.push_back( name );
+ m_indent += " ";
+ m_tagIsOpen = true;
+ return *this;
+ }
+
+ XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name ) {
+ ScopedElement scoped( this );
+ startElement( name );
+ return scoped;
+ }
+
+ XmlWriter& XmlWriter::endElement() {
+ newlineIfNecessary();
+ m_indent = m_indent.substr( 0, m_indent.size()-2 );
+ if( m_tagIsOpen ) {
+ m_os << "/>";
+ m_tagIsOpen = false;
+ }
+ else {
+ m_os << m_indent << "</" << m_tags.back() << ">";
+ }
+ m_os << std::endl;
+ m_tags.pop_back();
+ return *this;
+ }
+
+ XmlWriter& XmlWriter::writeAttribute( std::string const& name, std::string const& attribute ) {
+ if( !name.empty() && !attribute.empty() )
+ m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"';
+ return *this;
+ }
+
+ XmlWriter& XmlWriter::writeAttribute( std::string const& name, const char* attribute ) {
+ if( !name.empty() && attribute && attribute[0] != '\0' )
+ m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"';
+ return *this;
+ }
+
+ XmlWriter& XmlWriter::writeAttribute( std::string const& name, bool attribute ) {
+ m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"';
+ return *this;
+ }
+
+ XmlWriter& XmlWriter::writeText( std::string const& text, bool indent ) {
+ if( !text.empty() ){
+ bool tagWasOpen = m_tagIsOpen;
+ ensureTagClosed();
+ if( tagWasOpen && indent )
+ m_os << m_indent;
+ m_os << XmlEncode( text );
+ m_needsNewline = true;
+ }
+ return *this;
+ }
+
+ //XmlWriter& XmlWriter::writeComment( std::string const& text ) {
+ // ensureTagClosed();
+ // m_os << m_indent << "<!--" << text << "-->";
+ // m_needsNewline = true;
+ // return *this;
+ //}
+
+ //void XmlWriter::writeStylesheetRef( std::string const& url ) {
+ // m_os << "<?xml-stylesheet type=\"text/xsl\" href=\"" << url << "\"?>\n";
+ //}
+
+ //XmlWriter& XmlWriter::writeBlankLine() {
+ // ensureTagClosed();
+ // m_os << '\n';
+ // return *this;
+ //}
+
+ void XmlWriter::ensureTagClosed() {
+ if( m_tagIsOpen ) {
+ m_os << ">" << std::endl;
+ m_tagIsOpen = false;
+ }
+ }
+
+ void XmlWriter::writeDeclaration() {
+ m_os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+ }
+
+ void XmlWriter::newlineIfNecessary() {
+ if( m_needsNewline ) {
+ m_os << std::endl;
+ m_needsNewline = false;
+ }
+ }
+
+// =================================================================================================
+// End of copy-pasted code from Catch
+// =================================================================================================
+
+ // clang-format on
+
+ struct XmlReporter : public IReporter
+ {
+ XmlWriter xml;
+ DOCTEST_DECLARE_MUTEX(mutex)
+
+ // caching pointers/references to objects of these types - safe to do
+ const ContextOptions& opt;
+ const TestCaseData* tc = nullptr;
+
+ XmlReporter(const ContextOptions& co)
+ : xml(*co.cout)
+ , opt(co) {}
+
+ void log_contexts() {
+ int num_contexts = get_num_active_contexts();
+ if(num_contexts) {
+ auto contexts = get_active_contexts();
+ std::stringstream ss;
+ for(int i = 0; i < num_contexts; ++i) {
+ contexts[i]->stringify(&ss);
+ xml.scopedElement("Info").writeText(ss.str());
+ ss.str("");
+ }
+ }
+ }
+
+ unsigned line(unsigned l) const { return opt.no_line_numbers ? 0 : l; }
+
+ void test_case_start_impl(const TestCaseData& in) {
+ bool open_ts_tag = false;
+ if(tc != nullptr) { // we have already opened a test suite
+ if(std::strcmp(tc->m_test_suite, in.m_test_suite) != 0) {
+ xml.endElement();
+ open_ts_tag = true;
+ }
+ }
+ else {
+ open_ts_tag = true; // first test case ==> first test suite
+ }
+
+ if(open_ts_tag) {
+ xml.startElement("TestSuite");
+ xml.writeAttribute("name", in.m_test_suite);
+ }
+
+ tc = &in;
+ xml.startElement("TestCase")
+ .writeAttribute("name", in.m_name)
+ .writeAttribute("filename", skipPathFromFilename(in.m_file.c_str()))
+ .writeAttribute("line", line(in.m_line))
+ .writeAttribute("description", in.m_description);
+
+ if(Approx(in.m_timeout) != 0)
+ xml.writeAttribute("timeout", in.m_timeout);
+ if(in.m_may_fail)
+ xml.writeAttribute("may_fail", true);
+ if(in.m_should_fail)
+ xml.writeAttribute("should_fail", true);
+ }
+
+ // =========================================================================================
+ // WHAT FOLLOWS ARE OVERRIDES OF THE VIRTUAL METHODS OF THE REPORTER INTERFACE
+ // =========================================================================================
+
+ void report_query(const QueryData& in) override {
+ test_run_start();
+ if(opt.list_reporters) {
+ for(auto& curr : getListeners())
+ xml.scopedElement("Listener")
+ .writeAttribute("priority", curr.first.first)
+ .writeAttribute("name", curr.first.second);
+ for(auto& curr : getReporters())
+ xml.scopedElement("Reporter")
+ .writeAttribute("priority", curr.first.first)
+ .writeAttribute("name", curr.first.second);
+ } else if(opt.count || opt.list_test_cases) {
+ for(unsigned i = 0; i < in.num_data; ++i) {
+ 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("skipped", in.data[i]->m_skip);
+ }
+ xml.scopedElement("OverallResultsTestCases")
+ .writeAttribute("unskipped", in.run_stats->numTestCasesPassingFilters);
+ } else if(opt.list_test_suites) {
+ for(unsigned i = 0; i < in.num_data; ++i)
+ xml.scopedElement("TestSuite").writeAttribute("name", in.data[i]->m_test_suite);
+ xml.scopedElement("OverallResultsTestCases")
+ .writeAttribute("unskipped", in.run_stats->numTestCasesPassingFilters);
+ xml.scopedElement("OverallResultsTestSuites")
+ .writeAttribute("unskipped", in.run_stats->numTestSuitesPassingFilters);
+ }
+ xml.endElement();
+ }
+
+ 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
+ if(binary_name.rfind(".exe") != std::string::npos)
+ binary_name = binary_name.substr(0, binary_name.length() - 4);
+#endif // DOCTEST_PLATFORM_WINDOWS
+
+ xml.startElement("doctest").writeAttribute("binary", binary_name);
+ if(opt.no_version == false)
+ xml.writeAttribute("version", DOCTEST_VERSION_STR);
+
+ // only the consequential ones (TODO: filters)
+ xml.scopedElement("Options")
+ .writeAttribute("order_by", opt.order_by.c_str())
+ .writeAttribute("rand_seed", opt.rand_seed)
+ .writeAttribute("first", opt.first)
+ .writeAttribute("last", opt.last)
+ .writeAttribute("abort_after", opt.abort_after)
+ .writeAttribute("subcase_filter_levels", opt.subcase_filter_levels)
+ .writeAttribute("case_sensitive", opt.case_sensitive)
+ .writeAttribute("no_throw", opt.no_throw)
+ .writeAttribute("no_skip", opt.no_skip);
+ }
+
+ void test_run_end(const TestRunStats& p) override {
+ if(tc) // the TestSuite tag - only if there has been at least 1 test case
+ xml.endElement();
+
+ xml.scopedElement("OverallResultsAsserts")
+ .writeAttribute("successes", p.numAsserts - p.numAssertsFailed)
+ .writeAttribute("failures", p.numAssertsFailed);
+
+ xml.startElement("OverallResultsTestCases")
+ .writeAttribute("successes",
+ p.numTestCasesPassingFilters - p.numTestCasesFailed)
+ .writeAttribute("failures", p.numTestCasesFailed);
+ if(opt.no_skipped_summary == false)
+ xml.writeAttribute("skipped", p.numTestCases - p.numTestCasesPassingFilters);
+ xml.endElement();
+
+ xml.endElement();
+ }
+
+ void test_case_start(const TestCaseData& in) override {
+ 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("test_case_success", st.testCaseSuccess);
+ if(opt.duration)
+ xml.writeAttribute("duration", st.seconds);
+ if(tc->m_expected_failures)
+ xml.writeAttribute("expected_failures", tc->m_expected_failures);
+ xml.endElement();
+
+ xml.endElement();
+ }
+
+ void test_case_exception(const TestCaseException& e) override {
+ DOCTEST_LOCK_MUTEX(mutex)
+
+ xml.scopedElement("Exception")
+ .writeAttribute("crash", e.is_crash)
+ .writeText(e.error_string.c_str());
+ }
+
+ void subcase_start(const SubcaseSignature& in) override {
+ xml.startElement("SubCase")
+ .writeAttribute("name", in.m_name)
+ .writeAttribute("filename", skipPathFromFilename(in.m_file))
+ .writeAttribute("line", line(in.m_line));
+ xml.ensureTagClosed();
+ }
+
+ void subcase_end() override { xml.endElement(); }
+
+ void log_assert(const AssertData& rb) override {
+ if(!rb.m_failed && !opt.success)
+ return;
+
+ DOCTEST_LOCK_MUTEX(mutex)
+
+ xml.startElement("Expression")
+ .writeAttribute("success", !rb.m_failed)
+ .writeAttribute("type", assertString(rb.m_at))
+ .writeAttribute("filename", skipPathFromFilename(rb.m_file))
+ .writeAttribute("line", line(rb.m_line));
+
+ xml.scopedElement("Original").writeText(rb.m_expr);
+
+ if(rb.m_threw)
+ xml.scopedElement("Exception").writeText(rb.m_exception.c_str());
+
+ 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.c_str());
+ if((rb.m_at & assertType::is_normal) && !rb.m_threw)
+ xml.scopedElement("Expanded").writeText(rb.m_decomp.c_str());
+
+ log_contexts();
+
+ xml.endElement();
+ }
+
+ void log_message(const MessageData& mb) override {
+ DOCTEST_LOCK_MUTEX(mutex)
+
+ xml.startElement("Message")
+ .writeAttribute("type", failureString(mb.m_severity))
+ .writeAttribute("filename", skipPathFromFilename(mb.m_file))
+ .writeAttribute("line", line(mb.m_line));
+
+ xml.scopedElement("Text").writeText(mb.m_string.c_str());
+
+ log_contexts();
+
+ xml.endElement();
+ }
+
+ void test_case_skipped(const TestCaseData& in) override {
+ if(opt.no_skipped_summary == false) {
+ test_case_start_impl(in);
+ xml.writeAttribute("skipped", "true");
+ xml.endElement();
+ }
+ }
+ };
+
+ DOCTEST_REGISTER_REPORTER("xml", 0, XmlReporter);
+
+ void fulltext_log_assert_to_stream(std::ostream& s, const AssertData& rb) {
+ if((rb.m_at & (assertType::is_throws_as | assertType::is_throws_with)) ==
+ 0) //!OCLINT bitwise operator in conditional
+ s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << " ) "
+ << Color::None;
+
+ if(rb.m_at & assertType::is_throws) { //!OCLINT bitwise operator in conditional
+ s << (rb.m_threw ? "threw as expected!" : "did NOT throw at all!") << "\n";
+ } 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.c_str()
+ << "\", " << rb.m_exception_type << " ) " << Color::None;
+ if(rb.m_threw) {
+ if(!rb.m_failed) {
+ s << "threw as expected!\n";
+ } else {
+ s << "threw a DIFFERENT exception! (contents: " << rb.m_exception << ")\n";
+ }
+ } else {
+ s << "did NOT throw at all!\n";
+ }
+ } else if(rb.m_at &
+ assertType::is_throws_as) { //!OCLINT bitwise operator in conditional
+ s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", "
+ << rb.m_exception_type << " ) " << Color::None
+ << (rb.m_threw ? (rb.m_threw_as ? "threw as expected!" :
+ "threw a DIFFERENT exception: ") :
+ "did NOT throw at all!")
+ << Color::Cyan << rb.m_exception << "\n";
+ } 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.c_str()
+ << "\" ) " << Color::None
+ << (rb.m_threw ? (!rb.m_failed ? "threw as expected!" :
+ "threw a DIFFERENT exception: ") :
+ "did NOT throw at all!")
+ << Color::Cyan << rb.m_exception << "\n";
+ } else if(rb.m_at & assertType::is_nothrow) { //!OCLINT bitwise operator in conditional
+ s << (rb.m_threw ? "THREW exception: " : "didn't throw!") << Color::Cyan
+ << rb.m_exception << "\n";
+ } else {
+ s << (rb.m_threw ? "THREW exception: " :
+ (!rb.m_failed ? "is correct!\n" : "is NOT correct!\n"));
+ if(rb.m_threw)
+ s << rb.m_exception << "\n";
+ else
+ s << " values: " << assertString(rb.m_at) << "( " << rb.m_decomp << " )\n";
+ }
+ }
+
+ // TODO:
+ // - log_message()
+ // - respond to queries
+ // - honor remaining options
+ // - more attributes in tags
+ struct JUnitReporter : public IReporter
+ {
+ XmlWriter xml;
+ DOCTEST_DECLARE_MUTEX(mutex)
+ Timer timer;
+ std::vector<String> deepestSubcaseStackNames;
+
+ struct JUnitTestCaseData
+ {
+ 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)
+ time_t rawtime;
+ std::time(&rawtime);
+ auto const timeStampSize = sizeof("2017-01-16T17:06:45Z");
+
+ 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);
+ return std::string(timeStamp);
+ }
+
+ struct JUnitTestMessage
+ {
+ JUnitTestMessage(const std::string& _message, const std::string& _type, const std::string& _details)
+ : message(_message), type(_type), details(_details) {}
+
+ JUnitTestMessage(const std::string& _message, const std::string& _details)
+ : message(_message), type(), details(_details) {}
+
+ std::string message, type, details;
+ };
+
+ struct JUnitTestCase
+ {
+ JUnitTestCase(const std::string& _classname, const std::string& _name)
+ : classname(_classname), name(_name), time(0), failures() {}
+
+ std::string classname, name;
+ double time;
+ std::vector<JUnitTestMessage> failures, errors;
+ };
+
+ void add(const std::string& classname, const std::string& name) {
+ testcases.emplace_back(classname, name);
+ }
+
+ void appendSubcaseNamesToLastTestcase(std::vector<String> nameStack) {
+ for(auto& curr: nameStack)
+ if(curr.size())
+ testcases.back().name += std::string("/") + curr.c_str();
+ }
+
+ void addTime(double time) {
+ if(time < 1e-4)
+ time = 0;
+ testcases.back().time = time;
+ totalSeconds += time;
+ }
+
+ void addFailure(const std::string& message, const std::string& type, const std::string& details) {
+ testcases.back().failures.emplace_back(message, type, details);
+ ++totalFailures;
+ }
+
+ void addError(const std::string& message, const std::string& details) {
+ testcases.back().errors.emplace_back(message, details);
+ ++totalErrors;
+ }
+
+ std::vector<JUnitTestCase> testcases;
+ double totalSeconds = 0;
+ int totalErrors = 0, totalFailures = 0;
+ };
+
+ JUnitTestCaseData testCaseData;
+
+ // caching pointers/references to objects of these types - safe to do
+ const ContextOptions& opt;
+ const TestCaseData* tc = nullptr;
+
+ JUnitReporter(const ContextOptions& co)
+ : xml(*co.cout)
+ , opt(co) {}
+
+ unsigned line(unsigned l) const { return opt.no_line_numbers ? 0 : l; }
+
+ // =========================================================================================
+ // WHAT FOLLOWS ARE OVERRIDES OF THE VIRTUAL METHODS OF THE REPORTER INTERFACE
+ // =========================================================================================
+
+ void report_query(const QueryData&) override {
+ xml.writeDeclaration();
+ }
+
+ 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
+ std::string binary_name = skipPathFromFilename(opt.binary_name.c_str());
+#ifdef DOCTEST_PLATFORM_WINDOWS
+ if(binary_name.rfind(".exe") != std::string::npos)
+ binary_name = binary_name.substr(0, binary_name.length() - 4);
+#endif // DOCTEST_PLATFORM_WINDOWS
+ xml.startElement("testsuites");
+ xml.startElement("testsuite").writeAttribute("name", binary_name)
+ .writeAttribute("errors", testCaseData.totalErrors)
+ .writeAttribute("failures", testCaseData.totalFailures)
+ .writeAttribute("tests", p.numAsserts);
+ if(opt.no_time_in_output == false) {
+ xml.writeAttribute("time", testCaseData.totalSeconds);
+ xml.writeAttribute("timestamp", JUnitTestCaseData::getCurrentTimestamp());
+ }
+ if(opt.no_version == false)
+ xml.writeAttribute("doctest_version", DOCTEST_VERSION_STR);
+
+ for(const auto& testCase : testCaseData.testcases) {
+ xml.startElement("testcase")
+ .writeAttribute("classname", testCase.classname)
+ .writeAttribute("name", testCase.name);
+ if(opt.no_time_in_output == false)
+ xml.writeAttribute("time", testCase.time);
+ // This is not ideal, but it should be enough to mimic gtest's junit output.
+ xml.writeAttribute("status", "run");
+
+ for(const auto& failure : testCase.failures) {
+ xml.scopedElement("failure")
+ .writeAttribute("message", failure.message)
+ .writeAttribute("type", failure.type)
+ .writeText(failure.details, false);
+ }
+
+ for(const auto& error : testCase.errors) {
+ xml.scopedElement("error")
+ .writeAttribute("message", error.message)
+ .writeText(error.details);
+ }
+
+ xml.endElement();
+ }
+ xml.endElement();
+ xml.endElement();
+ }
+
+ void test_case_start(const TestCaseData& in) override {
+ testCaseData.add(skipPathFromFilename(in.m_file.c_str()), in.m_name);
+ timer.start();
+ }
+
+ void test_case_reenter(const TestCaseData& in) override {
+ testCaseData.addTime(timer.getElapsedSeconds());
+ testCaseData.appendSubcaseNamesToLastTestcase(deepestSubcaseStackNames);
+ deepestSubcaseStackNames.clear();
+
+ timer.start();
+ testCaseData.add(skipPathFromFilename(in.m_file.c_str()), in.m_name);
+ }
+
+ void test_case_end(const CurrentTestCaseStats&) override {
+ testCaseData.addTime(timer.getElapsedSeconds());
+ testCaseData.appendSubcaseNamesToLastTestcase(deepestSubcaseStackNames);
+ deepestSubcaseStackNames.clear();
+ }
+
+ void test_case_exception(const TestCaseException& e) override {
+ DOCTEST_LOCK_MUTEX(mutex)
+ testCaseData.addError("exception", e.error_string.c_str());
+ }
+
+ void subcase_start(const SubcaseSignature& in) override {
+ deepestSubcaseStackNames.push_back(in.m_name);
+ }
+
+ void subcase_end() override {}
+
+ void log_assert(const AssertData& rb) override {
+ if(!rb.m_failed) // report only failures & ignore the `success` option
+ return;
+
+ 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& mb) override {
+ if(mb.m_severity & assertType::is_warn) // report only failures
+ return;
+
+ DOCTEST_LOCK_MUTEX(mutex)
+
+ std::ostringstream os;
+ os << skipPathFromFilename(mb.m_file) << (opt.gnu_file_line ? ":" : "(")
+ << line(mb.m_line) << (opt.gnu_file_line ? ":" : "):") << std::endl;
+
+ os << mb.m_string.c_str() << "\n";
+ log_contexts(os);
+
+ testCaseData.addFailure(mb.m_string.c_str(),
+ mb.m_severity & assertType::is_check ? "FAIL_CHECK" : "FAIL", os.str());
+ }
+
+ 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);
+
+ struct Whitespace
+ {
+ int nrSpaces;
+ explicit Whitespace(int nr)
+ : nrSpaces(nr) {}
+ };
+
+ std::ostream& operator<<(std::ostream& out, const Whitespace& ws) {
+ if(ws.nrSpaces != 0)
+ out << std::setw(ws.nrSpaces) << ' ';
+ return out;
+ }
+
+ struct ConsoleReporter : public IReporter
+ {
+ std::ostream& s;
+ bool hasLoggedCurrentTestStart;
+ std::vector<SubcaseSignature> subcasesStack;
+ size_t currentSubcaseLevel;
+ DOCTEST_DECLARE_MUTEX(mutex)
+
+ // caching pointers/references to objects of these types - safe to do
+ const ContextOptions& opt;
+ const TestCaseData* tc;
+
+ ConsoleReporter(const ContextOptions& co)
+ : s(*co.cout)
+ , opt(co) {}
+
+ ConsoleReporter(const ContextOptions& co, std::ostream& ostr)
+ : s(ostr)
+ , opt(co) {}
+
+ // =========================================================================================
+ // WHAT FOLLOWS ARE HELPERS USED BY THE OVERRIDES OF THE VIRTUAL METHODS OF THE INTERFACE
+ // =========================================================================================
+
+ void separator_to_stream() {
+ s << Color::Yellow
+ << "==============================================================================="
+ "\n";
+ }
+
+ const char* getSuccessOrFailString(bool success, assertType::Enum at,
+ const char* success_str) {
+ if(success)
+ return success_str;
+ return failureString(at);
+ }
+
+ Color::Enum getSuccessOrFailColor(bool success, assertType::Enum at) {
+ return success ? Color::BrightGreen :
+ (at & assertType::is_warn) ? Color::Yellow : Color::Red;
+ }
+
+ void successOrFailColoredStringToStream(bool success, assertType::Enum at,
+ const char* success_str = "SUCCESS") {
+ s << getSuccessOrFailColor(success, at)
+ << getSuccessOrFailString(success, at, success_str) << ": ";
+ }
+
+ void log_contexts() {
+ int num_contexts = get_num_active_contexts();
+ if(num_contexts) {
+ auto contexts = get_active_contexts();
+
+ s << Color::None << " logged: ";
+ for(int i = 0; i < num_contexts; ++i) {
+ s << (i == 0 ? "" : " ");
+ contexts[i]->stringify(&s);
+ s << "\n";
+ }
+ }
+
+ s << "\n";
+ }
+
+ // this was requested to be made virtual so users could override it
+ virtual void file_line_to_stream(const char* file, int line,
+ const char* tail = "") {
+ s << Color::LightGrey << skipPathFromFilename(file) << (opt.gnu_file_line ? ":" : "(")
+ << (opt.no_line_numbers ? 0 : line) // 0 or the real num depending on the option
+ << (opt.gnu_file_line ? ":" : "):") << tail;
+ }
+
+ void logTestStart() {
+ if(hasLoggedCurrentTestStart)
+ return;
+
+ separator_to_stream();
+ file_line_to_stream(tc->m_file.c_str(), tc->m_line, "\n");
+ if(tc->m_description)
+ s << Color::Yellow << "DESCRIPTION: " << Color::None << tc->m_description << "\n";
+ if(tc->m_test_suite && tc->m_test_suite[0] != '\0')
+ s << Color::Yellow << "TEST SUITE: " << Color::None << tc->m_test_suite << "\n";
+ if(strncmp(tc->m_name, " Scenario:", 11) != 0)
+ s << Color::Yellow << "TEST CASE: ";
+ s << Color::None << tc->m_name << "\n";
+
+ for(size_t i = 0; i < currentSubcaseLevel; ++i) {
+ if(subcasesStack[i].m_name[0] != '\0')
+ s << " " << subcasesStack[i].m_name << "\n";
+ }
+
+ if(currentSubcaseLevel != subcasesStack.size()) {
+ s << Color::Yellow << "\nDEEPEST SUBCASE STACK REACHED (DIFFERENT FROM THE CURRENT ONE):\n" << Color::None;
+ for(size_t i = 0; i < subcasesStack.size(); ++i) {
+ if(subcasesStack[i].m_name[0] != '\0')
+ s << " " << subcasesStack[i].m_name << "\n";
+ }
+ }
+
+ s << "\n";
+
+ hasLoggedCurrentTestStart = true;
+ }
+
+ void printVersion() {
+ if(opt.no_version == false)
+ s << Color::Cyan << "[doctest] " << Color::None << "doctest version is \""
+ << DOCTEST_VERSION_STR << "\"\n";
+ }
+
+ void printIntro() {
+ if(opt.no_intro == false) {
+ printVersion();
+ s << Color::Cyan << "[doctest] " << Color::None
+ << "run with \"--" DOCTEST_OPTIONS_PREFIX_DISPLAY "help\" for options\n";
+ }
+ }
+
+ void printHelp() {
+ int sizePrefixDisplay = static_cast<int>(strlen(DOCTEST_OPTIONS_PREFIX_DISPLAY));
+ printVersion();
+ // clang-format off
+ s << Color::Cyan << "[doctest]\n" << Color::None;
+ s << Color::Cyan << "[doctest] " << Color::None;
+ s << "boolean values: \"1/on/yes/true\" or \"0/off/no/false\"\n";
+ s << Color::Cyan << "[doctest] " << Color::None;
+ s << "filter values: \"str1,str2,str3\" (comma separated strings)\n";
+ s << Color::Cyan << "[doctest]\n" << Color::None;
+ s << Color::Cyan << "[doctest] " << Color::None;
+ s << "filters use wildcards for matching strings\n";
+ s << Color::Cyan << "[doctest] " << Color::None;
+ s << "something passes a filter if any of the strings in a filter matches\n";
+#ifndef DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS
+ s << Color::Cyan << "[doctest]\n" << Color::None;
+ s << Color::Cyan << "[doctest] " << Color::None;
+ s << "ALL FLAGS, OPTIONS AND FILTERS ALSO AVAILABLE WITH A \"" DOCTEST_CONFIG_OPTIONS_PREFIX "\" PREFIX!!!\n";
+#endif
+ s << Color::Cyan << "[doctest]\n" << Color::None;
+ s << Color::Cyan << "[doctest] " << Color::None;
+ s << "Query flags - the program quits after them. Available:\n\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "?, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "help, -" DOCTEST_OPTIONS_PREFIX_DISPLAY "h "
+ << Whitespace(sizePrefixDisplay*0) << "prints this message\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "v, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "version "
+ << Whitespace(sizePrefixDisplay*1) << "prints the version\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "c, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "count "
+ << Whitespace(sizePrefixDisplay*1) << "prints the number of matching tests\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ltc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "list-test-cases "
+ << Whitespace(sizePrefixDisplay*1) << "lists all matching tests by name\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "lts, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "list-test-suites "
+ << Whitespace(sizePrefixDisplay*1) << "lists all matching test suites\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "lr, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "list-reporters "
+ << Whitespace(sizePrefixDisplay*1) << "lists all registered reporters\n\n";
+ // ================================================================================== << 79
+ s << Color::Cyan << "[doctest] " << Color::None;
+ s << "The available <int>/<string> options/filters are:\n\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "tc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "test-case=<filters> "
+ << Whitespace(sizePrefixDisplay*1) << "filters tests by their name\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "tce, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "test-case-exclude=<filters> "
+ << Whitespace(sizePrefixDisplay*1) << "filters OUT tests by their name\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "sf, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "source-file=<filters> "
+ << Whitespace(sizePrefixDisplay*1) << "filters tests by their file\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "sfe, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "source-file-exclude=<filters> "
+ << Whitespace(sizePrefixDisplay*1) << "filters OUT tests by their file\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ts, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "test-suite=<filters> "
+ << Whitespace(sizePrefixDisplay*1) << "filters tests by their test suite\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "tse, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "test-suite-exclude=<filters> "
+ << Whitespace(sizePrefixDisplay*1) << "filters OUT tests by their test suite\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "sc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "subcase=<filters> "
+ << Whitespace(sizePrefixDisplay*1) << "filters subcases by their name\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "sce, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "subcase-exclude=<filters> "
+ << Whitespace(sizePrefixDisplay*1) << "filters OUT subcases by their name\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "r, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "reporters=<filters> "
+ << Whitespace(sizePrefixDisplay*1) << "reporters to use (console is default)\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "o, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "out=<string> "
+ << Whitespace(sizePrefixDisplay*1) << "output filename\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ob, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "order-by=<string> "
+ << Whitespace(sizePrefixDisplay*1) << "how the tests should be ordered\n";
+ s << Whitespace(sizePrefixDisplay*3) << " <string> - [file/suite/name/rand/none]\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "rs, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "rand-seed=<int> "
+ << Whitespace(sizePrefixDisplay*1) << "seed for random ordering\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "f, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "first=<int> "
+ << Whitespace(sizePrefixDisplay*1) << "the first test passing the filters to\n";
+ s << Whitespace(sizePrefixDisplay*3) << " execute - for range-based execution\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "l, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "last=<int> "
+ << Whitespace(sizePrefixDisplay*1) << "the last test passing the filters to\n";
+ s << Whitespace(sizePrefixDisplay*3) << " execute - for range-based execution\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "aa, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "abort-after=<int> "
+ << Whitespace(sizePrefixDisplay*1) << "stop after <int> failed assertions\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "scfl,--" DOCTEST_OPTIONS_PREFIX_DISPLAY "subcase-filter-levels=<int> "
+ << Whitespace(sizePrefixDisplay*1) << "apply filters for the first <int> levels\n";
+ s << Color::Cyan << "\n[doctest] " << Color::None;
+ s << "Bool options - can be used like flags and true is assumed. Available:\n\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "s, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "success=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "include successful assertions in output\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "cs, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "case-sensitive=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "filters being treated as case sensitive\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "e, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "exit=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "exits after the tests finish\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "d, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "duration=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "prints the time duration of each test\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "m, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "minimal=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "minimal console output (only failures)\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "q, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "quiet=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "no console output\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nt, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-throw=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "skips exceptions-related assert checks\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ne, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-exitcode=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "returns (or exits) always with success\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nr, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-run=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "skips all runtime doctest operations\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ni, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-intro=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "omit the framework intro in the output\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nv, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-version=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "omit the framework version in the output\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-colors=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "disables colors in output\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "fc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "force-colors=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "use colors even when not in a tty\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nb, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-breaks=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "disables breakpoints in debuggers\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ns, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-skip=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "don't skip test cases marked as skip\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "gfl, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "gnu-file-line=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << ":n: vs (n): for line numbers in output\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "npf, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-path-filenames=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "only filenames and no paths in output\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "spp, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "skip-path-prefixes=<p1:p2> "
+ << Whitespace(sizePrefixDisplay*1) << "whenever file paths start with this prefix, remove it from the output\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nln, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-line-numbers=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "0 instead of real line numbers in output\n";
+ // ================================================================================== << 79
+ // clang-format on
+
+ s << Color::Cyan << "\n[doctest] " << Color::None;
+ s << "for more information visit the project documentation\n\n";
+ }
+
+ void printRegisteredReporters() {
+ printVersion();
+ auto printReporters = [this] (const reporterMap& reporters, const char* type) {
+ if(reporters.size()) {
+ s << Color::Cyan << "[doctest] " << Color::None << "listing all registered " << type << "\n";
+ for(auto& curr : reporters)
+ s << "priority: " << std::setw(5) << curr.first.first
+ << " name: " << curr.first.second << "\n";
+ }
+ };
+ printReporters(getListeners(), "listeners");
+ printReporters(getReporters(), "reporters");
+ }
+
+ // =========================================================================================
+ // WHAT FOLLOWS ARE OVERRIDES OF THE VIRTUAL METHODS OF THE REPORTER INTERFACE
+ // =========================================================================================
+
+ void report_query(const QueryData& in) override {
+ if(opt.version) {
+ printVersion();
+ } else if(opt.help) {
+ printHelp();
+ } else if(opt.list_reporters) {
+ printRegisteredReporters();
+ } else if(opt.count || opt.list_test_cases) {
+ if(opt.list_test_cases) {
+ s << Color::Cyan << "[doctest] " << Color::None
+ << "listing all test case names\n";
+ separator_to_stream();
+ }
+
+ for(unsigned i = 0; i < in.num_data; ++i)
+ s << Color::None << in.data[i]->m_name << "\n";
+
+ separator_to_stream();
+
+ 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 << "listing all test suites\n";
+ separator_to_stream();
+
+ for(unsigned i = 0; i < in.num_data; ++i)
+ s << Color::None << in.data[i]->m_test_suite << "\n";
+
+ separator_to_stream();
+
+ 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";
+ }
+ }
+
+ 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(static_cast<double>(std::max(p.numTestCasesPassingFilters, static_cast<unsigned>(p.numAsserts))) + 1)));
+ auto passwidth = int(std::ceil(log10(static_cast<double>(std::max(p.numTestCasesPassingFilters - p.numTestCasesFailed, static_cast<unsigned>(p.numAsserts - p.numAssertsFailed))) + 1)));
+ auto failwidth = int(std::ceil(log10(static_cast<double>(std::max(p.numTestCasesFailed, static_cast<unsigned>(p.numAssertsFailed))) + 1)));
+ const bool anythingFailed = p.numTestCasesFailed > 0 || p.numAssertsFailed > 0;
+ s << Color::Cyan << "[doctest] " << Color::None << "test cases: " << std::setw(totwidth)
+ << p.numTestCasesPassingFilters << " | "
+ << ((p.numTestCasesPassingFilters == 0 || anythingFailed) ? Color::None :
+ Color::Green)
+ << std::setw(passwidth) << p.numTestCasesPassingFilters - p.numTestCasesFailed << " passed"
+ << Color::None << " | " << (p.numTestCasesFailed > 0 ? Color::Red : 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) << numSkipped
+ << " skipped" << Color::None;
+ }
+ s << "\n";
+ s << Color::Cyan << "[doctest] " << Color::None << "assertions: " << std::setw(totwidth)
+ << p.numAsserts << " | "
+ << ((p.numAsserts == 0 || anythingFailed) ? Color::None : Color::Green)
+ << 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)
+ << ((p.numTestCasesFailed > 0) ? "FAILURE!" : "SUCCESS!") << Color::None << std::endl;
+ }
+
+ void test_case_start(const TestCaseData& in) override {
+ hasLoggedCurrentTestStart = false;
+ tc = &in;
+ 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 != static_cast<int>(TestCaseFailureReason::AssertFailure)))
+ logTestStart();
+
+ if(opt.duration)
+ s << Color::None << std::setprecision(6) << std::fixed << st.seconds
+ << " s: " << tc->m_name << "\n";
+
+ if(st.failure_flags & TestCaseFailureReason::Timeout)
+ s << Color::Red << "Test case exceeded time limit of " << std::setprecision(6)
+ << std::fixed << tc->m_timeout << "!\n";
+
+ if(st.failure_flags & TestCaseFailureReason::ShouldHaveFailedButDidnt) {
+ s << Color::Red << "Should have failed but didn't! Marking it as failed!\n";
+ } else if(st.failure_flags & TestCaseFailureReason::ShouldHaveFailedAndDid) {
+ s << Color::Yellow << "Failed as expected so marking it as not failed\n";
+ } else if(st.failure_flags & TestCaseFailureReason::CouldHaveFailedAndDid) {
+ s << Color::Yellow << "Allowed to fail so marking it as not failed\n";
+ } else if(st.failure_flags & TestCaseFailureReason::DidntFailExactlyNumTimes) {
+ s << Color::Red << "Didn't fail exactly " << tc->m_expected_failures
+ << " times so marking it as failed!\n";
+ } else if(st.failure_flags & TestCaseFailureReason::FailedExactlyNumTimes) {
+ s << Color::Yellow << "Failed exactly " << tc->m_expected_failures
+ << " times as expected so marking it as not failed!\n";
+ }
+ if(st.failure_flags & TestCaseFailureReason::TooManyFailedAsserts) {
+ s << Color::Red << "Aborting - too many failed asserts!\n";
+ }
+ s << Color::None; // lgtm [cpp/useless-expression]
+ }
+
+ 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, " ");
+ successOrFailColoredStringToStream(false, e.is_crash ? assertType::is_require :
+ assertType::is_check);
+ s << Color::Red << (e.is_crash ? "test case CRASHED: " : "test case THREW exception: ")
+ << Color::Cyan << e.error_string << "\n";
+
+ int num_stringified_contexts = get_num_stringified_contexts();
+ if(num_stringified_contexts) {
+ auto stringified_contexts = get_stringified_contexts();
+ s << Color::None << " logged: ";
+ for(int i = num_stringified_contexts; i > 0; --i) {
+ s << (i == num_stringified_contexts ? "" : " ")
+ << stringified_contexts[i - 1] << "\n";
+ }
+ }
+ s << "\n" << Color::None;
+ }
+
+ void subcase_start(const SubcaseSignature& subc) override {
+ subcasesStack.push_back(subc);
+ ++currentSubcaseLevel;
+ hasLoggedCurrentTestStart = false;
+ }
+
+ void subcase_end() override {
+ --currentSubcaseLevel;
+ hasLoggedCurrentTestStart = false;
+ }
+
+ void log_assert(const AssertData& rb) override {
+ if((!rb.m_failed && !opt.success) || tc->m_no_output)
+ return;
+
+ DOCTEST_LOCK_MUTEX(mutex)
+
+ logTestStart();
+
+ file_line_to_stream(rb.m_file, rb.m_line, " ");
+ successOrFailColoredStringToStream(!rb.m_failed, rb.m_at);
+
+ fulltext_log_assert_to_stream(s, rb);
+
+ log_contexts();
+ }
+
+ void log_message(const MessageData& mb) override {
+ if(tc->m_no_output)
+ return;
+
+ DOCTEST_LOCK_MUTEX(mutex)
+
+ logTestStart();
+
+ file_line_to_stream(mb.m_file, mb.m_line, " ");
+ s << getSuccessOrFailColor(false, mb.m_severity)
+ << getSuccessOrFailString(mb.m_severity & assertType::is_warn, mb.m_severity,
+ "MESSAGE") << ": ";
+ s << Color::None << mb.m_string << "\n";
+ log_contexts();
+ }
+
+ void test_case_skipped(const TestCaseData&) override {}
+ };
+
+ DOCTEST_REGISTER_REPORTER("console", 0, ConsoleReporter);
+
+#ifdef DOCTEST_PLATFORM_WINDOWS
+ struct DebugOutputWindowReporter : public ConsoleReporter
+ {
+ DOCTEST_THREAD_LOCAL static std::ostringstream oss;
+
+ DebugOutputWindowReporter(const ContextOptions& co)
+ : ConsoleReporter(co, oss) {}
+
+#define DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(func, type, arg) \
+ void func(type arg) override { \
+ bool with_col = g_no_colors; \
+ g_no_colors = false; \
+ ConsoleReporter::func(arg); \
+ if(oss.tellp() != std::streampos{}) { \
+ DOCTEST_OUTPUT_DEBUG_STRING(oss.str().c_str()); \
+ oss.str(""); \
+ } \
+ g_no_colors = with_col; \
+ }
+
+ DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_run_start, DOCTEST_EMPTY, DOCTEST_EMPTY)
+ DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_run_end, const TestRunStats&, in)
+ DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_start, const TestCaseData&, in)
+ DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_reenter, const TestCaseData&, in)
+ DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_end, const CurrentTestCaseStats&, in)
+ DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_exception, const TestCaseException&, in)
+ DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(subcase_start, const SubcaseSignature&, in)
+ DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(subcase_end, DOCTEST_EMPTY, DOCTEST_EMPTY)
+ DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(log_assert, const AssertData&, in)
+ DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(log_message, const MessageData&, in)
+ DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_skipped, const TestCaseData&, in)
+ };
+
+ DOCTEST_THREAD_LOCAL std::ostringstream DebugOutputWindowReporter::oss;
+#endif // DOCTEST_PLATFORM_WINDOWS
+
+ // the implementation of parseOption()
+ bool parseOptionImpl(int argc, const char* const* argv, const char* pattern, String* value) {
+ // going from the end to the beginning and stopping on the first occurrence from the end
+ for(int i = argc; i > 0; --i) {
+ auto index = i - 1;
+ auto temp = std::strstr(argv[index], pattern);
+ if(temp && (value || strlen(temp) == strlen(pattern))) { //!OCLINT prefer early exits and continue
+ // eliminate matches in which the chars before the option are not '-'
+ bool noBadCharsFound = true;
+ auto curr = argv[index];
+ while(curr != temp) {
+ if(*curr++ != '-') {
+ noBadCharsFound = false;
+ break;
+ }
+ }
+ if(noBadCharsFound && argv[index][0] == '-') {
+ if(value) {
+ // parsing the value of an option
+ temp += strlen(pattern);
+ const unsigned len = strlen(temp);
+ if(len) {
+ *value = temp;
+ return true;
+ }
+ } else {
+ // just a flag - no value
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ // parses an option and returns the string after the '=' character
+ bool parseOption(int argc, const char* const* argv, const char* pattern, String* value = nullptr,
+ const String& defaultVal = String()) {
+ if(value)
+ *value = defaultVal;
+#ifndef DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS
+ // offset (normally 3 for "dt-") to skip prefix
+ if(parseOptionImpl(argc, argv, pattern + strlen(DOCTEST_CONFIG_OPTIONS_PREFIX), value))
+ return true;
+#endif // DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS
+ return parseOptionImpl(argc, argv, pattern, value);
+ }
+
+ // locates a flag on the command line
+ bool parseFlag(int argc, const char* const* argv, const char* pattern) {
+ return parseOption(argc, argv, pattern);
+ }
+
+ // parses a comma separated list of words after a pattern in one of the arguments in argv
+ bool parseCommaSepArgs(int argc, const char* const* argv, const char* pattern,
+ std::vector<String>& res) {
+ String filtersString;
+ if(parseOption(argc, argv, pattern, &filtersString)) {
+ // 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('\\');
+ }
+ flush();
+ return true;
+ }
+ return false;
+ }
+
+ enum optionType
+ {
+ option_bool,
+ option_int
+ };
+
+ // parses an int/bool option from the command line
+ bool parseIntOption(int argc, const char* const* argv, const char* pattern, optionType type,
+ int& res) {
+ String parsedValue;
+ if(!parseOption(argc, argv, pattern, &parsedValue))
+ return false;
+
+ 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
+
+ // if the value matches any of the positive/negative possibilities
+ 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) {
+ res = 0; //!OCLINT parameter reassignment
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+} // namespace
+
+Context::Context(int argc, const char* const* argv)
+ : p(new detail::ContextState) {
+ parseArgs(argc, argv, true);
+ if(argc)
+ p->binary_name = argv[0];
+}
+
+Context::~Context() {
+ if(g_cs == p)
+ g_cs = nullptr;
+ delete p;
+}
+
+void Context::applyCommandLine(int argc, const char* const* argv) {
+ parseArgs(argc, argv);
+ if(argc)
+ p->binary_name = argv[0];
+}
+
+// parses args
+void Context::parseArgs(int argc, const char* const* argv, bool withDefaults) {
+ using namespace detail;
+
+ // clang-format off
+ parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "source-file=", p->filters[0]);
+ parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "sf=", p->filters[0]);
+ parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "source-file-exclude=",p->filters[1]);
+ parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "sfe=", p->filters[1]);
+ parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "test-suite=", p->filters[2]);
+ parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "ts=", p->filters[2]);
+ parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "test-suite-exclude=", p->filters[3]);
+ parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "tse=", p->filters[3]);
+ parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "test-case=", p->filters[4]);
+ parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "tc=", p->filters[4]);
+ parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "test-case-exclude=", p->filters[5]);
+ parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "tce=", p->filters[5]);
+ parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "subcase=", p->filters[6]);
+ parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "sc=", p->filters[6]);
+ parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "subcase-exclude=", p->filters[7]);
+ parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "sce=", p->filters[7]);
+ parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "reporters=", p->filters[8]);
+ parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "r=", p->filters[8]);
+ // clang-format on
+
+ int intRes = 0;
+ String strRes;
+
+#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 = static_cast<bool>(intRes); \
+ else if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX name) || \
+ parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX sname)) \
+ p->var = true; \
+ else if(withDefaults) \
+ p->var = default
+
+#define DOCTEST_PARSE_INT_OPTION(name, sname, var, default) \
+ if(parseIntOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX name "=", option_int, intRes) || \
+ parseIntOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX sname "=", option_int, intRes)) \
+ p->var = intRes; \
+ else if(withDefaults) \
+ p->var = default
+
+#define DOCTEST_PARSE_STR_OPTION(name, sname, var, default) \
+ if(parseOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX name "=", &strRes, default) || \
+ parseOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX sname "=", &strRes, default) || \
+ withDefaults) \
+ p->var = strRes
+
+ // clang-format off
+ DOCTEST_PARSE_STR_OPTION("out", "o", out, "");
+ DOCTEST_PARSE_STR_OPTION("order-by", "ob", order_by, "file");
+ DOCTEST_PARSE_INT_OPTION("rand-seed", "rs", rand_seed, 0);
+
+ DOCTEST_PARSE_INT_OPTION("first", "f", first, 0);
+ DOCTEST_PARSE_INT_OPTION("last", "l", last, UINT_MAX);
+
+ DOCTEST_PARSE_INT_OPTION("abort-after", "aa", abort_after, 0);
+ DOCTEST_PARSE_INT_OPTION("subcase-filter-levels", "scfl", subcase_filter_levels, INT_MAX);
+
+ DOCTEST_PARSE_AS_BOOL_OR_FLAG("success", "s", success, false);
+ 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);
+ DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-breaks", "nb", no_breaks, false);
+ DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-skip", "ns", no_skip, false);
+ 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_STR_OPTION("strip-file-prefixes", "sfp", strip_file_prefixes, "");
+ 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
+
+ if(withDefaults) {
+ p->help = false;
+ p->version = false;
+ p->count = false;
+ p->list_test_cases = false;
+ p->list_test_suites = false;
+ p->list_reporters = false;
+ }
+ if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "help") ||
+ parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "h") ||
+ parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "?")) {
+ p->help = true;
+ p->exit = true;
+ }
+ if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "version") ||
+ parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "v")) {
+ p->version = true;
+ p->exit = true;
+ }
+ if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "count") ||
+ parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "c")) {
+ p->count = true;
+ p->exit = true;
+ }
+ if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "list-test-cases") ||
+ parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "ltc")) {
+ p->list_test_cases = true;
+ p->exit = true;
+ }
+ if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "list-test-suites") ||
+ parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "lts")) {
+ p->list_test_suites = true;
+ p->exit = true;
+ }
+ if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "list-reporters") ||
+ parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "lr")) {
+ p->list_reporters = true;
+ p->exit = true;
+ }
+}
+
+// allows the user to add procedurally to the filters from the command line
+void Context::addFilter(const char* filter, const char* value) { setOption(filter, value); }
+
+// allows the user to clear all filters from the command line
+void Context::clearFilters() {
+ for(auto& curr : p->filters)
+ curr.clear();
+}
+
+// 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());
+}
+
+// allows the user to override procedurally the string options from the command line
+void Context::setOption(const char* option, const char* value) {
+ auto argv = String("-") + option + "=" + value;
+ auto lvalue = argv.c_str();
+ parseArgs(1, &lvalue);
+}
+
+// users should query this in their main() and exit the program if true
+bool Context::shouldExit() { return p->exit; }
+
+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;
+
+ // save the old context state in case such was setup - for using asserts out of a testing context
+ auto old_cs = g_cs;
+ // this is the current contest
+ g_cs = p;
+ is_running_in_test = true;
+
+ g_no_colors = p->no_colors;
+ p->resetRunData();
+
+ std::fstream 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();
+
+ // restore context
+ g_cs = old_cs;
+ is_running_in_test = false;
+
+ // we have to free the reporters which were allocated when the run started
+ for(auto& curr : p->reporters_currently_used)
+ delete curr;
+ p->reporters_currently_used.clear();
+
+ if(p->numTestCasesFailed && !p->no_exitcode)
+ return EXIT_FAILURE;
+ return EXIT_SUCCESS;
+ };
+
+ // setup default reporter if none is given through the command line
+ if(p->filters[8].empty())
+ p->filters[8].push_back("console");
+
+ // check to see if any of the registered reporters has been selected
+ for(auto& curr : getReporters()) {
+ if(matchesAny(curr.first.second.c_str(), p->filters[8], false, p->case_sensitive))
+ p->reporters_currently_used.push_back(curr.second(*g_cs));
+ }
+
+ // TODO: check if there is nothing in reporters_currently_used
+
+ // prepend all listeners
+ for(auto& curr : getListeners())
+ p->reporters_currently_used.insert(p->reporters_currently_used.begin(), curr.second(*g_cs));
+
+#ifdef DOCTEST_PLATFORM_WINDOWS
+ if(isDebuggerActive() && p->no_debug_output == false)
+ p->reporters_currently_used.push_back(new DebugOutputWindowReporter(*g_cs));
+#endif // DOCTEST_PLATFORM_WINDOWS
+
+ // handle version, help and no_run
+ if(p->no_run || p->version || p->help || p->list_reporters) {
+ DOCTEST_ITERATE_THROUGH_REPORTERS(report_query, QueryData());
+
+ return cleanup_and_return();
+ }
+
+ std::vector<const TestCase*> testArray;
+ for(auto& curr : getRegisteredTests())
+ testArray.push_back(&curr);
+ p->numTestCases = testArray.size();
+
+ // sort the collected records
+ if(!testArray.empty()) {
+ if(p->order_by.compare("file", true) == 0) {
+ std::sort(testArray.begin(), testArray.end(), fileOrderComparator);
+ } else if(p->order_by.compare("suite", true) == 0) {
+ std::sort(testArray.begin(), testArray.end(), suiteOrderComparator);
+ } else if(p->order_by.compare("name", true) == 0) {
+ std::sort(testArray.begin(), testArray.end(), nameOrderComparator);
+ } else if(p->order_by.compare("rand", true) == 0) {
+ std::srand(p->rand_seed);
+
+ // random_shuffle implementation
+ const auto first = &testArray[0];
+ for(size_t i = testArray.size() - 1; i > 0; --i) {
+ 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
+ }
+ }
+
+ std::set<String> testSuitesPassingFilt;
+
+ bool query_mode = p->count || p->list_test_cases || p->list_test_suites;
+ std::vector<const TestCaseData*> queryResults;
+
+ if(!query_mode)
+ DOCTEST_ITERATE_THROUGH_REPORTERS(test_run_start, DOCTEST_EMPTY);
+
+ // invoke the registered functions if they match the filter criteria (or just count them)
+ for(auto& curr : testArray) {
+ const auto& tc = *curr;
+
+ bool skip_me = false;
+ if(tc.m_skip && !p->no_skip)
+ skip_me = true;
+
+ if(!matchesAny(tc.m_file.c_str(), p->filters[0], true, p->case_sensitive))
+ skip_me = true;
+ if(matchesAny(tc.m_file.c_str(), p->filters[1], false, p->case_sensitive))
+ skip_me = true;
+ if(!matchesAny(tc.m_test_suite, p->filters[2], true, p->case_sensitive))
+ skip_me = true;
+ if(matchesAny(tc.m_test_suite, p->filters[3], false, p->case_sensitive))
+ skip_me = true;
+ if(!matchesAny(tc.m_name, p->filters[4], true, p->case_sensitive))
+ skip_me = true;
+ if(matchesAny(tc.m_name, p->filters[5], false, p->case_sensitive))
+ skip_me = true;
+
+ if(!skip_me)
+ p->numTestCasesPassingFilters++;
+
+ // skip the test if it is not in the execution range
+ if((p->last < p->numTestCasesPassingFilters && p->first <= p->last) ||
+ (p->first > p->numTestCasesPassingFilters))
+ skip_me = true;
+
+ if(skip_me) {
+ if(!query_mode)
+ DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_skipped, tc);
+ continue;
+ }
+
+ // do not execute the test if we are to only count the number of filter passing tests
+ if(p->count)
+ continue;
+
+ // print the name of the test and don't execute it
+ if(p->list_test_cases) {
+ queryResults.push_back(&tc);
+ continue;
+ }
+
+ // print the name of the test suite if not done already and don't execute it
+ if(p->list_test_suites) {
+ if((testSuitesPassingFilt.count(tc.m_test_suite) == 0) && tc.m_test_suite[0] != '\0') {
+ queryResults.push_back(&tc);
+ testSuitesPassingFilt.insert(tc.m_test_suite);
+ p->numTestSuitesPassingFilters++;
+ }
+ continue;
+ }
+
+ // execute the test if it passes all the filtering
+ {
+ p->currentTest = &tc;
+
+ p->failure_flags = TestCaseFailureReason::None;
+ p->seconds = 0;
+
+ // reset atomic counters
+ p->numAssertsFailedCurrentTest_atomic = 0;
+ p->numAssertsCurrentTest_atomic = 0;
+
+ 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->reachedLeaf = false;
+ // May not be empty if previous subcase exited via exception.
+ p->subcaseStack.clear();
+ p->currentSubcaseDepth = 0;
+
+ p->shouldLogCurrentException = true;
+
+ // reset stuff for logging with INFO()
+ p->stringifiedContexts.clear();
+
+#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;
+ } catch(...) {
+ DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_exception,
+ {translateActiveException(), false});
+ p->failure_flags |= TestCaseFailureReason::Exception;
+ }
+#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
+
+ // exit this loop if enough assertions have failed - even if there are more subcases
+ if(p->abort_after > 0 &&
+ p->numAssertsFailed + p->numAssertsFailedCurrentTest_atomic >= p->abort_after) {
+ run_test = false;
+ p->failure_flags |= TestCaseFailureReason::TooManyFailedAsserts;
+ }
+
+ if(!p->nextSubcaseStack.empty() && run_test)
+ DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_reenter, tc);
+ if(p->nextSubcaseStack.empty())
+ run_test = false;
+ } while(run_test);
+
+ p->finalizeTestCaseData();
+
+ DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_end, *g_cs);
+
+ p->currentTest = nullptr;
+
+ // stop executing tests if enough assertions have failed
+ if(p->abort_after > 0 && p->numAssertsFailed >= p->abort_after)
+ break;
+ }
+ }
+
+ if(!query_mode) {
+ DOCTEST_ITERATE_THROUGH_REPORTERS(test_run_end, *g_cs);
+ } else {
+ QueryData qdata;
+ qdata.run_stats = g_cs;
+ qdata.data = queryResults.data();
+ qdata.num_data = unsigned(queryResults.size());
+ DOCTEST_ITERATE_THROUGH_REPORTERS(report_query, qdata);
+ }
+
+ return cleanup_and_return();
+}
+
+DOCTEST_DEFINE_INTERFACE(IReporter)
+
+int IReporter::get_num_active_contexts() { return detail::g_infoContexts.size(); }
+const IContextScope* const* IReporter::get_active_contexts() {
+ return get_num_active_contexts() ? &detail::g_infoContexts[0] : nullptr;
+}
+
+int IReporter::get_num_stringified_contexts() { return detail::g_cs->stringifiedContexts.size(); }
+const String* IReporter::get_stringified_contexts() {
+ return get_num_stringified_contexts() ? &detail::g_cs->stringifiedContexts[0] : nullptr;
+}
+
+namespace detail {
+ void registerReporterImpl(const char* name, int priority, reporterCreatorFunc c, bool isReporter) {
+ if(isReporter)
+ getReporters().insert(reporterMap::value_type(reporterMap::key_type(priority, name), c));
+ else
+ getListeners().insert(reporterMap::value_type(reporterMap::key_type(priority, name), c));
+ }
+} // namespace detail
+
+} // namespace doctest
+
+#endif // DOCTEST_CONFIG_DISABLE
+
+#ifdef DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
+DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4007) // 'function' : must be 'attribute' - see issue #182
+int main(int argc, char** argv) { return doctest::Context(argc, argv).run(); }
+DOCTEST_MSVC_SUPPRESS_WARNING_POP
+#endif // DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
+
+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
diff --git a/json4cpp/tests/thirdparty/doctest/doctest_compatibility.h b/json4cpp/tests/thirdparty/doctest/doctest_compatibility.h
new file mode 100644
index 0000000000..e056a824d5
--- /dev/null
+++ b/json4cpp/tests/thirdparty/doctest/doctest_compatibility.h
@@ -0,0 +1,37 @@
+#ifndef DOCTEST_COMPATIBILITY
+#define DOCTEST_COMPATIBILITY
+
+#define DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS
+#define DOCTEST_THREAD_LOCAL // enable single-threaded builds on XCode 6/7 - https://github.com/onqtam/doctest/issues/172
+#include "doctest.h"
+
+// Catch doesn't require a semicolon after CAPTURE but doctest does
+#undef CAPTURE
+#define CAPTURE(x) DOCTEST_CAPTURE(x);
+
+// Sections from Catch are called Subcases in doctest and don't work with std::string by default
+#undef SUBCASE
+#define SECTION(x) DOCTEST_SUBCASE(x)
+
+// convenience macro around INFO since it doesn't support temporaries (it is optimized to avoid allocations for runtime speed)
+#define INFO_WITH_TEMP_IMPL(x, var_name) const auto var_name = x; INFO(var_name) // lvalue!
+#define INFO_WITH_TEMP(x) INFO_WITH_TEMP_IMPL(x, DOCTEST_ANONYMOUS(DOCTEST_STD_STRING_))
+
+// doctest doesn't support THROWS_WITH for std::string out of the box (has to include <string>...)
+#define CHECK_THROWS_WITH_STD_STR_IMPL(expr, str, var_name) \
+ do { \
+ const std::string var_name = str; \
+ CHECK_THROWS_WITH(expr, var_name.c_str()); \
+ } while (false)
+#define CHECK_THROWS_WITH_STD_STR(expr, str) \
+ CHECK_THROWS_WITH_STD_STR_IMPL(expr, str, DOCTEST_ANONYMOUS(DOCTEST_STD_STRING_))
+
+// included here because for some tests in the json repository private is defined as
+// public and if no STL header is included before that then in the json include when STL
+// stuff is included the MSVC STL complains (errors) that C++ keywords are being redefined
+#include <iosfwd>
+
+// Catch does this by default
+using doctest::Approx;
+
+#endif
diff --git a/json4cpp/tests/thirdparty/fifo_map/fifo_map.hpp b/json4cpp/tests/thirdparty/fifo_map/fifo_map.hpp
new file mode 100644
index 0000000000..549b71bf93
--- /dev/null
+++ b/json4cpp/tests/thirdparty/fifo_map/fifo_map.hpp
@@ -0,0 +1,530 @@
+/*
+The code is licensed under the MIT License <http://opensource.org/licenses/MIT>:
+
+Copyright (c) 2015-2017 Niels Lohmann.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+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.
+*/
+
+#ifndef NLOHMANN_FIFO_MAP_HPP
+#define NLOHMANN_FIFO_MAP_HPP
+
+#include <algorithm>
+#include <cstdlib>
+#include <functional>
+#include <iostream>
+#include <limits>
+#include <map>
+#include <memory>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+/*!
+@brief namespace for Niels Lohmann
+@see https://github.com/nlohmann
+*/
+namespace nlohmann
+{
+
+template<class Key>
+class fifo_map_compare
+{
+ public:
+ /// constructor given a pointer to a key storage
+ fifo_map_compare(std::unordered_map<Key, std::size_t>* k) : keys(k) {}
+
+ /*!
+ This function compares two keys with respect to the order in which they
+ were added to the container. For this, the mapping keys is used.
+ */
+ bool operator()(const Key& lhs, const Key& rhs) const
+ {
+ // look up timestamps for both keys
+ const auto timestamp_lhs = keys->find(lhs);
+ const auto timestamp_rhs = keys->find(rhs);
+
+ if (timestamp_lhs == keys->end())
+ {
+ // timestamp for lhs not found - cannot be smaller than for rhs
+ return false;
+ }
+
+ if (timestamp_rhs == keys->end())
+ {
+ // timestamp for rhs not found - timestamp for lhs is smaller
+ return true;
+ }
+
+ // compare timestamps
+ return timestamp_lhs->second < timestamp_rhs->second;
+ }
+
+ void add_key(const Key& key)
+ {
+ keys->insert({key, timestamp++});
+ }
+
+ void remove_key(const Key& key)
+ {
+ keys->erase(key);
+ }
+
+ private:
+ /// pointer to a mapping from keys to insertion timestamps
+ std::unordered_map<Key, std::size_t>* keys = nullptr;
+ /// the next valid insertion timestamp
+ size_t timestamp = 1;
+};
+
+
+template <
+ class Key,
+ class T,
+ class Compare = fifo_map_compare<Key>,
+ class Allocator = std::allocator<std::pair<const Key, T>>
+ > class fifo_map // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions,-warnings-as-errors)
+{
+ public:
+ using key_type = Key;
+ using mapped_type = T;
+ using value_type = std::pair<const Key, T>;
+ using size_type = std::size_t;
+ using difference_type = std::ptrdiff_t;
+ using key_compare = Compare;
+ using allocator_type = Allocator;
+ using reference = value_type&;
+ using const_reference = const value_type&;
+ using pointer = typename std::allocator_traits<Allocator>::pointer;
+ using const_pointer = typename std::allocator_traits<Allocator>::const_pointer;
+
+ using internal_map_type = std::map<Key, T, Compare, Allocator>;
+
+ using iterator = typename internal_map_type::iterator;
+ using const_iterator = typename internal_map_type::const_iterator;
+ using reverse_iterator = typename internal_map_type::reverse_iterator;
+ using const_reverse_iterator = typename internal_map_type::const_reverse_iterator;
+
+ public:
+ /// default constructor
+ fifo_map() : m_keys(), m_compare(&m_keys), m_map(m_compare) {}
+
+ /// copy constructor
+ fifo_map(const fifo_map &f) : m_keys(f.m_keys), m_compare(&m_keys), m_map(f.m_map.begin(), f.m_map.end(), m_compare) {}
+
+ /// constructor for a range of elements
+ template<class InputIterator>
+ fifo_map(InputIterator first, InputIterator last)
+ : m_keys(), m_compare(&m_keys), m_map(m_compare)
+ {
+ for (auto it = first; it != last; ++it)
+ {
+ insert(*it);
+ }
+ }
+
+ /// constructor for a list of elements
+ fifo_map(std::initializer_list<value_type> init) : fifo_map()
+ {
+ for (auto x : init)
+ {
+ insert(x);
+ }
+ }
+
+
+ /*
+ * Element access
+ */
+
+ /// access specified element with bounds checking
+ T& at(const Key& key)
+ {
+ return m_map.at(key);
+ }
+
+ /// access specified element with bounds checking
+ const T& at(const Key& key) const
+ {
+ return m_map.at(key);
+ }
+
+ /// access specified element
+ T& operator[](const Key& key)
+ {
+ m_compare.add_key(key);
+ return m_map[key];
+ }
+
+ /// access specified element
+ T& operator[](Key&& key)
+ {
+ m_compare.add_key(key);
+ return m_map[key];
+ }
+
+
+ /*
+ * Iterators
+ */
+
+ /// returns an iterator to the beginning
+ iterator begin() noexcept
+ {
+ return m_map.begin();
+ }
+
+ /// returns an iterator to the end
+ iterator end() noexcept
+ {
+ return m_map.end();
+ }
+
+ /// returns an iterator to the beginning
+ const_iterator begin() const noexcept
+ {
+ return m_map.begin();
+ }
+
+ /// returns an iterator to the end
+ const_iterator end() const noexcept
+ {
+ return m_map.end();
+ }
+
+ /// returns an iterator to the beginning
+ const_iterator cbegin() const noexcept
+ {
+ return m_map.cbegin();
+ }
+
+ /// returns an iterator to the end
+ const_iterator cend() const noexcept
+ {
+ return m_map.cend();
+ }
+
+ /// returns a reverse iterator to the beginning
+ reverse_iterator rbegin() noexcept
+ {
+ return m_map.rbegin();
+ }
+
+ /// returns a reverse iterator to the end
+ reverse_iterator rend() noexcept
+ {
+ return m_map.rend();
+ }
+
+ /// returns a reverse iterator to the beginning
+ const_reverse_iterator rbegin() const noexcept
+ {
+ return m_map.rbegin();
+ }
+
+ /// returns a reverse iterator to the end
+ const_reverse_iterator rend() const noexcept
+ {
+ return m_map.rend();
+ }
+
+ /// returns a reverse iterator to the beginning
+ const_reverse_iterator crbegin() const noexcept
+ {
+ return m_map.crbegin();
+ }
+
+ /// returns a reverse iterator to the end
+ const_reverse_iterator crend() const noexcept
+ {
+ return m_map.crend();
+ }
+
+
+ /*
+ * Capacity
+ */
+
+ /// checks whether the container is empty
+ bool empty() const noexcept
+ {
+ return m_map.empty();
+ }
+
+ /// returns the number of elements
+ size_type size() const noexcept
+ {
+ return m_map.size();
+ }
+
+ /// returns the maximum possible number of elements
+ size_type max_size() const noexcept
+ {
+ return m_map.max_size();
+ }
+
+
+ /*
+ * Modifiers
+ */
+
+ /// clears the contents
+ void clear() noexcept
+ {
+ m_map.clear();
+ m_keys.clear();
+ }
+
+ /// insert value
+ std::pair<iterator, bool> insert(const value_type& value)
+ {
+ m_compare.add_key(value.first);
+ return m_map.insert(value);
+ }
+
+ /// insert value
+ template<class P>
+ std::pair<iterator, bool> insert( P&& value ) // NOLINT(cppcoreguidelines-missing-std-forward)
+ {
+ m_compare.add_key(value.first);
+ return m_map.insert(value);
+ }
+
+ /// insert value with hint
+ iterator insert(const_iterator hint, const value_type& value)
+ {
+ m_compare.add_key(value.first);
+ return m_map.insert(hint, value);
+ }
+
+ /// insert value with hint
+ iterator insert(const_iterator hint, value_type&& value)
+ {
+ m_compare.add_key(value.first);
+ return m_map.insert(hint, value);
+ }
+
+ /// insert value range
+ template<class InputIt>
+ void insert(InputIt first, InputIt last)
+ {
+ for (const_iterator it = first; it != last; ++it)
+ {
+ m_compare.add_key(it->first);
+ }
+
+ m_map.insert(first, last);
+ }
+
+ /// insert value list
+ void insert(std::initializer_list<value_type> ilist)
+ {
+ for (auto value : ilist)
+ {
+ m_compare.add_key(value.first);
+ }
+
+ m_map.insert(ilist);
+ }
+
+ /// constructs element in-place
+ template<class... Args>
+ std::pair<iterator, bool> emplace(Args&& ... args)
+ {
+ typename fifo_map::value_type value(std::forward<Args>(args)...);
+ m_compare.add_key(value.first);
+ return m_map.emplace(std::move(value));
+ }
+
+ /// constructs element in-place with hint
+ template<class... Args>
+ iterator emplace_hint(const_iterator hint, Args&& ... args)
+ {
+ typename fifo_map::value_type value(std::forward<Args>(args)...);
+ m_compare.add_key(value.first);
+ return m_map.emplace_hint(hint, std::move(value));
+ }
+
+ /// remove element at position
+ iterator erase(const_iterator pos)
+ {
+ m_compare.remove_key(pos->first);
+ return m_map.erase(pos);
+ }
+
+ /// remove elements in range
+ iterator erase(const_iterator first, const_iterator last)
+ {
+ for (const_iterator it = first; it != last; ++it)
+ {
+ m_compare.remove_key(it->first);
+ }
+
+ return m_map.erase(first, last);
+ }
+
+ /// remove elements with key
+ size_type erase(const key_type& key)
+ {
+ size_type res = m_map.erase(key);
+
+ if (res > 0)
+ {
+ m_compare.remove_key(key);
+ }
+
+ return res;
+ }
+
+ /// swaps the contents
+ void swap(fifo_map& other) // NOLINT(cppcoreguidelines-noexcept-swap,performance-noexcept-swap)
+ {
+ std::swap(m_map, other.m_map);
+ std::swap(m_compare, other.m_compare);
+ std::swap(m_keys, other.m_keys);
+ }
+
+
+ /*
+ * Lookup
+ */
+
+ /// returns the number of elements matching specific key
+ size_type count(const Key& key) const
+ {
+ return m_map.count(key);
+ }
+
+ /// finds element with specific key
+ iterator find(const Key& key)
+ {
+ return m_map.find(key);
+ }
+
+ /// finds element with specific key
+ const_iterator find(const Key& key) const
+ {
+ return m_map.find(key);
+ }
+
+ /// returns range of elements matching a specific key
+ std::pair<iterator, iterator> equal_range(const Key& key)
+ {
+ return m_map.equal_range(key);
+ }
+
+ /// returns range of elements matching a specific key
+ std::pair<const_iterator, const_iterator> equal_range(const Key& key) const
+ {
+ return m_map.equal_range(key);
+ }
+
+ /// returns an iterator to the first element not less than the given key
+ iterator lower_bound(const Key& key)
+ {
+ return m_map.lower_bound(key);
+ }
+
+ /// returns an iterator to the first element not less than the given key
+ const_iterator lower_bound(const Key& key) const
+ {
+ return m_map.lower_bound(key);
+ }
+
+ /// returns an iterator to the first element greater than the given key
+ iterator upper_bound(const Key& key)
+ {
+ return m_map.upper_bound(key);
+ }
+
+ /// returns an iterator to the first element greater than the given key
+ const_iterator upper_bound(const Key& key) const
+ {
+ return m_map.upper_bound(key);
+ }
+
+
+ /*
+ * Observers
+ */
+
+ /// returns the function that compares keys
+ key_compare key_comp() const
+ {
+ return m_compare;
+ }
+
+
+ /*
+ * Non-member functions
+ */
+
+ friend bool operator==(const fifo_map& lhs, const fifo_map& rhs)
+ {
+ return lhs.m_map == rhs.m_map;
+ }
+
+ friend bool operator!=(const fifo_map& lhs, const fifo_map& rhs)
+ {
+ return lhs.m_map != rhs.m_map;
+ }
+
+ friend bool operator<(const fifo_map& lhs, const fifo_map& rhs)
+ {
+ return lhs.m_map < rhs.m_map;
+ }
+
+ friend bool operator<=(const fifo_map& lhs, const fifo_map& rhs)
+ {
+ return lhs.m_map <= rhs.m_map;
+ }
+
+ friend bool operator>(const fifo_map& lhs, const fifo_map& rhs)
+ {
+ return lhs.m_map > rhs.m_map;
+ }
+
+ friend bool operator>=(const fifo_map& lhs, const fifo_map& rhs)
+ {
+ return lhs.m_map >= rhs.m_map;
+ }
+
+ private:
+ /// the keys
+ std::unordered_map<Key, std::size_t> m_keys;
+ /// the comparison object
+ Compare m_compare;
+ /// the internal data structure
+ internal_map_type m_map;
+};
+
+} // namespace nlohmann
+
+// specialization of std::swap
+namespace std // NOLINT(cert-dcl58-cpp,-warnings-as-errors)
+{
+template <class Key, class T, class Compare, class Allocator>
+inline void swap(nlohmann::fifo_map<Key, T, Compare, Allocator>& m1, // NOLINT(cert-dcl58-cpp,cppcoreguidelines-noexcept-swap,performance-noexcept-swap)
+ nlohmann::fifo_map<Key, T, Compare, Allocator>& m2)
+{
+ m1.swap(m2);
+}
+} // namespace std
+
+#endif
diff --git a/json4cpp/tests/thirdparty/imapdl/filterbr.py b/json4cpp/tests/thirdparty/imapdl/filterbr.py
new file mode 100755
index 0000000000..49e37aa606
--- /dev/null
+++ b/json4cpp/tests/thirdparty/imapdl/filterbr.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python3
+
+# 2017, Georg Sauthoff <mail@gms.tf>, GPLv3
+# 2022, Alexander Stohr, ZF Friedrichshafen AG: benign handling of UTF-8 violations
+
+import sys
+
+def skip_comments(lines):
+ state = 0
+ for line in lines:
+ n = len(line)
+ l = ''
+ p = 0
+ while p < n:
+ if state == 0:
+ a = line.find('//', p)
+ b = line.find('/*', p)
+ if a > -1 and (a < b or b == -1):
+ l += line[p:a]
+ p = n
+ elif b > -1 and (b < a or a == -1):
+ l += line[p:b]
+ p = b+2
+ state = 1
+ else:
+ l += line[p:]
+ p = n
+ elif state == 1:
+ a = line.rfind('*/', p)
+ if a == -1:
+ p = n
+ else:
+ p = a + 2
+ state = 0
+ yield l
+
+def cond_lines(lines):
+ state = 0
+ pcnt = 0
+ for nr, line in enumerate(lines, 1):
+ if not line:
+ continue
+ n = len(line)
+ p = 0
+ do_yield = False
+ while p < n:
+ if state == 0:
+ p = line.find('if', p)
+ if p == -1:
+ p = n
+ continue
+ if (p == 0 or not line[p-1].isalpha()) \
+ and (p+2 == len(line) or not line[p+2].isalpha()):
+ do_yield = True
+ state = 1
+ p += 2
+ elif state == 1:
+ do_yield = True
+ p = line.find('(', p)
+ if p == -1:
+ p = n
+ else:
+ p += 1
+ state = 2
+ pcnt = 1
+ elif state == 2:
+ do_yield = True
+ for p in range(p, n):
+ if line[p] == '(':
+ pcnt += 1
+ elif line[p] == ')':
+ pcnt -= 1
+ if not pcnt:
+ state = 0
+ break
+ p += 1
+ if do_yield:
+ yield nr
+
+def cond_lines_from_file(filename):
+ with open(filename) as f:
+ yield from cond_lines(skip_comments(f))
+
+def filter_lcov_trace(lines):
+ nrs = set()
+ for line in lines:
+ if line.startswith('SF:'):
+ nrs = set(cond_lines_from_file(line[3:-1]))
+ elif line.startswith('BRDA:'):
+ xs = line[5:].split(',')
+ nr = int(xs[0]) if xs else 0
+ if nr not in nrs:
+ continue
+ yield line
+
+def filter_lcov_trace_file(s_filename, d_file):
+ # encoding is anyways the python default: utf-8
+ # standard error is "strict"; python style escaping is "backslashreplace"; alternate benign handler is "surrogateescape"
+ with open(s_filename, encoding="utf-8", errors="backslashreplace") as f:
+ for l in filter_lcov_trace(f):
+ print(l, end='', file=d_file)
+
+if __name__ == '__main__':
+ #for l in cond_lines_from_file(sys.argv[1]):
+ # print(l)
+
+ filter_lcov_trace_file(sys.argv[1], sys.stdout)
+
+ #with open(sys.argv[1]) as f:
+ # for l in skip_comments(f):
+ # print(l)