From b96b84704a49976063e6f6f51b6e17b76be3fb0f Mon Sep 17 00:00:00 2001 From: Thom Troy Date: Sun, 24 Sep 2017 00:36:47 +0100 Subject: [PATCH] Add example using google test --- 05-unit-testing/README.adoc | 1 + .../3rd_party/google-test/CMakeLists.txt | 68 ++++++ .../3rd_party/google-test/CMakeLists.txt.in | 19 ++ .../google-test-download/CMakeLists.txt | 30 +++ .../google-test-download/Palindrome.cpp | 11 + .../google-test-download/Palindrome.h | 15 ++ .../google-test-download/README.adoc | 231 ++++++++++++++++++ .../google-test-download/Reverse.cpp | 12 + .../google-test-download/Reverse.h | 15 ++ 05-unit-testing/google-test-download/main.cpp | 24 ++ .../google-test-download/post_test.sh | 6 + .../google-test-download/run_test.sh | 4 + .../google-test-download/unit_tests.cpp | 39 +++ test.sh | 1 + 14 files changed, 476 insertions(+) create mode 100644 05-unit-testing/google-test-download/3rd_party/google-test/CMakeLists.txt create mode 100644 05-unit-testing/google-test-download/3rd_party/google-test/CMakeLists.txt.in create mode 100644 05-unit-testing/google-test-download/CMakeLists.txt create mode 100644 05-unit-testing/google-test-download/Palindrome.cpp create mode 100644 05-unit-testing/google-test-download/Palindrome.h create mode 100644 05-unit-testing/google-test-download/README.adoc create mode 100644 05-unit-testing/google-test-download/Reverse.cpp create mode 100644 05-unit-testing/google-test-download/Reverse.h create mode 100644 05-unit-testing/google-test-download/main.cpp create mode 100755 05-unit-testing/google-test-download/post_test.sh create mode 100755 05-unit-testing/google-test-download/run_test.sh create mode 100644 05-unit-testing/google-test-download/unit_tests.cpp diff --git a/05-unit-testing/README.adoc b/05-unit-testing/README.adoc index d15c210..dcce24d 100644 --- a/05-unit-testing/README.adoc +++ b/05-unit-testing/README.adoc @@ -24,3 +24,4 @@ some of these frameworks and call them using the CMake testing utility CTest. The examples here include using the following frameworks: * http://www.boost.org/doc/libs/1_56_0/libs/test/doc/html/utf/user-guide.html[Boost Unit Test Framework] +* https://github.com/google/googletest[Google Test - Download] diff --git a/05-unit-testing/google-test-download/3rd_party/google-test/CMakeLists.txt b/05-unit-testing/google-test-download/3rd_party/google-test/CMakeLists.txt new file mode 100644 index 0000000..d2a1fb1 --- /dev/null +++ b/05-unit-testing/google-test-download/3rd_party/google-test/CMakeLists.txt @@ -0,0 +1,68 @@ +# Download and unpack googletest at configure time +# See: http://crascit.com/2015/07/25/cmake-gtest/ +configure_file(CMakeLists.txt.in googletest-download/CMakeLists.txt) +# Call CMake to download and Google Test +execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download ) +if(result) + message(FATAL_ERROR "CMake step for googletest failed: ${result}") +endif() +# Build the downloaded google test +execute_process(COMMAND ${CMAKE_COMMAND} --build . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download ) +if(result) + message(FATAL_ERROR "Build step for googletest failed: ${result}") +endif() + +# Prevent overriding the parent project's compiler/linker +# settings on Windows +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +# Prevent installation of GTest with your project +set(INSTALL_GTEST OFF CACHE BOOL "" FORCE) +set(INSTALL_GMOCK OFF CACHE BOOL "" FORCE) + +# Add googletest directly to our build. This defines +# the gtest and gtest_main targets. +add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/googletest-src + ${CMAKE_CURRENT_BINARY_DIR}/googletest-build) + +# This is a bit of a hack that can be used to get gtest libraries to build in C++11 if you aren't using CMAKE_CXX_STANDARD +# +#set(CXX11_FEATURES +# cxx_nullptr +# cxx_auto_type +# cxx_delegating_constructors +#) +#target_compile_features(gtest +# PRIVATE +# ${CXX11_FEATURES} +#) +# +#target_compile_features(gmock_main +# PRIVATE +# ${CXX11_FEATURES} +#) +# +#target_compile_features(gmock +# PRIVATE +# ${CXX11_FEATURES} +#) +# +#target_compile_features(gmock_main +# PRIVATE +# ${CXX11_FEATURES} +#) + +# Add aliases for GTest and GMock libraries +if(NOT TARGET GTest::GTest) + add_library(GTest::GTest ALIAS gtest) + add_library(GTest::Main ALIAS gtest_main) +endif() + +if(NOT TARGET GTest::GMock) + add_library(GMock::GMock ALIAS gmock) + add_library(GMock::Main ALIAS gmock_main) +endif() + diff --git a/05-unit-testing/google-test-download/3rd_party/google-test/CMakeLists.txt.in b/05-unit-testing/google-test-download/3rd_party/google-test/CMakeLists.txt.in new file mode 100644 index 0000000..05a5e39 --- /dev/null +++ b/05-unit-testing/google-test-download/3rd_party/google-test/CMakeLists.txt.in @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.0) + +project(googletest-download NONE) + +include(ExternalProject) + +# Version bfc0ffc8a698072c794ae7299db9cb6866f4c0bc happens to be master when I set this up. +# To prevent an issue with accidentally installing GTest / GMock with your project you should use a +# commit after 9469fb687d040b60c8749b7617fee4e77c7f6409 +# Note: This is after the release of v1.8 +ExternalProject_Add(googletest + URL https://github.com/google/googletest/archive/bfc0ffc8a698072c794ae7299db9cb6866f4c0bc.tar.gz + SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-src" + BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-build" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" +) diff --git a/05-unit-testing/google-test-download/CMakeLists.txt b/05-unit-testing/google-test-download/CMakeLists.txt new file mode 100644 index 0000000..bef7df7 --- /dev/null +++ b/05-unit-testing/google-test-download/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 3.0) + +# Set the project name +project (google_test_example) + +# Add an library for the example classes +add_library(example_google_test + Reverse.cpp + Palindrome.cpp +) + + +############################################# +# Unit tests + +add_subdirectory(3rd_party/google-test) + +# enable CTest testing +enable_testing() + +# Add a testing executable +add_executable(unit_tests unit_tests.cpp) + +target_link_libraries(unit_tests + example_google_test + GTest::GTest + GTest::Main +) + +add_test(test_all unit_tests) diff --git a/05-unit-testing/google-test-download/Palindrome.cpp b/05-unit-testing/google-test-download/Palindrome.cpp new file mode 100644 index 0000000..7276aec --- /dev/null +++ b/05-unit-testing/google-test-download/Palindrome.cpp @@ -0,0 +1,11 @@ +#include "Palindrome.h" + +bool Palindrome::isPalindrome(const std::string& toCheck) +{ + + if (toCheck == std::string(toCheck.rbegin(), toCheck.rend())) { + return true; + } + + return false; +} diff --git a/05-unit-testing/google-test-download/Palindrome.h b/05-unit-testing/google-test-download/Palindrome.h new file mode 100644 index 0000000..3990017 --- /dev/null +++ b/05-unit-testing/google-test-download/Palindrome.h @@ -0,0 +1,15 @@ +#ifndef __PALINDROME_H__ +#define __PALINDROME_H__ + +#include + +/** + * Trivial class to check if a string is a palindrome. + */ +class Palindrome +{ +public: + bool isPalindrome(const std::string& toCheck); +}; + +#endif diff --git a/05-unit-testing/google-test-download/README.adoc b/05-unit-testing/google-test-download/README.adoc new file mode 100644 index 0000000..0c8a8f1 --- /dev/null +++ b/05-unit-testing/google-test-download/README.adoc @@ -0,0 +1,231 @@ += Google Test Unit Testing Framework +:toc: +:toc-placement!: + +toc::[] + + +# Introduction + +Using link:https://cmake.org/Wiki/CMake/Testing_With_CTest[CTest] you can generate +a `make test` target to run automated unit-tests. This example shows how to +download and build the link:https://github.com/google/googletest[google test] library, +create tests and run them. + +The files in this tutorial are below: + +``` +$ tree +. +├── 3rd_party +│   └── google-test +│   ├── CMakeLists.txt +│   └── CMakeLists.txt.in +├── CMakeLists.txt +├── Reverse.h +├── Reverse.cpp +├── Palindrome.h +├── Palindrome.cpp +├── unit_tests.cpp +``` + + * link:3rd_party/google-test/CMakeLists.txt - CMake commands to build and prepare the google test library + * link:3rd_party/google-test/CMakeLists.txt.in - Helper script to do the download of google test + * link:CMakeLists.txt[] - Contains the CMake commands you wish to run + * link:Reverse.h[] / link:Reverse.cpp[.cpp] - Class to reverse a string + * link:Palindrome.h[] / link:Palindrome.cpp[.cpp] - Class to test if a string is a palindrome + * link:unit_test.cpp[] - Unit Tests using google test unit test framework + +# Requirements + +An internet connection. This example will download the google test library the first time it is built. See the +link:https://github.com/google/googletest/blob/master/googletest/README.md[google test readme] and link:http://crascit.com/2015/07/25/cmake-gtest/[here] for details. + +# Concepts + +## Download and Build Google Test + +[source,cmake] +---- +add_subdirectory(3rd_party/google-test) +---- + +This will add the CMake files which download and build Google Test. This is the recommended way to add google test and there are +more details from link:https://github.com/google/googletest/blob/master/googletest/README.md[google test readme] and link:http://crascit.com/2015/07/25/cmake-gtest/[here] + +Alternatives to this method include: + + * Use something like +git submodule+ to download the source to a folder in your tree and then do +add_subdirectory+ + * Vendor the google test source code within your repository + * Build google test externally and link it using +find_package(GTest)+ - Not recommended by the google test authors anymore + +## Enabling testing + +To enable testing you must include the following line in your top level CMakeLists.txt + +[source,cmake] +---- +enable_testing() +---- + +This will enable testing for the current folder and all folders below it. + +## Adding a testing executable + +The requirement for this step will depend on your unit-test framework. In the example +of google test, you can create binary(s) which includes all the unit tests that you want to run. + +[source,cmake] +---- +add_executable(unit_tests unit_tests.cpp) + +target_link_libraries(unit_tests + example_google_test + GTest::GTest + GTest::main +) +---- + +In the above code, a unit test binary is added, which links against the google test unit-test-framework using the +alias target setup during the link:3rd_party/google-test/CMakeLists.txt[download and build] of GTest. + +## Add A test + +To add a test you call the link:https://cmake.org/cmake/help/v3.0/command/add_test.html[`add_test()`] function. +This will create a named test which will run the supplied command. + +[source,cmake] +---- +add_test(test_all unit_tests) +---- + +In this example, we create a test called `test_all` which will run the executable +created by the `unit_tests` executable created from the call to `add_executable` + +# Building the Example + +[source,bash] +---- +$ mkdir build + +$ cd build/ + +$ cmake .. +-- The C compiler identification is GNU 5.4.0 +-- The CXX compiler identification is GNU 5.4.0 +-- Check for working C compiler: /usr/bin/cc +-- Check for working C compiler: /usr/bin/cc -- works +-- Detecting C compiler ABI info +-- Detecting C compiler ABI info - done +-- Detecting C compile features +-- Detecting C compile features - done +-- Check for working CXX compiler: /usr/bin/c++ +-- Check for working CXX compiler: /usr/bin/c++ -- works +-- Detecting CXX compiler ABI info +-- Detecting CXX compiler ABI info - done +-- Detecting CXX compile features +-- Detecting CXX compile features - done +-- Configuring done +-- Generating done +-- Build files have been written to: /data/data/code/cmake-examples/05-unit-testing/google-test-download/build/3rd_party/google-test/googletest-download +Scanning dependencies of target googletest +[ 11%] Creating directories for 'googletest' +[ 22%] Performing download step (download, verify and extract) for 'googletest' +-- downloading... + src='https://github.com/google/googletest/archive/bfc0ffc8a698072c794ae7299db9cb6866f4c0bc.tar.gz' + dst='/data/data/code/cmake-examples/05-unit-testing/google-test-download/build/3rd_party/google-test/googletest-download/googletest-prefix/src/bfc0ffc8a698072c794ae7299db9cb6866f4c0bc.tar.gz' + timeout='none' +-- downloading... done +-- verifying file... + file='/data/data/code/cmake-examples/05-unit-testing/google-test-download/build/3rd_party/google-test/googletest-download/googletest-prefix/src/bfc0ffc8a698072c794ae7299db9cb6866f4c0bc.tar.gz' +-- verifying file... warning: did not verify file - no URL_HASH specified? +-- extracting... + src='/data/code/cmake-examples/05-unit-testing/google-test-download/build/3rd_party/google-test/googletest-download/googletest-prefix/src/bfc0ffc8a698072c794ae7299db9cb6866f4c0bc.tar.gz' + dst='/data/code/cmake-examples/05-unit-testing/google-test-download/build/3rd_party/google-test/googletest-src' +-- extracting... [tar xfz] +-- extracting... [analysis] +-- extracting... [rename] +-- extracting... [clean up] +-- extracting... done +[ 33%] No patch step for 'googletest' +[ 44%] No update step for 'googletest' +[ 55%] No configure step for 'googletest' +[ 66%] No build step for 'googletest' +[ 77%] No install step for 'googletest' +[ 88%] No test step for 'googletest' +[100%] Completed 'googletest' +[100%] Built target googletest +-- Found PythonInterp: /usr/bin/python (found version "2.7.12") +-- Looking for pthread.h +-- Looking for pthread.h - found +-- Looking for pthread_create +-- Looking for pthread_create - not found +-- Check if compiler accepts -pthread +-- Check if compiler accepts -pthread - yes +-- Found Threads: TRUE +-- Configuring done +-- Generating done +-- Build files have been written to: /data/code/cmake-examples/05-unit-testing/google-test-download/build + +$ make +Scanning dependencies of target example_google_test +[ 6%] Building CXX object CMakeFiles/example_google_test.dir/Reverse.cpp.o +[ 12%] Building CXX object CMakeFiles/example_google_test.dir/Palindrome.cpp.o +[ 18%] Linking CXX static library libexample_google_test.a +[ 18%] Built target example_google_test +Scanning dependencies of target gtest +[ 25%] Building CXX object 3rd_party/google-test/googletest-build/googlemock/gtest/CMakeFiles/gtest.dir/src/gtest-all.cc.o +[ 31%] Linking CXX static library libgtest.a +[ 31%] Built target gtest +Scanning dependencies of target gtest_main +[ 37%] Building CXX object 3rd_party/google-test/googletest-build/googlemock/gtest/CMakeFiles/gtest_main.dir/src/gtest_main.cc.o +[ 43%] Linking CXX static library libgtest_main.a +[ 43%] Built target gtest_main +Scanning dependencies of target unit_tests +[ 50%] Building CXX object CMakeFiles/unit_tests.dir/unit_tests.cpp.o +[ 56%] Linking CXX executable unit_tests +[ 56%] Built target unit_tests +Scanning dependencies of target gmock_main +[ 62%] Building CXX object 3rd_party/google-test/googletest-build/googlemock/CMakeFiles/gmock_main.dir/__/googletest/src/gtest-all.cc.o +[ 68%] Building CXX object 3rd_party/google-test/googletest-build/googlemock/CMakeFiles/gmock_main.dir/src/gmock-all.cc.o +[ 75%] Building CXX object 3rd_party/google-test/googletest-build/googlemock/CMakeFiles/gmock_main.dir/src/gmock_main.cc.o +[ 81%] Linking CXX static library libgmock_main.a +[ 81%] Built target gmock_main +Scanning dependencies of target gmock +[ 87%] Building CXX object 3rd_party/google-test/googletest-build/googlemock/CMakeFiles/gmock.dir/__/googletest/src/gtest-all.cc.o +[ 93%] Building CXX object 3rd_party/google-test/googletest-build/googlemock/CMakeFiles/gmock.dir/src/gmock-all.cc.o +[100%] Linking CXX static library libgmock.a +[100%] Built target gmock + +$ make test +Running tests... +Test project /data/code/cmake-examples/05-unit-testing/google-test-download/build + Start 1: test_all +1/1 Test #1: test_all ......................... Passed 0.00 sec + +100% tests passed, 0 tests failed out of 1 + +Total Test time (real) = 0.00 sec +---- + +If the code is changed and it causes the unit tests to produce an error. +Then when running the tests you will see the following output. + +[source,bash] +---- +$ make test +Running tests... +Test project /data/code/cmake-examples/05-unit-testing/google-test-download/build + Start 1: test_all +1/1 Test #1: test_all .........................***Failed 0.00 sec + +0% tests passed, 1 tests failed out of 1 + +Total Test time (real) = 0.00 sec + +The following tests FAILED: + 1 - test_all (Failed) +Errors while running CTest +Makefile:72: recipe for target 'test' failed +make: *** [test] Error 8 +---- diff --git a/05-unit-testing/google-test-download/Reverse.cpp b/05-unit-testing/google-test-download/Reverse.cpp new file mode 100644 index 0000000..135757a --- /dev/null +++ b/05-unit-testing/google-test-download/Reverse.cpp @@ -0,0 +1,12 @@ +#include "Reverse.h" + +std::string Reverse::reverse(std::string& toReverse) +{ + std::string ret; + + for(std::string::reverse_iterator rit=toReverse.rbegin(); rit!=toReverse.rend(); ++rit) + { + ret.insert(ret.end(), *rit); + } + return ret; +} diff --git a/05-unit-testing/google-test-download/Reverse.h b/05-unit-testing/google-test-download/Reverse.h new file mode 100644 index 0000000..b06d5f2 --- /dev/null +++ b/05-unit-testing/google-test-download/Reverse.h @@ -0,0 +1,15 @@ +#ifndef __REVERSE_H__ +#define __REVERSE_H__ + +#include + +/** + * Trivial class whose only function is to reverse a string. + * Should use std::reverse instead but want to have example with own class + */ +class Reverse +{ +public: + std::string reverse(std::string& toReverse); +}; +#endif diff --git a/05-unit-testing/google-test-download/main.cpp b/05-unit-testing/google-test-download/main.cpp new file mode 100644 index 0000000..c7b58af --- /dev/null +++ b/05-unit-testing/google-test-download/main.cpp @@ -0,0 +1,24 @@ +#include +#include +#include + +int main(int argc, char *argv[]) +{ + std::cout << "Hello Third Party Include!" << std::endl; + + // use a shared ptr + boost::shared_ptr isp(new int(4)); + + // trivial use of boost filesystem + boost::filesystem::path path = "/usr/share/cmake/modules"; + if(path.is_relative()) + { + std::cout << "Path is relative" << std::endl; + } + else + { + std::cout << "Path is not relative" << std::endl; + } + + return 0; +} diff --git a/05-unit-testing/google-test-download/post_test.sh b/05-unit-testing/google-test-download/post_test.sh new file mode 100755 index 0000000..f89ce58 --- /dev/null +++ b/05-unit-testing/google-test-download/post_test.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +echo "Disabled for now because of issue with libcurl and https" +exit 0 + +#make test diff --git a/05-unit-testing/google-test-download/run_test.sh b/05-unit-testing/google-test-download/run_test.sh new file mode 100755 index 0000000..adfa0a8 --- /dev/null +++ b/05-unit-testing/google-test-download/run_test.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +echo "Disabled for now because of issue with libcurl and https" +exit 0 diff --git a/05-unit-testing/google-test-download/unit_tests.cpp b/05-unit-testing/google-test-download/unit_tests.cpp new file mode 100644 index 0000000..2593511 --- /dev/null +++ b/05-unit-testing/google-test-download/unit_tests.cpp @@ -0,0 +1,39 @@ +#include +#include "Reverse.h" +#include "Palindrome.h" + +#include + +class ReverseTests : public ::testing::Test +{ +}; + +TEST_F(ReverseTests, simple ) +{ + std::string toRev = "Hello"; + + Reverse rev; + std::string res = rev.reverse(toRev); + + EXPECT_EQ(res, "olleH" ); + +} + +TEST_F(ReverseTests, empty ) +{ + std::string toRev; + + Reverse rev; + std::string res = rev.reverse(toRev); + + EXPECT_EQ(res, "" ); +} + +TEST_F(ReverseTests, is_palindrome ) +{ + std::string pal = "mom"; + Palindrome pally; + + EXPECT_TRUE(pally.isPalindrome(pal)); + +} diff --git a/test.sh b/test.sh index 36ca41e..64b47ba 100755 --- a/test.sh +++ b/test.sh @@ -33,6 +33,7 @@ dirs=(./01-basic/A-hello-cmake \ ./04-static-analysis/clang-analyzer \ ./04-static-analysis/clang-format \ ./05-unit-testing/boost \ +./05-unit-testing/google-test-download \ ./06-installer/deb \ )