GoogleTest CMake and Make tests not running - c++

I admit I have a unique situation. We build our application using Make. But my IDE, CLion, uses CMake. So I have tried to set up GoogleTest to run on both (kind of). I can compile my code both ways (using make at the command line and build from my IDE). But from within CLion when I select the test fixture and click the run button, no tests are found and this is what I receive:
Running main() from gtest_main.cc
[==========] Running 0 tests from 0 test cases.
[==========] 0 tests from 0 test cases ran. (0 ms total)
[ PASSED ] 0 tests.
Process finished with exit code 0
Here is my test fixture:
#include <gtest/gtest.h>
#include "OPProperties.h"
namespace {
// The fixture for testing class OPPropertiesTestTest.
class OPPropertiesTestTest : public ::testing::Test {
protected:
// You can remove any or all of the following functions if its body
// is empty.
OPPropertiesTestTest() {
// You can do set-up work for each test here.
}
virtual ~OPPropertiesTestTest() {
// You can do clean-up work that doesn't throw exceptions here.
}
// If the constructor and destructor are not enough for setting up
// and cleaning up each test, you can define the following methods:
virtual void SetUp() {
// Code here will be called immediately after the constructor (right
// before each test).
}
virtual void TearDown() {
// Code here will be called immediately after each test (right
// before the destructor).
}
// Objects declared here can be used by all tests in the test case for OPPropertiesTestTest.
};
TEST_F(OPPropertiesTestTest, ThisTestWillPass) {
EXPECT_EQ(0, 0);
}
TEST_F(OPPropertiesTestTest, ThisTestWillFail) {
EXPECT_EQ(0, 5);
}
} // namespace
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
Here is my CMakeLists.txt file:
cmake_minimum_required(VERSION 2.8)
project(oneprint)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -pthread -lgtest")
add_definitions(-DBOOST_LOG_DYN_LINK)
set(SOURCE_FILES
src/controllers/OPProperties.cpp
src/controllers/OPProperties.h
src/main.cpp)
include_directories(src/controllers)
set(BOOST_ROOT "/usr/local/lib")
set(Boost_USE_STATIC_LIBS OFF)
set(Boost_USE_MULTITHREADED ON)
unset(Boost_INCLUDE_DIR CACHE)
unset(Boost_LIBRARY_DIRS CACHE)
#set(Boost_LIBRARY_DIR /usr/local/arm/lib)
set(OpenCV_LIBRARY_DIR /usr/include/opencv2)
set(Innovatrics_LIBRARY_DIR /usr/local/arm/lib)
find_package(OpenCV REQUIRED)
find_package(Boost 1.57.0 COMPONENTS filesystem thread log chrono system atomic program_options REQUIRED)
include_directories(${Boost_INCLUDE_DIRS})
enable_testing()
find_package(GTest REQUIRED)
include_directories(${GTEST_INCLUDE_DIRS})
add_executable(OnePrint ${SOURCE_FILES})
target_link_libraries(OnePrint ${OpenCV_LIBS})
target_link_libraries(OnePrint ${Boost_LIBRARIES})
target_link_libraries(OnePrint ${Innovatrics_LIBRARY_DIR})
target_link_libraries(OnePrint ${GTEST_BOTH_LIBRARIES})
target_link_libraries(OnePrint pthread)
I've added a folder under src called tests, which is where my test fixture, OPPropertiesTestTest, is located. I've also added a test folder at the top level. Included in this folder is a Makefile and a Srcs.mak file.
Here is the Makefile:
TARGET = oneprint
BASE = ../
-include $(BASE)Defs.x86.mak
-include $(BASE)OpenCV.mak
-include $(BASE)Boost.mak
-include $(BASE)Innovatrics.mak
-include $(BASE)GTest.mak
-include $(BASE)Incl.mak
-include Srcs.mak
-include $(BASE)Common.mak
-include $(BASE)App.mak
Here is the Srcs.mak file:
VPATH = \
../src/controllers:\
../src:\
../src/tests
CPP_SRCS = \
OPProperties.cpp \
# test files
CPP_SRCS += \
OPPropertiesTest.cpp

