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'
- fix a warning noticed by clang-tidy
- install Doxygen on Windows in CI because it is no longer available
 out-of-the-box. The installation of DoxyGen is placed into a separate
 CI task in order to update the PATH at the right time on Windows.
  • Loading branch information
slali87 authored Feb 26, 2024
1 parent d1b2eba commit cfdbfc7
Show file tree
Hide file tree
Showing 13 changed files with 146 additions and 20 deletions.
9 changes: 8 additions & 1 deletion .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,20 @@ jobs:
run: |
cd Projects/${{ github.event.repository.name }}
./run.sh formatCode
- name: Create the document
- name: Install Doxygen
run: |
cd Projects/${{ github.event.repository.name }}
if [ "$RUNNER_OS" == "Linux" ]; then
sudo apt install doxygen
sudo apt install graphviz
elif [ "$RUNNER_OS" == "Windows" ]; then
choco install doxygen.install
choco install graphviz
echo "/C/Program Files/doxygen/bin" >> $GITHUB_PATH
fi
- name: Create the document
run: |
cd Projects/${{ github.event.repository.name }}
./run.sh doc
- name: Check complexity
run: |
Expand Down
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();
[[nodiscard]] const char* getString(const IHelloWorld& helloWorld) const;
};

#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) const {
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)
30 changes: 25 additions & 5 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) {
Lib lib;
const char* returnValue{lib.helloWorld()};
TEST(LibTestSuite, testGetString_ReturnExpectedString) {
const Lib lib;
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) {
const Lib lib;
const MockHelloWorld helloWorld;
EXPECT_CALL(helloWorld, hello())
.Times(1);

static_cast<void>(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 cfdbfc7

Please sign in to comment.