1# - Add tests using boost::test
2#
3# Add this line to your test files in place of including a basic boost test header:
4#  #include <BoostTestTargetConfig.h>
5#
6# If you cannot do that and must use the included form for a given test,
7# include the line
8#  // OVERRIDE_BOOST_TEST_INCLUDED_WARNING
9# in the same file with the boost test include.
10#
11#  include(BoostTestTargets)
12#  add_boost_test(<testdriver_name> SOURCES <source1> [<more sources...>]
13#   [FAIL_REGULAR_EXPRESSION <additional fail regex>]
14#   [LAUNCHER <generic launcher script>]
15#   [LIBRARIES <library> [<library>...]]
16#   [RESOURCES <resource> [<resource>...]]
17#   [TESTS <testcasename> [<testcasename>...]])
18#
19#  If for some reason you need access to the executable target created,
20#  it can be found in ${${testdriver_name}_TARGET_NAME} as specified when
21#  you called add_boost_test
22#
23# Requires CMake 2.6 or newer (uses the 'function' command)
24#
25# Requires:
26# 	GetForceIncludeDefinitions
27# 	CopyResourcesToBuildTree
28#
29# Original Author:
30# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
31# http://academic.cleardefinition.com
32# Iowa State University HCI Graduate Program/VRAC
33#
34# Copyright Iowa State University 2009-2010.
35# Distributed under the Boost Software License, Version 1.0.
36# (See accompanying file LICENSE_1_0.txt or copy at
37# http://www.boost.org/LICENSE_1_0.txt)
38
39if(__add_boost_test)
40	return()
41endif()
42set(__add_boost_test YES)
43
44set(BOOST_TEST_TARGET_PREFIX "boosttest")
45
46if(NOT Boost_FOUND)
47	find_package(Boost 1.34.0 QUIET)
48endif()
49if("${Boost_VERSION}0" LESS "1034000")
50	set(_shared_msg
51		"NOTE: boost::test-based targets and tests cannot "
52		"be added: boost >= 1.34.0 required but not found. "
53		"(found: '${Boost_VERSION}'; want >=103400) ")
54	if(BUILD_TESTING)
55		message(FATAL_ERROR
56			${_shared_msg}
57			"You may disable BUILD_TESTING to continue without the "
58			"tests.")
59	else()
60		message(STATUS
61			${_shared_msg}
62			"BUILD_TESTING disabled, so continuing anyway.")
63	endif()
64endif()
65
66include(GetForceIncludeDefinitions)
67include(CopyResourcesToBuildTree)
68
69if(Boost_FOUND AND NOT "${Boost_VERSION}0" LESS "1034000")
70	set(_boosttesttargets_libs)
71	set(_boostConfig "BoostTestTargetsIncluded.h")
72	if(NOT Boost_UNIT_TEST_FRAMEWORK_LIBRARY)
73		find_package(Boost 1.34.0 QUIET COMPONENTS unit_test_framework)
74	endif()
75	if(Boost_UNIT_TEST_FRAMEWORK_LIBRARY)
76		set(_boosttesttargets_libs "${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}")
77		if(Boost_USE_STATIC_LIBS)
78			set(_boostConfig "BoostTestTargetsStatic.h")
79		else()
80			if(NOT APPLE)
81				set(_boostConfig "BoostTestTargetsDynamic.h")
82			endif()
83		endif()
84	endif()
85	get_filename_component(_moddir ${CMAKE_CURRENT_LIST_FILE} PATH)
86	configure_file("${_moddir}/${_boostConfig}"
87		"${CMAKE_CURRENT_BINARY_DIR}/BoostTestTargetConfig.h"
88		COPYONLY)
89	include_directories("${CMAKE_CURRENT_BINARY_DIR}")
90endif()
91
92function(add_boost_test _name)
93	if(NOT BUILD_TESTING)
94		return()
95	endif()
96
97	# parse arguments
98	set(_nowhere)
99	set(_curdest _nowhere)
100	set(_val_args
101		SOURCES
102		FAIL_REGULAR_EXPRESSION
103		LAUNCHER
104		LIBRARIES
105		RESOURCES
106		TESTS)
107	set(_bool_args
108		USE_COMPILED_LIBRARY)
109	foreach(_arg ${_val_args} ${_bool_args})
110		set(${_arg})
111	endforeach()
112	foreach(_element ${ARGN})
113		list(FIND _val_args "${_element}" _val_arg_find)
114		list(FIND _bool_args "${_element}" _bool_arg_find)
115		if("${_val_arg_find}" GREATER "-1")
116			set(_curdest "${_element}")
117		elseif("${_bool_arg_find}" GREATER "-1")
118			set("${_element}" ON)
119			set(_curdest _nowhere)
120		else()
121			list(APPEND ${_curdest} "${_element}")
122		endif()
123	endforeach()
124
125	if(_nowhere)
126		message(FATAL_ERROR "Syntax error in use of add_boost_test!")
127	endif()
128
129	if(NOT SOURCES)
130		message(FATAL_ERROR
131			"Syntax error in use of add_boost_test: at least one source file required!")
132	endif()
133
134	if(Boost_FOUND AND NOT "${Boost_VERSION}0" LESS "1034000")
135
136		include_directories(${Boost_INCLUDE_DIRS})
137
138		set(includeType)
139		foreach(src ${SOURCES})
140			file(READ ${src} thefile)
141			if("${thefile}" MATCHES ".*BoostTestTargetConfig.h.*")
142				set(includeType CONFIGURED)
143				set(includeFileLoc ${src})
144				break()
145			elseif("${thefile}" MATCHES ".*boost/test/included/unit_test.hpp.*")
146				set(includeType INCLUDED)
147				set(includeFileLoc ${src})
148				set(_boosttesttargets_libs)	# clear this out - linking would be a bad idea
149				if(NOT
150					"${thefile}"
151					MATCHES
152					".*OVERRIDE_BOOST_TEST_INCLUDED_WARNING.*")
153					message("Please replace the include line in ${src} with this alternate include line instead:")
154					message("  \#include <BoostTestTargetConfig.h>")
155					message("Once you've saved your changes, re-run CMake. (See BoostTestTargets.cmake for more info)")
156				endif()
157				break()
158			endif()
159		endforeach()
160
161		if(NOT _boostTestTargetsNagged${_name} STREQUAL "${includeType}")
162			if("includeType" STREQUAL "CONFIGURED")
163				message(STATUS
164					"Test '${_name}' uses the CMake-configurable form of the boost test framework - congrats! (Including File: ${includeFileLoc})")
165			elseif("${includeType}" STREQUAL "INCLUDED")
166				message("In test '${_name}': ${includeFileLoc} uses the 'included' form of the boost unit test framework.")
167			else()
168				message("In test '${_name}': Didn't detect the CMake-configurable boost test include.")
169				message("Please replace your existing boost test include in that test with the following:")
170				message("  \#include <BoostTestTargetConfig.h>")
171				message("Once you've saved your changes, re-run CMake. (See BoostTestTargets.cmake for more info)")
172			endif()
173		endif()
174		set(_boostTestTargetsNagged${_name}
175			"${includeType}"
176			CACHE
177			INTERNAL
178			""
179			FORCE)
180
181
182		if(RESOURCES)
183			list(APPEND SOURCES ${RESOURCES})
184		endif()
185
186		# Generate a unique target name, using the relative binary dir
187		# and provided name. (transform all / into _ and remove all other
188		# non-alphabet characters)
189		file(RELATIVE_PATH
190			targetpath
191			"${CMAKE_BINARY_DIR}"
192			"${CMAKE_CURRENT_BINARY_DIR}")
193		string(REGEX REPLACE "[^A-Za-z/_]" "" targetpath "${targetpath}")
194		string(REPLACE "/" "_" targetpath "${targetpath}")
195
196		set(_target_name ${BOOST_TEST_TARGET_PREFIX}-${targetpath}-${_name})
197		set(${_name}_TARGET_NAME "${_target_name}" PARENT_SCOPE)
198
199		# Build the test.
200		add_executable(${_target_name} ${SOURCES})
201
202		list(APPEND LIBRARIES ${_boosttesttargets_libs})
203
204		if(LIBRARIES)
205			target_link_libraries(${_target_name} ${LIBRARIES})
206		endif()
207
208		if(RESOURCES)
209			set_property(TARGET ${_target_name} PROPERTY RESOURCE ${RESOURCES})
210			copy_resources_to_build_tree(${_target_name})
211		endif()
212
213		if(NOT Boost_TEST_FLAGS)
214#			set(Boost_TEST_FLAGS --catch_system_error=yes --output_format=XML)
215			set(Boost_TEST_FLAGS --catch_system_error=yes)
216		endif()
217
218		# TODO: Figure out why only recent boost handles individual test running properly
219
220		if(LAUNCHER)
221			set(_test_command ${LAUNCHER} "\$<TARGET_FILE:${_target_name}>")
222		else()
223			set(_test_command ${_target_name})
224		endif()
225
226		if(TESTS AND ( "${Boost_VERSION}" VERSION_GREATER "103799" ))
227			foreach(_test ${TESTS})
228				add_test(
229					${_name}-${_test}
230					${_test_command} --run_test=${_test} ${Boost_TEST_FLAGS}
231				)
232				if(FAIL_REGULAR_EXPRESSION)
233					set_tests_properties(${_name}-${_test}
234						PROPERTIES
235						FAIL_REGULAR_EXPRESSION
236						"${FAIL_REGULAR_EXPRESSION}")
237				endif()
238			endforeach()
239		else()
240			add_test(
241				${_name}-boost_test
242				${_test_command} ${Boost_TEST_FLAGS}
243			)
244			if(FAIL_REGULAR_EXPRESSION)
245				set_tests_properties(${_name}-boost_test
246					PROPERTIES
247					FAIL_REGULAR_EXPRESSION
248					"${FAIL_REGULAR_EXPRESSION}")
249			endif()
250		endif()
251
252		# CppCheck the test if we can.
253		if(COMMAND add_cppcheck)
254			add_cppcheck(${_target_name} STYLE UNUSED_FUNCTIONS)
255		endif()
256
257	endif()
258endfunction()
259