I wouldn't normally expect to see makefiles being mixed into a CMake-managed project; it sounds like you have quite a complicated setup?
That aside, I think the root cause here is likely to be twofold. I don't think the test executable in /src/tests/ is actually being built. I can't see anything in your CMakelists.txt which would cause this to be built, so unless you're doing something extra you've not shown us, the test files aren't being compiled.
What perhaps makes it look like they are, is that you're linking the helper lib which gtest provides into your OnePrint target. This helper lib is unusual in that it implements a main() function to save users having to specify their own.
You're doing this in the line
target_link_libraries(OnePrint ${GTEST_BOTH_LIBRARIES})
From the docs GTEST_BOTH_LIBRARIES is a variable containing both libgtest and libgtest-main. You only want libgtest since you've written your own main(). So you should use GTEST_LIBRARIES instead.
Note: There are a few other issues with the CMake code:
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -pthread -lgtest")
This isn't cross-platform (which may not matter to you) e.g. MSVC won't recognise any of these flags. If it does matter, you'd need to wrap this in an if(UNIX) or if(NOT MSVC) block.
Also, this sets the compiler flags globally, i.e. for every target defined in your project. If you need more fine-grained control, have a look at target_compile_options which lets you specify these per-target.
Another issue is the -lgtest part. This is really a call to the linker to link the gtest library. However, CMake takes care of that for you - you're doing this when you call target_link_libraries(OnePrint ${GTEST_LIBRARIES}).
add_definitions(-DBOOST_LOG_DYN_LINK)
While this is fine, the same comments about this being applied globally applies. The per-target equivalent is target_compile_definitions.
set(BOOST_ROOT "/usr/local/lib")
This is maybe the location of Boost on your machine, but that may well not be the case for other devs. That variable is really meant to be specified by individual users when they invoke CMake (if required... many paths are automatically searched by CMake) - you shouldn't really hard-code paths like this into the CMake file.
target_link_libraries(OnePrint pthread)
Again, pthread wouldn't be appropriate to link if you're using MSVC; this should probably be wrapped in an if block.
The final trivial point is that you can specify more than one dependency in the target_link_libraries command, so you could change these to:
if(NOT MSVC)
set(PThreadLib pthread)
endif()
target_link_libraries(OnePrint
${OpenCV_LIBS}
${Boost_LIBRARIES}
${Innovatrics_LIBRARY_DIR}
${GTEST_LIBRARIES}
${PThreadLib})

Related

cmake: build dependency downloaded with ExternalProject_Add before hand

I'm trying to get my test program main.cpp to link against a an externally downloaded library (in this case fmt 8.1.1) via cmake's 'ExternalProject_Add' function as follows
CMakeLists.txt
cmake_minimum_required(VERSION 3.23)
project(test)
set(CMAKE_CXX_STANDARD 20)
include(ExternalProject)
ExternalProject_Add(fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
GIT_TAG 8.1.1
CMAKE_ARGS -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_STANDARD=20 -DFMT_TEST=OFF -DFMT_DOC=OFF -DFMT_INSTALL=ON -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/fmt-build
STEP_TARGETS build
)
set(fmt_BINARY_DIR "${CMAKE_BINARY_DIR}/fmt-prefix/src/fmt-build")
set(fmt_SOURCE_DIR "${CMAKE_BINARY_DIR}/fmt-prefix/src/fmt/include")
add_executable(${PROJECT_NAME} main.cpp)
add_dependencies(${PROJECT_NAME} fmt-build) # does not have an effect
target_link_libraries(${PROJECT_NAME} PRIVATE ${fmt_BINARY_DIR}/libfmt.a)
target_include_directories(${PROJECT_NAME} PUBLIC ${fmt_SOURCE_DIR})
main.cpp
#include <chrono>
#include <fmt/core.h>
#include <fmt/chrono.h>
int main() {
const auto now = std::chrono::system_clock::now();
fmt::print("The time is {}", now);
return 0;
}
The test program main.cpp runs OK if I build the target fmt-build before hand, but complains that
ninja: error: 'fmt-prefix/src/fmt-build/libfmt.a', needed by 'test', missing and no known rule to make it
indicating that the dependency fmt-build must be built before hand. While that's doable, how do I get cmake to autmoatically build fmt-build as a dependency for test without haing to run it manually first? I thought the line add_dependencies(${PROJECT_NAME} fmt-build) should take care of that but evidently it does not.
Based on what #Tsyvarev commented, simply adding the line
BUILD_BYPRODUCTS ${CMAKE_BINARY_DIR}/fmt-prefix/src/fmt-build/libfmt.a
to the ExternalProject_Add call does the trick, and one does not have to manually build fmt-build before hand.

CMake split target_sources in CMakeLists.txt into different targets

