How can I use Google Test with my project that builds via autotools?
Asked Answered
J

3

26

It seems like there are a few answers that kind-of, sort-of make sense, but that I don't know how to carry out. And I haven't found a comprehensive answer.

The First Problem

Google Test should not be an installed library, it should be built with the project. (See the FAQ.) As far as I can tell, this means the Google Test libraries are a dependency of my unit tests, and should be built when I run "make check" within my project for the first time. This should build Google Test libraries in some directory. I don't know how to do this. It mentions some autotools script that's deprecated, and I'm not sure what they're talking about or how to point my build at it properly.

The Second Problem

Assuming the build is successful, how do I write a test that uses my locally-compiled version of Google Test to run tests? I assume that there are a bunch of Makefile.am commands I put in my tests directory. But what are they? And what's an example of a unit test that uses Google Test?

Jerold answered 14/3, 2016 at 21:56 Comment(0)
J
28

I have solved the problem to my satisfaction! I will move on entirely now. This is basically asking for a tutorial. There are a lot of decisions that must be made, hopefully logically, so that Google Test dovetails nicely into autotools. So I apologize in advance for the long answer, but all the details should be there.

The First Problem

In order to understand the answer, the question needs to be rephrased a little. We are compiling Google Test as a library which our test code will link to. The library will not be installed. The question we want to ask is

"How do we configure autotools to compile Google Test as a library which our test code can link against?"

In order to do that, we need to download Google Test and place it into our project. I use Github, so I do that by adding a submodule in the root path of my project:

$ git submodule add [email protected]:google/googletest.git
$ git submodule init
$ git submodule update

This downloads googletest into my root of my project:

/:
    Makefile.am
    configure.ac
    src/:
        (files for my project)
    tests/:
        (test files)
    googletest/:
        googletest/:
            include/:
                (headers, etc., to be included)
                gtest/:
                    gtest.h
            m4/:
                (directory for m4 scripts and things)
            src/:
                (source files for Google Test)

I need to compile per the instructions. I only want the Google Test library to be built upon running make check, so I will use check_LTLIBRARIES. I add the following to my tests Makefile.am in /tests:

check_LTLIBRARIES = libgtest.la
libgtest_la_SOURCES = ../googletest/googletest/src/gtest-all.cc
libgtest_la_CPPFLAGS = -I$(top_srcdir)/googletest/googletest/include -I$(top_srcdir)/googletest/googletest
libgtest_la_LDFLAGS = -pthread

