mock_build_owner reads chrootuid and chrootgid from the text of the rendered configuration. It does not see a value set in an included file, an assignment with an inline comment or a double-quoted key, or an include that overrides an earlier assignment. prepare_mock_resultdirs then gives the result directories to the wrong uid, or to none. prepare_mock_resultdirs changes only the two directories. A root build that stopped part way leaves root-owned 0644 logs in them, and mock, running as uid 1000, cannot append to them on the next run. native/mock_resultdir_owner.t runs mock's loader on each configuration case, and checks as root that uid 1000 can append to the stale logs in both directories. Ubuntu ships no mock, so the workflow runs it in a Fedora container and fails when it skips. The unit test checks, with chown recorded, which owner the directories and their files get. Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
xCAT-test/unit
Unit tests. These run against the source tree only -- no xCAT installation, no running daemons, no management node.
They are executed on every pull request by the xcat_test GitHub Actions workflow,
which calls run_unit_tests() in github_action_xcat_test.pl:
prove -r xCAT-test/unit
You can run exactly the same thing from a clean checkout:
cd <xcat-core checkout>
prove -r xCAT-test/unit
What belongs here
A test belongs in unit/ when everything it needs is in the checkout: plugin and
library sources, kickstart/preseed/subiquity templates, postscripts, packaging
metadata. Such a test asserts on rendered output or module logic and reaches the
repository root through FindBin:
use FindBin;
use lib "$FindBin::Bin/../../perl-xCAT";
use lib "$FindBin::Bin/../../xCAT-server/lib/perl";
Because of those FindBin paths the tests only work from a source tree. The copy
installed under /opt/xcat/share/xcat/tools/autotest/unit is not a substitute --
../.. resolves to /opt/xcat/share/xcat/tools there and the tests die or silently
skip. The CI takes a copy of the checkout before the build for this reason; see
preserve_source_tree().
Running commands from Perl tests
Use XCAT::Test::Process when a test needs a command's exit code and combined
standard output and standard error:
use FindBin;
use lib "$FindBin::Bin/../lib";
use XCAT::Test::Process qw(run_command);
my ($status, $output) = run_command('/bin/sh', $script, '--check');
Call run_command in list context. It passes arguments without shell expansion
and returns the normal exit code with the captured output. A command that cannot
execute or terminates on a signal raises an error. Scalar and void contexts also
raise errors, so they cannot discard the exit code.
The helper uses Capture::Tiny, which the unit suite already requires. Captured
output follows the caller's STDOUT PerlIO layers. Use binmode STDOUT, ':raw'
before capture when comparing raw bytes.
The command inherits the caller's environment and working directory. Keep fixture-specific environment changes and directory selection in the test.
What does not belong here
Tests that build RPMs, create Linux namespaces or start service binaries belong
in ../native. They run explicitly on a suitable host,
outside the default unit suite.
Anything that needs an installed xCAT, a populated /install, a real service binary
or a live daemon. Those go in ../integration and run on
a management node through xcattest. Both suites run on every pull request -- the
workflow installs xCAT on the runner and then runs the ci_test cases against it --
so putting a test in integration/ does not cost it CI coverage. What differs is what
each suite is allowed to depend on, and that unit tests also run standalone from a
bare checkout with no xCAT at all.
Shell-script unit tests belong in ../bats
and run with BATS. Do not add Perl .t tests that grep shell source when the
behavior can be exercised by sourcing a shell library or script and shadowing the
external commands it calls.
The distinction matters because a test that needs an absent environment does not fail
-- it calls plan skip_all and reports as skipped. A handful of those in a suite of
several hundred assertions is easy to stop reading. Keeping the two kinds in separate
directories means a skip in unit/ is a real signal rather than routine noise.
Guarding on a source file, on the other hand, is fine and common here:
plan skip_all => "compute.subiquity.tmpl not found" unless -f $tmpl_path;
That guard never fires when the tree is intact.
Fixture files
Use XCAT::Test::File::repo_path for checkout paths. slurp_repo_file reads a
checkout-relative path. For an already resolved path or a temporary byte-oriented
fixture, use File::Slurper::read_binary and write_binary:
use File::Slurper qw(read_binary write_binary);
write_binary($fixture, $contents);
is(scalar read_binary($fixture), $expected, 'fixture contents are preserved');
Read in scalar context, including assertion arguments and hash values. An empty
file can return an empty list in list context. Use read_text and write_text
when the fixture requires decoded text, with its encoding explicit.
Keep caller-specific missing-file handling and executable permissions explicit. Use the existing handle when its lifetime, position or redirection is under test.