# Test the library

## In this learning path

- [Introduction](https://learn.arm.com/learning-paths/cross-platform/matrix/)
- [Laying the foundations](https://learn.arm.com/learning-paths/cross-platform/matrix/1-foundations/)
- [Test the library](https://learn.arm.com/learning-paths/cross-platform/matrix/2-testing/)
- [Start coding](https://learn.arm.com/learning-paths/cross-platform/matrix/3-code-1/)
- [Implement matrix operations](https://learn.arm.com/learning-paths/cross-platform/matrix/4-code-2/)
- [Next Steps](https://learn.arm.com/learning-paths/cross-platform/matrix/_next-steps/)

## Learn about unit testing
It’s common practice when developing software to create unit tests. While it might appear unnecessary at the outset, tests provide significant benefits:
- When adding new functionality, or porting to a new platform, the tests allow developers to feel confident in the process.
- They also inspire confidence in users that the software is at the appropriate level of quality.
- They offer an opportunity to catch regressions.
- They demonstrate how to use the library in practice.
- They create opportunities for those new to the project to easily check their patches, and verify that the introduction of the new code has not created unintended negative changes.

You will notice that setting up testing precedes library code development.

There are many unit testing frameworks available, and C++ is not short of them. See this [wikipedia article](https://en.wikipedia.org/wiki/List_of_unit_testing_frameworks#C++).
This particular Learning Path uses [GoogleTest](https://github.com/google/googletest) as the testing framework.

## Set up GoogleTest
One method you can use is to rely on the operating system platform to provide GoogleTest, then ask developers to install it on each machine they use, but this is an unnecessary step.

As testing is a cornerstone of your Matrix library development, GoogleTest should be installed automatically as a dependency in the build tree of your project.

One great feature of GoogleTest is that it provides a seamless integration with CMake.

Adding external dependencies is easily done with CMake. This is done with a separate `CMakeLists.txt` file, placed in the `external/` directory. This file covers all external dependencies. It will be used by the main `CMakeLists.txt`.

Create the file `external/CMakeLists.txt` with the following content:
```cmake
cmake_minimum_required(VERSION 3.6)

project(external LANGUAGES CXX)

# Get the functionality to configure, build and install external project
# from CMake module 'ExternalProject'.
include(ExternalProject)

# Use the same compiler, build type and instalation directory than those
# from our caller.
set(EXTERNAL_PROJECT_CMAKE_ARGS
      -DCMAKE_CXX_COMPILER:PATH=${CMAKE_CXX_COMPILER}
      -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
      -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_INSTALL_PREFIX})

# Add 'googletext' as an external project, that will be cloned with git,
# from the official googletest repository, at version v1.14.
# We ask for a shallow clone, which is a clone with only the revision
# we are interested in rather than googletest's full history ---
# this makes the clone much faster (less data traffic), and uses much
# less disk space ; furthermore, as we are not developping googletest
# but just merely using it, we don't need thre full history. It will be
# built and installed with our build configuration passed with CMAKE_ARGS.
ExternalProject_Add(googletest
    PREFIX "external"
    GIT_REPOSITORY "https://github.com/google/googletest"
    GIT_TAG "v1.14.0"
    GIT_SHALLOW TRUE
    CMAKE_ARGS ${EXTERNAL_PROJECT_CMAKE_ARGS}
)
```
You might notice a new CMake feature: variables. Variables start with the `$` character and have a name inserted between curly braces. A CMake variable can be set by the CMake itself, or by the user, and they can be modified or used as they are.

In this case, the variable `${EXTERNAL_PROJECT_CMAKE_ARGS}` is set with the options to pass to CMake for installing the external dependencies:
- `${CMAKE_CXX_COMPILER}`: the C++ compiler used by CMake
- `${CMAKE_BUILD_TYPE}`: the type of build used by CMake (`Release`, `Debug`, …)
- `${CMAKE_INSTALL_PREFIX}`: where CMake will install the project

The project now looks like this:
```
Matrix/
├── CMakeLists.txt
├── build/
│   ...
├── external/
│   └── CMakeLists.txt
├── include/
│   └── Matrix/
│       └── Matrix.h
├── lib/
│   └── Matrix/
│       └── Matrix.cpp
└── src/
    ├── getVersion.cpp
    └── howdy.cpp
```
Next, you need to use the new `CMakeLists.txt` in the top level CMake file.

Add the following lines after the Matrix project declaration in the top-level `CMakeLists.txt`:
```cmake
# ===================================================================
# Download, configure, build and install locally our external dependencies.
# This is done once, at configuration time.
# -------------------------------------------------------------------

# Build CMake command line so that it will use the same CMake configuration than
# the one we have been invoked with (generator, compiler, build type, build directory)
set(EXTERNAL_PROJECT_CMAKE_ARGS
      -G ${CMAKE_GENERATOR}
      -DCMAKE_CXX_COMPILER:PATH=${CMAKE_CXX_COMPILER}
      -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
      -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_BINARY_DIR})

# Download and configure our external dependencies
execute_process(
  COMMAND ${CMAKE_COMMAND}
      -S ${CMAKE_SOURCE_DIR}/external
      -B ${CMAKE_BINARY_DIR}/external
      ${EXTERNAL_PROJECT_CMAKE_ARGS}
)

# Build our external dependencies.
execute_process(
  COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR}/external
)
# Install our external dependencies.
execute_process(
  COMMAND ${CMAKE_COMMAND} --install ${CMAKE_BINARY_DIR}/external
)

# Import googletest package information (library names, paths, dependencies...)
set(GTest_DIR "${CMAKE_BINARY_DIR}/lib/cmake/GTest"
    CACHE PATH "Path to the googletest package configuration files")
find_package(GTest REQUIRED
  CONFIG
  NO_DEFAULT_PATH
  NO_PACKAGE_ROOT_PATH
  NO_SYSTEM_ENVIRONMENT_PATH
)
```
The variable `${CMAKE_GENERATOR}` is what CMake uses to perform the build, usually GNU Make or Ninja, but it can be an IDE specific project file as well.

The variable `${CMAKE_SOURCE_DIR}` is the path to the top level directory of your project where the main `CMakeLists.txt` is located, and `${CMAKE_BINARY_DIR}` is the build directory.

At configuration time, CMake downloads, builds and installs GoogleTest and makes it available to your project using `find_package`.

Now if you build the project, CMake notices it has been updated and will perform all necessary steps.

The output below shows that besides the executable, CMake has configured, built, and installed GoogleTest.

Copy and paste the commands to run the build yourself to see the output:
```bash
cd build
ninja
```

## Add your first test
Now that GoogleTest is available, you can add the first test.

In order to keep the project clean, all tests go inside a `tests/` directory. One file, `tests/main.cpp`, contains the top level directions for testing.

As a project might contain many tests, it’s good to split them across several files inside the `tests/` directory.

Create the top-level test in `tests/main.cpp` and paste the following code into the file:
```cpp
#include "gtest/gtest.h"

using namespace testing;

int main(int argc, char **argv) {
    InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
```
Create `tests/Version.cpp` and add the `getVersion` unit test into the file:
```cpp
#include "Matrix/Matrix.h"
#include "gtest/gtest.h"

using namespace MatComp;

TEST(Matrix, getVersion) {
    const Version &version = getVersion();
    EXPECT_EQ(version.major, 0);
    EXPECT_EQ(version.minor, 1);
    EXPECT_EQ(version.patch, 0);
}
```
This test invokes `getVersion` and checks that the `major`, `minor` and `patch` levels match the expected values.

The last step is to tell CMake about the tests. All tests are linked together in a single `matrix-test` executable (linking with the Matrix library and GoogleTest), and you add a convenience `check` target so the tests can be run easily.

Add the following at the bottom of the top-level `CMakeLists.txt`:
```cmake
# ===================================================================
# Testing
# -------------------------------------------------------------------
add_executable(matrix-test tests/main.cpp tests/Version.cpp)
target_link_libraries(matrix-test GTest::gtest Matrix)
add_custom_target(check
   COMMAND matrix-test --gtest_color=yes --gtest_output=xml:matrix-test.xml
)
```
Run the build again:
```bash
cd build
ninja
```

And run the tests:
```bash
ninja check
```
Congratulations, your first unit test of the Matrix library passes!

## What have you achieved so far?
Your directory structure now looks like this:
```
Matrix/
├── CMakeLists.txt
├── build/
│   ├── howdy*              <- The howdy executable program
...
│   ├── libMatrix.a         <- The Matrix library
│   ├── matrix-getVersion*  <- The getVersion executable program
│   ├── matrix-test*        <- The Matrix library tests executable program
│   └── matrix-test.xml     <- The Matrix test results in XML format>
├── external/
│   └── CMakeLists.txt
├── include/
│   └── Matrix/
│       └── Matrix.h
├── lib/
│   └── Matrix/
│       └── Matrix.cpp
├── src/
│   ├── getVersion.cpp
│   └── howdy.cpp
└── tests/
    ├── Version.cpp
    └── main.cpp
```
CMake makes it easy to use GoogleTest as an external project. Adding unit tests as you go is now easy.

You have created the unit testing environment for your Matrix library and added a test. The infrastructure is now in place to implement the core of the Matrix processing library.

You can refer to this chapter source code in `code-examples/learning-paths/cross-platform/matrix/chapter-2` in the archive that you have downloaded earlier.
