diff --git a/tests/README.md b/tests/README.md index 5e3f0a3232..d846bac401 100644 --- a/tests/README.md +++ b/tests/README.md @@ -3,8 +3,11 @@ To run these tests, you need at least these dependencies on the host: 1. [bwrap](https://github.com/containers/bubblewrap/) -1. [gdb](https://www.gnu.org/software/gdb/) -1. [gnupg](https://www.gnupg.org/) >= 2.0 + +If your host is *not* running Fedora Linux, you will also need either of: + +1. [podman](https://github.com/containers/podman/) +1. [docker](https://github.com/docker/) Then run the command @@ -14,6 +17,8 @@ The number of tests performed depends on features enabled at configure time, at least `--with-`/`--without-lua` and `--enable-`/`--disable-python`. See also the [INSTALL](../INSTALL) file for more information. +## Selecting tests + To run *single tests*, you can run the commands: make check TESTOPTS="$NNN $MMM" @@ -34,66 +39,154 @@ For all available options, see the output of the command: By default, tests are executed in parallel using all available cores, pass a specific -jN value to limit. -To drop into a test-like shell, run: +## Developing RPM - make env - -See the printed help for details on how to use it. - -You can also run a containerized shell with your RPM checkout built, installed -and ready to use: +For manual testing of RPM, run: make shell -This is equivalent to running: +This will drop you into a containerized shell with your RPM checkout built and +installed into the configured prefix. The container is a minimal version of +your host OS with only the basic tools installed, and has the `tests` directory +mounted at `/srv` for your convenience. - make env - snapshot shell +For any RPM source changes to take effect, simply rerun this target, any other +changes to the filesystem will be retained. -To factory-reset the container, run: +To factory-reset the container (drop any of your changes), run: make reset ## How it works +### Architecture + The test suite is written using GNU Autotest and, once built, is a standalone -Bourne shell script named `rpmtests`. The script is supposed to be run as root -and exercises the RPM installation in the root filesystem, preferably mounted -as read-only. +Bourne shell script named `rpmtests`. The script is intended to be run as root +and will exercise the RPM installation in the root filesystem. Each test in the suite that needs write access to the root filesystem (such as to install packages) runs RPM in a mutable, disposable container (a *snapshot*) -on top of the root filesystem, using Bubblewrap and OverlayFS. - -Since running the test suite natively as root and installing build artifacts -into the system is usually not desired on a workstation, `make check` runs the -`rpmtests` script itself in a container on top of an OS filesystem tree that -mirrors the running host and contains a fresh `make install` from the build -directory. This all works without root privileges thanks to the use of Linux -`namespaces(7)`. - -### Building the tree - -The `mktree` executable is responsible for setting up the filesystem tree and -running containers against it. It is invoked by `make tree` which in turn is -invoked by `make check`, `make env` and `make shell`, and may also be invoked -directly if one wishes to avoid the CMake overhead. - -The executable is made during CMake configuration by choosing a *backend* (one -of the files starting with `mktree.`) that's native to the host, configuring it -and copying it to the build directory with the suffix stripped. - -Native backends use a package manager such as DNF or Zypper to bootstrap the -tree with RPM's runtime dependencies matching the development headers used, RPM -itself by running `make install` from the build directory with the `DESTDIR` -environment variable set accordingly, and all the required test binaries. - -If no native backend is available, `mktree.podman` will be chosen as a fallback -which performs the whole RPM build process in a Fedora container and reuses the -same image for the tests. This backend is primarily intended for use in our CI -where portability is key (currently, Ubuntu VMs through GitHub Actions) and is -therefore not optimized for iterative `make check` use. - -Developers wishing to contribute a native backend for their platform of choice -are encouraged to consult the `mktree.README` file for the details on how to -write one. +on top of the root filesystem. This is to prevent the individual tests from +affecting each other. + +When hacking on RPM, one typically does not wish to install the build artifacts +into the host system and run the risk of having their files purged by accident +in case of a misbehaving test. To avoid that, `make check` creates a minimal +OS filesystem tree that mirrors the host, `make install`s RPM into it and runs +the `rpmtests` script in a container on top. This completely isolates the test +suite from the host. + +The container technology used is a combination of +[Bubblewrap](https://github.com/containers/bubblewrap/), +[OverlayFS](https://docs.kernel.org/filesystems/overlayfs.html) and Linux +`namespaces(7)`. Thanks to the latter, this all works under a regular, +non-root user. Since most of these technologies are only available on Linux, +this currently limits the test suite to Linux hosts only. + +### Making the tree + +The `mktree` executable is responsible for creating the filesystem tree and is +invoked on `make tree` (which is a dependency of `make check` or `make shell`). +The executable is made during CMake configuration by choosing an implementation +(a *backend*) native to the running host, configuring it and copying it to the +build directory under the `mktree` name. + +Most backends use the native package manager such as DNF or Zypper to install +RPM's runtime dependencies matching the development headers used in the build, +the test dependencies, and finally RPM itself using `make install` with the +appropriate `$DESTDIR` value. The tree is then cached in the form of layers +(OS and RPM) and reused on subsequent `make check` runs, with only the RPM +layer being redone. + +Currently, only Fedora Linux is supported natively with `mktree.fedora`, other +distros automatically fall back to `mktree.podman` which is an implementation +using Podman/Docker and the official Fedora OCI image to achieve the same, with +the difference being that RPM is configured and built in a container. This +makes it more portable and thus ideal for our CI purposes where we currently +run Ubuntu VMs, but it is not optimized for iterative `make check` use. + +## Advanced features + +### Common OS layer + +If you use multiple CMake build directories during development, you may want +them to reuse the same OS layer to save time and disk space. To enable that, +simply do this from the source directory: + + mkdir mktree.output + +This directory will then be used by `mktree` to store the OS layer, instead of +one in the build directory. + +Note that if you're using `git-worktree(1)`, the `mktree.output` directory will +only be looked for in the original (full) checkout. + +### Rebuilding the OS layer + +Sometimes, you may want to recreate this layer, e.g. if some RPM dependencies +change and/or to update the software installed in the layer. Do it simply by +running: + + make clean + +If you're using a [common OS layer](#common-os-layer), you have to delete it +manually. + + rm -rf mktree.output/ + +Next time the tree is needed, the OS layer will be rebuilt. + +### Interactive test-like environment + +This is like `make shell` on steroids, invoke it with: + + make env + +This shell runs natively on your host, sources the `atlocal` file and mounts a +test tree at `$RPMTEST`, much like what a typical test would do. The advantage +over `make shell` is that you can use your native tools to view and manipulate +the contents of the filesystem. + +Note that the filesystem mounted at `$RPMTEST` is the same as the one used by +`make shell`. In fact, `make shell` is just a shorthand for + + make env + runroot_other $SHELL + +with a few extras such as the `tests` directory mounted at `/srv`. + +A `motd(5)`-like piece of text is printed upon entering `make env` which +contains more information on how to use it. + +### Specifying a mktree backend + +To override the autodetection, use the CMake option `MKTREE_BACKEND` with the +desired backend name (`mktree` suffix). This feature can be useful if you wish +to use an alternative backend. + +For example, if you already have a throwaway development container or VM with +RPM's build dependencies, you may prefer to just reuse the same filesystem to +run the test suite against. In such a case, configure your build with: + + cmake -DMKTREE_BACKEND=rootfs ... + +In the future, this option may also be useful if we add an alternative, OS +agnostic backend that e.g. reflinks the required binaries and libraries from +the host instead of installing them from packages. + +### Running CI locally + +If you wish to verify that your changes pass in our CI before opening a PR, the +following will run the same CI setup locally (needs Podman or Docker): + + make ci + +This is equivalent to doing: + + cmake -DMKTREE_BACKEND=podman ... + make check + +## Known Issues + +CONTINUE HERE diff --git a/tests/mktree.README b/tests/mktree.README deleted file mode 100644 index fbdd64a3ea..0000000000 --- a/tests/mktree.README +++ /dev/null @@ -1,55 +0,0 @@ -Writing mktree backends ------------------------ - -The mktree executable is responsible for building a minimal OS filesystem tree -that includes an RPM installation built from these sources and its runtime and -test dependencies that are ABI compatible with the development headers used in -the build. This tree is then used to spawn disposable containers on top that -run RPM tests or interactive shells. The executable accepts a series of -commands described below. - -Most Linux package managers such as DNF or Zypper allow for specifying a custom -root directory and OS version which makes them a good candidate for setting up -such a tree with matching library versions. To support that, the final mktree -executable is made during CMake configuration by choosing a host-native backend -from this directory (files named with the "mktree." prefix), configuring it and -copying it to the CMake build directory under the name "mktree" which is then -invoked by "make tree" (a dependency of "make check" and "make shell"). It can -also be invoked directly from the tests/ binary directory if one wishes to skip -the CMake overhead. - - - - - - -Most backends should use the local RPM build artifacts to CONTINUE HERE - -To install the local RPM artifacts, backends should run "make install" with the -appropriate DESTDIR setting. This should produce a separate tree that can be -rebuilt independently and layered on top of the base tree which is mostly -static, in order for "make check" to run quickly. - -To manage such overlays and containers, backends may use the snapshot() shell -function defined in the "atlocal" file (by sourcing it) which is used by the -test-suite to isolate writable tests from each other. The function uses -Bubblewrap and OverlayFS to spawn lightweight containers on top of a set of -directories, see README for more information. - -A reference implementation for Fedora Linux hosts (mktree.fedora) is available. -Developers wishing to test RPM on other distros are encouraged to write and -contribute a backend for their distro by copying and adjusting mktree.fedora as -needed. Common logic can later be refactored into the mktree.common file. - -On distros that don't have a native backend (yet), CMake will automatically -fall back to mktree.podman. This backend uses the official Fedora OCI image to -completely isolate the build process (including CMake) from the host and reuses -the same image for the runtime (after removing the stock RPM installation from -it). One downside is that a new CMake build is done from scratch on each "make -check" invocation. The primary use case for this backend is for our CI -environment which runs the test-suite in disposable Ubuntu VMs, one per job. - -Commands to implement ---------------------- - -* Command: build