From 7e1b9ea039b159daba8d7015e361bcfe4b98b632 Mon Sep 17 00:00:00 2001 From: slali87 <109802779+slali87@users.noreply.github.com> Date: Fri, 23 Feb 2024 17:16:15 +0100 Subject: [PATCH] IMPR: Use Google Mock To enhance testing, Google Mock was utilized, requiring modifications to the 'Lib'. Additionally, an issue in the 'analyseCode' function was addressed in 'run.sh'. --- README.md | 4 ++-- incl/HelloWorld.h | 25 +++++++++++++++++++++++++ incl/IHelloWorld.h | 34 ++++++++++++++++++++++++++++++++++ incl/Lib.h | 12 ++++++++---- run.sh | 14 +++++++++++--- src/App.cpp | 4 +++- src_lib/CMakeLists.txt | 2 +- src_lib/HelloWorld.cpp | 3 +++ src_lib/Lib.cpp | 6 ++++-- test/CMakeLists.txt | 4 +++- test/LibTestCases.cpp | 28 ++++++++++++++++++++++++---- test/MockHelloWorld.h | 19 +++++++++++++++++++ 12 files changed, 137 insertions(+), 18 deletions(-) create mode 100644 incl/HelloWorld.h create mode 100644 incl/IHelloWorld.h create mode 100644 src_lib/HelloWorld.cpp create mode 100644 test/MockHelloWorld.h diff --git a/README.md b/README.md index 5637016..c53f084 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ ## Welcome to **CppSampleProject**, the C++ sample project! -This is a cross-platform **C++** sample project which presents a base project structure which is ideal to develop many projects in parallel. The dependencies are organized into one common place in order to avoid its unnecessary downloading and building. It contains templates to create libraries and applications (executable programs) as well. It uses [**CMake**](https://cmake.org/), [**Git**](https://git-scm.com/), [**GitHub**](https://github.com/), [**Google Test**](https://github.com/google/googletest), [**Markdown**](https://www.markdownguide.org/), **Bash** script, [**Valgrind**](https://valgrind.org/), [**LCOV Code Coverage**](https://wiki.documentfoundation.org/Development/Lcov), [**Clang-Tidy**](https://clang.llvm.org/extra/clang-tidy/), [**ClangFormat**](https://clang.llvm.org/docs/ClangFormat.html), [**Doxygen**](https://www.doxygen.nl/), [**Graphviz**](https://graphviz.org/), [**Lizard**](https://github.com/terryyin/lizard). +This is a cross-platform **C++** sample project which presents a base project structure which is ideal to develop many projects in parallel. The dependencies are organized into one common place in order to avoid its unnecessary downloading and building. It contains templates to create libraries and applications (executable programs) as well. It uses [**CMake**](https://cmake.org/), [**Git**](https://git-scm.com/), [**GitHub**](https://github.com/), [**Google Test**](https://github.com/google/googletest), [**Google Mock**](https://github.com/google/googletest/tree/main/googlemock), [**Markdown**](https://www.markdownguide.org/), **Bash** script, [**Valgrind**](https://valgrind.org/), [**LCOV Code Coverage**](https://wiki.documentfoundation.org/Development/Lcov), [**Clang-Tidy**](https://clang.llvm.org/extra/clang-tidy/), [**ClangFormat**](https://clang.llvm.org/docs/ClangFormat.html), [**Doxygen**](https://www.doxygen.nl/), [**Graphviz**](https://graphviz.org/), [**Lizard**](https://github.com/terryyin/lizard). -### **Structure of the project:** +### **Structure of the project (only the major files are listed):** ``` ProgrammingRepo Deps diff --git a/incl/HelloWorld.h b/incl/HelloWorld.h new file mode 100644 index 0000000..70d432c --- /dev/null +++ b/incl/HelloWorld.h @@ -0,0 +1,25 @@ +#ifndef HELLOWORLD_H +#define HELLOWORLD_H + +/** @file + * @brief HelloWorld related elements. + * + * It contains the HelloWorld related elements. + */ + +#include "IHelloWorld.h" + +/** + * @brief The HelloWorld implementation. + * + * This class is an implementation of the IHelloWorld interface. + */ +class HelloWorld : public IHelloWorld { + public: + /** + * @brief See IHelloWorld::hello + */ + [[nodiscard]] const char* hello() const override; +}; + +#endif // HELLOWORLD_H \ No newline at end of file diff --git a/incl/IHelloWorld.h b/incl/IHelloWorld.h new file mode 100644 index 0000000..f1e02fd --- /dev/null +++ b/incl/IHelloWorld.h @@ -0,0 +1,34 @@ +#ifndef IHELLOWORLD_H +#define IHELLOWORLD_H + +/** @file + * @brief IHelloWorld interface related elements. + * + * It contains the Hello World interface related elements. + */ + +/** + * @brief The IHelloWorld interface. + * + * This class is the interface of Hello World. + */ +class IHelloWorld { + public: + IHelloWorld() = default; + virtual ~IHelloWorld() = default; + constexpr IHelloWorld(const IHelloWorld&) = default; + constexpr IHelloWorld(IHelloWorld&&) = default; + IHelloWorld& operator=(const IHelloWorld&) = default; + IHelloWorld& operator=(IHelloWorld&&) = default; + + /** + * @brief It returns with the hello world string. + * + * This function returns with the hello world c-string. + * + * @return the hello world c-string. + */ + [[nodiscard]] virtual const char* hello() const = 0; +}; + +#endif // IHELLOWORLD_H \ No newline at end of file diff --git a/incl/Lib.h b/incl/Lib.h index 85300c3..b57ff57 100644 --- a/incl/Lib.h +++ b/incl/Lib.h @@ -7,6 +7,8 @@ * It contains the Lib related elements. */ +class IHelloWorld; + /** * @brief A template for libraries. * @@ -15,13 +17,15 @@ class Lib { public: /** - * @brief It returns with the hello world string. + * @brief It returns with an IHelloWorld based string. * - * This function returns with the hello world c-string. + * This function returns with the hello world c-string based on a IHelloWorld + * implementation. * - * @return the hello world c-string. + * @param helloWorld is a reference to the Hello World interface + * @return the IHelloWorld based c-string. */ - const char* helloWorld(); + const char* getString(const IHelloWorld& helloWorld); }; #endif // LIB_H diff --git a/run.sh b/run.sh index 8005531..7de89b3 100755 --- a/run.sh +++ b/run.sh @@ -228,6 +228,7 @@ function testCoverage test/"$executableName"Test lcov --capture --rc lcov_branch_coverage=1 --directory . --output-file lcovResultAfter lcov --rc lcov_branch_coverage=1 --add-tracefile lcovResultBefore --add-tracefile lcovResultAfter --output-file lcovResultCombined + sed -i '/_ZN11IHelloWorldD0Ev/d' lcovResultCombined # _ZN11IHelloWorldD0Ev and _ZN11IHelloWorldD2Ev destructors are created by compiler, but the former (deleting destructor of a pure virtual class) is expectedly never called. lcov --remove --rc lcov_branch_coverage=1 lcovResultCombined '/usr/*' '*/Deps/*' -o lcovResult genhtml lcovResult --rc genhtml_branch_coverage=1 --output-directory CodeCoverage > log (( error |= $? )) @@ -252,8 +253,15 @@ function testCoverage function analyseCode { - find . -path ./build -prune -o -regex '.*\.\(cpp\|h\)' -exec echo " --- Checking file: {} --- " \; -exec clang-tidy {} -p ./build/"$buildType"/ \; - return $? + error=0 + for file in `find . -path ./build -prune -o -regex '.*\.\(cpp\|h\)' -print` + do + echo " --- Checking file: $file --- " + clang-tidy $file -p ./build/"$buildType"/ + (( error |= $? )) + done + + return $error } function formatCode @@ -261,7 +269,7 @@ function formatCode # Check if there is any modified files numBefore=$(git diff --shortstat | tr -dc '0-9'); - find . -path ./build -prune -o -regex '.*\.\(cpp\|h\)' -exec clang-format -style=file -i {} \; + find . -path ./build -prune -o -regex '.*\.\(cpp\|h\)' -print -exec clang-format -style=file -i {} \; error=$? # Check if there is any modified files diff --git a/src/App.cpp b/src/App.cpp index 977d5d6..8c75859 100644 --- a/src/App.cpp +++ b/src/App.cpp @@ -1,5 +1,6 @@ #include "App.h" +#include "HelloWorld.h" #include "Lib.h" #include @@ -12,7 +13,8 @@ using std::cout; // NOLINTNEXTLINE(readability-convert-member-functions-to-static, bugprone-exception-escape) // clang-format on int App::main() { cout << "Hello World!\n"; - cout << Lib{}.helloWorld() << "\n"; + + cout << Lib{}.getString(HelloWorld{}) << "\n"; return 0; } diff --git a/src_lib/CMakeLists.txt b/src_lib/CMakeLists.txt index 1ee398d..15b4ae7 100644 --- a/src_lib/CMakeLists.txt +++ b/src_lib/CMakeLists.txt @@ -1,3 +1,3 @@ include_directories(../incl) -add_library (${CMAKE_PROJECT_NAME}Lib ../incl/Lib.h Lib.cpp) +add_library (${CMAKE_PROJECT_NAME}Lib ../incl/Lib.h Lib.cpp ../incl/IHelloWorld.h ../incl/HelloWorld.h HelloWorld.cpp) diff --git a/src_lib/HelloWorld.cpp b/src_lib/HelloWorld.cpp new file mode 100644 index 0000000..7dc4c8a --- /dev/null +++ b/src_lib/HelloWorld.cpp @@ -0,0 +1,3 @@ +#include "HelloWorld.h" + +const char* HelloWorld::hello() const { return "Hello LibWorld!"; } \ No newline at end of file diff --git a/src_lib/Lib.cpp b/src_lib/Lib.cpp index 9ff3f0f..bb4d1d1 100644 --- a/src_lib/Lib.cpp +++ b/src_lib/Lib.cpp @@ -1,8 +1,10 @@ #include "Lib.h" +#include "IHelloWorld.h" + const char* // Silence because meber function is used by desing since it is a demo code // NOLINTNEXTLINE(readability-convert-member-functions-to-static) -Lib::helloWorld() { - return "Hello LibWorld!"; +Lib::getString(const IHelloWorld& helloWorld) { + return helloWorld.hello(); } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4e51b31..91860b4 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,14 +1,16 @@ include_directories(../incl) include_directories(../src) include_directories(../../../Deps/googletest/googletest/include) +include_directories(../../../Deps/googletest/googlemock/include) add_executable (${CMAKE_PROJECT_NAME}Test main.cpp LibTestCases.cpp AppTestCases.cpp) find_library(GTest gtest HINTS ${CMAKE_CURRENT_SOURCE_DIR}/../../../Deps/googletest/build/lib/) +find_library(GMock gmock HINTS ${CMAKE_CURRENT_SOURCE_DIR}/../../../Deps/googletest/build/lib/) if(UNIX) set(PThreadLib -pthread) else() set(StaticLink -static) endif() -target_link_libraries(${CMAKE_PROJECT_NAME}Test PUBLIC ${CMAKE_PROJECT_NAME}Lib ${CMAKE_PROJECT_NAME}InnerLib ${PThreadLib} ${StaticLink} ${GTest}) +target_link_libraries(${CMAKE_PROJECT_NAME}Test PUBLIC ${CMAKE_PROJECT_NAME}Lib ${CMAKE_PROJECT_NAME}InnerLib ${PThreadLib} ${StaticLink} ${GTest} ${GMock}) add_test(NAME ${CMAKE_PROJECT_NAME}Test COMMAND ${CMAKE_PROJECT_NAME}Test) diff --git a/test/LibTestCases.cpp b/test/LibTestCases.cpp index 0090639..26a1ebf 100644 --- a/test/LibTestCases.cpp +++ b/test/LibTestCases.cpp @@ -4,21 +4,41 @@ * It contains unit tests to test the Lib. */ +#include "HelloWorld.h" #include "Lib.h" +#include "MockHelloWorld.h" +#include "gmock/gmock.h" #include "gtest/gtest.h" /** @test * @brief Test returning with expected string. * - * It tests if Lib::helloWorld function returns with the expected (helloWorld) - * c-string. + * It tests if Lib::getString function returns with the expected (Hello + * LibWorld!) c-string. */ // Silence because these warnings based on using google test // clang-format off // NOLINTNEXTLINE(cert-err58-cpp, cppcoreguidelines-owning-memory, fuchsia-default-arguments-calls, fuchsia-statically-constructed-objects, cppcoreguidelines-avoid-non-const-global-variables, clang-diagnostic-comment) // clang-format on -TEST(LibTestSuite, testHelloWorld_ReturnExpectedString) { +TEST(LibTestSuite, testGetString_ReturnExpectedString) { Lib lib; - const char* returnValue{lib.helloWorld()}; + const char* returnValue{lib.getString(HelloWorld{})}; EXPECT_STREQ(returnValue, "Hello LibWorld!"); } + +/** @test + * @brief Test calling the expected function. + * + * It tests if Lib::getString function calls the expected function (MockHelloWorld::hello). + */ +// Silence because these warnings based on using google test +// clang-format off +// NOLINTNEXTLINE(cert-err58-cpp, cppcoreguidelines-owning-memory, fuchsia-default-arguments-calls, fuchsia-statically-constructed-objects, cppcoreguidelines-avoid-non-const-global-variables, clang-diagnostic-comment) // clang-format on +TEST(LibTestSuite, testGetString_CallHello) { + Lib lib; + MockHelloWorld helloWorld; + EXPECT_CALL(helloWorld, hello()) + .Times(1); + + lib.getString(helloWorld); +} \ No newline at end of file diff --git a/test/MockHelloWorld.h b/test/MockHelloWorld.h new file mode 100644 index 0000000..5ba3ea4 --- /dev/null +++ b/test/MockHelloWorld.h @@ -0,0 +1,19 @@ +/** @file + * @brief Mocks of IHelloWorld. + * + * It contains mocks of IHelloWorld. + */ + +#include "IHelloWorld.h" +#include "gmock/gmock.h" + +/** + * @brief A mock of IHelloWorld. + * + * This class is a mock of IHelloWorld. + */ +class MockHelloWorld : public IHelloWorld { + public: + // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes) + MOCK_METHOD(const char*, hello, (), (const, override)); +}; \ No newline at end of file