GoogleTest for Android NDK C++ with CMake
Asked Answered
Q

2

12

How do I setup GoogleTest for my native C++ code on Android with CMake? The Android NDK comes bundled with googletest but instructions are available only for Android.mk (here). How do I port the Android.mk gtest setup to my CMakeLists.txt? And once that is setup, is it possible to run the test runner through Android Studio's test configurations?

Quizzical answered 19/4, 2017 at 18:12 Comment(0)
M
0

I can only provide an answer to your first and main question, but it's a working solution. It's not specific to Android ; it works as long as you can run CMake and CTest.

I don't know much about GoogleTest, but I remember having a very similar question a few months ago. In my case, I wanted to use CMake with the Boost UnitTestFramework.

I searched a bit for it, then I fell across a certain tutorial. The solution they provided was merely to parse your test files' contents using a regular expression. The upside is, it's quite customizable and allows for several unit tests per file. I did this, and it worked quite nicely. You have to rerun CMake when you add new unit tests, of course.

Like I said, what I'll post below is for the Boost UnitTestFramework, but the biggest differences are in the regular expressions, and the variable names I use. In case you're not used to using regular expressions with CMake (I'm not), here is an official documentation page regarding string operations.

Here is the CMakeLists.txt file I ended up with. I used this as a standalone CMakeLists.txt file under a test/ directory.

# =============================
# =       ADDING TESTS        =
# =============================

include(CTest)

find_package(Boost COMPONENTS unit_test_framework REQUIRED)
include_directories(${Boost_INCLUDE_DIRS})

file(GLOB_RECURSE TEST_SRCS RELATIVE ${TEST_SOURCE_DIR} *.cpp)

set(TEST_EXTRA_LIBS ${Boost_LIBRARIES} ${TEST_MAIN_LIB})


# Function which, given a file name and a test name, yields the
# name of the GoogleTest test case.
# That way, several different files can have the same test name.
# Adapt this to GoogleTest.
function(getTestCase testFileName testName outTestCase)
    string(REGEX MATCH "Test([_a-zA-Z][_a-zA-Z0-9]*)" match ${testFileName})
    string(REGEX REPLACE ".*Test([_a-zA-Z][_a-zA-Z0-9]*).*" "\\1" testCase ${match})
    set(testCase Test${testCase}${testName})
    set(${outTestCase} ${testCase} PARENT_SCOPE)
endfunction()


# Function which adds all tests within a certain test file.
function(add_all_tests_in testSrc)
    get_filename_component(testFileName ${testSrc} NAME_WE)

    add_executable(${testFileName} ${testSrc})
    target_link_libraries(${testFileName} ${TEST_EXTRA_LIBS})
    set_target_properties(${testFileName} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})

    file(READ "${testSrc}" testFileContents)

    # You should change this Regex for what you need in your case.
    # Maybe something like:
    # "TEST\\( *([_a-zA-Z][_a-zA-Z0-9]* *, *[_a-zA-Z][_a-zA-Z0-9]*) *\\)"
    string(REGEX MATCHALL "DEF_TEST_CASE\\( *([_a-zA-Z][_a-zA-Z0-9]*) *\\)" unitTests ${testFileContents})

    foreach(match ${unitTests})
        # This replace will also probably need some change.
        string(REGEX REPLACE ".*\\( *([_a-zA-Z][_a-zA-Z0-9]*) *\\).*" "\\1" testName ${match})
        getTestCase(${testFileName} ${testName} testCase)
        # Actually add the test.
        # I wanted my CTest test names to be in the form
        # <fileName>.<testName>, but you can use any
        # format you want. Suit yourself.
        # 
        # Also, in order for CMake to run the tests one by one,
        # you have to find how to invoke the test executable.
        # In the case of Boost, the option --run_test=<Boost_Test_Name>
        # runs only the test called <Boost_Test_Name>. There should be
        # an equivalent option for GoogleTest, I'm sure.
        add_test(NAME "${testFileName}.${testName}" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${testFileName} --run_test=${testCase} --catch_system_error=yes)
    endforeach()
endfunction()



# Actually add all tests in all test files.
foreach(testSrc ${TEST_SRCS})
    add_all_tests_in(${testSrc})
endforeach()

Works for me. After running CMake under a build/ directory, running all tests yields:

$ cd test && ctest

Test project /home/anthonyd973/Git/Git_Projects/MySweeper/build/test
      Start  1: TestFieldMaker.makeFromFile
 1/12 Test  #1: TestFieldMaker.makeFromFile ........   Passed    0.03 sec
      Start  2: TestFieldMaker.make
 2/12 Test  #2: TestFieldMaker.make ................   Passed    0.01 sec
      Start  3: TestFieldMaker._computeFieldDims
 3/12 Test  #3: TestFieldMaker._computeFieldDims ...   Passed    0.01 sec
      Start  4: TestFieldMaker._populateField
 4/12 Test  #4: TestFieldMaker._populateField ......   Passed    0.00 sec
      Start  5: TestInputField.InputField
 5/12 Test  #5: TestInputField.InputField ..........   Passed    0.00 sec
      Start  6: TestCell.Cell
 6/12 Test  #6: TestCell.Cell ......................   Passed    0.00 sec
      Start  7: TestCell.initNeighbours
 7/12 Test  #7: TestCell.initNeighbours ............   Passed    0.00 sec
      Start  8: TestCell.updateNeighbours
 8/12 Test  #8: TestCell.updateNeighbours ..........   Passed    0.00 sec
      Start  9: TestCell._mark
 9/12 Test  #9: TestCell._mark .....................   Passed    0.00 sec
      Start 10: TestMySweeper.MySweeper
10/12 Test #10: TestMySweeper.MySweeper ............   Passed    0.00 sec
      Start 11: TestField.Field
11/12 Test #11: TestField.Field ....................   Passed    0.01 sec
      Start 12: TestField._initNeighbours
12/12 Test #12: TestField._initNeighbours ..........   Passed    0.00 sec
100% tests passed, 0 tests failed out of 12
Total Test time (real) =   0.10 sec

Hopefully, using regular expression does not cause you two problems :) .

Marijn answered 2/5, 2017 at 1:34 Comment(3)
@Quizzical Was this answer useful? If it was, feel free to accept it and/or award the bounty. If not, could you briefly explain why so that someone else has a chance of providing you a better answer?Marijn
Thanks so much for your insights. I still can't seem to understand how to go from your information to the Android setup.Quizzical
@Quizzical Yeah, it's true I presented you a rather elaborate solution. But I don't think there is any easy way to port GoogleTest to CMake without writing CMake code.Marijn
D
0

add gtest in your cmakelists.txt 1)include gtest/gmock header files;

include_directories("yourgooglestestdir/googletest/include/")
include_directories("yourgooglestestdir/googlemock/include/")

2)link gtest/gmock libs

LINK_DIRECTORIES("yourgooglestestdir/lib/")

3)include file Android.mk in your cmakelists.txt use the android mk part。

Dismuke answered 15/9, 2022 at 5:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.