| Step 8: Testing and CTest | |
| ========================= | |
| Testing is, historically, not the role of the build system. At best it might | |
| have a specific target which maps to building and running the project's tests. | |
| In the CMake ecosystem, the opposite is true. CMake's testing ecosystem is | |
| known as CTest. This ecosystem is both deceivingly simple and incredibly | |
| powerful. In fact it is so powerful it deserves its own full tutorial to | |
| describe everything we could achieve with it. | |
| This is not that tutorial. In this step, we will scratch the surface of some | |
| of the facilities that CTest provides. | |
| Background | |
| ^^^^^^^^^^ | |
| At its core, CTest is a task launcher which runs commands and reports if they | |
| have returned zero or non-zero values. This is the level we will be dealing | |
| with CTest at. | |
| CMake provides direct integration with CTest via the :command:`enable_testing` | |
| and :command:`add_test` commands. These allow CMake to setup the necessary | |
| infrastructure in the build folder for CTest to discover, run, and report | |
| on various tests we might be interested in. | |
| After setting up and building tests, the easiest way to invoke CTest is to run | |
| it directly on the build directory with: | |
| .. code-block:: console | |
| ctest --test-dir build | |
| Which will run all available tests. Specific tests can be run with regular | |
| expressions. | |
| .. code-block:: console | |
| ctest --test-dir build -R SpecificTest | |
| CTest also has advanced mechanisms for scripting, fixtures, sanitizers, | |
| job servers, metric reportings, and much more. See the :manual:`ctest(1)` | |
| manual for more information. | |
| Exercise 1 - Adding Tests | |
| ^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| CTest convention dictates the building and running of tests be based on a | |
| default-``ON`` variable named :variable:`BUILD_TESTING`. When using the full | |
| suite of CTest capabilities via the :module:`CTest` module, this | |
| :command:`option` is setup for us. When using a more stripped-down approach to | |
| testing, it's expected the project will setup the option (or at least one of a | |
| similar name) on its own. | |
| When :variable:`BUILD_TESTING` is true, the :command:`enable_testing` command | |
| should be called in the root CML. | |
| .. code-block:: cmake | |
| enable_testing() | |
| This will generate all the necessary metadata into the build tree for CTest to | |
| find and run tests. | |
| Once that has been done, the :command:`add_test` command can be used to create | |
| a test anywhere in the project. The semantics of this command are similar to | |
| :command:`add_custom_command`; we can name an executable target as the "command". | |
| .. code-block:: cmake | |
| add_test( | |
| NAME MyAppWithTestFlag | |
| COMMAND MyApp --test | |
| ) | |
| Goal | |
| ---- | |
| Add tests for the MathFunctions library to the project and run them with CTest. | |
| Helpful Resources | |
| ----------------- | |
| * :variable:`BUILD_TESTING` | |
| * :command:`enable_testing` | |
| * :command:`function` | |
| * :command:`add_test` | |
| Files to Edit | |
| ------------- | |
| * ``Tests/CMakeLists.txt`` | |
| * ``CMakeLists.txt`` | |
| Getting Started | |
| --------------- | |
| A testing program has been written in the file ``Tests/TestMathFunctions.cxx``. | |
| This program takes a single command line argument, the math function to be | |
| tested, with valid values of ``add``, ``mul``, ``sqrt``, and ``sub``. The return | |
| code is zero if the operation is recognized and the calculated value is valid, | |
| otherwise it is non-zero. | |
| Complete ``TODO 1`` through ``TODO 7``. | |
| Build and Run | |
| ------------- | |
| No special configuration is needed, configure and build as usual. | |
| .. code-block:: console | |
| cmake --preset tutorial | |
| cmake --build build | |
| Verify all the tests pass with CTest. | |
| .. note:: | |
| When using a multi-config generator such as Visual Studio, it | |
| will be necessary to specify a configuration like ``Debug`` or ``Release`` | |
| using :option:`ctest -C`. This is true whenever using a multi-config | |
| generator, and won't be called out specifically in future commands. | |
| .. code-block:: console | |
| ctest --test-dir build | |
| You can run individual tests with the :option:`-R <ctest -R>` flag. | |
| .. code-block:: console | |
| ctest --test-dir build -R sqrt | |
| Solution | |
| -------- | |
| First we add a new executable for the tests. | |
| .. raw:: html | |
| <details><summary>TODO 1-2: Click to show/hide answer</summary> | |
| .. literalinclude:: Step9/Tests/CMakeLists.txt | |
| :caption: TODO 1-2: Tests/CMakeLists.txt | |
| :name: Tests/CMakeLists.txt-add_executable | |
| :language: cmake | |
| :start-at: add_executable | |
| :end-at: TestMathFunctions.cxx | |
| :append: ) | |
| .. raw:: html | |
| </details> | |
| Then we link in the library we are testing. | |
| .. raw:: html | |
| <details><summary>TODO 3: Click to show/hide answer</summary> | |
| .. literalinclude:: Step9/Tests/CMakeLists.txt | |
| :caption: TODO 3: Tests/CMakeLists.txt | |
| :name: Tests/CMakeLists.txt-target_link_libraries | |
| :language: cmake | |
| :start-at: target_link_libraries(TestMathFunctions | |
| :end-at: ) | |
| .. raw:: html | |
| </details> | |
| We need to call :command:`add_test` for each of the valid operations, but this | |
| would get repetitive, so we write a :command:`function` to do it for us. | |
| .. raw:: html | |
| <details><summary>TODO 4: Click to show/hide answer</summary> | |
| .. literalinclude:: Step9/Tests/CMakeLists.txt | |
| :caption: TODO 4: Tests/CMakeLists.txt | |
| :name: Tests/CMakeLists.txt-function | |
| :language: cmake | |
| :start-at: function | |
| :end-at: endfunction | |
| .. raw:: html | |
| </details> | |
| Now we can use our :command:`function` to add all the tests. | |
| .. raw:: html | |
| <details><summary>TODO 5: Click to show/hide answer</summary> | |
| .. literalinclude:: Step9/Tests/CMakeLists.txt | |
| :caption: TODO 5: Tests/CMakeLists.txt | |
| :name: Tests/CMakeLists.txt-add_test | |
| :language: cmake | |
| :start-at: MathFunctionTest(add | |
| :end-at: MathFunctionTest(sub | |
| .. raw:: html | |
| </details> | |
| Finally, we can add the :variable:`BUILD_TESTING` option and conditionally | |
| enable building and running tests in the top-level CML. | |
| .. raw:: html | |
| <details><summary>TODO 6-7: Click to show/hide answer</summary> | |
| .. literalinclude:: Step9/CMakeLists.txt | |
| :caption: TODO 6: CMakeLists.txt | |
| :name: CMakeLists.txt-BUILD_TESTING | |
| :language: cmake | |
| :start-at: option(BUILD_TESTING | |
| :end-at: option(BUILD_TESTING | |
| .. literalinclude:: Step9/CMakeLists.txt | |
| :caption: TODO 7: CMakeLists.txt | |
| :name: CMakeLists.txt-enable_testing | |
| :language: cmake | |
| :start-at: if(BUILD_TESTING) | |
| :end-at: endif() | |
| .. raw:: html | |
| </details> |