Skip to content

Commit

Permalink
IMPR: Use Google Mock
Browse files Browse the repository at this point in the history
To enhance testing, Google Mock was utilized, requiring modifications to
 the 'Lib'. Additionally, an issue in the 'analyseCode' function was
 addressed in 'run.sh'.
  • Loading branch information
slali87 committed Feb 23, 2024
1 parent d1b2eba commit 7e1b9ea
Show file tree
Hide file tree
Showing 12 changed files with 137 additions and 18 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
25 changes: 25 additions & 0 deletions incl/HelloWorld.h
Original file line number Diff line number Diff line change
@@ -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
34 changes: 34 additions & 0 deletions incl/IHelloWorld.h
Original file line number Diff line number Diff line change
@@ -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
12 changes: 8 additions & 4 deletions incl/Lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
* It contains the Lib related elements.
*/

class IHelloWorld;

/**
* @brief A template for libraries.
*
Expand All @@ -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
14 changes: 11 additions & 3 deletions run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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 |= $? ))
Expand All @@ -252,16 +253,23 @@ 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
{
# 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
Expand Down
4 changes: 3 additions & 1 deletion src/App.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "App.h"

#include "HelloWorld.h"
#include "Lib.h"

#include <iostream>
Expand All @@ -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;
}
2 changes: 1 addition & 1 deletion src_lib/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
3 changes: 3 additions & 0 deletions src_lib/HelloWorld.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#include "HelloWorld.h"

const char* HelloWorld::hello() const { return "Hello LibWorld!"; }
6 changes: 4 additions & 2 deletions src_lib/Lib.cpp
Original file line number Diff line number Diff line change
@@ -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();
}
4 changes: 3 additions & 1 deletion test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
28 changes: 24 additions & 4 deletions test/LibTestCases.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
19 changes: 19 additions & 0 deletions test/MockHelloWorld.h
Original file line number Diff line number Diff line change
@@ -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));
};

0 comments on commit 7e1b9ea

Please sign in to comment.