Testing strategy
Introduction
ska-sdp-spack is a Spack package repository containing:
Spack packages (
/packages)Spack environments (
/envand/os)Python helper scripts (
/scripts)
This page describes the testing strategy for each of these.
Python helper scripts
The scripts/ directory contains Python helper scripts. Tests for these scripts
are in the tests/ directory. This repository uses the SKAO standard Makefile
templates for running these tests, which means you can run these tests using:
make python-test
Duplicate package check
This repository duplicates some packages that are already in the upstream package repository from Spack itself. These packages overwrite the upstream package and add new versions and/or extra patches.
The scripts/check_upstream_duplicates.py script checks for Spack
packages that are duplicates of packages in the upstream package repository from
Spack itself. This check runs in CI to prevent accidentally adding duplicate packages.
If duplicating an upstream package is required, it must be added to the
.ignore-duplicates file in the root of the repository. The
scripts/check_upstream_duplicates.py script ignores the packages in that list.
Spack environments
The env/ and os/ directories contain Spack environments. Each environment defines
a set of packages. Different tests run on CI for each environment:
os/Builds all packages in both Rocky Linux and Ubuntu Linux.
Runs
spack containerizeto create aDockerfile.Builds an OCI image using the standard SKAO CI templates.
flowchart LR A[Rocky Linux build] ~~~ B[Ubuntu Linux build] B ~~~ C[Create Dockerfile] C --> D[Build OCI image]CI steps for the os/ environment
env/aws/contains all packages that are deployed to the DP AWS cluster.Concretises the environment to check if concretising the packages in the enviroment is possible at all.
Builds the concretised packages to check for build errors in the packages.
Runs smoke tests for the built packages, which are described below.
flowchart LR F --> G subgraph Concretise job F[Concretise] end subgraph Build and test job G[Build] --> H[Run smoke tests] endCI steps for the env/aws/ environment
Spack package smoke tests
As explained above, CI runs smoke tests for all packages in the env/aws
environment. A smoke test is defined by adding a function with a test_ prefix,
e.g. test_something to a spack package recipe.
Spack is very flexible regarding smoke tests and supports various types of smoke tests, see the Spack packaging guide. Each package can have multiple tests. The sections below describe the test types.
Python import tests
Classes that inherit from the PythonPackage class automatically inherit a
test_imports test. This test searches for Python modules in the package and
tests importing the found modules. For most Python packages, this automatic
test is an adequate smoke test and does not require any changes to the package.
The test can be customized if necessary by adjusting the list of Python modules.
The customizations below can be specified by defining the list directly or using
a property function that generates the list.
For example, import_modules = ["module"] defines a list directly.
A property function can include or exclude modules depending on the package variant,
for example. The py-py-key-value-aio package uses this approach.
Override all Python modules
A package can override the Python module list using import_modules.
For example, the py-ducc package defines its own modules since Spack doesn’t
detect Python modules that are binary wheels.
Exclude Python modules
A package can exclude modules from the Python module list using skip_modules.
For example, the
py-alembic package
skips modules from an internal test suite.
Importing such modules often fails because of extra requirements, like pytest,
which are not needed during normal operation. The smoke test should therefore
skip those modules.
Executable tests
Packages that install executables should test that those executables can run,
for example by having the executable print a help message. A package can define
such a test by adding a test_ function, for example:
def test_application(self):
"""Ensure that 'application' runs."""
application = which(self.prefix.bin.application)
application("--version")
By default, the test succeeds when the application returns a 0 exit code.
Testing an application with different input, output, or a custom exit code is
also possible. The documentation for Executable::__call__
shows the various options.
Binary library tests
Packages that install binary libraries should test that the library can be used. If the package also installs executables that dynamically link to the library, running those executables also uses and thereby tests the library. Adding an executable test, as explained above, is then the easiest method of testing the library.
If an executable test does not suffice, a package can test the library
by linking a small application to the library and running it. This works by
creating an application in the test/ directory of the package,
and adding a test that compiles and runs the test application.
The test_register function in lofarstman package is a good example.
It compiles and runs a simple C application.