C++ testing classes with Catch - c++

I decided to make a small project which I will cover with tests as much as I can. I'm using CLion (which uses CMake) and Catch library for testing.
The problem is that I'm getting undefined reference to TestClass::add method while I run test class.
Here is my setup (it's a dummy one since I wanted to make sure if everything works):
TestClass.h
#ifndef LLL_TESTCLASS_H
#define LLL_TESTCLASS_H
class TestClass {
public:
int add(int a, int b);
};
#endif //LLL_TESTCLASS_H
TestClass.cpp
#include "TestClass.h"
int TestClass::add(int a, int b) {
return a + b;
}
test.cpp -- file with tests
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
#include "../src/TestClass.h"
TEST_CASE("addition") {
TestClass testClass;
REQUIRE(testClass.add(2,3) == 5);
REQUIRE(testClass.add(-1, 1) == 0);
REQUIRE(testClass.add(2, 4) == 1);
}
CMakeLIsts.txt
cmake_minimum_required(VERSION 3.2)
project(LLL)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(LIB_DIR "lib")
include_directories(${LIB_DIR}/Catch)
include_directories(${LIB_DIR}/Eigen)
set(SRC_DIR src)
set(SOURCE_FILES ${SRC_DIR}/main.cpp src/TestClass.h src/TestClass.cpp)
add_executable(LLL ${SOURCE_FILES})
set(TEST_DIR test)
set(TEST_FILES ${TEST_DIR}/test.cpp)
add_executable(LLL_TEST ${TEST_FILES})

You did not specify the TestClass.{.h, .cpp} to be compiled in LLL_TEST executable target:
set(TEST_FILES src/TestClass.h src/TestClass.cpp ${TEST_DIR}/test.cpp)
Or better move it in some shared library and just link to it.
The problem is because your project produces 2 executables: LLL and LLL_TEST targets. Each of them have a object files and a references. In LLL target these references to TestClass are satistifed because they are compiled in the LLL target so the ld can simply link them. But in case of LLL_TEST target compiler can't find these symbols because they are not specified in this target - that's why compiler says that he can't resolve a reference.

Your question has already been answered by Polevoy and he's also correct about factorizing out your class to a library.
I'd just like to show how the library version would look like. You can decide whether it's really so complicated you need to read books or if it has any overhead for doing TDD.
cmake_minimum_required(VERSION 3.2)
project(LLL)
set(CMAKE_CXX_STANDARD 11) # adds -std=c++11
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(LIB_DIR "lib")
include_directories(${LIB_DIR}/Catch)
include_directories(${LIB_DIR}/Eigen)
add_library(LLL_LIB src/TestClass.h src/TestClass.cpp)
add_executable(LLL src/main.cpp)
target_link_libraries(LLL LLL_LIB)
add_executable(LLL_TEST test/test.cpp)
target_link_libraries(LLL_TEST LLL_LIB)
This script creates the static library LLL_LIB which is linked into both executables. You can build a shared one instead if you like:
add_library(LLL_LIB SHARED src/TestClass.h src/TestClass.cpp)
but static is just fine here.

Related

C++ function not exported by .so seemingly because of pybind11 parameters

I am currently trying to define a shared library that I aim to use from a Python C++ extension as well as from vanilla C++ applications.
I managed to build the shared library, and tried to link a simple C++ application against it to test its functionalities, but one of the functions of the shared library is treated as an undefined reference by the linker. After checking with nm --demangle --dynamic --defined-only --extern-only libmylib.so, I realized the function is not being exported by the shared library, but I have no idea why.
The function's signature is as follows:
void bootstrap_mylib(std::vector<std::string> python_path,
std::vector<std::string> python_scripts,
std::string interface_module,
std::function<void (pybind11::module_, pybind11::object)> interface_module_initializer);
Everything goes well if I comment out the last parameter, so the problem seems to be coming from the way I declare the dependencies with pybind11 somehow.
Here are the relevant parts of my CMakeLists.txt:
set(CMAKE_CXX_COMPILER /usr/bin/g++)
project(monilog LANGUAGES CXX VERSION 0.0.1)
set(PYBIND11_PYTHON_VERSION 3.8)
find_package(pybind11 REQUIRED)
include_directories(${PYTHON_INCLUDE_DIRS})
add_library(mylib SHARED MyLib.cc MyLib.h)
set_property(TARGET mylib PROPERTY CXX_STANDARD 17)
target_link_libraries(mylib ${PYTHON_LIBRARIES})
set_target_properties(mylib PROPERTIES VERSION ${PROJECT_VERSION})
set_target_properties(mylib PROPERTIES SOVERSION 1)
set_target_properties(mylib PROPERTIES PUBLIC_HEADER MyLib.h)
Any idea of what I might be doing wrong?
Edit: minimal working example
Here is a minimal example of my problem, consisting of the following files:
example.h
#include <pybind11/stl.h>
namespace Example
{
void simple_func(std::string some_string);
void pybind11_func(pybind11::function some_func);
}
example.cc
#include "example.h"
namespace Example
{
void simple_func(std::string some_string)
{
std::cout << "Simple function" << '\n';
}
void pybind11_func(pybind11::function some_func)
{
std::cout << "Pybind11 function" << '\n';
}
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
cmake_policy(SET CMP0074 NEW)
# SET VARIABLES
set(CMAKE_CXX_COMPILER /usr/bin/g++)
set(CMAKE_CXX_STANDARD 17)
project(example CXX)
set(PYBIND11_PYTHON_VERSION 3.8)
find_package(pybind11 REQUIRED)
# include_directories(${PYTHON_INCLUDE_DIRS})
include_directories(${pybind11_INCLUDE_DIRS})
add_library(example SHARED example.cc example.h)
target_link_libraries(example ${PYTHON_LIBRARIES})
When I build the project, if I then search for func in the exposed symbols, I get the following result:
> nm -D libexample.so | grep "func"
00000000000039d9 T _ZN7Example11simple_funcENSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
pybind11_func is thus not exported, while simple_func is correctly exported.
Namespace pybind11 has hidden visibility.
/usr/include/pybind11/detail/common.h:#
define PYBIND11_NAMESPACE pybind11 __attribute__((visibility("hidden")))
so all functions that have anything to do with that namespace (like having an argument type from it) are also hidden.
You can override this by explicitly setting visibility to default:
__attribute__((visibility("default")))
void bootstrap_mylib( ... )

DLL Made with CMake makes the program crash

I'm struggling to use a DLL generated using CMAKE and C++. I'm able to build the library, include it and build the target project, the problem is that when I run the target build it crashes immediately.
My code is super easy and I don't know what I'm missing.
The DLL is built using CMAKE in a separate project. Here's the code
DLL PROJECT:
CMakeLists
cmake_minimum_required(VERSION 3.5)
project(LibProj LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_definitions("-DBUILD_LIB")
file(GLOB
INCLUDE_FILES
baselibraryclass.h
)
file(GLOB
SOURCE_FILES
baselibraryclass.cpp
)
add_library(yourlib SHARED ${INCLUDE_FILES} ${SOURCE_FILES} )
baselibraryclass.h
#ifndef BASELIBRARYCLASS_H
#define BASELIBRARYCLASS_H
#ifdef BUILD_LIB
#define EXT_DLL __declspec(dllexport)
#else
#define EXT_DLL __declspec(dllimport)
#endif
#include <string>
class EXT_DLL BaseLibraryClass
{
public:
BaseLibraryClass();
};
#endif // BASELIBRARYCLASS_H
baselibraryclass.cpp
#include "baselibraryclass.h"
#include <iostream>
EXT_DLL BaseLibraryClass::BaseLibraryClass()
{
std::cout << "Hi from the library Class Object " << std::endl;
}
Target project
CMakeLists
cmake_minimum_required(VERSION 3.5)
project(TargetProject LANGUAGES CXX)
#Including the path of the library header
include_directories(D:/TestingDLLNativeCpp/Library/include)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_executable(TargetProject main.cpp)
#The path of the built library is D:/TestingDLLNativeCpp/Library/Debug/yourlib.lib
target_link_libraries(TargetProject PRIVATE D:/TestingDLLNativeCpp/Library/Debug/yourlib.lib)
Target project main.cpp
#include <iostream>
#include <baselibraryclass.h>
using namespace std;
int main()
{
BaseLibraryClass testObk;
return 0;
}
As I wrote above, cmake configures properly and the compiler is able to build for both projects, however the target executables crashes immediately.
What am I doing wrong o.O??
Thanks for the attention
The issue of the dll not being included in the build directory might be solved by setting the CMAKE_RUNTIME_OUTPUT_DIRECTORY. However, a better practice would be to set the output directory of the .dll on a target basis. This will ensure that you have no unwanted side-effects in the long run.
set_target_property(yourlib PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}")

How to use dynamic link library with CMake?

I have simple program as follow:
CMakeLists.txt:
cmake_minimum_required(VERSION 3.5)
project(test LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
LINK_DIRECTORIES(${PROJECT_SOURCE_DIR})
add_executable(test main.cpp)
target_include_directories(test PRIVATE ${PROJECT_SOURCE_DIR})
target_link_libraries(test PRIVATE power.dll)
main.cpp:
#include <iostream>
#include "power.h"
using namespace std;
int main()
{
cout << "Hello World!" << endl;
power(4.);
return 0;
}
power.h:
#ifndef POWER_H
#define POWER_H
double power(double number) noexcept;
#endif // POWER_H
Implementation of power.h is in a .dll named power.dll.
If I compile this project with MinGW 7.3.0 X64 says:
error: undefined reference to `power(double)'
If I compile it with MSVC 2017 X64 says:
error: LNK1104: cannot open file 'power.lib'
both errors show that power.dll can't detect by the linker.
I did many searches but none of solutions worked for me!
Can anyone help about this?
Thanks in advance!
Your modelling of the dynamic library is incorrect, both on CMake and on the source level.
As a starting point, try building the dll as part of the same CMake project as the consuming executable:
cmake_minimum_required(VERSION 3.5)
project(test LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
include(GenerateExportHeader)
add_library(power SHARED power_sources.cpp power.h)
generate_export_header(power)
target_include_directories(power PUBLIC ${PROJECT_BINARY_DIR} ${PROJECT_SOURCE_DIR})
add_executable(test main.cpp)
target_link_libraries(test PRIVATE power)
Note the use of the generate_export_header function, which instructs CMake to generate macros for exporting functions on shared library interfaces in a portable way. Since generated files go to the binary directory tree, we have to adjust the include directories for the library accordingly.
To make sure the function gets properly exported, change your header as follows:
#ifndef POWER_H
#define POWER_H
#include <power_export.h>
POWER_EXPORT double power(double number) noexcept;
#endif // POWER_H
Note that generare_export_header allows you to customize the generated export header extensively.
Be sure you get the project to build and run from this baseline.
If you want to build the dll externally (which is not strictly necessary, but since that's what your question is about...), we have to modify the CMake file to something like:
cmake_minimum_required(VERSION 3.5)
project(test LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(power)
add_executable(test main.cpp)
target_link_libraries(test PRIVATE power)
With all the magic here happening in the find_package call. That call is now responsible for providing all the information that was previously handled by the lines for building the library:
Providing of an imported target power for consumption by the target_link_libraries call
Association of the library name of the import library (the power.lib file) via that imported target
Exposure of the public include directories for both power.h and power_export.h via that imported target
You can either construct such an imported target manually in the find script, or have CMake do it for you. In the first case, create a FindPower.cmake script file, make sure it's location is part of the CMAKE_MODULE_PATH and write the code for finding the library and header files and constructing the imported target in there. Note that getting this right in a portable way can be very tricky and goes far beyond the scope of a StackOverflow question. In the second case, have the CMake script that builds the power library perform an install step during which a config file package will get generated, which can then be consumed by your test project. Note that this approach is not viable if the power library is not itself being built with CMake, so in that case you will have to stick with the first option.
Dynamic linking in Windows requires that externally visible symbols are declared with the keyword __declspec. Your header "power.h" should be modified:
#ifndef POWER_H
#define POWER_H
#if defined(__WIN32__) && !defined(__CYGWIN__)
# if (defined(_MSC_VER) || defined(__MINGW32__)) && defined(BUILD_DLL)
# define POWERAPI __declspec(dllexport)
# elif (defined(_MSC_VER) || defined(__MINGW32__))
# define POWERAPI __declspec(dllimport)
# endif
#endif
POWERAPI double power(double number) noexcept;
#endif // POWER_H
In the project building the DLL power.dll with CMake, you should define the symbol BUILD_DLL:
add_definitions(-DBUILD_DLL)
then it should generate a power.lib file when the MSVC compiler and a power.a when using MINGW. Don't define BUILD_DLL in the project using the DLL, and it should work.

GoogleTest CMake and Make tests not running

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})

Linking project source code with boost tests under CMake

I am trying to find the best way to link my project source code with my boost unit tests. I have a fairly basic project setup right now using CMake, however all examples of boost UTF that I have come across have shown very basic tests which do not touch source code in a project that is along side the tests.
As a minimal example I have the following:
CMakeLists.txt
cmake_minimum_required(version 2.8)
project(test-project)
find_package(Boost 1.55 REQUIRED COMPONENTS unit_test_framework )
include_directories(${Boost_INCLUDE_DIRS})
link_directories(${Boost_LIBRARY_DIRS})
add_subdirectory(src)
enable_testing()
add_subdirectory(test)
src/CMakeLists.txt
add_executable(example main.cpp foo.cpp)
src/foo.h
#include <string>
std::string hello(std::string name);
src/foo.cpp
#include "foo.h"
std::string hello(std::string name) { return "Hello " + name; }
src/main.cpp - Uses foo in a simple way
test/CMakeLists.txt
include_directories (../src)
set(TEST_REQUIRED_SOURCES ../src/foo.cpp)
add_executable (test test.cpp ${TEST_REQUIRED_SOURCES})
target_link_libraries(test ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
add_test(SimpleTest test)
test/test.cpp
#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MAIN
#define BOOST_TEST_MODULE SimpleTest
#include <boost/test/unit_test.hpp>
#include "foo.h"
BOOST_AUTO_TEST_CASE(ShouldPass) {
BOOST_CHECK_EQUAL(hello("fred"), "Hello fred")
}
While this works, I'd like to avoid the following:
Defining TEST_REQUIRED_SOURCES with a list of all of my files required to compile.
Avoiding the duplicate compilation of code.
Does my structure look correct for this sort of project? Would it make sense to compile my code under src into a library instead? Most of my experience with testing is from C# where this is much simpler.
You can have a look at how I did it there: https://github.com/NewbiZ/mengine/blob/master/CMakeLists.txt
Basically, I build an object file with my library, and reuse it in the main executable AND the tests. That way you only build once.
The interesting piece of CMake is:
# Just build the object files, so that we could reuse them
# apart from the main executable (e.g. in test)
ADD_LIBRARY(mengine_objects OBJECT ${MENGINE_SOURCES})
Then build the main executable:
ADD_EXECUTABLE(mengine $<TARGET_OBJECTS:mengine_objects>
src/main.cpp)
And the tests:
ADD_EXECUTABLE(test_tga $<TARGET_OBJECTS:mengine_objects>
test_tga.cpp)
Hope that helps!