This requires subdir-objects to be enabled in configure.ac. That is accomplished by adding it to the AM_INIT_AUTOMAKE line. I also need to include the makefile in AC_CONFIG_FILES. We also want to use libtool, because we are compiling library files (I'll explain why and how that works in a moment). To use libtool, we add AM_PROG_AR, LT_INIT. We want autoreconf to install m4 macros to /m4, and then we want automake to find them, so we need AC_CONFIG_MACRO_DIRS. My configure.ac has lines updated:

AM_INIT_AUTOMAKE([-Wall -Werror subdir-objects])
...
AM_PROG_AR
LT_INIT
AC_CONFIG_MACRO_DIRS([m4])
...
AC_CONFIG_FILES([Makefile
                 src/Makefile
                 tests/Makefile
                 ])

I also need to include the subdirectory and a line pointing to the macros in the /m4 macros directory in my /Makefile.am:

ACLOCAL_AMFLAGS = -I m4

SUBDIRS = src tests

What has this done? Libtool has been enabled with AM_PROG_AR and LT_INIT. The check_LTLIBRARIES means we will use libtool to create what's called a convenience library called libgtest.la. With subdir-objects enabled, it will be built into the /tests directory, but not installed. This means that, whenever we want to update our tests, we don't have to recompile the Google Test library libgtest.la. This will save time when testing and help us iterate faster. Then, we will want to compile our unit tests against it later as we update them. The library will only be compiled upon running make check, saving time by not compiling it if all we want to do is make or make install.

The Second Problem

Now, the second problem needs to be refined: How do you (a) create a test (b) that is linked to the Google Test libraries and thus uses them? The questions are kind of intertwined, so we answer them at once.

Creating a test is just a matter of putting the following code into a gtest.cpp file located at /tests/gtest.cpp:

#include "gtest/gtest.h" // we will add the path to C preprocessor later

TEST(CategoryTest, SpecificTest)
{
    ASSERT_EQ(0, 0);
}

int main(int argc, char **argv)
{
    ::testing::InitGoogleTest(&argc, argv);

    return RUN_ALL_TESTS();
}

This runs only the simple test 0=0. To create a test for your library, you need to read the primer. You'll notice we don't need a header for this (yet). We are linking to the file "gtest/gtest.h", so we'll need to make sure that we tell automake to include a directory that has gtest/gtest.h.

Next, we need to tell automake that we want to build a test and run it. The test is going to build into an executable that we don't want to install. Then automake is going to run that executable. It will report whether that executable says the tests passed or failed.

Automake does that by looking in the makefile for the variable check_PROGRAMS. These are the programs it will compile, but it won't necessarily run them. So we add to /tests/Makefile.am:

check_PROGRAMS = gtest

gtest_SOURCES = gtest.cpp

gtest_LDADD = libgtest.la

gtest_LDFLAGS = -pthread

gtest_CPPFLAGS = -I$(top_srcdir)/googletest/googletest/include -I$(top_srcdir)/googletest/googletest -pthread

The gtest_SOURCES finds the /tests/gtest.cpp file and compiles it. gtest_LDADD links against libgtest.la which will be compiled into the /tests directory. Google wants us to use the gtest_LDFLAGS line to enable pthreads. Finally, we need to include the location where the header "gtest/gtest.h" will be found, and that is the gtest_CPPFLAGS line. Google also wants us to include the /googletest/googletest location, and include the

The state of things: The Google Test library libgtest.la will compile with make into the directory /tests, but not be installed. The binary gtest will only be compiled with make check, but will not be installed.

Next we want to tell automake to actually run the compiled binary gtest and report errors. This is accomplished by adding a line to /tests/Makefile.am:

TESTS = gtest

The final /tests/Makefile.am looks like this:

check_LTLIBRARIES = libgtest.la
libgtest_la_SOURCES = ../googletest/googletest/src/gtest-all.cc
libgtest_la_CPPFLAGS = -I$(top_srcdir)/googletest/googletest/include -I$(top_srcdir)/googletest/googletest -pthread

check_PROGRAMS = gtest demo

gtest_SOURCES = gtest.cpp ../src/fields.cpp

gtest_LDADD = libgtest.la

gtest_LDFLAGS = -pthread

gtest_CPPFLAGS = -I$(top_srcdir)/googletest/googletest/include -I$(top_srcdir)/src

demo_SOURCES = demo.cpp ../src/fields.cpp

demo_CPPFLAGS = -I$(top_srcdir)/src

TESTS = gtest

Now, autoreconf -fiv (note any errors and hopefully fix them) from /, and make check and you should get a test that runs:

build(dev)$ make check
Making check in tests
/Applications/Xcode.app/Contents/Developer/usr/bin/make  gtest
make[2]: `gtest' is up to date.
/Applications/Xcode.app/Contents/Developer/usr/bin/make  check-TESTS
PASS: gtest
============================================================================
Testsuite summary for IonMotion 0.0.1
============================================================================
# TOTAL: 1
# PASS:  1
# SKIP:  0
# XFAIL: 0
# FAIL:  0
# XPASS: 0
# ERROR: 0
============================================================================
Jerold answered 15/3, 2016 at 0:55 Comment(5)
The initial example of Makefile.am omits src/ directory component from libgtest_la_SOURCES. Otherwise great write up.Milker
Link to primer should use lowercase p (primer.md) but I can't make a one-character edit.Ankledeep
How do you achieve this with CMake?Alikee
Thanks, used your solution here: github.com/CarloWood/ai-evio-testsuite/blob/master/tests/…Isobar
@RyanH. Thank you. I have fixed that and a lot more.Thorne
M
2

Here is a sample Makefile.am for the unit test project (project name: TestProject). It depends on GTEST and GMOCK:

Makefile.am

#######################################
# The list of executables we are building seperated by spaces
# the 'bin_' indicates that these build products will be installed
# in the $(bindir) directory. For example /usr/bin
#bin_PROGRAMS=exampleProgram

# Because a.out is only a sample program we don't want it to be installed.
# The 'noinst_' prefix indicates that the following targets are not to be
# installed.
noinst_PROGRAMS=utTestProject

#######################################
# Build information for each executable. The variable name is derived
# by use the name of the executable with each non alpha-numeric character is
# replaced by '_'. So a.out becomes a_out and the appropriate suffex added.
# '_SOURCES' for example.

# Sources for the a.out 
utTestProject_SOURCES= \
    utTestProject.cpp

# Library dependencies
utTestProject_LDADD = \
    $(top_srcdir)/../TestProject/build/${host}/libTestProject/.libs/libTestProject.a \
    ../$(PATH_TO_GTEST)/lib/libgtest.a \
    ../$(PATH_TO_GMOCK)/lib/libgmock.a 

# Compiler options for a.out
utTestProject_CPPFLAGS = \
    -std=c++11 \
    -I../$(PATH_TO_GTEST)/include \
    -I../$(PATH_TO_GMOCK)/include \
    -I$(top_srcdir)/include \
    -I$(top_srcdir)/..

TESTS = utTestProject

TESTS_ENVIRONMENT = export UT_FOLDER_PATH=$(top_srcdir)/utTestProject; \
                    export GTEST_OUTPUT="xml";

Compiling gtest:

# Useful vars
SourceVersionedArchiveFolderName="gtest-1.7.0"

#
# Make it
#
pushd .
cd ./${SourceVersionedArchiveFolderName}/make

make gtest.a
if [ $? != 0 ]; then
    echo "$0: Make failed"
    exit 1
fi

popd
Merchandising answered 14/3, 2016 at 22:27 Comment(10)
Where did you get that? Or did you make it up all on your own??Jerold
I extracted this from my own project. Hope it helps!Merchandising
It looks like this may answer my question about a Makefile.am for unit testing. But how do you get gtest to compile when you run make check? I don't see any links to something that would cause gtest to compile...Jerold
Updated the answer with the install script. It works on mac os x. You can copy the output i.e., .a and include files to suit your include path.Merchandising
Don't we not want an install script? Wouldn't we prefer for your test (which it looks like you're calling utTestProject) to depend on some way on gtest being compiled? I suppose that would add any Google Test dependencies to your projectJerold
It feels like we have to add a whole bunch of stuff to configure.ac, then somehow get our build system to make Google Test a target, and make tests depend on that target...?Jerold
Makefile.am depends on the gtest being compiled. I am not showing everything here. You can compile gtest using the snippet i provided. Once it is compiled, it can be included in whatever way you would like either through Configure.ac or by directly hardcoding the path.Merchandising
Let us continue this discussion in chat.Jerold
Sorry, I am not available for chat now. Hope this helps: github.com/google/googletest/tree/master/googletest Good luck!Merchandising
I already know about that, unfortunately. It's not concrete or explicit enough for me, I'm still not getting it :/Jerold
J
0

It's worth noting that Googletest doesn't officially maintain its Autotools integration anymore:

Before settling on CMake, we have been providing hand-maintained build projects/scripts for Visual Studio, Xcode, and Autotools. While we continue to provide them for convenience, they are not actively maintained any more. We highly recommend that you follow the instructions in the above sections to integrate Google Test with your existing build system.

https://github.com/google/googletest/tree/master/googletest#legacy-build-scripts

It's now recommended to build Googletest with CMake.

Making GoogleTest's source code available to the main build can be done a few different ways:

  • Download the GoogleTest source code manually and place it at a known location. This is the least flexible approach and can make it more difficult to use with continuous integration systems, etc.
  • Embed the GoogleTest source code as a direct copy in the main project's source tree. This is often the simplest approach, but is also the hardest to keep up to date. Some organizations may not permit this method.
  • Add GoogleTest as a git submodule or equivalent. This may not always be possible or appropriate. Git submodules, for example, have their own set of advantages and drawbacks.
  • Use CMake to download GoogleTest as part of the build's configure step. This is just a little more complex, but doesn't have the limitations of the other methods.

https://github.com/google/googletest/tree/master/googletest#incorporating-into-an-existing-cmake-project

Jerol answered 24/2, 2019 at 16:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.