Adding Googletest To Existing CMake Project
Asked Answered
N

1

6

I'm having trouble integrating googletest into my existing project. I put together a simple project to represent my project's structure:

Project Structure

Project Structure Image (Can't post directly without 10 rep)

CMakeLists.txt:

cmake_minimum_required(VERSION 3.13)
project(TestTester)
set(CMAKE_CXX_STANDARD 14)

include_directories(existing_source)
add_subdirectory(existing_source)
add_subdirectory(new_test_source)

existing_source/CMakeLists.txt:

cmake_minimum_required(VERSION 3.13)
project(Test_TestTester)
set(CMAKE_CXX_STANDARD 14)

add_executable(TestTester main.cpp existing.h)

new_test_source/CMakeLists.txt:

cmake_minimum_required(VERSION 3.13)
project(Test_TestTester)
set(CMAKE_CXX_STANDARD 14)

find_package(PkgConfig REQUIRED)
pkg_check_modules(gtest REQUIRED gtest>=1.8.1)

SET(CMAKE_CXX_FLAGS -pthread)
enable_testing()

include_directories(${gtest_INCLUDE_DIRS})

add_executable(Test_TestTester main_test.cpp ../existing_source/existing.h)
target_link_libraries(Test_TestTester ${gtest_LIBRARIES})
add_test(NAME Test COMMAND Test_TestTester)

existing_source/existing.h

#ifndef TESTTESTER_EXISTING_H
#define TESTTESTER_EXISTING_H

int sample() {
    return 1;
}

#endif //TESTTESTER_EXISTING_H

existing_source/main.cpp

#include <iostream>
#include "existing.h"

int main() {
    std::cout << "sample() output = " << sample() << std::endl;
    return 0;
}

new_test_source/main_test.cpp

#include <gtest/gtest.h>
#include "../existing_source/existing.h"

TEST(SampleTestCase, TestOneIsOne) {
    EXPECT_EQ(1, 1);
}

TEST(ExistingCodeTestCase, TestSample) {
    EXPECT_EQ(1, sample());
}


GTEST_API_ int main(int argc, char **argv) {
    printf("Running main() from %s\n", __FILE__);
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

Goal:

Building with CMake will create two executables, one, TestTester, and another called Test_TestTester (sorry for the odd name, it looks like I could have chosen a better project name!).

TestTester will be the main project executable which will run the code from existing_course/main.cpp and output sample() output = 1.

Test_TestTester should be the unit tests from main_test.cpp which tests that 1 == 1 and 1 == sample(). This should run when the project is built.

Attempts:

I've tried using CMake's add_subdirectory() to expose a second CMakeLists.txt in the test subdirectory which has its own add_executable() with the name of the test program, however I cannot find any output related to the test program. Using enable_testing() followed by add_test() is also failing to produce any changes.

Update:

I realized some problems and assumptions were wrong.

  • Within CLion, it defaults to building a particular target. Build all (cmake --build ... --target all) must be invoked to build the other executables.
  • The other questions I read related to this don't seem to be using the pre-compiled library. I compiled and installed googletest on my machine prior to its inclusion into the project.
  • This may not be a problem, but it looks like most people choose to structure their projects with each directory having its own CMakeLists.txt file. I reorganized mine to match to make following others' advice easier.

I updated the CMakeLists files with my changes. Using --target all builds everything appropriately, but I still can't get the tests to run when the project is built.

Nevski answered 12/2, 2019 at 16:59 Comment(2)
get the tests to run what do you mean? How? Is there an error message? What did you tried? How do you run them? Does the test executable build properly? What if you run the test executable by itself? And why do you provide the definition for a symbol with external linkage inside a .h file? Shouldn't the sample function have static or static inline at least?Franni
The tests run by executing the Test_TestTester executable. Sorry if I wasn't clear, I meant the hang-up at this point is integrating execution of the tests into the build process. The COMMAND part of add_test() is to tell CMake what command to execute in order to run the test, right? Static or inline specifiers may be more correct but it's just a valid minimal example.Nevski
F
10

There's very little wrong with the specimen project you've posted.

It seems you've mistakenly assumed that:

add_test(NAME Test COMMAND Test_TestTester)

in your new_test_source/CMakeLists.txt is all it takes to get your Test_TestTester executed by make.

In fact, as declared by the first line of the add_test documentation:

Add a test to the project to be run by ctest(1).

your add_test command only suffices to get Test_TestTester run when, after make, you run ctest in the build directory of the Test_TestTester sub-project.

Furthermore, even this will only happen if you enable ctest testing for that sub-project by invoking enable_testing() in new_test_source/CMakeLists.txt, which you don't. You say you did that later:

Using enable_testing() followed by add_test() is also failing to produce any changes.

But that's because you still haven't done anything but create tests that you can run with ctest, and still haven't run ctest.

So assume I've got your specimen project in my working directory (which I have), and that I've just changed new_test_source/CMakeLists.txt by changing:

project(Test_TestTester)

to:

project(Test_TestTester)
enable_testing()

Then:

$ mkdir build
$ cd build
$ cmake ..

generates the build system, and:

$ make
Scanning dependencies of target TestTester
[ 25%] Building CXX object existing_source/CMakeFiles/TestTester.dir/main.cpp.o
[ 50%] Linking CXX executable TestTester
[ 50%] Built target TestTester
Scanning dependencies of target Test_TestTester
[ 75%] Building CXX object new_test_source/CMakeFiles/Test_TestTester.dir/main_test.cpp.o
[100%] Linking CXX executable Test_TestTester
[100%] Built target Test_TestTester

builds everything, and:

# We're still in `./build`
$ cd new_test_source/
$ ctest
Test project /home/imk/develop/so/scrap2/build/new_test_source
    Start 1: Test
1/1 Test #1: Test .............................   Passed    0.00 sec

100% tests passed, 0 tests failed out of 1

Total Test time (real) =   0.00 sec

runs your tests. The complete test log is saved at:

$ cat ./Testing/Temporary/LastTest.log
Start testing: Feb 12 19:23 GMT
----------------------------------------------------------
1/1 Testing: Test
1/1 Test: Test
Command: "/home/imk/develop/so/scrap2/build/new_test_source/Test_TestTester"
Directory: /home/imk/develop/so/scrap2/build/new_test_source
"Test" start time: Feb 12 19:23 GMT
Output:
----------------------------------------------------------
Running main() from /home/imk/develop/so/scrap2/new_test_source/main_test.cpp
[==========] Running 2 tests from 2 test suites.
[----------] Global test environment set-up.
[----------] 1 test from SampleTestCase
[ RUN      ] SampleTestCase.TestOneIsOne
[       OK ] SampleTestCase.TestOneIsOne (0 ms)
[----------] 1 test from SampleTestCase (0 ms total)

[----------] 1 test from ExistingCodeTestCase
[ RUN      ] ExistingCodeTestCase.TestSample
[       OK ] ExistingCodeTestCase.TestSample (0 ms)
[----------] 1 test from ExistingCodeTestCase (0 ms total)

[----------] Global test environment tear-down
[==========] 2 tests from 2 test suites ran. (0 ms total)
[  PASSED  ] 2 tests.
<end of output>
Test time =   0.00 sec
----------------------------------------------------------
Test Passed.
"Test" end time: Feb 12 19:23 GMT
"Test" time elapsed: 00:00:00
----------------------------------------------------------

End testing: Feb 12 19:23 GMT

If you'd like to see all that on your console at the time when you run ctest, you can run it in verbose mode ctest -V. Or if you'd like only to see the details if the tests fail, ctest --output-on-failure.

Everything's working as it should, and maybe you're happy with that, knowing how it works. If you still want to your tests to be run automatically by make - which is not conventional - then you'll need to add a custom post-build command to the Test_TestTester target to run ctest automatically. Just append, e.g.

add_custom_command(TARGET Test_TestTester
                   COMMENT "Run tests"
                   POST_BUILD COMMAND ctest ARGS --output-on-failure
)

to new_test_source/CMakeLists.txt. Then the output of a clean make is:

$ make
Scanning dependencies of target TestTester
[ 25%] Building CXX object existing_source/CMakeFiles/TestTester.dir/main.cpp.o
[ 50%] Linking CXX executable TestTester
[ 50%] Built target TestTester
Scanning dependencies of target Test_TestTester
[ 75%] Building CXX object new_test_source/CMakeFiles/Test_TestTester.dir/main_test.cpp.o
[100%] Linking CXX executable Test_TestTester
Run tests
Test project /home/imk/develop/so/scrap2/build/new_test_source
    Start 1: Test
1/1 Test #1: Test .............................   Passed    0.00 sec

100% tests passed, 0 tests failed out of 1

Total Test time (real) =   0.00 sec
[100%] Built target Test_TestTester
Forbid answered 12/2, 2019 at 19:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.