Skip to content

Commit cac4e9f

Browse files
Refactor unit test creation
Add functions for creating tests and to supply test- and standard-specific build settings. Raises minimum CMake version to 3.13 in test directory. json_test_add_test_for( <file> MAIN <main> [CXX_STANDARDS <version_number>...] [FORCE]) Given a <file> unit-foo.cpp, produces test-foo_cpp<version_number> if C++ standard <version_number> is supported by the compiler and thesource file contains JSON_HAS_CPP_<version_number>. Use FORCE to create the test regardless of the file containing JSON_HAS_CPP_<version_number>. Test targets are linked against <main>. CXX_STANDARDS defaults to "11". json_test_set_test_options( all|<tests> [CXX_STANDARDS all|<args>...] [COMPILE_DEFINITIONS <args>...] [COMPILE_FEATURES <args>...] [COMPILE_OPTIONS <args>...] [LINK_LIBRARIES <args>...] [LINK_OPTIONS <args>...]) Supply test- and standard-specific build settings. Specify multiple tests using a list e.g., "test-foo;test-bar". Must be called BEFORE the test is created.
1 parent 700b95f commit cac4e9f

3 files changed

Lines changed: 310 additions & 133 deletions

File tree

CMakeLists.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,13 @@ if (POLICY CMP0077)
3030
cmake_policy(SET CMP0077 NEW)
3131
endif ()
3232

33-
option(JSON_BuildTests "Build the unit tests when BUILD_TESTING is enabled." ${MAIN_PROJECT})
33+
# VERSION_GREATER_EQUAL is not available in CMake 3.1
34+
if(${MAIN_PROJECT} AND (${CMAKE_VERSION} VERSION_EQUAL 3.13 OR ${CMAKE_VERSION} VERSION_GREATER 3.13))
35+
set(JSON_BuildTests_INIT ON)
36+
else()
37+
set(JSON_BuildTests_INIT OFF)
38+
endif()
39+
option(JSON_BuildTests "Build the unit tests when BUILD_TESTING is enabled." ${JSON_BuildTests_INIT})
3440
option(JSON_CI "Enable CI build targets." OFF)
3541
option(JSON_Diagnostics "Use extended diagnostic messages." OFF)
3642
option(JSON_ImplicitConversions "Enable implicit conversions." ON)

