1

Recently, I have been building a simple test-suite with CTest. In its most simple form it looked something like this:

cmake_minimum_required(VERSION 3.7)
project(testsuite)
enable_testing()

add_test(NAME test_0
  WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
  COMMAND script.sh arg-0)

add_test(NAME test_1
  WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
  COMMAND script.sh arg-1)

add_test(NAME test_2
  WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
  COMMAND script.sh arg-2)

However, as it was growing, I quickly became tired of having to repeat WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} in every test.

Question: Is there a way to set WORKING_DIRECTORY globally (or, yet better, locally within each test subdirectory)? Something like

cmake_minimum_required(VERSION 3.7)
project(testsuite)
enable_testing()

set(DEFAULT_WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})

add_test(NAME test_0
  COMMAND script.sh arg-0)

add_test(NAME test_1
  COMMAND script.sh arg-1)

add_test(NAME test_2
  COMMAND script.sh arg-2)

The closest to a solution so far is a recommendation of my dear colleague to define a test-generating function:

cmake_minimum_required(VERSION 3.7)
project(testsuite)
enable_testing()

function(add_my_test TEST_NAME TEST_COMMAND TEST_ARGUMENTS)
  add_test(NAME ${TEST_NAME}
    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
    COMMAND ${TEST_COMMAND} ${TEST_ARGUMENTS})
endfunction()

add_my_test(test_0 script.sh arg-0)
add_my_test(test_1 script.sh arg-1)
add_my_test(test_2 script.sh arg-2)

This works fine but it feels like an unnecessary level of indirection.

In the end, I opted for using a fixture to copy the necessary parts into the build folder, test there and clean up again; this is probably a cleaner and more idiomatic solution. Nevertheless, out of curiosity I would still be interested in a solution to the original problem.

Dominik Mokriš
  • 1,118
  • 1
  • 8
  • 29
  • If your tests don't write files in the working directory (only read), then a wrapper is fine and maybe even better (avoids copies). Otherwise, you should never pollute the source tree, so the fixture solution would be best. – Alex Reinking May 22 '22 at 16:05

1 Answers1

1

Is there a way to set WORKING_DIRECTORY globally

No. The default WORKING_DIRECTORY for add_test is specified in documentation.

This works fine

Do it. I would do:

macro(add_my_test)
  add_test(
    WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
    ${ARGV}
  )
endmacro()

add_my_test(
  NAME test_1
  COMMAND script.sh arg-1
)

You can also roll your own functionality 'Overwrite' cmake command using a macro and restore its default behavior , but I believe just a wrapper function is great and clean.

KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • The maintainers state in no uncertain terms that no one should attempt to redefine built in CMake commands. A wrapper is the right approach here – Alex Reinking May 22 '22 at 16:01
  • @AlexReinking and yet people use it because sometimes the alternatives are worse. So the maintainers have to [budge](https://gitlab.kitware.com/cmake/cmake/-/issues/23482). Sometimes an inadvertent feature become so widespread that you have no choice but ack and improve it. Like it was with SFINAE in C++. But I'm not saying that it is worth it in this particular example. – ixSci May 22 '22 at 16:12
  • @ixSci - In the linked issue, the maintainers remained _steadfast_ that users should not override built-in commands. The discussion is about developing new, more principled features that obviate the need. To call this "budging" is silly. – Alex Reinking May 22 '22 at 16:16
  • @AlexReinking, yeah and Brad created the issue just for fun. Call it whatever you want, I call it budging. And that's actually a normal response from a maintainer: it is a tool for people not the other way around. So it is obvious that people need an ability to modify the behavior of the CMake commands and that ability should be added in whatever form the maintainers see fit. – ixSci May 22 '22 at 16:34
  • @KamilCuk: How would you invoke your version of ```add_my_test```? When I call ```add_my_test(script.sh arg-0)``` as before, it gets confused, thinking that the test is called "WORKING_DIRECTORY" and that the output of ```${CMAKE_CURRENT_SOURCE_DIR}``` is my executable. – Dominik Mokriš May 23 '22 at 06:48
  • @DominikMokriš you should use it as if you would use `add_test`, so you need to use `NAME`, `COMMAND` etc. I would also wrap `"${CMAKE_CURRENT_SOURCE_DIR}"` to make sure you won't be bitten by whitespaces (it is usually a good idea to wrap any dereferenced variable in quotes until otherwise is required) – ixSci May 23 '22 at 07:33
  • 1
    `to make sure you won't be bitten by whitespaces` CMake is not shell, there is no word splitting expansion. CMake splits on `;`. – KamilCuk May 23 '22 at 07:52
  • @KamilCuk Thanks for updating the code (macro really seems necessary instead of a function) and ```$ARGV``` is a neat trick that I hadn't known. However, I still had to swap the order (first ```$ARGV```, then the ```WORKING_DIRECTORY```) to make it work. – Dominik Mokriš May 23 '22 at 19:05