testers.testBuildFailure': move to separate directory and use buildCommandPath

This commit is contained in:
Connor Baker
2025-02-27 15:56:46 +00:00
parent 2ac21a1375
commit 95c8770fa2
5 changed files with 211 additions and 173 deletions
+2 -65
View File
@@ -36,71 +36,8 @@
# See https://nixos.org/manual/nixpkgs/unstable/#tester-testBuildFailurePrime
# or doc/build-helpers/testers.chapter.md
testBuildFailure' =
let
mkBuildCommand =
script:
''
set -euo pipefail
if [[ -n ''${expectedBuilderExitCode:-} ]]; then
nixLog "checking original builder exit code"
builderExitCode=$(<"$failed/testBuildFailure.exit")
if ((expectedBuilderExitCode == builderExitCode)); then
nixLog "original builder exit code matches expected value of $expectedBuilderExitCode"
else
nixErrorLog "original builder produced exit code $builderExitCode but was expected to produce $expectedBuilderExitCode"
exit 1
fi
unset builderExitCode
fi
if ((''${#expectedBuilderLogEntries[@]})); then
nixLog "checking original builder log"
builderLogEntries="$(<"$failed/testBuildFailure.log")"
shouldExit=0
for expectedBuilderLogEntry in "''${expectedBuilderLogEntries[@]}"; do
if [[ ''${builderLogEntries} == *"$expectedBuilderLogEntry"* ]]; then
nixLog "original builder log contains ''${expectedBuilderLogEntry@Q}"
else
nixErrorLog "original builder log does not contain ''${expectedBuilderLogEntry@Q}"
shouldExit=1
fi
done
unset builderLogEntries
((shouldExit)) && exit 1
unset shouldExit
fi
''
+ lib.optionalString (script != "") ''
nixLog "running additional checks from user-provided script"
${script}
''
+ ''
touch "$out"
'';
final =
{
drv,
name ? null,
expectedBuilderExitCode ? 1, # NOTE: Should be an integer.
expectedBuilderLogEntries ? [ ], # NOTE: Should be an array of string-coercible values. TODO: Only checks for inclusion, not order!
script ? "", # Succeed by default if checks pass.
}:
(runCommand name {
__structuredAttrs = true;
strictDeps = true;
failed = testers.testBuildFailure drv;
inherit expectedBuilderExitCode expectedBuilderLogEntries;
} (mkBuildCommand script)).overrideAttrs
(
finalAttrs: _: {
# Fix name so the default value uses whatever failed ends up as.
name = if name != null then name else "testBuildFailure-${finalAttrs.failed.name}";
}
);
in
lib.makeOverridable final;
# NOTE: Must be `import`-ed rather than `callPackage`-d to preserve the `override` attribute.
testBuildFailure' = import ./testBuildFailurePrime/tester.nix { inherit lib stdenvNoCC testers; };
# See https://nixos.org/manual/nixpkgs/unstable/#tester-testEqualDerivation
# or doc/build-helpers/testers.chapter.md
+3 -108
View File
@@ -220,114 +220,9 @@ lib.recurseIntoAttrs {
sideEffectStructuredAttrs = overrideStructuredAttrs true sideEffects;
};
testBuildFailure' = lib.recurseIntoAttrs rec {
# NOTE: This example is used in the docs.
# See https://nixos.org/manual/nixpkgs/unstable/#tester-testBuildFailurePrime
# or doc/build-helpers/testers.chapter.md
doc-example = testers.testBuildFailure' {
drv = runCommand "doc-example" { } ''
echo ok-ish >"$out"
echo failing though
exit 3
'';
expectedBuilderExitCode = 3;
expectedBuilderLogEntries = [ "failing though" ];
script = ''
grep --silent -F 'ok-ish' "$failed/result"
'';
};
happy = testers.testBuildFailure' {
drv = runCommand "happy" { } ''
echo ok-ish >$out
echo failing though
echo also stderr 1>&2
echo 'line\nwith-\bbackslashes'
printf "incomplete line - no newline"
exit 3
'';
expectedBuilderExitCode = 3;
expectedBuilderLogEntries = [
"failing though"
"also stderr"
''line\nwith-\bbackslashes''
"incomplete line - no newline"
];
script = ''
grep --silent -F 'ok-ish' "$failed/result"
'';
};
happyStructuredAttrs = overrideStructuredAttrs true happy;
helloDoesNotFail = testers.testBuildFailure' {
drv = testers.testBuildFailure hello;
expectedBuilderLogEntries = [
"testBuildFailure: The builder did not fail, but a failure was expected"
];
};
multiOutput = testers.testBuildFailure' {
drv =
runCommand "multiOutput"
{
# dev will be the default output
outputs = [
"dev"
"doc"
"out"
];
}
''
echo i am failing
exit 1
'';
expectedBuilderLogEntries = [
"i am failing"
];
script = ''
# Checking our note that dev is the default output
echo $failed/_ | grep -- '-dev/_' >/dev/null
echo 'All good.'
'';
};
multiOutputStructuredAttrs = overrideStructuredAttrs true multiOutput;
sideEffects = testers.testBuildFailure' {
drv = stdenvNoCC.mkDerivation {
name = "fail-with-side-effects";
src = emptyDirectory;
postHook = ''
echo touching side-effect...
# Assert that the side-effect doesn't exist yet...
# We're checking that this hook isn't run by expect-failure.sh
if [[ -e side-effect ]]; then
echo "side-effect already exists"
exit 1
fi
touch side-effect
'';
buildPhase = ''
echo i am failing
exit 1
'';
};
expectedBuilderLogEntries = [
"touching side-effect..."
"i am failing"
];
script = ''
[[ ! -e side-effect ]]
'';
};
sideEffectStructuredAttrs = overrideStructuredAttrs true sideEffects;
};
testBuildFailure' = lib.recurseIntoAttrs (
pkgs.callPackages ../testBuildFailurePrime/tests.nix { inherit overrideStructuredAttrs; }
);
testEqualContents = lib.recurseIntoAttrs {
equalDir = testers.testEqualContents {
@@ -0,0 +1,40 @@
# shellcheck shell=bash
set -eu
scriptPhase() {
runHook preScript
nixLog "checking original builder exit code"
local -ir builderExitCode=$(<"${failed:?}/testBuildFailure.exit")
# shellcheck disable=SC2154
if ((expectedBuilderExitCode == builderExitCode)); then
nixLog "original builder exit code matches expected value of $expectedBuilderExitCode"
else
nixErrorLog "original builder produced exit code $builderExitCode but was expected to produce $expectedBuilderExitCode"
exit 1
fi
# shellcheck disable=SC2154
if ((${#expectedBuilderLogEntries[@]})); then
nixLog "checking original builder log"
local -r builderLogEntries="$(<"${failed:?}/testBuildFailure.log")"
local -i shouldExit=0
for expectedBuilderLogEntry in "${expectedBuilderLogEntries[@]}"; do
if [[ ${builderLogEntries} == *"$expectedBuilderLogEntry"* ]]; then
nixLog "original builder log contains ${expectedBuilderLogEntry@Q}"
else
nixErrorLog "original builder log does not contain ${expectedBuilderLogEntry@Q}"
shouldExit=1
fi
done
((shouldExit)) && exit 1
fi
runHook script
runHook postScript
}
runHook scriptPhase
touch "${out:?}"
@@ -0,0 +1,47 @@
# NOTE: Must be `import`-ed rather than `callPackage`-d to preserve the `override` attribute.
{
lib,
stdenvNoCC,
testers,
}:
let
inherit (lib) maintainers;
inherit (lib.customisation) makeOverridable;
inherit (testers) testBuildFailure;
# See https://nixos.org/manual/nixpkgs/unstable/#tester-testBuildFailurePrime
# or doc/build-helpers/testers.chapter.md
testBuildFailure' =
{
drv,
name ? "testBuildFailure-${drv.name}",
expectedBuilderExitCode ? 1,
expectedBuilderLogEntries ? [ ],
script ? "",
}:
let
failed = testBuildFailure drv;
in
stdenvNoCC.mkDerivation {
__structuredAttrs = true;
strictDeps = true;
inherit name;
nativeBuildInputs = [ failed ];
inherit failed;
inherit expectedBuilderExitCode expectedBuilderLogEntries;
inherit script;
buildCommandPath = ./build-command.sh;
meta = {
description = "A wrapper around testers.testBuildFailure to simplify common use cases";
maintainers = [ maintainers.connorbaker ];
};
};
in
makeOverridable testBuildFailure'
@@ -0,0 +1,119 @@
{
emptyDirectory,
hello,
overrideStructuredAttrs,
runCommand,
stdenvNoCC,
testers,
}:
let
final = {
# NOTE: This example is used in the docs.
# See https://nixos.org/manual/nixpkgs/unstable/#tester-testBuildFailurePrime
# or doc/build-helpers/testers.chapter.md
doc-example = testers.testBuildFailure' {
drv = runCommand "doc-example" { } ''
echo ok-ish >"$out"
echo failing though
exit 3
'';
expectedBuilderExitCode = 3;
expectedBuilderLogEntries = [ "failing though" ];
script = ''
grep --silent -F 'ok-ish' "$failed/result"
'';
};
happy = testers.testBuildFailure' {
drv = runCommand "happy" { } ''
echo ok-ish >$out
echo failing though
echo also stderr 1>&2
echo 'line\nwith-\bbackslashes'
printf "incomplete line - no newline"
exit 3
'';
expectedBuilderExitCode = 3;
expectedBuilderLogEntries = [
"failing though"
"also stderr"
''line\nwith-\bbackslashes''
"incomplete line - no newline"
];
script = ''
grep --silent -F 'ok-ish' "$failed/result"
'';
};
happyStructuredAttrs = overrideStructuredAttrs true final.happy;
helloDoesNotFail = testers.testBuildFailure' {
drv = testers.testBuildFailure hello;
expectedBuilderLogEntries = [
"testBuildFailure: The builder did not fail, but a failure was expected"
];
};
multiOutput = testers.testBuildFailure' {
drv =
runCommand "multiOutput"
{
# dev will be the default output
outputs = [
"dev"
"doc"
"out"
];
}
''
echo i am failing
exit 1
'';
expectedBuilderLogEntries = [
"i am failing"
];
script = ''
# Checking our note that dev is the default output
echo $failed/_ | grep -- '-dev/_' >/dev/null
echo 'All good.'
'';
};
multiOutputStructuredAttrs = overrideStructuredAttrs true final.multiOutput;
sideEffects = testers.testBuildFailure' {
drv = stdenvNoCC.mkDerivation {
name = "fail-with-side-effects";
src = emptyDirectory;
postHook = ''
echo touching side-effect...
# Assert that the side-effect doesn't exist yet...
# We're checking that this hook isn't run by expect-failure.sh
if [[ -e side-effect ]]; then
echo "side-effect already exists"
exit 1
fi
touch side-effect
'';
buildPhase = ''
echo i am failing
exit 1
'';
};
expectedBuilderLogEntries = [
"touching side-effect..."
"i am failing"
];
script = ''
[[ ! -e side-effect ]]
'';
};
sideEffectsStructuredAttrs = overrideStructuredAttrs true final.sideEffects;
};
in
final