Expected build-failure tests in CMake - c++

Sometimes it's good to check that certain things fail to build, e.g.:
// Next line should fail to compile: can't convert const iterator to iterator.
my_new_container_type::iterator it = my_new_container_type::const_iterator();
Is it possible to incorporate these types of things into CMake/CTest? I'm looking for something like this in CMakeLists.txt:
add_build_failure_executable(
test_iterator_conversion_build_failure
iterator_conversion_build_failure.cpp)
add_build_failure_test(
test_iterator_conversion_build_failure
test_iterator_conversion_build_failure)
(Of course, these specific CMake directives don't exist, to the best of my knowledge.)

You can do this more or less as you described. You can add a target which will fail to compile, then add a test which invokes cmake --build to try to build the target. All that remains is to set the test property WILL_FAIL to true.
So, say you have your tests in a file named "will_fail.cpp" which contains:
#if defined TEST1
non-compiling code for test 1
#elif defined TEST2
non-compiling code for test 2
#endif
Then you can have something like the following in your CMakeLists.txt:
cmake_minimum_required(VERSION 3.0)
project(Example)
include(CTest)
# Add a couple of failing-to-compile targets
add_executable(will_fail will_fail.cpp)
add_executable(will_fail_again will_fail.cpp)
# Avoid building these targets normally
set_target_properties(will_fail will_fail_again PROPERTIES
EXCLUDE_FROM_ALL TRUE
EXCLUDE_FROM_DEFAULT_BUILD TRUE)
# Provide a PP definition to target the appropriate part of
# "will_fail.cpp", or provide separate files per test.
target_compile_definitions(will_fail PRIVATE TEST1)
target_compile_definitions(will_fail_again PRIVATE TEST2)
# Add the tests. These invoke "cmake --build ..." which is a
# cross-platform way of building the given target.
add_test(NAME Test1
COMMAND ${CMAKE_COMMAND} --build . --target will_fail --config $<CONFIGURATION>
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
add_test(NAME Test2
COMMAND ${CMAKE_COMMAND} --build . --target will_fail_again --config $<CONFIGURATION>
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
# Expect these tests to fail (i.e. cmake --build should return
# a non-zero value)
set_tests_properties(Test1 Test2 PROPERTIES WILL_FAIL TRUE)
You can obviously wrap all of this into a function or macro if you have a lot of these to write.

#Fraser's answer is a good approach, in particular the WILL_FAIL property is good advice. There is an alternative to making the failing target part of the main project though. The use case in the question is pretty much what the ctest --build-and-test mode is meant for. Rather than making the expected-to-fail target part of the main build, you can put it in its own separate mini project which is then built as part of a test. An example of how this might look in the main project goes something like this:
add_test(NAME iter_conversion
COMMAND ${CMAKE_CTEST_COMMAND}
--build-and-test
${CMAKE_CURRENT_LIST_DIR}/test_iter
${CMAKE_CURRENT_BINARY_DIR}/test_iter
--build-generator ${CMAKE_GENERATOR}
--test-command ${CMAKE_CTEST_COMMAND}
)
set_tests_properties(iter_conversion PROPERTIES WILL_FAIL TRUE)
This has the advantage that it will be part of the project's test results and will therefore be more likely to get executed regularly as part of normal testing processes. In the above example, the test_iter directory is essentially it's own separate project. If you need to pass information to it from the main build, you can do that by adding --build-options to define cache variables to pass to it's CMake run. Check the latest docs for recently corrected/clarified help on this area.

For the particular example in the question I agree with the comment. This is should be tested with a static_assert rather than a build failure test.
For a way of adding build failure test with CMake (in the cases where this is a good idea) I recently created a library which more or less allows adding such tests with a single CMake function call. It expands on the accepted answer and allows build-failure test and checks that the failure happened because of a specific error (the expected error can be provided in CMake or in a source file): https://github.com/iboB/icm/blob/master/icm_build_failure_testing.cmake

Related

Setting up GoogleTest to run all tests in one executable

In setting up my most recent project I am attempting to use GoogleTest. Currently I have this:
macro(add_test_l TEST_NAME)
# Create the executable and use the last arguments as source files.
add_executable(${TEST_NAME} ${ARGN})
# Link in the AMR and gtest libraries.
target_link_libraries(${TEST_NAME}
${PROJECT_NAME}
gtest
gmock
gtest_main
)
# Add the test to gtest.
add_test(
NAME ${TEST_NAME}
COMMAND ${TEST_NAME}
)
list(APPEND TEST_SOURCE_FILES ${ARGN})
endmacro()
Then I can add my tests like add_test_l(Element Sets/Element.test.cpp) which is working and convenient. But this of course creates an executable for each test, which is not a bad thing as it allows for quick testing of a single file.
Though I do want the ability to run all the tests with a single exe (that way CI will be easier) so I have that list at the end of my macro and after adding all my individual tests I have:
add_executable(all_tests ${TEST_SOURCE_FILES})
target_link_libraries(all_tests
${PROJECT_NAME}
gtest
gmock
gtest_main
)
Which creates an EXE to run all my test cases.
This does not seem efficient as I compile all my files twice. Is there a better way for me to achieve the desired outcome? Perhaps I can just add an option to enable / disable individual vs all tests exes.
It is unnecessary to have an executable per each file. Build one executable for all tests and learn the gtest option --gtest_filter. You can run each test individually:
all_tests --gtest_filter=Element.Test
Or you can run all Element tests like the macro add_test_l does it:
all_tests --gtest_filter=Element.*
More info about command line options is available:
all_tests --help
One of the useful commands:
all_tests --gtest_list_tests

How can I keep ctest from deleting my tests on failure? [duplicate]

I am building a test executable using CMake. During the build process, I would like to run the executable, which returns whether the tests pass or not. If not, I would like the build to fail. However, when I use add_custom_command(... POST_BUILD ... ), and use a Makefile generator, the test executable will be deleted (explain in this question: Why does GNU make delete a file).
Is there a way to have CMake treat the executable as a .PRECIOUS, or otherwise change the CMakeLists.txt such that the executable doesn't get deleted if the tests fail?
For reference, my CMakeList.txt looks like the following (simplified from actual):
add_executable(UnitTest unittest.cpp)
add_custom_command(TARGET UnitTest POST_BUILD COMMAND $<TARGET_FILE:UnitTest>)
The solution that I was alluding to was to use add_custom_target instead of add_custom_command. While it will not delete the executable if the test fails and the build process as a whole fails if runUnitTest fails, this target does not get built as a result of building the UnitTest target specifically.
add_executable(UnitTest unittest.cpp)
add_custom_target(runUnitTest UnitTest COMMAND $<TARGET_FILE:UnitTest> DEPENDS UnitTest)
I struggled with this same problem: I have a unit test I only want to run in the following conditions:
The test has been modified.
The code under test has been modified.
The test failed its previous execution.
In the event the test is run and fails I want the test binary to remain for debugging.
The solution I ended up with uses a flag file to indicate the test has run and no longer needs to execute again. Note the flag must live in the binary directory so that other builds are not impacted. However the WORKING_DIRECTORY is set so the test can access read-only data files relative to its source location. Here's what it all looks like -- I stuck this in a macro so all my various unit tests can invoke this, the only input to the macro is the test executable:
set (TEST_FLAG_FILE ${CMAKE_CURRENT_BINARY_DIR}/${TEST_EXECUTABLE}.PASSED)
# This isn't normally needed since a rebuild will have a newer time stamp.
# But it adds robustness to handle changes to the system clock.
add_custom_command(TARGET ${TEST_EXECUTABLE}
COMMENT "Unit test ${TEST_EXECUTABLE} rebuilt; clearing flag ${TEST_FLAG_FILE}."
COMMAND ${CMAKE_COMMAND} -E remove -f ${TEST_FLAG_FILE}
POST_BUILD
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
# This command only runs if the flag isn't present or is older than the executable.
add_custom_command(OUTPUT ${TEST_FLAG_FILE}
COMMENT "Unit Test Execution: ${TEST_EXECUTABLE}"
COMMAND ${TEST_EXECUTABLE}
COMMAND ${CMAKE_COMMAND} -E touch ${TEST_FLAG_FILE}
MAIN_DEPENDENCY ${TEST_EXECUTABLE}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
# Any "make all" will evaluate the preceding custom command.
add_custom_target(run_${TEST_EXECUTABLE} ALL DEPENDS ${TEST_FLAG_FILE})

How do I enable test build options in CMake? [duplicate]

I'm trying CTest in CMake in order to automatically run some of my tests using make test target. The problem is CMake does not "understand" that the test I'm willing to run has to be built since it is part of the project.
So I'm looking for a way to explicitly specify this dependency.
It is arguably a bug in CMake (previously tracked here) that this doesn't work out of the box. A workaround is to do the following:
add_test(TestName ExeName)
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND}
DEPENDS ExeName)
Then you can run make check and it will compile and run the test. If you have several tests, then you would have to use DEPENDS exe1 exe2 exe3 ... in the above line.
There is actually a way to use make test. You need to define the build of the test executable as one of the tests and then add dependencies between the tests. That is:
ADD_TEST(ctest_build_test_code
"${CMAKE_COMMAND}" --build ${CMAKE_BINARY_DIR} --target test_code)
ADD_TEST(ctest_run_test_code test_code)
SET_TESTS_PROPERTIES(ctest_run_test_code
PROPERTIES DEPENDS ctest_build_test_code)
I use a variant of richq's answer. In the top-level CMakeLists.txt, I add a custom target, build_and_test, for building and running all tests:
find_package(GTest)
if (GTEST_FOUND)
enable_testing()
add_custom_target(build_and_test ${CMAKE_CTEST_COMMAND} -V)
add_subdirectory(test)
endif()
In the various sub-project CMakeLists.txt files under test/, I add each test executable as a dependency of build_and_test:
include_directories(${CMAKE_SOURCE_DIR}/src/proj1)
include_directories(${GTEST_INCLUDE_DIRS})
add_executable(proj1_test proj1_test.cpp)
target_link_libraries(proj1_test ${GTEST_BOTH_LIBRARIES} pthread)
add_test(proj1_test proj1_test)
add_dependencies(build_and_test proj1_test)
With this approach, I just need to make build_and_test instead of make test (or make all test), and it has the benefit of only building test code (and its dependencies). It's a shame I can't use the target name test. In my case, it's not so bad because I have a top-level script that does out-of-tree debug and release (and cross-compiled) builds by calling cmake and then make, and it translates test into build_and_test.
Obviously, the GTest stuff isn't required. I just happen to use/like Google Test, and wanted to share a complete example of using it with CMake/CTest. IMHO, this approach also has the benefit of allowing me to use ctest -V, which shows the Google Test output while the tests run:
1: Running main() from gtest_main.cc
1: [==========] Running 1 test from 1 test case.
1: [----------] Global test environment set-up.
1: [----------] 1 test from proj1
1: [ RUN ] proj1.dummy
1: [ OK ] proj1.dummy (0 ms)
1: [----------] 1 test from proj1 (1 ms total)
1:
1: [----------] Global test environment tear-down
1: [==========] 1 test from 1 test case ran. (1 ms total)
1: [ PASSED ] 1 test.
1/2 Test #1: proj1_test ....................... Passed 0.03 sec
If you are using CMake >= 3.7, then the recommended approach is to use fixtures:
add_executable(test test.cpp)
add_test(test_build
"${CMAKE_COMMAND}"
--build "${CMAKE_BINARY_DIR}"
--config "$<CONFIG>"
--target test
)
set_tests_properties(test_build PROPERTIES FIXTURES_SETUP test_fixture)
add_test(test test)
set_tests_properties(test PROPERTIES FIXTURES_REQUIRED test_fixture)
This does the following:
Adds a test executable target built from test.cpp
Adds a test_build "test" that runs Cmake to build target test
Marks the test_build test to be a setup task of fixture test_fixture
Add a test test that just runs the test executable
Marks the test test to need fixture test_fixture.
So, every time test test is to be run, it first runs test test_build, which builds the necessary executable.
If you are trying to emulate make check, you may find this wiki entry usefull :
http://www.cmake.org/Wiki/CMakeEmulateMakeCheck
I have just checked that is does what it says with success (CMake 2.8.10).
Save yourself the headache:
make all test
Works out of the box for me and will build dependencies before running the test. Given how simple this is, it almost makes the native make test functionality convenient because it gives you the option of running the last compiling tests even if your code is broken.
For CMake 3.10 or later, another option is to use the TEST_INCLUDE_FILES directory property to set up a script that triggers a build before a test is run. In your outermost CMakeLists.txt add the following code:
set_property(DIRECTORY APPEND
PROPERTY TEST_INCLUDE_FILES "${CMAKE_CURRENT_BINARY_DIR}/BuildTestTarget.cmake")
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/BuildTestTarget.cmake"
"execute_process(COMMAND \"${CMAKE_COMMAND}\""
" --build \"${CMAKE_BINARY_DIR}\""
" --config \"\$ENV{CMAKE_CONFIG_TYPE}\")")
The actual test configuration is passed through to the build via the environment variable CMAKE_CONFIG_TYPE. Optionally you can add a --target option to only build targets required by the test.
This is what I hammered out and have been using:
set(${PROJECT_NAME}_TESTS a b c)
enable_testing()
add_custom_target(all_tests)
foreach(test ${${PROJECT_NAME}_TESTS})
add_executable(${test} EXCLUDE_FROM_ALL ${test}.cc)
add_test(NAME ${test} COMMAND $<TARGET_FILE:${test}>)
add_dependencies(all_tests ${test})
endforeach(test)
build_command(CTEST_CUSTOM_PRE_TEST TARGET all_tests)
string(CONFIGURE \"#CTEST_CUSTOM_PRE_TEST#\" CTEST_CUSTOM_PRE_TEST_QUOTED ESCAPE_QUOTES)
file(WRITE "${CMAKE_BINARY_DIR}/CTestCustom.cmake" "set(CTEST_CUSTOM_PRE_TEST ${CTEST_CUSTOM_PRE_TEST_QUOTED})" "\n")
YMMV
Derrick's answer, simplified and commented:
# It is impossible to make target "test" depend on "all":
# https://gitlab.kitware.com/cmake/cmake/-/issues/8774
# Set a magic variable in a magic file that tells ctest
# to invoke the generator once before running the tests:
file(WRITE "${CMAKE_BINARY_DIR}/CTestCustom.cmake"
"set(CTEST_CUSTOM_PRE_TEST ${CMAKE_MAKE_PROGRAM})\n"
)
It is not perfectly correct, as it does not solve the concurrency problem of running ninja all test, in case anyone does that. On the contrary, because now, you have two ninja processes.
(Ftr, I also shared this solution here.)
All above answers are perfect. But actually CMake use CTest as its testing tools, so the standard method (I think it is) to do the mission is:
enable_testing ()
add_test (TestName TestCommand)
add_test (TestName2 AnotherTestCommand)
Then run cmake and make to build the targets. After that, you can either run make test, or just run
ctest
you will get the result. This is tested under CMake 2.8 .
Check details at: http://cmake.org/Wiki/CMake/Testing_With_CTest#Simple_Testing
All the answers are good, but they imply a breach of tradition to run a test by command make test. I've done this trick:
add_test(NAME <mytest>
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMAND sh -c "make <mytarget>; $<TARGET_FILE:<mytarget>>")
This means that the test consists of building (optionally) and running of executable target.

CMake Set option ON in just one CMakeLists

Actually, I've a project with different CMakeLists : one for compilations and one for unit tests (with goole test) for each "module". I've a structure like that :
module_1
--|src
--CMakeLists.txt // for compilation of module 1
--test
----|CMakeLists.txt // for unit tests of module 1
module_2
--|src
--CMakeLists.txt // for compilation of module 2
--test
----|CMakeLists // for unit tests of module 2
Now, my goal is to make only one CMakeLists.txt for each module and have compilation and unit test in the same CMakeLists.txt. So I make an OPTION in my CMakeLists.txt (for compilation) who trigger the test.
option(TEST_ENABLED
"Tests Unitaires Core"
OFF
)
if(TEST_ENABLED)
set...
endif()
It's working good for one CMakeLists. But if I build all my modules, the option is activate for each module (I hope you follow me).
cmake -DTEST_ENABLED=ON -G"Unix Makefiles" ../module1
And I trigger compilation with a server (Jenkins) with some $variables so I want to activate the options only for the main CMakeLists (the one who's called with cmake command line)
How I can do that ?
If you want more information, tell me
Thanks in advnace for help.
Options and other user-provided parameters are set globally for a project, so they affect on all its parts.
You may use different options for different parts of you project:
module1/CMakeLists.txt:
option(TEST1_ENABLED
"Test for module 1"
OFF
)
...
Alternatively, in case of tests, you can use single user-supplied parameter enumerating all modules, for which option should be set:
CMakeLists.txt:
set(TESTS_LIST "" CACHE STRING "List of tests")
module1/CMakeLists.txt:
list(module_index FIND "module1" ${TESTS_LIST})
if(module_index EQUAL -1)
set(TEST_ENABLED OFF) # No CACHE, so variable is local for the module1.
else()
set(TEST_ENABLED ON)
endif()
if(TEST_ENABLED)
...
endif()
Possible usage:
cmake "-DTESTS_LIST=module1;module3" -G "Unix Makefiles" ..
This will enable testing for module1 and module3, but not for module2.
About testing, my favorite way is enabling testing with two conditions :
Variable TEST_ENABLED is ON
Current project is the main project for cmake
The second condition may be tested with PROJECT_NAME and CMAKE_PROJECT_NAME
Source : https://stackoverflow.com/a/8587949/1380225
Sample :
if(TEST_ENABLED AND ("${PROJECT_NAME}" STREQUAL "${CMAKE_PROJECT_NAME}"))
...
endif()

What is the name of CMake's default build target?

I have a custom target, and I want it to depend on the default target (the one that is built with make).
add_custom_target(foo ....)
add_dependency(foo default_target_name_goes_here)
What is the name of the default target?
I've tried ALL, ALL_BUILD, MyProjectsName, DEFAULT,...
Finding anything in the CMake documentation is always an unsuccessful adventure...
UPDATE: it seems CMake was designed in such a way that this is extremely hard to fix/implement: bugreport getting +1's since 2009. Who indeed would like to have a custom target that depends on, for example, the all target? Or in other words: who does ever write make && make test?...
The default build target does not exist as a CMake target at CMake configure time. It is only exists in the generated build system. Therefore it is not possible to have the default target depend on a custom target.
I think a possible solution depends strongly on the use case. E.g. if this is for executing a test after the system has been build you would use CTest instead of calling make directly.
To your CMakeLists.txt you would add:
add_test(NAME foo COMMAND ...)
and then use CTest for building and executing:
ctest --build-and-test ...
More generally speaking and not considering the question of why you would like to do it - I think the best thing would be to just name and rely on concrete target dependencies instead of just taking ALL targets - I just wanted to add two possibilities to do what you wanted to do.
One would be to determine/track the list of all targets used as discussed here. This would look e.g. for library targets like this (getting your own/private GlobalTargetList):
macro(add_library _target)
_add_library(${_target} ${ARGN})
set_property(GLOBAL APPEND PROPERTY GlobalTargetList ${_target})
endmacro()
and use it at the end of your main CMakeLists.txt with
get_property(_allTargets GLOBAL PROPERTY GlobalTargetList)
add_dependencies(foo ${_allTargets})
Edit: Global BUILDSYSTEM_TARGETS property was released with CMake 3.7
The second - less favorable - approach does require that the foo target is not part of the ALL build (otherwise you end-up in an endless loop):
add_custom_target(foo)
set_target_properties(foo PROPERTIES EXCLUDE_FROM_ALL 1)
add_custom_command(
TARGET foo
PRE_BUILD
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target ALL_BUILD --config $<CONFIGURATION>
)