mirror of
https://github.com/nlohmann/json.git
synced 2026-09-30 22:15:19 +00:00
* Bound the descent of the copy constructor basic_json's copy constructor copied objects and arrays by handing the container to its own copy constructor, which copy-constructs every element and so reaches this constructor again, once per nesting level. A value nested deeply enough exhausted the call stack and terminated the process with a segmentation fault - no exception, nothing the caller could catch. Parsing such a value works, as the parser is iterative, and so does destroying one, as #1436 made destruction iterative. Bound how far the copy descends rather than take the call stack away from it. The first levels are copied exactly as they were - the containers copy their own elements, which is by far the fastest way to fill them - and only once the copy has descended 128 levels is the value below it finished without the call stack, through an explicit worklist. Copying can therefore no longer exhaust the stack, however deeply a value is nested, while a value nested less deeply than the bound - all but a vanishing minority - is copied by the very same code as before and pays only for one counter. That counter lives in thread_local storage, as one shared between threads would be raced. JSON_NO_THREAD_LOCAL switches it off for toolchains without thread_local; copying then goes through the worklist right away, which yields the same values but is measurably slower. The deferred values are completed before the copy they belong to returns, so a value copied while another copy is going on - by a custom base class, say - is unaffected by the copy it is nested in. operator= takes its argument by value, so copy assignment is fixed as well. Copying is as fast as it was, within measurement noise (medians of 9 interleaved runs, clang -O3): -1.3% for an array of strings, +0.0% for a flat object, +0.1% for a flat array of numbers, +0.3% for nested arrays, +0.6% for nested objects and +1.2% for a twitter-like document. Copying a three-key object costs about ten nanoseconds more, the counter. Deferring every level instead, rather than only those below the bound, measured between 3% and 9% slower depending on the shape of the value. This fixes #5387 for the copy constructor. dump() is still recursive. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Test the copy constructor's iterative path in CI The copy constructor descends into 128 levels before it finishes a value without the call stack, so the iterative path is otherwise only reached by the few tests that nest deeper than that. JSON_NO_THREAD_LOCAL switches the descent off, which sends every value down that path. Running the whole test suite that way covers it with every object type, string type, allocator, and base class the suite already exercises. The new ci_test_no_thread_local target does that; the macro had no build coverage at all before. Copying a nested value also has to carry over what the element-wise copy constructor would have copied: the parents that JSON_DIAGNOSTICS relies on, and the positions that JSON_DIAGNOSTIC_POSITIONS reports. Both are now checked on either side of the descent bound, for objects and arrays. Neither was tested before, and dropping either one makes the new tests fail. Also quantify what JSON_NO_THREAD_LOCAL costs a copy instead of calling it "measurably slower". Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Split the regression tests so that they keep linking Linking test-regression2 fails with "relocation truncated to fit: IMAGE_REL_AMD64_REL32 against `.rdata'" once its object grows past what the MinGW linker copes with, and the copy constructor's helpers push it over: the object grows by 6.3%, from 4,654,128 to 4,944,920 bytes at -O0, and develop links at the smaller of the two. Building the tests optimized shrinks the object enough to link, but the binaries clang 11.0.1 and clang 18.1.8 then produce crash before doctest prints its first line - 39 of 102 tests on clang 18 - so the objects have to become smaller rather than denser. Moving the test cases that follow "regression tests 2" into a file of their own brings that object to 4,687,888 bytes, which is 0.7% above the size that links today rather than 6.3%. Both files still build for C++11, C++17 and C++20, and run the same 9 test cases and 135 assertions as before, now spread over two binaries. New regression tests belong in unit-regression3.cpp from here on, which is what CONTRIBUTING.md now says. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Do not use thread_local storage with Clang targeting MinGW Every test that copies a value segfaults there - 42 of 105 on clang 11.0.1, 39 of 102 on clang 18.1.8 - while the same tests pass with GCC targeting MinGW, with Clang targeting MSVC, and with every other toolchain the library is tested on. The counter that bounds the copy constructor's descent is the library's first use of thread_local, so that job had never exercised it before. JSON_NO_THREAD_LOCAL already covers toolchains without thread_local storage, and copying yields the same values with it, only more slowly. Define it for this one automatically. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Balance the warning suppression the split separated unit-regression2.cpp opens a DOCTEST_CLANG_SUPPRESS_WARNING_PUSH block at the top and closed it at the very bottom, which the split moved into unit-regression3.cpp: one file was left with a push and no pop, the other with a pop and no push, which clang reports as an error. Give each file the pair it needs. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Check both shapes without a C-style array clang-tidy rejects the array the two shapes were iterated over (cppcoreguidelines-avoid-c-arrays). The array only existed because astyle reformats a range-for over a braced initializer list into something unreadable; naming the two cases avoids both. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Split the regression tests far enough to leave room The first split left unit-regression2.cpp 0.7% below the size develop links at, which the comparison change in the follow-up immediately used up: the MinGW linker fails on test-regression2_cpp20 again, naming copy_shallow and to_partial_ordering among the relocations it cannot fit. Move the sections from "issue #2067" on, and the helper types they use, so that the file stops being the one that decides whether the tests can be linked at all. At -O0 and C++20, unit-regression2.cpp is now 2,964,944 bytes against develop's 4,708,248, and 3,070,568 bytes with the follow-up applied - roughly a third smaller either way, rather than a fraction of a percent larger. The 135 assertions are the same ones as before, now spread over three test cases in two files. Also silence the clang-tidy findings the deep-nesting tests draw: the copies they make are what is being tested, and the reserve() computation gets its parentheses. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Move the #4804 alias to the file that uses it The split left the json_4804 alias behind in unit-regression2.cpp while the test case that uses it went to unit-regression3.cpp, which does not build for C++17 and C++20 as a result. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Include <span> where the split moved its only use The #2546 test case guards itself with __has_include(<span>), but the include itself sat in unit-regression2.cpp's preamble and stayed behind, so the section compiled without a declaration wherever the guard passed - which nvhpc reported and libc++ builds do not, as they skip the section altogether. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Keep the descent bookkeeping in one place Copying carried a depth count, a depth limit and a guard of its own, and the comparison in the follow-up added a second set beside them. Neither operation needs its own: they are never nested inside one another by the library - copying a value does not compare one, and comparing two values does not copy them - and where user code nests them anyway, sharing the count only ends a descent sooner than it had to. So there is now one nesting_depth(), one nesting_depth_limit() and one nesting_depth_guard, which the follow-up uses instead of adding its own. Inverting the test in copy_structured leaves the too-deep case and the no-thread-local case as the same code. The guard takes the count rather than looking it up, because the caller has looked it up already to test it against the limit, and reaching thread-local storage twice on the path that is taken almost every time is worth avoiding. The switch that copies the value of anything that is not an object or an array was written twice - once in the copy constructor, once in copy_shallow - so that adding a value_t meant editing both, and missing one would have been silent. It is copy_leaf_value now, and inlined: both callers have already sorted the containers out, and folding that test into the switch is what keeps a value made mostly of numbers copying as fast as it did. Copying canada.json, citm_catalog.json and twitter.json is within 0.6% of what it was before, measured as a paired ratio over 18 interleaved rounds against a run-to-run spread of 0.3%. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Check that an abandoned copy can still be destroyed Copying a value without the call stack builds the copy from the top down, and every value whose own copy has not been made yet stays a null value until it is. That is what lets a copy be abandoned half-built: the destructor finds nothing but complete values and null ones. Nothing tested it. Failing an allocation part-way through a copy of a deeply nested value does, with the allocator the file already has for exactly this kind of test. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Name the test's locals so Flawfinder stops matching them The code scanning job reports CWE-362 - "check when opening files" - for a test that opens no files: Flawfinder matched a local variable called open. Rename it and its partner. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Keep the descent guard's bookkeeping self-contained nesting_depth_limit() and nesting_depth_guard were only used inside the JSON_NO_THREAD_LOCAL-guarded branch of copy_structured(), but were defined unconditionally. Move them inside the #ifndef, and have the guard look up the depth and test it against the limit itself (via okay()) instead of making the caller do it - the caller no longer needs to touch nesting_depth() at all. Also shrink the thread-local counter to std::uint8_t, matching what its own doc comment already argued. Addresses gregmarr's review comments on #5389. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Make nesting_depth_guard usable regardless of JSON_NO_THREAD_LOCAL nesting_depth_limit() and nesting_depth() stay behind #ifndef JSON_NO_THREAD_LOCAL, since a descent cannot be bounded without a per-thread count. But the guard itself now always exists, becoming a no-op that is never okay() under that macro - the same way the bound is already reached on every call without one. copy_structured() no longer needs to know which case it is in. This is what lets #5390 reuse the guard for comparison, which cannot test JSON_NO_THREAD_LOCAL where the macro-based operators use it: the guard now carries that distinction itself instead of requiring every caller to. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Silence VS2015's C4503 for the custom-base-class test The deep-copy support added for #5387 lengthened the mangled name of std::allocator_traits<...>::construct for the test's map type past VS2015's limit, which /WX turns into a build failure even though the name is only used for (now-truncated) debug info. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Remove dead unused-parameter casts from copy_metadata() @gregmarr asked whether the static_cast<void> pair in the JSON_DIAGNOSTIC_POSITIONS-off branch was needed for an empty json_base_class_t. It isn't: src and dst are already referenced unconditionally by the base-class copy above, so no -Wunused-parameter warning fires either way (checked with -Wall -Wextra -Wunused-parameter, JSON_DIAGNOSTIC_POSITIONS 0 and 1). Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix CI: build custom array types without a fill constructor, re-amalgamate copy_array_level() built the destination array with the fill constructor array_t(count, value), which is not part of the array container interface the library otherwise assumes (e.g. custom ArrayTypes that only provide a default and an iterator-pair constructor, as covered by unit-custom-array-type.cpp). Default- construct the array and resize() it instead, matching how the rest of the codebase already grows array_t. Also re-run the amalgamation, which had fallen out of sync with include/nlohmann/json.hpp. Signed-off-by: Niels Lohmann <mail@nlohmann.me> Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
361 lines
16 KiB
CMake
361 lines
16 KiB
CMake
cmake_minimum_required(VERSION 3.13...4.0)
|
|
|
|
option(JSON_Valgrind "Execute test suite with Valgrind." OFF)
|
|
option(JSON_FastTests "Skip expensive/slow tests." OFF)
|
|
option(JSON_TestSimdutf "Build the unit tests against the simdutf UTF-8 validation backend." OFF)
|
|
|
|
set(JSON_SIMDUTF_VERSION 9.1.0 CACHE STRING "The simdutf version used by JSON_TestSimdutf.")
|
|
|
|
set(JSON_32bitTest AUTO CACHE STRING "Enable the 32bit unit test (ON/OFF/AUTO/ONLY).")
|
|
set(JSON_TestStandards "" CACHE STRING "The list of standards to test explicitly.")
|
|
|
|
# using an env var, since this will also affect targets executing cmake (such as "ci_test_compiler_default")
|
|
set(JSON_FORCED_GLOBAL_COMPILE_OPTIONS $ENV{JSON_FORCED_GLOBAL_COMPILE_OPTIONS})
|
|
if (NOT "" STREQUAL "$ENV{JSON_FORCED_GLOBAL_COMPILE_OPTIONS}")
|
|
add_compile_options($ENV{JSON_FORCED_GLOBAL_COMPILE_OPTIONS})
|
|
endif()
|
|
if (NOT "" STREQUAL "$ENV{JSON_FORCED_GLOBAL_LINK_OPTIONS}")
|
|
add_link_options($ENV{JSON_FORCED_GLOBAL_LINK_OPTIONS})
|
|
endif()
|
|
|
|
include(test)
|
|
|
|
#############################################################################
|
|
# override standard support
|
|
#############################################################################
|
|
|
|
# Clang only supports C++14 starting from Clang 3.5 (lesser versions miss std::enable_if_t)
|
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.5)
|
|
unset(compiler_supports_cpp_14)
|
|
endif()
|
|
|
|
# Clang only supports C++17 starting from Clang 5.0
|
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
|
|
unset(compiler_supports_cpp_17)
|
|
endif()
|
|
# MSVC 2015 (14.0) does not support C++17
|
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.10)
|
|
unset(compiler_supports_cpp_17)
|
|
endif()
|
|
# GCC 5 and 6 do claim experimental support for C++17, but do not implement <optional>
|
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0)
|
|
unset(compiler_supports_cpp_17)
|
|
endif()
|
|
|
|
# Clang C++20 support appears insufficient prior to Clang 9.0 (based on CI build failure)
|
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0)
|
|
unset(compiler_supports_cpp_20)
|
|
endif()
|
|
# MSVC 2017 (15.x) does not support C++20
|
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.20)
|
|
unset(compiler_supports_cpp_20)
|
|
endif()
|
|
# GCC started supporting C++20 features in 8.0 but a test for #3070 segfaults prior to 9.0
|
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0)
|
|
unset(compiler_supports_cpp_20)
|
|
endif()
|
|
|
|
#############################################################################
|
|
# test_main library with shared code to speed up build and common settings
|
|
#############################################################################
|
|
|
|
add_library(test_main OBJECT src/unit.cpp)
|
|
target_compile_definitions(test_main PUBLIC
|
|
DOCTEST_CONFIG_SUPER_FAST_ASSERTS
|
|
JSON_TEST_KEEP_MACROS
|
|
JSON_TEST_USING_MULTIPLE_HEADERS=$<BOOL:${JSON_MultipleHeaders}>)
|
|
target_compile_features(test_main PRIVATE cxx_std_11)
|
|
target_compile_options(test_main PUBLIC
|
|
$<$<CXX_COMPILER_ID:MSVC>:/EHsc>
|
|
# MSVC: Force to always compile with W4
|
|
# Disable warning C4566: character represented by universal-character-name '\uFF01'
|
|
# cannot be represented in the current code page (1252)
|
|
# Disable warning C4996: 'nlohmann::basic_json<...>::operator <<': was declared deprecated
|
|
# Disable warning C4702: unreachable code; wide_string_input_adapter::get_elements()
|
|
# is annotated JSON_HEDLEY_NO_RETURN (it always throws), which
|
|
# makes MSVC flag the code following its call in binary_reader.hpp
|
|
# as unreachable for that instantiation, in both Debug and Release
|
|
# Disable warning C4503: decorated name length exceeded, name was truncated; the deep
|
|
# copy support added for #5387 pushes the mangled name of
|
|
# std::allocator_traits<...>::construct for the custom-base-class
|
|
# test's map type past VS2015's limit. The name is only used for
|
|
# debug info, so truncation does not affect the build.
|
|
$<$<CXX_COMPILER_ID:MSVC>:/W4;/wd4566;/wd4996;/wd4702;/wd4503>
|
|
# https://github.com/nlohmann/json/issues/1114
|
|
$<$<CXX_COMPILER_ID:MSVC>:/bigobj> $<$<BOOL:${MINGW}>:-Wa,-mbig-obj>
|
|
|
|
# https://github.com/nlohmann/json/pull/3229
|
|
$<$<CXX_COMPILER_ID:Intel>:-diag-disable=2196>
|
|
|
|
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wno-deprecated;-Wno-float-equal>
|
|
$<$<CXX_COMPILER_ID:GNU>:-Wno-deprecated-declarations>
|
|
$<$<CXX_COMPILER_ID:Intel>:-diag-disable=1786>)
|
|
target_include_directories(test_main PUBLIC
|
|
thirdparty/doctest
|
|
thirdparty/fifo_map
|
|
${PROJECT_BINARY_DIR}/include)
|
|
target_link_libraries(test_main PUBLIC ${NLOHMANN_JSON_TARGET_NAME})
|
|
|
|
#############################################################################
|
|
# define test- and standard-specific build settings
|
|
#############################################################################
|
|
|
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU"
|
|
AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 8.0
|
|
AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0 AND NOT MINGW)
|
|
# fix for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90050
|
|
json_test_set_test_options(all CXX_STANDARDS 17 LINK_LIBRARIES stdc++fs)
|
|
endif()
|
|
|
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
|
# avoid stack overflow, see https://github.com/nlohmann/json/issues/2955
|
|
json_test_set_test_options("test-cbor;test-msgpack;test-ubjson;test-bjdata;test-binary_formats" LINK_OPTIONS /STACK:4000000)
|
|
endif()
|
|
|
|
# disable exceptions for test-disabled_exceptions
|
|
json_test_set_test_options(test-disabled_exceptions
|
|
COMPILE_DEFINITIONS
|
|
JSON_NOEXCEPTION
|
|
# disabled due to https://github.com/nlohmann/json/discussions/2824
|
|
#$<$<CXX_COMPILER_ID:MSVC>:_HAS_EXCEPTIONS=0>
|
|
COMPILE_OPTIONS
|
|
$<$<CXX_COMPILER_ID:AppleClang>:-fno-exceptions> $<$<CXX_COMPILER_ID:Clang>:-fno-exceptions>
|
|
$<$<CXX_COMPILER_ID:GNU>:-fno-exceptions>
|
|
$<$<CXX_COMPILER_ID:Intel>:-fno-exceptions> $<$<CXX_COMPILER_ID:IntelLLVM>:-fno-exceptions>
|
|
# disabled due to https://github.com/nlohmann/json/discussions/2824
|
|
#$<$<CXX_COMPILER_ID:MSVC>:/EH>
|
|
)
|
|
|
|
# raise timeout of expensive Unicode test
|
|
json_test_set_test_options(test-unicode4 TEST_PROPERTIES TIMEOUT 3000)
|
|
|
|
#############################################################################
|
|
# add unit tests
|
|
#############################################################################
|
|
|
|
# Generate the leak checks for every JSON_HEDLEY_* macro defined in
|
|
# hedley.hpp; tests/src/unit-no-macro-leak.cpp #include-s the result after
|
|
# nlohmann/json.hpp (see issue #5408). Using the shared
|
|
# cmake/scripts/gen_hedley_undef_check.cmake script (also used by `make
|
|
# update_hedley_undef`) instead of a hand-maintained list of macro names
|
|
# means this test can never go stale after a future `make update_hedley`.
|
|
set(hedley_hpp "${PROJECT_SOURCE_DIR}/include/nlohmann/thirdparty/hedley/hedley.hpp")
|
|
set(hedley_undef_check_script "${PROJECT_SOURCE_DIR}/cmake/scripts/gen_hedley_undef_check.cmake")
|
|
set(hedley_undef_checks "${PROJECT_BINARY_DIR}/include/hedley_undef_checks.inc")
|
|
|
|
# Reconfigure whenever the vendored header or the generator script changes,
|
|
# so a `cmake --build` after `make update_hedley` does not silently keep a
|
|
# stale generated file around.
|
|
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS
|
|
"${hedley_hpp}"
|
|
"${hedley_undef_check_script}")
|
|
|
|
# Generate once at configure time, so the very first build (before any
|
|
# custom-command build step has run) already has an up-to-date file.
|
|
execute_process(
|
|
COMMAND ${CMAKE_COMMAND}
|
|
"-DHEDLEY_HPP=${hedley_hpp}"
|
|
"-DOUTPUT=${hedley_undef_checks}"
|
|
-DMODE=checks
|
|
-P "${hedley_undef_check_script}"
|
|
RESULT_VARIABLE hedley_undef_check_result
|
|
)
|
|
if(NOT hedley_undef_check_result EQUAL 0)
|
|
message(FATAL_ERROR "Failed to generate ${hedley_undef_checks}")
|
|
endif()
|
|
|
|
# Also (re)generate as a build step, so an incremental build after editing
|
|
# hedley.hpp without a full reconfigure still picks up the change.
|
|
add_custom_command(
|
|
OUTPUT "${hedley_undef_checks}"
|
|
COMMAND ${CMAKE_COMMAND}
|
|
"-DHEDLEY_HPP=${hedley_hpp}"
|
|
"-DOUTPUT=${hedley_undef_checks}"
|
|
-DMODE=checks
|
|
-P "${hedley_undef_check_script}"
|
|
DEPENDS "${hedley_hpp}" "${hedley_undef_check_script}"
|
|
COMMENT "Generating Hedley undef leak checks"
|
|
VERBATIM)
|
|
add_custom_target(generate_hedley_undef_checks DEPENDS "${hedley_undef_checks}")
|
|
|
|
if("${JSON_TestStandards}" STREQUAL "")
|
|
set(test_cxx_standards 11 14 17 20 23)
|
|
unset(test_force)
|
|
else()
|
|
set(test_cxx_standards ${JSON_TestStandards})
|
|
set(test_force FORCE)
|
|
endif()
|
|
|
|
# Print selected standards marking unavailable ones with brackets
|
|
set(msg_standards "")
|
|
foreach(cxx_standard ${test_cxx_standards})
|
|
if(compiler_supports_cpp_${cxx_standard})
|
|
list(APPEND msg_standards ${cxx_standard})
|
|
else()
|
|
list(APPEND msg_standards [${cxx_standard}])
|
|
endif()
|
|
endforeach()
|
|
string(JOIN " " msg_standards ${msg_standards})
|
|
set(msg "Testing standards: ${msg_standards}")
|
|
if(test_force)
|
|
string(APPEND msg " (forced)")
|
|
endif()
|
|
message(STATUS "${msg}")
|
|
|
|
#############################################################################
|
|
# optionally validate UTF-8 with simdutf (JSON_USE_SIMDUTF)
|
|
#############################################################################
|
|
|
|
# The simdutf backend is opt-in and not vendored, so it is fetched here rather
|
|
# than being a checked-in dependency. Everything below hangs off test_main,
|
|
# whose usage requirements every test target inherits; the library target and
|
|
# the installed CMake package are deliberately left untouched.
|
|
if (JSON_TestSimdutf)
|
|
# simdutf requires C++17, both to compile itself and to be reachable from
|
|
# the library, which keeps its scalar validator below that. Find a tested
|
|
# standard that satisfies it.
|
|
set(simdutf_standard "")
|
|
foreach(cxx_standard ${test_cxx_standards})
|
|
if(NOT cxx_standard LESS 17 AND compiler_supports_cpp_${cxx_standard})
|
|
set(simdutf_standard ${cxx_standard})
|
|
break()
|
|
endif()
|
|
endforeach()
|
|
|
|
if("${simdutf_standard}" STREQUAL "")
|
|
# Building simdutf would fail outright without a C++17 compiler, and
|
|
# even with one it would go unused if no C++17-or-later standard is
|
|
# tested. Say so and fall back to the scalar validator rather than
|
|
# failing the build.
|
|
if(NOT compiler_supports_cpp_17)
|
|
set(simdutf_reason "the compiler does not support C++17")
|
|
else()
|
|
set(simdutf_reason "no tested standard is C++17 or later (testing ${msg_standards})")
|
|
endif()
|
|
message(WARNING
|
|
"JSON_TestSimdutf is enabled, but ${simdutf_reason}. simdutf requires C++17, so it "
|
|
"is not fetched and JSON_USE_SIMDUTF is not defined: the tests run against the "
|
|
"built-in scalar UTF-8 validator instead. Set JSON_TestStandards to include 17 or "
|
|
"later, or build with a compiler that supports C++17.")
|
|
else()
|
|
if (CMAKE_VERSION VERSION_LESS 3.18)
|
|
message(FATAL_ERROR "JSON_TestSimdutf requires CMake 3.18 or later (simdutf's minimum).")
|
|
endif()
|
|
|
|
include(FetchContent)
|
|
|
|
# simdutf builds its tests and tools by default, and its tests pull
|
|
# further dependencies of their own; only the library is needed here
|
|
set(SIMDUTF_TESTS OFF CACHE BOOL "" FORCE)
|
|
set(SIMDUTF_TOOLS OFF CACHE BOOL "" FORCE)
|
|
set(SIMDUTF_BENCHMARKS OFF CACHE BOOL "" FORCE)
|
|
set(SIMDUTF_ICONV OFF CACHE BOOL "" FORCE)
|
|
|
|
FetchContent_Declare(simdutf
|
|
URL https://github.com/simdutf/simdutf/archive/refs/tags/v${JSON_SIMDUTF_VERSION}.tar.gz
|
|
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
|
|
)
|
|
FetchContent_MakeAvailable(simdutf)
|
|
|
|
target_compile_definitions(test_main PUBLIC JSON_USE_SIMDUTF)
|
|
target_link_libraries(test_main PUBLIC simdutf::simdutf)
|
|
|
|
# simdutf.h requires C++17; below that the library keeps its scalar
|
|
# validator, so any C++11/14 test targets exercise the fallback and the
|
|
# C++17-and-later ones exercise simdutf. Both must agree.
|
|
message(STATUS "UTF-8 validation delegated to simdutf ${JSON_SIMDUTF_VERSION} for C++17 and later (JSON_USE_SIMDUTF)")
|
|
endif()
|
|
endif()
|
|
|
|
# *DO* use json_test_set_test_options() above this line
|
|
|
|
json_test_should_build_32bit_test(json_32bit_test json_32bit_test_only "${JSON_32bitTest}")
|
|
file(GLOB files CONFIGURE_DEPENDS src/unit-*.cpp)
|
|
if(json_32bit_test_only)
|
|
set(files src/unit-32bit.cpp)
|
|
elseif(NOT json_32bit_test)
|
|
list(FILTER files EXCLUDE REGEX src/unit-32bit.cpp)
|
|
endif()
|
|
|
|
foreach(file ${files})
|
|
json_test_add_test_for(${file} MAIN test_main CXX_STANDARDS ${test_cxx_standards} ${test_force})
|
|
endforeach()
|
|
|
|
# tests/src/unit-no-macro-leak.cpp #include-s the generated leak-check file,
|
|
# so its test targets must be built after generate_hedley_undef_checks.
|
|
foreach(cxx_standard ${test_cxx_standards})
|
|
if(TARGET test-no-macro-leak_cpp${cxx_standard})
|
|
add_dependencies(test-no-macro-leak_cpp${cxx_standard} generate_hedley_undef_checks)
|
|
endif()
|
|
endforeach()
|
|
|
|
if(json_32bit_test_only)
|
|
# Skip all other tests in this file
|
|
return()
|
|
endif()
|
|
|
|
# test legacy comparison of discarded values
|
|
json_test_set_test_options(test-comparison_legacy
|
|
COMPILE_DEFINITIONS JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON=1
|
|
)
|
|
json_test_add_test_for(src/unit-comparison.cpp
|
|
NAME test-comparison_legacy
|
|
MAIN test_main CXX_STANDARDS ${test_cxx_standards} ${test_force}
|
|
)
|
|
|
|
# test the parser again with JSON_DIAGNOSTIC_POSITIONS enabled
|
|
json_test_set_test_options(test-class_parser_diagnostic_positions
|
|
COMPILE_DEFINITIONS JSON_DIAGNOSTIC_POSITIONS=1
|
|
)
|
|
json_test_add_test_for(src/unit-class_parser.cpp
|
|
NAME test-class_parser_diagnostic_positions
|
|
MAIN test_main CXX_STANDARDS ${test_cxx_standards} ${test_force}
|
|
)
|
|
|
|
# test diagnostic positions again without regular diagnostics (JSON pointer paths)
|
|
json_test_set_test_options(test-diagnostic-positions_only
|
|
COMPILE_DEFINITIONS JSON_DIAGNOSTICS=0
|
|
)
|
|
json_test_add_test_for(src/unit-diagnostic-positions.cpp
|
|
NAME test-diagnostic-positions_only
|
|
MAIN test_main CXX_STANDARDS ${test_cxx_standards} ${test_force}
|
|
)
|
|
|
|
# *DO NOT* use json_test_set_test_options() below this line
|
|
|
|
#############################################################################
|
|
# test ABI compatibility
|
|
#############################################################################
|
|
|
|
add_subdirectory(abi)
|
|
|
|
#############################################################################
|
|
# Test the generated build configs
|
|
#############################################################################
|
|
|
|
# these tests depend on the generated file nlohmann_jsonConfig.cmake
|
|
if (JSON_Install)
|
|
add_subdirectory(cmake_import)
|
|
add_subdirectory(cmake_import_minver)
|
|
endif()
|
|
|
|
add_subdirectory(cmake_add_subdirectory)
|
|
add_subdirectory(cmake_fetch_content)
|
|
add_subdirectory(cmake_fetch_content2)
|
|
add_subdirectory(cmake_target_include_directories)
|
|
|
|
# fmt (fetched by tests/fmt_formatter) requires a genuinely modern, C++17-capable
|
|
# toolchain; skip it on legacy/niche toolchains where fmt itself is known not to build
|
|
set(JSON_FMT_FORMATTER_TEST_SUPPORTED ${compiler_supports_cpp_17})
|
|
# fmt 12's 128-bit integer emulation does not build with 32-bit MinGW
|
|
if (MINGW AND CMAKE_SIZEOF_VOID_P EQUAL 4)
|
|
set(JSON_FMT_FORMATTER_TEST_SUPPORTED FALSE)
|
|
endif()
|
|
# the MSVC STL rejects Clang versions older than 19 as a host compiler
|
|
if (WIN32 AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.0)
|
|
set(JSON_FMT_FORMATTER_TEST_SUPPORTED FALSE)
|
|
endif()
|
|
if (JSON_FMT_FORMATTER_TEST_SUPPORTED)
|
|
add_subdirectory(fmt_formatter)
|
|
endif()
|