diff --git a/docs/ioc/user-guides/index.rst b/docs/ioc/user-guides/index.rst index 70c488d2..517fd5e7 100644 --- a/docs/ioc/user-guides/index.rst +++ b/docs/ioc/user-guides/index.rst @@ -8,13 +8,12 @@ User guides ---- .. toctree:: - :maxdepth: 1 + :titlesonly: ./private-repo-setup.rst ./flake-registry.rst ./developing-modules.rst ./mrf-devices.rst - ./testing/packaging-python-scripts.rst + ./testing/index.rst -.. TODO: make a NixOS test user-guide, - and add a toctree for testing-related guides there +.. TODO: make a NixOS test user-guide diff --git a/docs/ioc/user-guides/testing/index.rst b/docs/ioc/user-guides/testing/index.rst new file mode 100644 index 00000000..7ff77f89 --- /dev/null +++ b/docs/ioc/user-guides/testing/index.rst @@ -0,0 +1,14 @@ +Testing +======= + +`How-to guides`_ for users of the EPNix project wanting to test EPICS IOCs. + +.. _How-to guides: https://diataxis.fr/how-to-guides/ + +---- + +.. toctree:: + :titlesonly: + + ./unit-testing.rst + ./packaging-python-scripts.rst diff --git a/docs/ioc/user-guides/testing/unit-testing.rst b/docs/ioc/user-guides/testing/unit-testing.rst new file mode 100644 index 00000000..a6492723 --- /dev/null +++ b/docs/ioc/user-guides/testing/unit-testing.rst @@ -0,0 +1,256 @@ +Unit testing +============ + +This document is a user guide for adding unit tests +to your C and C++ functions. + +Using the gtest EPICS module +---------------------------- + +:ref:`pkg-support.gtest` is an EPICS module +that enables you to use `GoogleTest`_ as a framework for unit testing and mocking. + +The GoogleTest framework is a well-known framework for writing unit tests in C++. +You can test C functions using GoogleTest, +but the tests definitions must be in C++. + +.. _GoogleTest: https://google.github.io/googletest/ + +.. _is-library: + +Functions to test are in a library +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. note:: + Read this section if your top is an IOC. + + If the top you want to test already is a library / a support top, + you can skip this section. + +To test your C/C++ functions, +it's best to have a library +containing all the functions you want to test. +This makes it easier to have your functions both in your IOC and in your tests. + +For example, +if you have a :file:`myFunctions.cpp`, +don't add it to the sources of your IOC. +Instead, +create a library, +for example ``myLibrary``, +and add your :file:`myFunctions.cpp` to the sources of this library. + +Then, +link your IOC to ``myLibrary``. + +If you don't have a library, +change your :file:`src/Makefile` +so that it has these lines: + +.. code-block:: make + :caption: :file:`src/Makefile`: building a library + + LIBRARY += myLibrary + myLibrary_SRCS += myFunctions + # Libraries needed by 'myLibrary' + myLibrary_LIBS += Com + +Then make sure your IOC links with ``myLibrary``: + +.. code-block:: make + :caption: :file:`src/Makefile`: linking an IOC to a library + :emphasize-lines: 10-11 + + # ... + + #============================= + # Build the IOC application + + PROD_IOC += myIoc + + # ... + + # Link myIoc with myLibrary + myIoc_LIBS += myLibrary_LIBS + + +Adding gtest to the top +^^^^^^^^^^^^^^^^^^^^^^^ + +Add :ref:`pkg-support.gtest` to the build environment: + +.. code-block:: nix + :caption: :file:`flake.nix`: adding the ``gtest`` support module + :emphasize-lines: 5 + + # Add one of the supported modules here: + # --- + support.modules = with pkgs.epnix.support; [ + # other support modules... + gtest + ]; + + +Writing tests +^^^^^^^^^^^^^ + +To write your unit tests, +create a separate C++ file, +and follow the `GoogleTest documentation`_. + +Here is an example of a test: + +.. code-block:: cpp + :caption: :file:`myTest.cpp`: example ``gtest`` test definition + + #include + + #include "myLibrary.hpp" + + namespace { + class MyTest : public testing::Test { + protected: + MyTest() { /* Set-up work for each test */ } + ~MyTest() { /* Clean up work for each test */ } + }; + + TEST_F(MyTest, myTest1) + { + // Test something + EXPECT_EQ(2 + 2, 4) << "2 * 2 must be equal to 4"; + } + + TEST_F(MyTest, myTest2) + { + EXPECT_EQ(6 * 7, 42) << "6 * 7 must be equal to 42"; + } + + } // namespace + +.. _GoogleTest documentation: https://google.github.io/googletest/ + +Adding tests to the build +^^^^^^^^^^^^^^^^^^^^^^^^^ + +To add your tests to the build, +add these lines to your :file:`src/Makefile`: + +.. code-block:: make + :caption: :file:`src/Makefile`: adding ``gtest`` tests + + GTESTPROD_HOST += myTest + myTest_SRCS += myTest.cpp + myTest_LIBS += myLibrary + GTESTS += myTest + +.. _running-tests: + +Running the tests +^^^^^^^^^^^^^^^^^ + +To run the tests in your development shell, +run: + +.. code-block:: bash + + make runtests + +Running ``nix build`` also runs tests by default. + +To turn off tests inside the Nix build, +add this to your configuration: + +.. code-block:: nix + :caption: :file:`flake.nix`: turning tests off + :emphasize-lines: 4 + + # ... + # checks.imports = [./checks/simple.nix]; + + buildConfig.attrs.doCheck = false; + +Using the epics-base facility +----------------------------- + +epics-base provides the `epicsUnitTest.h`_ facility for declaring unit tests, +which can be useful if you don't want to import an external EPICS module. + +`epicsUnitTest.h`_ is made in C, +but tests can be written in either C or C++. + +.. _epicsUnitTest.h: https://github.com/epics-base/epics-base/blob/R7.0.8.1/modules/libcom/src/misc/epicsUnitTest.h + +Pre-requisites +^^^^^^^^^^^^^^ + +Examine gtest's :ref:`is-library`. + +Writing tests +^^^^^^^^^^^^^ + +To write epics-base unit tests, +make sure: + +- You include ``epicsUnitTest.h`` +- You include ``testMain.h`` + and use the ``MAIN`` macro + to define your main function +- Your main function starts with a :samp:`testPlan({n})`, + with *n* being the number of checks that your test will run +- Your main function returns ``testDone()`` +- You're using ``testOk``, ``testPass``, and ``testFail`` to add checks + +Here is an example test: + +.. code-block:: c + :caption: :file:`myTest.cpp`: example epics-base test definition + + #include + #include + + #include "myLibrary.hpp" + + static void succeed() + { + testPass("No issues succeeding"); + } + + static void checkAddition(int x) + { + testOk(x + x == 2 * x, "x + x must be equal to 2 * x"); + testOk(x + x + x == 3 * x, "x + x + x must be equal to 3 * x"); + } + + MAIN(myTest) + { + // 1 for succeed, + // 2 for checkAddition which is called 5 times + testPlan(1 + 5 * 2); + + succeed(); + + for(int i = 0; i < 5; ++i) { + checkAddition(i); + } + + return testDone(); + } + +Adding tests to the build +^^^^^^^^^^^^^^^^^^^^^^^^^ + +To add your tests to the build, +add these lines to your :file:`src/Makefile`: + +.. code-block:: make + :caption: :file:`src/Makefile`: adding epics-base tests + + TESTPROD_HOST += myTest + myTest_SRCS += myTest.cpp + myTest_LIBS += myLibrary + TESTSCRIPTS_HOST += myTest.t + +Running the tests +^^^^^^^^^^^^^^^^^ + +Examine gtest's :ref:`running-tests`. diff --git a/ioc/modules/common.nix b/ioc/modules/common.nix index 11838b6b..4954c92c 100644 --- a/ioc/modules/common.nix +++ b/ioc/modules/common.nix @@ -44,7 +44,7 @@ with lib; { `nixpkgs.overlays` option. ''; default = pkgs; - defaultText = literalExpression ""; + defaultText = literalExpression ""; readOnly = true; type = types.attrs; }; diff --git a/nixos/modules/archiver-appliance.nix b/nixos/modules/archiver-appliance.nix index 85407f00..6ba9db6f 100644 --- a/nixos/modules/archiver-appliance.nix +++ b/nixos/modules/archiver-appliance.nix @@ -167,7 +167,7 @@ in { ''; type = lib.types.path; default = "${cfg.package}/share/archappl/policies.py"; - defaultText = lib.literalExpression "\${cfg.package}/share/archappl/policies.py"; + defaultText = lib.literalExpression ''"''${cfg.package}/share/archappl/policies.py"''; }; ARCHAPPL_SHORT_TERM_FOLDER = lib.mkOption {