I have a test project called test_containers. Inside directory there are only cpp files and CMakeLists.txt which looks like this:
cmake_minimum_required(VERSION 3.0.0)
project(test_containers)
add_executable(${PROJECT_NAME})
target_sources(${PROJECT_NAME} PRIVATE
test_linear_hash_table.cpp
test_list.cpp
test_stack.cpp
test_string.cpp
test_vector.cpp
test_set.cpp
test_array.cpp
test_span.cpp
)
target_link_libraries(${PROJECT_NAME} PRIVATE
gtest_main
)
add_test(
NAME ${PROJECT_NAME}
COMMAND ${PROJECT_NAME}
)
Now the problem is that after build I get only 1 executable bin file called test_containers and it contains all the tests from directory. I want to split this CMakeLists.txt into different targets so that these are different tests. How to do it right?
For your question, as described, you will need to create 1 target per file -- each building the individual source, linking the correct dependencies, and being added as a test.
You might be able to do something like (untested):
set(source_files
test_linear_hash_table.cpp
test_list.cpp
test_stack.cpp
test_string.cpp
test_vector.cpp
test_set.cpp
test_array.cpp
test_span.cpp
)
# Iterate each source
foreach(source IN LISTS source_files)
# Add the executable + source + dependencies
add_executable(${PROJECT_NAME}.${source})
target_sources(${PROJECT_NAME}.${source} PRIVATE ${source})
target_link_libraries(${PROJECT_NAME}.${source} PRIVATE gtest_main)
# Add a test
add_test(
NAME ${PROJECT_NAME}.${source}
COMMAND ${PROJECT_NAME}.${source}
)
endforeach()
(This assumes ${source} doesn't use any special or invalid characters for target names)
However, generally when people ask this question, it's because they would actually like 1 CTest test-case per test in a GTest executable. This would allow the CTest dashboard to show each of the different tests independently, which makes scanning for failures quite easy.
If this is what you are looking for, then there's already a module/function that can provide this for you with little effort. You can use the GoogleTest module with the gtest_discover_tests function, which will produce 1 CTest test-case per GTest test-case:
include(GoogleTest)
gtest_discover_tests(${PROJECT_NAME})

How can I use Catch2 to test my CMake static library project?

I'm writing a static library which contains some shared code between several projects. In order to verify that the code in this library functions properly I'd like to use Catch2 to do some unit testing on it.
Unfortunately, when attempting to run the tests I run into the problem that the compilation's output file is a shared library (.a), rather than an executable.
I'm sure I can create a separate project which uses the functions from my static library, and subsequently run tests that way, but ideally I'd like to keep the tests and build configurations as close as possible to one another.
So my question is:
what's the best way to set up my project such that I can use Catch2 for unit testing my static library code?
Here's my project's CMakeLists.txt file for reference:
project(sharedLib CXX)
find_package(OpenMP)
if (CMAKE_COMPILER_IS_GNUCC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fopenmp -lpthread -Wall -Wextra -Wpedantic -std=c++17")
endif()
if (MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fopenmp /W4 /std:c++latest")
endif()
include_directories (
src/
lib/Catch2/single_include/
)
file (GLOB_RECURSE LIBRARY_SOURCES src/*.cpp
src/*.c
tests/*.cpp)
add_library(${PROJECT_NAME} STATIC ${LIBRARY_SOURCES})
target_include_directories(${PROJECT_NAME} PUBLIC src/)
A common pattern for testing static libraries is to have a separate executable which contains all the tests, and then consumes your library. for example
file (GLOB_RECURSE TEST_SOURCES tests/*.cpp)
add_executable(my_lib_tests test_main.cpp ${TEST_SOURCES})
target_link_libraries(my_lib_tests PRIVATE sharedLib)
target_include_directories(my_lib_tests PRIVATE ../path/to/secret/impl/details)
Here I also have added some include some directories to implementation details of your shared lib which you may need to test, but don't want to expose to clients via a public header.
test_main.cpp need only be:
#define CONFIG_CATCH_MAIN
#include <catch2/catch.hpp>
Then you don't have to include things in your library's build that are unrelated to the library itself, speeding up compilation time for clients, while you can work from the perspective of the test fixture

gtest setup cmake to get it run

I want to include gtest to my C++ project. I am using Clion as IDE, which should work. Some tests are already working, but I cannot use any functions from B_RocChoice.h. It says that the function is not declared in this scope.
Can somebody tell me what I am doing wrong? How I must change my CMakeLists.txt files that it recogizes my methods?
This is my basic_tests.cpp, where my testcases will be written.
This is my Directory.
Here, the most outer CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(cli)
find_package( OpenCV REQUIRED )
include_directories( ${OpenCV_INCLUDE_DIRS} )
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -pthread")
set(SOURCE_FILES
include/A_WowbaggerChoice.h
include/AbstractChoice.h
include/B_RocChoice.h
include/C_CnnChoice.h
include/D_DetectorChoice.h
include/E_LearningChoice.h
include/Help.h
include/MyException.h
include/StartScreen.h
include/tinyxml.h
include/types.h
src/A_WowbaggerChoice.cpp
src/AbstractChoice.cpp
src/B_RocChoice.cpp
src/C_CnnChoice.cpp
src/D_DetectorChoice.cpp
src/E_LearningChoice.cpp
src/Help.cpp
src/main.cpp
src/MyException.cpp
src/StartScreen.cpp
tinyxml/tinystr.cpp
tinyxml/tinystr.h
tinyxml/tinyxml.cpp
tinyxml/tinyxml.h)
add_subdirectory(googletest)
add_executable(cli ${SOURCE_FILES})
target_link_libraries( cli ${OpenCV_LIBS} )
CMakeLists.txt for gtest.
cmake_minimum_required(VERSION 2.6.2)
project( googletest-distribution )
enable_testing()
option(BUILD_GTEST "Builds the googletest subproject" ON)
#Note that googlemock target already builds googletest
option(BUILD_GMOCK "Builds the googlemock subproject" OFF)
if(BUILD_GMOCK)
add_subdirectory( googlemock )
elseif(BUILD_GTEST)
add_subdirectory( googletest )
endif()
add_subdirectory(basic_tests)
CMakeLists.txt for basic_tests
include_directories($(gtest_SOURCE_DIR}/include
${getest_SOURCE_DIR}))
#include_directories(../../src/)
include_directories(../../include/)
add_executable(runBasicCli
basic_check.cpp)
target_link_libraries(runBasicCli gtest gtest_main)
#target_link_libraries(cli)
I'm assuming your compiler is complaining it can't find the B_RocChoices.h header? Your question seems to imply the compiler error is about not finding a function, but B_RocChoices is a header and not a function in your basic_tests.cpp file.
Assuming your problem is that the compiler isn't finding the B_RocChoices.h header, I expect that when you include_directories(../../include) you are wanting to make the directory where B_RocChoices.h resides part of the header search path. This is a relative path, so it depends where the compiler is being run from as to what path it means (it wouldn't work if you were doing out of source builds, for example). Try using either CMAKE_SOURCE_DIR or CMAKE_CURRENT_SOURCE_DIR to define the path unambiguously. For example:
include_directories(${CMAKE_SOURCE_DIR}/include)
If you are using CMake 2.8.11 or later, I'd recommend you consider using target_include_directories() instead and probably also read up on target_link_libraries(). Together, these allow you to make the header search paths and linked libraries specific to a target rather than global to all targets. Lastly, if you prefer to download GoogleTest as part of your build rather than embedding it directly in your project sources, you may find the following link useful:
https://crascit.com/2015/07/25/cmake-gtest/

include cmake headerfile boost.hpp with cmake

I like to add the boost/operators.hpp with cmake
From CMake's FindBoost Documentation:
find_package(Boost 1.36.0)
if(Boost_FOUND)
include_directories(${Boost_INCLUDE_DIRS})
add_executable(foo foo.cc)
endif()
So I added
find_package(Boost 1.60.0)
if (Boost_FOUND)
include_directories(${Boost_INCLUDE_DIRS})
add_library(core ${core_SRCS})
add_executable(app main.cpp)
target_link_libraries(app core)
endif ()
Output from message:
-- Boost version: 1.60.0
-- BOOST_ROOT=~/Projects/ClionProjects/.repo/boost_1_60_0
-- Boost_DIR=Boost_DIR-NOTFOUND
-- Boost_INCLUDE_DIR=/home/dornathal/Projects/ClionProjects/.repo/boost_1_60_0
However it builds (I can run the program and tests), but as soon as I try to include #include<boost/operators.hpp> it won't find it in the test project.
I actually extended one class by the boost::operators<T> and strangely my IDE (CLion) lets me jump to that sourcefile.
The include_directories CMake command adds include directories for the current directory and its subdirectories. Since you get the error in the test project and use include_directories in the main project, I guess the problem is that you have separate directories for these, for example:
src/
CMakeLists.txt - include_directories used here
test/
test.cc - no effect on this file
If this is the case you can either move include_directories up to their common parent directory or use target_include_directories that can propagate the public INCLUDE_DIRECTORIES property.
You can also see the commands passed to your compiler by adding VERBOSE=1 to your make command:
make VERBOSE=1
This shows what include directories are passed to the compiler via the -I... option among other things.