2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-10-21 15:18:19 +00:00
Files
xcat-core/xCAT-test/unit
Daniel Hilst 926c076533 test(xcat-core): the mock result directory owner ignores include() and stale logs
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>
2026-10-12 11:02:51 -03:00
..

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.