cmake/test.cmake

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
set(_json_test_cmake_list_file ${CMAKE_CURRENT_LIST_FILE})
2+
3+
#############################################################################
4+
# download test data
5+
#############################################################################
6+
7+
include(${CMAKE_CURRENT_SOURCE_DIR}/../cmake/download_test_data.cmake)
8+
9+
# test fixture to download test data
10+
add_test(NAME "download_test_data" COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR}
11+
--target download_test_data
12+
)
13+
set_tests_properties(download_test_data PROPERTIES FIXTURES_SETUP TEST_DATA)
14+
15+
if(JSON_Valgrind)
16+
find_program(CMAKE_MEMORYCHECK_COMMAND valgrind)
17+
message(STATUS "Executing test suite with Valgrind (${CMAKE_MEMORYCHECK_COMMAND})")
18+
set(memcheck_command "${CMAKE_MEMORYCHECK_COMMAND} ${CMAKE_MEMORYCHECK_COMMAND_OPTIONS} --error-exitcode=1 --leak-check=full")
19+
separate_arguments(memcheck_command)
20+
endif()
21+
22+
#############################################################################
23+
# detect standard support
24+
#############################################################################
25+
26+
# C++11 is the minimum required
27+
set(compiler_supports_cpp_11 TRUE)
28+
29+
foreach(feature ${CMAKE_CXX_COMPILE_FEATURES})
30+
if (${feature} STREQUAL cxx_std_14)
31+
set(compiler_supports_cpp_14 TRUE)
32+
elseif (${feature} STREQUAL cxx_std_17)
33+
set(compiler_supports_cpp_17 TRUE)
34+
elseif (${feature} STREQUAL cxx_std_20)
35+
set(compiler_supports_cpp_20 TRUE)
36+
elseif (${feature} STREQUAL cxx_std_23)
37+
set(compiler_supports_cpp_23 TRUE)
38+
endif()
39+
endforeach()
40+
41+
#############################################################################
42+
# test functions
43+
#############################################################################
44+
45+
#############################################################################
46+
# json_test_set_test_options(
47+
# all|<tests>
48+
# [CXX_STANDARDS all|<args>...]
49+
# [COMPILE_DEFINITIONS <args>...]
50+
# [COMPILE_FEATURES <args>...]
51+
# [COMPILE_OPTIONS <args>...]
52+
# [LINK_LIBRARIES <args>...]
53+
# [LINK_OPTIONS <args>...])
54+
#
55+
# Supply test- and standard-specific build settings.
56+
# Specify multiple tests using a list e.g., "test-foo;test-bar".
57+
#
58+
# Must be called BEFORE the test is created.
59+
#############################################################################
60+
61+
function(json_test_set_test_options tests)
62+
cmake_parse_arguments(args "" ""
63+
"CXX_STANDARDS;COMPILE_DEFINITIONS;COMPILE_FEATURES;COMPILE_OPTIONS;LINK_LIBRARIES;LINK_OPTIONS"
64+
${ARGN})
65+
66+
if(NOT args_CXX_STANDARDS)
67+
set(args_CXX_STANDARDS "all")
68+
endif()
69+
70+
foreach(test ${tests})
71+
if("${test}" STREQUAL "all")
72+
set(test "")
73+
endif()
74+
75+
foreach(cxx_standard ${args_CXX_STANDARDS})
76+
if("${cxx_standard}" STREQUAL "all")
77+
if("${test}" STREQUAL "")
78+
message(FATAL_ERROR "Not supported. Change defaults in: ${_json_test_cmake_list_file}")
79+
endif()
80+
set(test_interface _json_test_interface_${test})
81+
else()
82+
set(test_interface _json_test_interface_${test}_cpp_${cxx_standard})
83+
endif()
84+
85+
if(NOT TARGET ${test_interface})
86+
add_library(${test_interface} INTERFACE)
87+
endif()
88+
89+
target_compile_definitions(${test_interface} INTERFACE ${args_COMPILE_DEFINITIONS})
90+
target_compile_features(${test_interface} INTERFACE ${args_COMPILE_FEATURES})
91+
target_compile_options(${test_interface} INTERFACE ${args_COMPILE_OPTIONS})
92+
target_link_libraries (${test_interface} INTERFACE ${args_LINK_LIBRARIES})
93+
target_link_options(${test_interface} INTERFACE ${args_LINK_OPTIONS})
94+
endforeach()
95+
endforeach()
96+
endfunction()
97+
98+
# for internal use by json_test_add_test_for()
99+
function(_json_test_add_test test_name file main cxx_standard)
100+
set(test_target ${test_name}_cpp${cxx_standard})
101+
102+
if(TARGET ${test_target})
103+
message(FATAL_ERROR "Target ${test_target} has already been added.")
104+
endif()
105+
106+
add_executable(${test_target} ${file})
107+
target_link_libraries(${test_target} PRIVATE ${main})
108+
109+
# set and require C++ standard
110+
set_target_properties(${test_target} PROPERTIES
111+
CXX_STANDARD ${cxx_standard}
112+
CXX_STANDARD_REQUIRED ON
113+
)
114+
115+
# apply standard-specific build settings
116+
if(TARGET _json_test_interface_""_cpp_${cxx_standard})
117+
target_link_libraries(${testcase_target} PRIVATE _json_test_interface_""_cpp_${cxx_standard})
118+
endif()
119+
120+
# apply test-specific build settings
121+
if(TARGET _json_test_interface_${test_name})
122+
target_link_libraries(${test_target} PRIVATE _json_test_interface_${test_name})
123+
endif()
124+
125+
# apply test- and standard-specific build settings
126+
if(TARGET _json_test_interface_${test_name}_cpp_${cxx_standard})
127+
target_link_libraries(${test_target} PRIVATE
128+
_json_test_interface_${test_name}_cpp_${cxx_standard}
129+
)
130+
endif()
131+
132+
if (JSON_FastTests)
133+
add_test(NAME ${test_target}
134+
COMMAND ${test_target} ${DOCTEST_TEST_FILTER}
135+
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
136+
)
137+
else()
138+
add_test(NAME ${test_target}
139+
COMMAND ${test_target} ${DOCTEST_TEST_FILTER} --no-skip
140+
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
141+
)
142+
endif()
143+
set_tests_properties(${test_target} PROPERTIES LABELS "all" FIXTURES_REQUIRED TEST_DATA)
144+
145+
if(JSON_Valgrind)
146+
add_test(NAME ${test_target}_valgrind
147+
COMMAND ${memcheck_command} $<TARGET_FILE:${test_target}> ${DOCTEST_TEST_FILTER}
148+
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
149+
)
150+
set_tests_properties(${test_target}_valgrind PROPERTIES
151+
LABELS "valgrind" FIXTURES_REQUIRED TEST_DATA
152+
)
153+
endif()
154+
endfunction()
155+
156+
#############################################################################
157+
# json_test_add_test_for(
158+
# <file>
159+
# MAIN <main>
160+
# [CXX_STANDARDS <version_number>...] [FORCE])
161+
#
162+
# Given a <file> unit-foo.cpp, produces
163+
#
164+
# test-foo_cpp<version_number>
165+
#
166+
# if C++ standard <version_number> is supported by the compiler and the
167+
# source file contains JSON_HAS_CPP_<version_number>.
168+
# Use FORCE to create the test regardless of the file containing
169+
# JSON_HAS_CPP_<version_number>.
170+
# Test targets are linked against <main>.
171+
# CXX_STANDARDS defaults to "11".
172+
#############################################################################
173+
174+
function(json_test_add_test_for file)
175+
cmake_parse_arguments(args "FORCE" "MAIN" "CXX_STANDARDS" ${ARGN})
176+
177+
get_filename_component(file_basename ${file} NAME_WE)
178+
string(REGEX REPLACE "unit-([^$]+)" "test-\\1" test_name ${file_basename})
179+
180+
if("${args_MAIN}" STREQUAL "")
181+
message(FATAL_ERROR "Required argument MAIN <main> missing.")
182+
endif()
183+
184+
if("${args_CXX_STANDARDS}" STREQUAL "")
185+
set(args_CXX_STANDARDS 11)
186+
endif()
187+
188+
file(READ ${file} file_content)
189+
foreach(cxx_standard ${args_CXX_STANDARDS})
190+
if(NOT compiler_supports_cpp_${cxx_standard})
191+
continue()
192+
endif()
193+
194+
# add unconditionally if C++11 (default) or forced
195+
if(NOT ("${cxx_standard}" STREQUAL 11 OR args_FORCE))
196+
string(FIND "${file_content}" JSON_HAS_CPP_${cxx_standard} has_cpp_found)
197+
if(${has_cpp_found} EQUAL -1)
198+
continue()
199+
endif()
200+
endif()
201+
202+
_json_test_add_test(${test_name} ${file} ${args_MAIN} ${cxx_standard})
203+
endforeach()
204+
endfunction()

0 commit comments

Comments
 (0)