* Scan json_view strings with SIMD and index large objects Speed up json_view's parser with SIMD scanning and a hash table for large objects. Long runs of string bytes are scanned 16 bytes at a time with NEON (AArch64, GCC and Clang) and SSE2 (x86-64), both baseline instruction sets. Keys keep 16 table checks before the vector loop, because their lengths repeat from record to record; string values get 8, because their lengths vary more. Non-ASCII text is validated 16 bytes at a time with simdjson's "lookup4" check (Keiser and Lemire, 2021), with NEON on AArch64 and, on x86-64, with SSSE3. SSSE3 is not part of baseline x86-64, so the check is compiled for SSSE3 with a function attribute and used only where CPUID reports it, which all x86-64 CPUs since about 2011 do; the answer is cached in a statically initialized atomic, so there is no guard of a local static and no global constructor. The same input is accepted either way. JSON_VIEW_NO_SIMD selects the portable code. On x86-64, string runs are now checked vector-first: one SSE2 compare from the first byte finds the end of most keys and short values, instead of a branch per byte for the first 8-16 bytes. AArch64 keeps the byte-wise steps, where a NEON mask costs more and the branches predict well. Entering an object or array no longer stalls: open() stores the parent's frame field by field instead of building it on the stack and reading it back with wider loads, which waited for the narrower stores to retire. Objects with 128 members or more get an open-addressing hash table built when the object closes, so operator[], at(), find(), contains(), count(), value(), and JSON pointers take constant time on average in such objects; of duplicate keys, the first is kept, as for the linear search. The idea comes from Boost.JSON. simdjson is credited in simd.hpp's SPDX block, the README, and license.md. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix GCC module build and MSVC C4127 in the json_view SIMD code GCC ignores the target attribute in modules, so the SSSE3 dispatch is disabled for the module interface (the check stays portable, SSE2 is kept). Make the 8-vs-16 byte unrolling condition in scan_string_run a preprocessor/template split to avoid a constant condition (C4127). Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Remove an accidentally committed GCC module cache Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Bound the probe length of the large-object hash index The key hash is not seeded, so keys chosen to collide made building the table quadratic (20,000 colliding keys took 470 ms to parse). A key may now sit at most 64 slots from its home slot; if a key would sit further away, the table is dropped and the object is searched linearly. Lookups stop after the same distance. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Release the spare capacity of the hash index in shrink_to_fit shrink_to_fit() now trims the tables of large objects like the node array and the decoded strings, and the list of large objects is released as soon as the tables are built. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Use the portable string scan on non-x86 targets that define __SSE2__ WebAssembly with -msse2 defines __SSE2__, but has no <cpuid.h> or x86 intrinsics headers. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Document the hash index number in node::extra Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Keep the last of duplicate keys in the object hash index Lookups in objects with 128 members or more now return the last member of a repeated key, like the linear search of smaller objects and like materialize() and basic_json::parse(). build_object_index let the first occurrence win, so the same text gave different results depending on the size of the object. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Keep the first of duplicate keys in the object hash index again Lookups in objects with 128 members or more return the first member of a repeated key again, like the linear search of smaller objects. This undoes the code change of a69542046; its test now expects the first member from lookups (with and without a table) and the last value from materialize() and basic_json::parse(). Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix clang-tidy 22 findings in the json_view headers Silence bugprone-casting-through-void for the SSE loads (a reinterpret_cast would trip -Wcast-align=strict), use auto for a cast initialiser, add parentheses to a mixed expression, and name bugprone-std-namespace-modification in the NOLINTs of the tuple_size/tuple_element specialisations. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix clang-tidy 22 findings in unit-json_view.cpp and unit-json_view_builder.cpp Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Avoid GCC useless-cast and strict-overflow warnings in unit-json_view.cpp Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Index strings with size_t in the colliding-keys test (MSVC C4244 on Win32) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Make json_view.hpp pass include-what-you-use ci_single_binaries runs IWYU with --error on every header. For json_view.hpp it suggested adding <array> (std::array is used), the headers that json.hpp already provides (abi_config, abi_macros, input_adapters, json_pointer, cpp_future, string_concat, value_t, json_fwd), and <version> for std::nullptr_t, and removing <cstddef>. - include <array> - keep <cstddef> (nullptr_t, size_t; IWYU attributes them to <version> and <cstring>) - tell IWYU not to suggest the headers that json.hpp provides: the amalgamated json_view.hpp only includes json.hpp, so including them here would duplicate their definitions Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Move the dump, comparison, and large-object tests to unit-json_view_dump.cpp The dump and comparison tests moved to their own file in the dump pull request; the hash index tests of this branch join them there. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Regenerate the amalgamated headers Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Follow the renames from the review of the view builder The review of #5620 renamed document_data's tape/tape_size/tape_cap to nodes/node_count/node_capacity and made the decode arena a std::vector<char>; adapt the code of this change to it. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Rename slots in the object index for Qt compatibility Qt defines slots as a macro, so a Qt header included before json_view.hpp broke the declarations of the local variable (see #5865). The test variable is renamed as well. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Address the review of the json_view object index and SIMD scan From gregmarr's review of #5628: - Docs and comments say that the hash index is one of the object's keys. - object_index.hpp: conditional_static_cast for the hash, "slot" for the table position, no flag for a degenerate table, and a static_assert that the smallest indexed object is larger than the probe window. - scan.hpp unrolls with NLOHMANN_VIEW_REPEAT_FIRST_8/SECOND_8. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Regenerate the amalgamated headers Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Encode JSON_VIEW_NO_SIMD and JSON_VIEW_USE_SSSE3 in the ABI namespace Both macros select between definitions of the same inline functions, so translation units that disagree on them violated the ODR. Like the other ABI-affecting macros, they now add a tag to the inline namespace (_vns and _vssse3), and the docs replace the "Define consistently" warning with the usual ABI compatibility note. From gregmarr's review of #5628. nlohmann_json.natvis covers twice as many tags now, so it lists four times as many namespaces (14.7 MB instead of 3.4 MB). Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Regenerate the amalgamated headers Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Do not include type_traits.hpp directly in the object index json.hpp, which object_index.hpp already includes, provides it; included directly, the json_view amalgamation (which only treats nlohmann/json.hpp as external) copied the json internals into json_view.hpp a second time, so the fuzzers, which include both single headers, failed with redefinitions (make_void, integer_sequence, priority_tag, ...). Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Compare keys with key_equals() in the object hash index As the linear lookups do since the second review round of #5622. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Regenerate the amalgamated headers Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me>
461 lines
21 KiB
CMake
461 lines
21 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.")
|
|
set(JSON_TestShard "" CACHE STRING "Build only a part of the unit tests, given as <index>/<count> (e.g. 0/2), to split them across CI jobs with a time limit.")
|
|
|
|
# using an env var, since this will also affect targets executing cmake (such as "ci_test_compiler_default")
|
|
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.
|
|
# Disable warning C5285: cannot declare a specialization for 'std::tuple'; MSVC 19.51
|
|
# reports the forward declarations of standard library
|
|
# templates in the vendored doctest.h
|
|
$<$<CXX_COMPILER_ID:MSVC>:/W4;/wd4566;/wd4996;/wd4702;/wd4503;/wd5285>
|
|
# 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>
|
|
|
|
# several tests call deprecated functions on purpose to keep them covered
|
|
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wno-deprecated-declarations>
|
|
$<$<CXX_COMPILER_ID:Intel>:-diag-disable=1786>)
|
|
target_include_directories(test_main SYSTEM PUBLIC
|
|
thirdparty/doctest)
|
|
target_include_directories(test_main PUBLIC
|
|
${PROJECT_BINARY_DIR}/include)
|
|
target_link_libraries(test_main PUBLIC ${NLOHMANN_JSON_TARGET_NAME})
|
|
|
|
# thirdparty/fifo_map is only used by the #972 regression test, so only
|
|
# test-regression1 needs it on its include path (see json_test_set_test_options
|
|
# below), rather than every test-* target via test_main.
|
|
add_library(fifo_map_include INTERFACE)
|
|
target_include_directories(fifo_map_include SYSTEM INTERFACE thirdparty/fifo_map)
|
|
|
|
#############################################################################
|
|
# 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-bon8;test-cbor;test-msgpack;test-ubjson;test-bjdata;test-bjdata_round_trip_draft2;test-bjdata_round_trip_draft3;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>
|
|
)
|
|
|
|
# only the #972 regression test needs thirdparty/fifo_map on its include path
|
|
json_test_set_test_options(test-regression1 LINK_LIBRARIES fifo_map_include)
|
|
|
|
# Regression test for GCC's false -Warray-bounds error with JSON_DIAGNOSTICS (#5742, fixed in #5585). It only
|
|
# showed up when optimizing, so build this test with -O3 and the warning as an error.
|
|
# -O3 makes the optimizer-driven warnings of the ci_test_gcc flag set (-Winline,
|
|
# -Wsuggest-attribute=...) fire on the library's inline functions; they are not
|
|
# what this test checks, so turn them off for it.
|
|
json_test_set_test_options(test-diagnostics-optimized
|
|
COMPILE_OPTIONS $<$<CXX_COMPILER_ID:GNU>:-O3 -Werror=array-bounds -Wno-inline -Wno-suggest-attribute=pure -Wno-suggest-attribute=const>
|
|
)
|
|
|
|
#############################################################################
|
|
# 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()
|
|
|
|
# test-file_input checks from a second thread that parsing from a FILE* releases the file's lock
|
|
find_package(Threads REQUIRED)
|
|
json_test_set_test_options("test-file_input;test-file_input_unlocked_io_disabled" LINK_LIBRARIES Threads::Threads)
|
|
|
|
# *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()
|
|
|
|
# with JSON_TestShard=<index>/<count>, keep every <count>-th unit test file,
|
|
# starting at <index> (the glob is sorted, so the split is stable)
|
|
set(test_shard_index 0)
|
|
if(NOT "${JSON_TestShard}" STREQUAL "")
|
|
if(NOT JSON_TestShard MATCHES "^([0-9]+)/([1-9][0-9]*)$")
|
|
message(FATAL_ERROR "JSON_TestShard must be <index>/<count>, e.g. 0/2, not '${JSON_TestShard}'.")
|
|
endif()
|
|
set(test_shard_index ${CMAKE_MATCH_1})
|
|
set(test_shard_count ${CMAKE_MATCH_2})
|
|
if(NOT test_shard_index LESS test_shard_count)
|
|
message(FATAL_ERROR "JSON_TestShard: the index must be less than the count, not '${JSON_TestShard}'.")
|
|
endif()
|
|
list(LENGTH files test_file_count)
|
|
set(shard_files "")
|
|
set(file_position 0)
|
|
foreach(file ${files})
|
|
math(EXPR file_shard "${file_position} % ${test_shard_count}")
|
|
if(file_shard EQUAL test_shard_index)
|
|
list(APPEND shard_files ${file})
|
|
endif()
|
|
math(EXPR file_position "${file_position} + 1")
|
|
endforeach()
|
|
set(files ${shard_files})
|
|
list(LENGTH files shard_file_count)
|
|
message(STATUS "Test shard ${JSON_TestShard}: ${shard_file_count} of ${test_file_count} unit test files")
|
|
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()
|
|
|
|
# the following variants of single test files are only built in the first shard
|
|
if(test_shard_index EQUAL 0)
|
|
|
|
# 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}
|
|
)
|
|
|
|
# test reading from a std::istream again without scanning the get area in bulk
|
|
json_test_set_test_options(test-stream-bulk-scan_disabled
|
|
COMPILE_DEFINITIONS JSON_DISABLE_STREAM_BULK_SCAN=1
|
|
)
|
|
json_test_add_test_for(src/unit-stream-bulk-scan.cpp
|
|
NAME test-stream-bulk-scan_disabled
|
|
MAIN test_main CXX_STANDARDS ${test_cxx_standards} ${test_force}
|
|
)
|
|
|
|
# test reading from a std::istream again with JSON_PRECISE_STREAM_POSITION
|
|
json_test_set_test_options(test-stream-bulk-scan_precise
|
|
COMPILE_DEFINITIONS JSON_PRECISE_STREAM_POSITION=1
|
|
)
|
|
json_test_add_test_for(src/unit-stream-bulk-scan.cpp
|
|
NAME test-stream-bulk-scan_precise
|
|
MAIN test_main CXX_STANDARDS ${test_cxx_standards} ${test_force}
|
|
)
|
|
|
|
# test reading from a FILE* again with the locking stdio functions
|
|
json_test_set_test_options(test-file_input_unlocked_io_disabled
|
|
COMPILE_DEFINITIONS JSON_DISABLE_UNLOCKED_FILE_IO=1
|
|
)
|
|
json_test_add_test_for(src/unit-file_input.cpp
|
|
NAME test-file_input_unlocked_io_disabled
|
|
MAIN test_main CXX_STANDARDS ${test_cxx_standards} ${test_force}
|
|
)
|
|
|
|
# the json_view parser again with the portable string scanning instead of
|
|
# NEON/SSE2, and on x86-64 with the SSSE3 UTF-8 check (JSON_VIEW_USE_SSSE3)
|
|
json_test_set_test_options(test-json_view_builder_portable
|
|
COMPILE_DEFINITIONS JSON_VIEW_NO_SIMD
|
|
)
|
|
json_test_add_test_for(src/unit-json_view_builder.cpp
|
|
NAME test-json_view_builder_portable
|
|
MAIN test_main CXX_STANDARDS ${test_cxx_standards} ${test_force}
|
|
)
|
|
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|AMD64|amd64)$" AND NOT MSVC)
|
|
json_test_set_test_options(test-json_view_builder_ssse3
|
|
COMPILE_DEFINITIONS JSON_VIEW_USE_SSSE3
|
|
COMPILE_OPTIONS -mssse3
|
|
)
|
|
json_test_add_test_for(src/unit-json_view_builder.cpp
|
|
NAME test-json_view_builder_ssse3
|
|
MAIN test_main CXX_STANDARDS ${test_cxx_standards} ${test_force}
|
|
)
|
|
endif()
|
|
endif()
|
|
|
|
# *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()
|