Blame docs/contribute/testing.adoc

Packit Service 569379
= Writing and running tests for OpenSCAP
Packit Service 569379
Packit Service 569379
This document should help you with writing and running tests for your pull
Packit Service 569379
requests. It is recommended to add a new test if a new functionality is added
Packit Service 569379
or if a code that is not covered by any test is updated. Another recommendation
Packit Service 569379
is to add your test in a separate commit.
Packit Service 569379
Packit Service 569379
All the tests reside in the link:../../tests[tests] directory. It has multiple
Packit Service 569379
subdirectories which should represent various parts of the OpenSCAP library and
Packit Service 569379
its utilities. When you contribute to some part of the OpenSCAP project you
Packit Service 569379
should put a test for your contribution into the corresponding subdirectory
Packit Service 569379
in the link:../../tests[tests] directory. Use your best judgement when deciding
Packit Service 569379
where to put the test for your pull request and if you are not sure don't be
Packit Service 569379
affraid to ask in the pull request, someone will definitely help you with that.
Packit Service 569379
Packit Service 569379
NOTE: OpenSCAP project uses the **CMake** buildsystem which has built-in
Packit Service 569379
testing support through the
Packit Service 569379
link:https://gitlab.kitware.com/cmake/community/wikis/doc/ctest/Testing-With-CTest[CTest]
Packit Service 569379
testing tool.
Packit Service 569379
Packit Service 569379
Packit Service 569379
== Preparing test environment and running tests
Packit Service 569379
To run a specific test or all tests you first need to compile the OpenSCAP
Packit Service 569379
library and then install additional packages required for testing. See the
Packit Service 569379
*Building OpenSCAP on Linux* section in the link:../developer/developer.adoc[OpenSCAP Developer Manual]
Packit Service 569379
for more details.
Packit Service 569379
Packit Service 569379
Packit Service 569379
== Writing a new test
Packit Service 569379
In this guide we will use an example to describe the process of writing a test
Packit Service 569379
for the OpenSCAP project. Let's suppose you want to write a new test for
Packit Service 569379
the Script Check Engine (SCE) to test its basic functionality. SCE allows you
Packit Service 569379
to define your own scripts (usually written in Bash or Python) to extend XCCDF
Packit Service 569379
rule checking capabilities. Custom check scripts can be referenced from
Packit Service 569379
an XCCDF rule using `<check-content-ref>` element, for example:
Packit Service 569379
----
Packit Service 569379
<check system="http://open-scap.org/page/SCE">
Packit Service 569379
    <check-content-ref href="YOUR_BASH_SCRIPT.sh"/>
Packit Service 569379
</check>
Packit Service 569379
----
Packit Service 569379
Packit Service 569379
Packit Service 569379
=== Deciding where to put a new test
Packit Service 569379
In our example, we are testing the SCE module, therefore we will look for
Packit Service 569379
its subdirectory in the link:../../tests[tests] direcotory and we will find it
Packit Service 569379
at the following link: link:../../tests/sce[tests/sce]. We will add our new test
Packit Service 569379
into this subdirectory.
Packit Service 569379
Packit Service 569379
Packit Service 569379
==== Scenario A: There is a suitable directory for my new test
Packit Service 569379
This will happen most of the times. As stated above in our example we will place
Packit Service 569379
our test into the link:../../tests/sce[tests/sce] directory.
Packit Service 569379
Packit Service 569379
Packit Service 569379
==== Scenario B: There is no suitable directory for my new test
Packit Service 569379
This might happen if your test covers a part of the OpenSCAP which has no tests
Packit Service 569379
at all. In this case you would need to add a new directory with suitable name
Packit Service 569379
into the link:../../tests[tests] directory structure and create/update
Packit Service 569379
`CMakeLists.txt` files.
Packit Service 569379
Packit Service 569379
To have an example also for this scenario, let's suppose we want to add the
Packit Service 569379
`foo` subdirectory into the link:../../tests[tests] directory. We need to:
Packit Service 569379
Packit Service 569379
. Create the `foo` subdirectory in the link:../../tests[tests] directory.
Packit Service 569379
. Use the link:https://cmake.org/cmake/help/latest/command/add_subdirectory.html[add_subdirectory]
Packit Service 569379
  command from the link:../../tests/CMakeLists.txt[tests/CMakeLists.txt]
