reimda
reimda

Reputation: 23

Running unit tests during build of Windows DLL with CMake

I have a C++ library that is built with cmake. The library is tested by unit tests which I would like to run as part of the build. I want the tests to run when the library or unit test source changes.

I found some help in past posts on the cmake mailing list:

http://www.cmake.org/pipermail/cmake/2010-January/034419.html

This approach, using add_custom_command() on the unit test program, works on Linux when the library is shared or static and on Windows when the libary is static. The problem is building the library shared on Windows. In that case, the test runs in the initial build, but doesn't run when the library changes.

Here is an example that illustrates the problem:

CMakeLists.txt

cmake_minimum_required(VERSION 2.8)
project(xyz)

SET(BUILD_SHARED_LIBS ON)

#----------- build library
if(${BUILD_SHARED_LIBS})
    add_definitions(-DXYZ_SHARED)
endif(${BUILD_SHARED_LIBS})
add_library(${PROJECT_NAME} xyz.cpp xyz.h)

#----------- build unit tests program
add_executable(${PROJECT_NAME}_unit_tests unit_tests.cpp)
target_link_libraries(${PROJECT_NAME}_unit_tests ${PROJECT_NAME})

#----------- run unit tests program after it is built
add_custom_command(TARGET ${PROJECT_NAME}_unit_tests
    POST_BUILD COMMAND ${PROJECT_NAME}_unit_tests)

xyz.h

#pragma once

#ifdef XYZ_SHARED
#ifdef _WIN32
#ifdef xyz_EXPORTS
#define XYZ_EXPORT __declspec(dllexport)
#else
#define XYZ_EXPORT __declspec(dllimport)
#endif
#else //_WIN32
#define XYZ_EXPORT
#endif //_WIN32
#else //XYZ_SHARED
#define XYZ_EXPORT
#endif //XYZ_SHARED

XYZ_EXPORT bool xyz_return_true();

xyz.cpp

#include "xyz.h"

XYZ_EXPORT bool xyz_return_true()
{
    return true;
}

unit_tests.cpp

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

int main(int argc, char *argv[])
{
    using namespace std;

    cout << "running unit tests: ";

    if (xyz_return_true()) {
        //no error
        cout << "pass" << endl;
        return 0;
    }

    //error
    cout << "fail" << endl;
    return 1;
}

If I build on Windows, then change xyz.cpp's function to return false and build again, the tests aren't run. What can I do to make the tests run in this case?

I think the tests aren't being run because at build time xyz_unit_tests.exe only depends on the import library (xyz.lib). The import library doesn't change if the library interface doesn't change.

I'm using Visual Studio 10 and cmake 2.8.11.2.

Upvotes: 2

Views: 1449

Answers (1)

Fraser
Fraser

Reputation: 78290

You could trigger a test rebuild by deleting the test executable as a post-build event of the library. So, for example:

if(WIN32 AND BUILD_SHARED_LIBS)
  #----------- trigger rebuild of tests if lib has changed
  get_target_property(TestPath ${PROJECT_NAME}_unit_tests LOCATION)
  add_custom_command(TARGET ${PROJECT_NAME}
      POST_BUILD COMMAND ${CMAKE_COMMAND} -E remove ${TestPath}
      COMMENT "Removing ${TestPath}")
endif()

You have to use the LOCATION target property rather than a generator expression in the COMMAND argument since that doesn't introduce a cyclic dependency in CMake's dependency graph. Ideally, we'd have the COMMAND argument as:

COMMAND ${CMAKE_COMMAND} -E remove $<TARGET_FILE:${PROJECT_NAME}_unit_tests>

but this would cause the library to depend on the executable, and clearly the target_link_libraries call causes the exe to depend on the lib.

There's probably a better way to make the test executable go out-of-date rather than just deleting it; it seems a bit brute-force to me. But it should work.

Upvotes: 1

Related Questions