Hi,
PKGBUILDs include the check() function which allows one to run tests on the software after it has been built, but before it has been packaged.
This message proposes to add a test() function, which essentially allows to run tests after package(), and in particular after the package has been installed in a chroot (for these testing purposes).
This allows the packager to run tests against the final installation (or as final as can be in a chroot). Between package() and test(), the resulting pkg is installed in a chroot like on the regular system (i.e. /usr/bin instead of $pkgdir/usr/bin). Everything
but the pkg files and the test files is removed, so only the installation is under test.
The idea for this came from the issue that Python packages usually don't need a build or rather, that that build can't be tested without fully installing it. This installation however isn't really possible since /usr/ is inaccessible for obvious reasons. Workarounds
usually involve setting the PYTHONPATH, but that's tedious at best and ultimately unnecessary.
Because Python has an extensive auto-detection of libraries and dependencies, the tests can be executed easily, if Python can find them. And after installation (in /usr/lib/python*/site-packages/), the package is easily found. This of course is in contrast
to most compiled languages, who would require lots of paths and links in order to be able to compile the tests. It shouldn't be this way, but it is.
A slight complication arises from the fact that tests can theoretically be anywhere in the python source code. So either makepkg figures out what is a test, or the packager specifies that. In most cases though, all tests are in a single directory, and the edge
cases are vast, so the latter case with some boilerplate is probably better.
In total, a PKGBUILD might look something like this (names are subject to change):
```
build() {
cd ${pkgname}-${pkgver}/
python -m build --wheel --no-isolation
}
check() {
cd ${pkgname}-${pkgver}/
python -m pytest
}
package() {
cd ${pkgname}-${pkgver}/
python -m installer --destdir="$pkgdir" dist/*.whl
}
collect() {
cd ${pkgname}-${pkgver}/
# collects the tests and puts them into $testdir
python -m pytest --co | grep -o "[^ ]*\.py" | xargs cp -t $testdir
}
# inbetween collect() and test(), the package is installed in the chroot and $testdir is copied
test() {
# we are in $testdir (e.g. /home/builduser)
pytest
}
```
Notice the line in collect() which is responsible to collect the tests. We basically ask pytest what it wants to run, then filter out the files, and lastly copy them into $testdir, which is ultimately copied over and run.
In most cases, this line might just be something like `cp tests/ $testdir/`.
------------
Makepkg
------------
This post is already quite long, so I'll make it quick with some implementation thoughts.
Since collect() and test() always run together or don't run together (test() needs collect() and only collect() has no effect), only a single option is necessary to toggle whether to run the post-package tests.
The order of package() and collect() conceptually doesn't matter, but collect() should probably run after to guarantee that it does not influence package(). Either case, the packager is responsible.
This is a pure addition, so if collect() and test() are missing, then just nothing happens. If only collect() is implemented, that might be ignored too. If only test() exists, then an error is probably most helpful, since no packages are copied. Alternatively,
maybe makepkg does some limited test identification (like looking for tests/ folder). Or
just nothing happens because the packager made a mistake. Should be obvious enough.
In conclusion, two new methods are introduced to allow for better and more precise testing of the final output, as well as easier testing in case of Python.
The first function - collect() - collects all the tests in the codebase and copies them into $testdir, while package() creates the pkg in $pkgdir. After that, a chroot with all depends packages is created and the pkg installed therein. $testdir is also copied
there. Lastly, the second function - test() - is called and executed to test the installation.
Thanks for your attention, if you have further ideas or questions, I'd like to hear them.
If something similar has already been discussed, feel free to point out that thread. AFAICT, it hasn't been before.