CMake and Test Suites

The FindGTest module that ships with CMake provides the command GTEST_ADD_TESTS , which registers all the test cases from a test executable with CMake so they are listed individually in the report generated by CTest.

The [FindGTest](http://www.cmake.org/cmake/help/v3.3/module/FindGTest.html) module that ships with CMake provides the command GTESTADDTESTS, which registers all the test cases from a test executable with CMake so they are listed individually in the report generated by CTest. The following snippet shows its use:

```cmake

enable_testing()

find_package(GTest REQUIRED)

includedirectories(${GTESTINCLUDE_DIRS})

set(SOURCES foo.cpp bar.cpp cow.cpp)

add_executable(foobar ${SOURCES})

targetlinklibraries(foobar ${GTESTBOTHLIBRARIES})

GTESTADDTESTS(foobar "" ${SOURCES})

```

As we see, the GTESTADDTESTS command receives the list of source files. Starting with CMake version 3.1, we can also pass AUTO instead of a list of sources, which tells GTESTADDTESTS to retrieve the list of source files from the SOURCES target property. Either way, it then analyzes the source code with some regular expressions to find all the test cases.

This approach is problematic. Whenever we add or remove test cases, CMake needs to rerun. This approach also fails to find test cases where the header spans two lines...

```cpp

TEST(SomeLongSectionName,

AnEvenLongerTestName)

{

}

```

```cpp

/* Add tests like this:

TEST(Section, Test) {

put the test code here.

}

*/

```

Why are the sources parsed at all? The list of test cases can easily be retrieved from the compiled test executable via the command line. Instead of parsing the source code, we could parse the output:

```cmake

executeprocess(COMMAND "/path/to/foobar" --gtestlist_tests

OUTPUT_VARIABLE output)

string(REPLACE "\n" ";" lines "${output}")

set(section)

foreach(line IN LISTS lines)

if(line MATCHES "^([A-Za-z_/0-9]+)\\.$")

set(section "${CMAKEMATCH1}.")

elseif(line MATCHES "^ ([A-Za-z_/0-9]+)")

addtest("${section}${CMAKEMATCH_1}"

"/path/to/foobar" --gtestfilter="${section}${CMAKEMATCH_1}")

endif()

endforeach()

```

Putting the above snippet of CMake code into our CMakeLists.txt file requires the test executable to be available at configure time. Of course, that does not make any sense at all. No, we want this code to be in the CTestTestfile.cmake file!

What we need is a CMake command that writes the above block of code, and we pass it only the options that are specific to the test framework in use.

Such a command might be used for [GTest](https://code.google.com/p/googletest/):

```cmake

addtestsuite(foobar

LISTTESTFLAGS "--gtestlisttests"

SELECTTESTFLAGS "--gtest_filter=<SECTION><TEST>"

SECTIONREGEX "^([A-Za-z/0-9]+)\\.$"

TESTREGEX "^ ([A-Za-z/0-9]+)"

)

```

And for [Catch](https://github.com/philsquared/Catch):

```cmake

addtestsuite(foobar

LISTTESTFLAGS "--list-test-names-only"

SELECTTESTFLAGS "<TEST>"

SECTION_REGEX "^$"

TEST_REGEX "^(.+)$"

)

```

And for [GLib Test](https://developer.gnome.org/glib/stable/glib-Testing.html):

```cmake

addtestsuite(foobar

LISTTESTFLAGS "-l"

SELECTTESTFLAGS "-p <TEST>"

SECTION_REGEX "^$"

TEST_REGEX "^(.+)$"

)

```

And for [Qt Test](http://doc.qt.io/qt-5/qtest-overview.html):

```cmake

addtestsuite(foobar

LISTTESTFLAGS "-functions"

SELECTTESTFLAGS "<TEST>"

SECTION_REGEX "^$"

TEST_REGEX "([(]+)$"

)

```

And a sad running joke: For Boost.Test, it would be possible too, [but only with the version on trunk](http://stackoverflow.com/a/10746477/269803).


Write a comment
No comments yet.