Packit Service 569379
  to add the `foo` subdirectory to the CMake buildsystem.
Packit Service 569379
. Create `CMakeLists.txt` inside the `tests/foo` directory and use the
Packit Service 569379
  `add_oscap_test` function (defined in the
Packit Service 569379
  link:../../tests/CMakeLists.txt[tests/CMakeLists.txt]) to add all your test
Packit Service 569379
  scripts from the `foo` directory.
Packit Service 569379
Packit Service 569379
Please see the
Packit Service 569379
link:https://gitlab.kitware.com/cmake/community/wikis/doc/ctest/Testing-With-CTest[
Packit Service 569379
CTest documentation] for more details.
Packit Service 569379
Packit Service 569379
Packit Service 569379
=== Common test library
Packit Service 569379
When writing tests for OpenSCAP you should use the common test library which is
Packit Service 569379
located at link:../../tests/test_common.sh.in[tests/test_common.sh.in].
Packit Service 569379
Packit Service 569379
NOTE: The `cmake` command will generate the `tests/test_common.sh` file from
Packit Service 569379
the link:../../tests/test_common.sh.in[tests/test_common.sh.in] adding
Packit Service 569379
configuration specific settings.
Packit Service 569379
Packit Service 569379
You will need to source the `tests/test_common.sh` in your test scripts to use
Packit Service 569379
the functions which it provides:
Packit Service 569379
[source,bash]
Packit Service 569379
----
Packit Service 569379
#!/usr/bin/env bash
Packit Service 569379
Packit Service 569379
set -o pipefail
Packit Service 569379
Packit Service 569379
. $builddir/tests/test_common.sh
Packit Service 569379
Packit Service 569379
test1
Packit Service 569379
test2
Packit Service 569379
...
Packit Service 569379
----
Packit Service 569379
Packit Service 569379
NOTE: The `$builddir` variable contains the path to the top level of the build
Packit Service 569379
tree. It is defined in the link:../../tests/CMakeLists.txt[tests/CMakeLists.txt]
Packit Service 569379
as `builddir=${CMAKE_BINARY_DIR}`.
Packit Service 569379
Packit Service 569379
Packit Service 569379
==== Global variables exported by test library
Packit Service 569379
Always use `$OSCAP` variable instead of the plain `oscap` in your tests when
Packit Service 569379
calling the `oscap` command line tool. This is because tests might be run with
Packit Service 569379
`CUSTOM_OSCAP` variable.
Packit Service 569379
Packit Service 569379
You can use `$XMLDIFF` in your tests which will call the
Packit Service 569379
link:../../tests/xmldiff.pl[tests/xmldiff.pl] script.
Packit Service 569379
Packit Service 569379
It is also possible to do XPath queries using `$XPATH` variable, the usage is:
Packit Service 569379
[source,bash]
Packit Service 569379
----
Packit Service 569379
$XPATH FILE 'QUERY'
Packit Service 569379
----
Packit Service 569379
Packit Service 569379
Packit Service 569379
==== Best practices when writing bash tests
Packit Service 569379
It is always good to set `pipefail` option for all your test scripts so you
Packit Service 569379
accidentally don't lose non-zero exit statuses of commands in a pipeline:
Packit Service 569379
[source,bash]
Packit Service 569379
----
Packit Service 569379
set -o pipefail
Packit Service 569379
----
Packit Service 569379
Packit Service 569379
The next option you can consider is `errexit` which exits your script
Packit Service 569379
immediately if a command exits with a non-zero status. You might want to use
Packit Service 569379
this option if tests are somehow dependent and it doesn't make sense to continue
Packit Service 569379
testing after one test fails.
Packit Service 569379
[source,bash]
Packit Service 569379
----
Packit Service 569379
set -e
Packit Service 569379
----
Packit Service 569379
Packit Service 569379
Also consider splitting the tests up into several separate bash scripts if
Packit Service 569379
you test many things at once; long running "all.sh" test should be avoided
Packit Service 569379
if possible, as when these fail it is harder to debug than having more,
Packit Service 569379
smaller tests. However, make sure these independent tests don't have hidden
Packit Service 569379
dependencies: often we'll run the test suite in parallel, meaning in-order
Packit Service 569379
execution of different test scripts cannot be ensured. (However, order
Packit Service 569379
order inside the test script is guaranteed per bash's rules).
Packit Service 569379
Packit Service 569379
Lastly, make sure your tests are architecture, distribution, and operating
Packit Service 569379
system independent. Try to make your tests dependent on local file contents,
Packit Service 569379
not system file contents, and make them independent of listening ports and
Packit Service 569379
installed software as much as possible.
Packit Service 569379
Packit Service 569379
==== test_init function
Packit Service 569379
This function does nothing. Logging is done by the CTest and the log from
Packit Service 569379
testing can be found at `build/Testing/Temporary/LastTest.log`.
Packit Service 569379
Packit Service 569379
Packit Service 569379
==== test_run function
Packit Service 569379
The function is responsible for executing a test script file or a function and
Packit Service 569379
logging its result into the log file.
Packit Service 569379
[source,bash]
Packit Service 569379
----
Packit Service 569379
test_run "DESCRIPTION" TEST_FUNCTION|$srcdir/TEST_SCRIPT_FILE ARG [ARG...]
Packit Service 569379
----
Packit Service 569379
Packit Service 569379
NOTE: The `$srcdir` variable contains the path to the directory with the test
Packit Service 569379
script. It is defined in the link:../../tests/CMakeLists.txt[tests/CMakeLists.txt]
Packit Service 569379
as `srcdir=${CMAKE_CURRENT_SOURCE_DIR}`. The reason is backward compatibility
Packit Service 569379
as originally tests were executed by the GNU automake.
Packit Service 569379
Packit Service 569379
The `test_run` function reports the following results into the log file:
Packit Service 569379
Packit Service 569379
* *PASS* when script/function returns *0*,
Packit Service 569379
* *FAIL* when script/function returns *1*,
Packit Service 569379
* *SKIP* when script/function returns *255*,
Packit Service 569379
* *WARN* when script/function returns none of the above exit statuses.
Packit Service 569379
Packit Service 569379
The result of every test executed by the `test_run` function will be reported
Packit Service 569379
in the log file in a following way:
Packit Service 569379
[source,bash]
Packit Service 569379
----
Packit Service 569379
TEST: DESCRIPTION
Packit Service 569379
<test stdout + stderr output>
Packit Service 569379
RESULT: PASS/FAIL/SKIP/WARN
Packit Service 569379
----
Packit Service 569379
Packit Service 569379
Packit Service 569379
==== test_exit function
Packit Service 569379
The function is responsible for cleaning-up the testing environment. You can
Packit Service 569379
call it without arguments or with one argument -- a script/function which will
Packit Service 569379
do additional clean-up tasks.
Packit Service 569379
[source,bash]
Packit Service 569379
----
Packit Service 569379
test_exit [CLEAN_SCRIPT|CLEAN_FUNCTION]
Packit Service 569379
----
Packit Service 569379
Packit Service 569379
Packit Service 569379
==== require function
Packit Service 569379
Checks if requirements are in the `$PATH`, use it as follows:
Packit Service 569379
[source,bash]
Packit Service 569379
----
Packit Service 569379
require 'program' || return 255
Packit Service 569379
----
Packit Service 569379
Packit Service 569379
Packit Service 569379
==== probecheck function
Packit Service 569379
Checks if probe exists, use it as follows:
Packit Service 569379
[source,bash]
Packit Service 569379
----
Packit Service 569379
probecheck 'probe' || return 255
Packit Service 569379
----
Packit Service 569379
Packit Service 569379
Packit Service 569379
==== verify_results function
Packit Service 569379
Verifies that there is the `COUNT` number of results of selected OVAL `TYPE` in
Packit Service 569379
a `RESULTS_FILE`:
Packit Service 569379
[source,bash]
Packit Service 569379
----
Packit Service 569379
verify_results TYPE CONTENT_FILE RESULTS_FILE COUNT
Packit Service 569379
Packit Service 569379
verify_results "def" test_probe_foo.xml results.xml 13
Packit Service 569379
----
Packit Service 569379
Packit Service 569379
The function extracts the actual test/definition result, and compares it with the respective test/definition comment.
Packit Service 569379
For example, if the test contains `comment="true"`, the test passes only if result of the respective test is `true`,
Packit Service 569379
if `comment="false"`, a `false` result is expected.
Packit Service 569379
If the comment is missing or it has other value, it is assumed that the result should be neither `true` nor `false`.
Packit Service 569379
Packit Service 569379
NOTE: This function expects that the OVAL `TYPE` is numbered from `1` to `COUNT`
Packit Service 569379
in the `RESULTS_FILE`.
Packit Service 569379
`TYPE` is typically `def` or `tst` for definitions and tests respectively.
Packit Service 569379
Packit Service 569379
Packit Service 569379
==== assert_exists function
Packit Service 569379
Does an XPath query to a file specified in the `$result` variable and checks if
Packit Service 569379
number of results matches with an expected number specified as an argument:
Packit Service 569379
[source,bash]
Packit Service 569379
----
Packit Service 569379
result="relative_path_to_file"
Packit Service 569379
assert_exists EXPECTED_NUMBER_OF_RESULTS XPATH_QUERY_STRING
Packit Service 569379
----
Packit Service 569379
Packit Service 569379
For example, let's say you want to check that in the `results.xml` file the
Packit Service 569379
result of the rule `xccdf_com.example.www_rule_test` is fail:
Packit Service 569379
[source,bash]
Packit Service 569379
----
Packit Service 569379
result="./results.xml"
Packit Service 569379
my_rule_="xccdf_com.example.www_rule_test"
Packit Service 569379
assert_exists 1 "//rule-result[@idref=\"$my_rule\"]/result[text()=\"fail\"]"
Packit Service 569379
----
Packit Service 569379
Packit Service 569379
Packit Service 569379
=== Adding test files
Packit Service 569379
Now, as we know where a new test should go and what functions and capabilities
Packit Service 569379
are provided by the common test library, we can add test files which will
Packit Service 569379
contain test scripts and content required for testing.
Packit Service 569379
Packit Service 569379
To sum up, we are adding a tests to check the basic functionality of the Script
Packit Service 569379
Check Engine (SCE) and we have decided that the test will go into the
Packit Service 569379
link:../../tests/sce[tests/sce] directory.
Packit Service 569379
Packit Service 569379
We will add the link:../../tests/sce/test_sce.sh[tests/sce/test_sce.sh]
Packit Service 569379
script which will contain our test and
Packit Service 569379
link:../../tests/sce/sce_xccdf.xml[tests/sce/sce_xccdf.xml], an XML file with
Packit Service 569379
XCCDF rules which are referencing various check scripts (grep the
Packit Service 569379
`check-content-ref` element to see the referenced files). All the referenced
Packit Service 569379
check script files are set to always pass and the
Packit Service 569379
link:../../tests/sce/test_sce.sh[tests/sce/test_sce.sh] script will perform
Packit Service 569379
evaluation of the link:../../tests/sce/sce_xccdf.xml[tests/sce/sce_xccdf.xml]
Packit Service 569379
XCCDF document file and it will check that all rule results are `pass`.
Packit Service 569379
Packit Service 569379
Packit Service 569379
=== Plugging your new test into the test library
Packit Service 569379
You need to plug your test into the test library so it will be run automatically
Packit Service 569379
everytime `make test` is run. To do this, you need to add your test script
Packit Service 569379
into the `CMakeLists.txt`. The `CMakeLists.txt` which you need to modify is
Packit Service 569379
located in the same directory as your test script.
Packit Service 569379
Packit Service 569379
We will demonstrate this on our example with the SCE test. We have prepared our
Packit Service 569379
test script, the XML document file with custom rules and various check scripts
Packit Service 569379
for testing. We placed all our test files into the
Packit Service 569379
link:../../tests/sce[tests/sce] directory. Now we will modify the
Packit Service 569379
link:../../tests/sce/CMakeLists.txt[tests/sce/CMakeLists.txt] and we will add
Packit Service 569379
our test script file using the `add_oscap_test` function which will make sure
Packit Service 569379
that our test will be executed by the `make test`:
Packit Service 569379
----
Packit Service 569379
if(ENABLE_SCE)
Packit Service 569379
	...
Packit Service 569379
	*add_oscap_test("test_sce.sh")*
Packit Service 569379
	...
Packit Service 569379
endif()
Packit Service 569379
----
Packit Service 569379
Packit Service 569379
Packit Service 569379
=== Running your new test
Packit Service 569379
To run your new test you first need to compile the OpenSCAP library. See the
Packit Service 569379
*Building OpenSCAP on Linux* section in the link:../developer/developer.adoc[OpenSCAP Developer Manual]
Packit Service 569379
for more details.
Packit Service 569379
Also you don't need to run all the tests using `make test`, you can run only
Packit Service 569379
the specific test(s). To do so, you need to be in the build directory and
Packit Service 569379
run `ctest -R` from there, for example:
Packit Service 569379
[source,bash]
Packit Service 569379
----
Packit Service 569379
$ cd build/
Packit Service 569379
$ ctest -R sce/test_sce.sh
Packit Service 569379
$ less Testing/Temporary/LastTest.log
Packit Service 569379
----
Packit Service 569379
Packit Service 569379
Results from testing will be printed on the stdout and detailed log file with
Packit Service 569379
your test results can be found in the `Testing/Temporary/LastTest.log` file.
Packit Service 569379
Packit Service 569379
== Running the MITRE tests
Packit Service 569379
Packit Service 569379
The MITRE tests (in `tests/mitre`) are functionality tests for several
Packit Service 569379
key probes.
Packit Service 569379
Packit Service 569379
There are several reasons for putting these tests behind an `ENABLE_MITRE`
Packit Service 569379
CMake flag: they cannot be run in parallel as they race to create and
Packit Service 569379
delete temporary support files; they require port 25 to be open and
Packit Service 569379
listening (`mitre/test_mitre_linux_probes.sh`); there are outdated assumptions
Packit Service 569379
which need to be gated around operating system version checks; and a number of
Packit Service 569379
probes had to be disabled because they're not supported (e.g., SQL and LDAP
Packit Service 569379
checks, etc.).
Packit Service 569379
Packit Service 569379
To run only the MITRE tests, please make sure you've installed and started a
Packit Service 569379
SMTP server that is listening on `127.0.0.1:25`, and that you're running on a
Packit Service 569379
RPM-based distribution. Then:
Packit Service 569379
Packit Service 569379
----
Packit Service 569379
$ cd build/
Packit Service 569379
$ cmake -DENABLE_MITRE=TRUE ..
Packit Service 569379
$ make
Packit Service 569379
$ ctest --output-on-failure -R mitre
Packit Service 569379
----
Packit Service 569379
Packit Service 569379
To run the containerized MITRE tests (which installs a SMTP server and tests
Packit Service 569379
it):
Packit Service 569379
Packit Service 569379
----
Packit Service 569379
$ cd openscap/
Packit Service 569379
$ docker build --tag openscap_mitre_tests:latest -f Dockerfiles/mitre_tests .
Packit Service 569379
$ docker run openscap_mitre_tests:latest
Packit Service 569379
----