Home > OS >  Is it possible to distribute unit test modules in diffrent source files if I use Boost.Test?
Is it possible to distribute unit test modules in diffrent source files if I use Boost.Test?

Time:07-07

I have many testing source files in which I use Boost.Test and I'm trying to run them, but get multiple definition of boost::unit_test::runtime_config::argument_store() error and the same for many modules of this library.

For example, there are two files: test_size.cpp

#define BOOST_TEST_MODULE Test_Repeats
#include "../include/test_config.h"

BOOST_AUTO_TEST_CASE(test_repeats) {
  int n = 30;
  Repeats r = Repeats(n);
  BOOST_REQUIRE(r.rep == n);
}

And test_repeats.cpp:

#define BOOST_TEST_MODULE Test_Size
#include "../include/test_config.h"

  BOOST_AUTO_TEST_CASE(test_size) {
  int n = 20;
  int m = 30;
  Size sz = Size(n, m);
  BOOST_REQUIRE(sz.n == n && sz.m == m);
}

And test_config.h:

#include <boost/test/included/unit_test.hpp>

#include "../include/size.h"
#include "../include/repeats.h"

Here is my Makefile to run tests:

FAST_HEADERS := $(wildcard Fast/**/*.h)

FAST_TEST_SOURCES := $(filter-out Fast/src/main.cpp, $(wildcard Fast/src/*.cpp Fast/tests/test_size.cpp Fast/tests/test_repeats.cpp))

FAST_TEST_OBJECTS := ${FAST_TEST_SOURCES:.cpp=.o}

fast_test: $(FAST_TEST_OBJECTS)
    g   $(FAST_TEST_OBJECTS) -lboost_unit_test_framework -o fast_test 
    ./fast_test

%.o: Fast/**/%.cpp  $(FAST_HEADERS)
    g   $@ -o $< 

I know that it's possible to have all tests in one source file and it will work well, but I'm curious whether it's possible to distribute all test units in different source files to make it more structured.

What is the problem with my Makefile or with source files?

CodePudding user response:

%.o: Fast/**/%.cpp  $(FAST_HEADERS)
    g   $@ -o $< 

This never applies due to **. You also reversed $@ and $<. You could use

Fast/tests/%.o: Fast/tests/%.cpp | $(FAST_HEADERS)
Fast/src/%.o: Fast/src/%.cpp | $(FAST_HEADERS)

%.o: %.cpp
    g   -o $@ $<

The Real Problem

That said, let's find out what the problem is with the linker messages. To be honest, I spent way more time than I'm comfortable admitting here to "accidentally" find it. First I, obviously per-used the documentation, mainly:

And also several mailing list/SO posts:

Honestly the only tangible thing I learned was that

However, the other documented flags BOOST_TEST_DYN_LINK and BOOST_TEST_NO_MAIN had zero effect. After some trial and error and preprocessor #error debugging I found out that those symbols are all just undef-ined after inclusion of

#include <boost/test/included/unit_test.hpp>

I didn't immediately recognize the /included/ part. On a whim I thought to change to:

#include <boost/test/unit_test.hpp>

That worked, but now main was undefined. So, I came up with a the following refactored makefile and an extra Fast/tests/module.cpp to define the main. No doubt this is all still not completely optimal. E.g. I think, since you're linking the dynamic library, BOOST_TEST_DYN_LINK ought to be defined, but it appears to work fine without that ¯\(ツ)/¯.

So part of the Makefile shown here is for inspiration on how to achieve particular advanced tasks in case you need them.

test: # make default target

FAST_HEADERS := $(wildcard Fast/**/*.h)
FAST_TEST_SOURCES := $(filter-out Fast/src/main.cpp, $(wildcard Fast/src/*.cpp Fast/tests/*.cpp))
FAST_TEST_OBJECTS := ${FAST_TEST_SOURCES:.cpp=.o}

CPPFLAGS = $(INCLUDES)
CPPFLAGS = -D BOOST_TEST_DYN_LINK
CXXFLAGS = -std=c  11 $(CPPFLAGS)
LDFLAGS = -lboost_unit_test_framework

Fast/tests/test_%.o: CPPFLAGS =-DBOOST_TEST_NO_MAIN
Fast/tests/%.o: Fast/tests/%.cpp | $(FAST_HEADERS)
Fast/src/%.o: Fast/src/%.cpp | $(FAST_HEADERS)

%.o: %.cpp
    g   $(CXXFLAGS) -o $@ -c $<

fast_test: $(FAST_TEST_OBJECTS)
    g   $(CXXFLAGS) $^ $(LDFLAGS) -o $@

test: fast_test
    ./$<

.PHONY: test

Testing with make -Bsn shows how the flags combine:

g   -std=c  11  -D BOOST_TEST_DYN_LINK -o Fast/src/size.o -c Fast/src/size.cpp
g   -std=c  11  -D BOOST_TEST_DYN_LINK -o Fast/src/repeats.o -c Fast/src/repeats.cpp
g   -std=c  11  -D BOOST_TEST_DYN_LINK -DBOOST_TEST_NO_MAIN -o Fast/tests/test_size.o -c Fast/tests/test_size.cpp
g   -std=c  11  -D BOOST_TEST_DYN_LINK -DBOOST_TEST_NO_MAIN -o Fast/tests/test_repeats.o -c Fast/tests/test_repeats.cpp
g   -std=c  11  -D BOOST_TEST_DYN_LINK -o Fast/tests/module.o -c Fast/tests/module.cpp
g   -std=c  11  -D BOOST_TEST_DYN_LINK Fast/src/size.o Fast/src/repeats.o Fast/tests/test_size.o Fast/tests/test_repeats.o Fast/tests/module.o
 -lboost_unit_test_framework -o fast_test
./fast_test

The output of e.g. ./fast_test -l all:

Running 2 test cases...
Entering test module "Fast_Tests"
Fast/tests/test_size.cpp(3): Entering test case "test_size"
Fast/tests/test_size.cpp(8): info: check sz.n == n && sz.m == m has passed
Fast/tests/test_size.cpp(3): Leaving test case "test_size"; testing time: 136us
Fast/tests/test_repeats.cpp(3): Entering test case "test_repeats"
Fast/tests/test_repeats.cpp(7): info: check r.rep == n has passed
Fast/tests/test_repeats.cpp(3): Leaving test case "test_repeats"; testing time: 125us
Leaving test module "Fast_Tests"; testing time: 311us

*** No errors detected

Loose Ends

I kept the "test-driven" implementations for Size/Repeats header-only. You need to link the dependent objects if you change that for your real code.

For the GNU Make features I didn't explain, see https://www.gnu.org/software/make/manual/

Code Dump:

  • File Makefile

     test: # make default target
    
     FAST_HEADERS := $(wildcard Fast/**/*.h)
     FAST_TEST_SOURCES := $(filter-out Fast/src/main.cpp, $(wildcard Fast/src/*.cpp Fast/tests/*.cpp))
     FAST_TEST_OBJECTS := ${FAST_TEST_SOURCES:.cpp=.o}
    
     CPPFLAGS = $(INCLUDES)
     CPPFLAGS = -D BOOST_TEST_DYN_LINK
     CXXFLAGS = -std=c  11 $(CPPFLAGS)
     LDFLAGS = -lboost_unit_test_framework
    
     Fast/tests/test_%.o: CPPFLAGS =-DBOOST_TEST_NO_MAIN
     Fast/tests/%.o: Fast/tests/%.cpp | $(FAST_HEADERS)
     Fast/src/%.o: Fast/src/%.cpp | $(FAST_HEADERS)
    
     %.o: %.cpp
         g   $(CXXFLAGS) -o $@ -c $<
    
     fast_test: $(FAST_TEST_OBJECTS)
         g   $(CXXFLAGS) $^ $(LDFLAGS) -o $@
    
     test: fast_test
         ./$<
    
     .PHONY: test
    
  • File Fast/include/repeats.h

     #pragma once
    
     struct Repeats {
         explicit Repeats(int n) : rep(n){}
         int const rep;
     };
    
  • File Fast/include/size.h

     #pragma once
    
     struct Size {
         explicit Size(int n, int m)
             : n(n)
             , m(m)
         {
         }
         int const n, m;
     };
    
  • File Fast/include/test_config.h

     #include <boost/test/unit_test.hpp>
    
     #include "../include/size.h"
     #include "../include/repeats.h"
    
  • File Fast/src/main.cpp

  • File Fast/src/repeats.cpp

  • File Fast/src/size.cpp

  • File Fast/tests/module.cpp

     #define BOOST_TEST_MODULE Fast_Tests
     #include <boost/test/included/unit_test.hpp>
    
  • File Fast/tests/test_repeats.cpp

     #include "../include/test_config.h"
    
     BOOST_AUTO_TEST_CASE(test_repeats)
     {
         int n = 30;
         Repeats r = Repeats(n);
         BOOST_REQUIRE(r.rep == n);
     }
    
  • File Fast/tests/test_size.cpp

     #include "../include/test_config.h"
    
     BOOST_AUTO_TEST_CASE(test_size)
     {
         int n = 20;
         int m = 30;
         Size sz = Size(n, m);
         BOOST_REQUIRE(sz.n == n && sz.m == m);
     }
    
  • Related