testers.testEqualArrayOrMap: use buildCommandPath and change checkSetupScript argument to script

This commit is contained in:
Connor Baker 2025-02-20 22:37:59 +00:00
parent 261693fe5d
commit 731b74db8b
7 changed files with 132 additions and 111 deletions

View file

@ -351,7 +351,7 @@ testers.testEqualContents {
Check that bash arrays (including associative arrays, referred to as "maps") are populated correctly.
This can be used to ensure setup hooks are registered in a certain order, or write unit tests for shell functions which transform arrays.
This can be used to ensure setup hooks are registered in a certain order, or to write unit tests for shell functions which transform arrays.
:::{.example #ex-testEqualArrayOrMap-test-function-add-cowbell}
@ -369,7 +369,7 @@ testers.testEqualArrayOrMap {
"cowbell"
"cowbell"
];
checkSetupScript = ''
script = ''
addCowbell() {
local -rn arrayNameRef="$1"
arrayNameRef+=( "cowbell" )
@ -390,8 +390,8 @@ testers.testEqualArrayOrMap {
### Inputs {#tester-testEqualArrayOrMap-inputs}
NOTE: Internally, this tester uses `__structuredAttrs` to handle marhsalling between Nix expressions and shell variables.
This imposes the restriction that arrays and "maps" have values which are string-coercible.
NOTE: Internally, this tester uses `__structuredAttrs` to handle marshalling between Nix expressions and shell variables.
This imposes the restriction that arrays and "maps" have values which are string-like.
NOTE: At least one of `expectedArray` and `expectedMap` must be provided.
@ -399,39 +399,39 @@ NOTE: At least one of `expectedArray` and `expectedMap` must be provided.
: The name of the test.
`checkSetupScript` (string)
`script` (string)
: The singular task of `checkSetupScript` is to populate `actualArray` or `actualMap` (it may populate both).
To do this, checkSetupScript may access the following shell variables:
: The singular task of `script` is to populate `actualArray` or `actualMap` (it may populate both).
To do this, `script` may access the following shell variables:
- `valuesArray`
- `valuesMap`
- `actualArray`
- `actualMap`
- `valuesArray` (available when `valuesArray` is provided to the tester)
- `valuesMap` (available when `valuesMap` is provided to the tester)
- `actualArray` (available when `expectedArray` is provided to the tester)
- `actualMap` (available when `expectedMap` is provided to the tester)
While both `expectedArray` and `expectedMap` are in scope during the execution of `checkSetupScript`, they *must not* be accessed or modified from within `checkSetupScript`.
While both `expectedArray` and `expectedMap` are in scope during the execution of `script`, they *must not* be accessed or modified from within `script`.
`valuesArray` (array of string-like values, optional)
: An array of string-coercible values.
This array may be used within `checkSetupScript`.
: An array of string-like values.
This array may be used within `script`.
`valuesMap` (attribute set of string-like values, optional)
: An attribute set of string-coercible values.
This attribute set may be used within `checkSetupScript`.
: An attribute set of string-like values.
This attribute set may be used within `script`.
`expectedArray` (array of string-like values, optional)
: An array of string-coercible values.
This array *must not* be accessed or modified from within `checkSetupScript`.
When provided, `checkSetupScript` is expected to populate `actualArray`.
: An array of string-like values.
This array *must not* be accessed or modified from within `script`.
When provided, `script` is expected to populate `actualArray`.
`expectedMap` (attribute set of string-like values, optional)
: An attribute set of string-coercible values.
This attribute set *must not* be accessed or modified from within `checkSetupScript`.
When provided, `checkSetupScript` is expected to populate `actualMap`.
: An attribute set of string-like values.
This attribute set *must not* be accessed or modified from within `script`.
When provided, `script` is expected to populate `actualMap`.
### Return value {#tester-testEqualArrayOrMap-return}

View file

@ -72,7 +72,7 @@
# See https://nixos.org/manual/nixpkgs/unstable/#tester-testEqualArrayOrMap
# or doc/build-helpers/testers.chapter.md
# NOTE: Must be `import`-ed rather than `callPackage`-d to preserve the `override` attribute.
testEqualArrayOrMap = import ./testEqualArrayOrMap/tester.nix { inherit lib runCommand; };
testEqualArrayOrMap = import ./testEqualArrayOrMap/tester.nix { inherit lib stdenvNoCC; };
# See https://nixos.org/manual/nixpkgs/unstable/#tester-testVersion
# or doc/build-helpers/testers.chapter.md

View file

@ -1,5 +1,11 @@
# shellcheck shell=bash
# Tests if an array is declared.
isDeclaredArray() {
# shellcheck disable=SC2034
local -nr arrayRef="$1" && [[ ${!arrayRef@a} =~ a ]]
}
# Asserts that two arrays are equal, printing out differences if they are not.
# Does not short circuit on the first difference.
assertEqualArray() {
@ -12,13 +18,13 @@ assertEqualArray() {
local -nr expectedArrayRef="$1"
local -nr actualArrayRef="$2"
if [[ ! ${expectedArrayRef@a} =~ a ]]; then
nixErrorLog "first arugment expectedArrayRef must be an array reference"
if ! isDeclaredArray expectedArrayRef; then
nixErrorLog "first arugment expectedArrayRef must be an array reference to a declared array"
exit 1
fi
if [[ ! ${actualArrayRef@a} =~ a ]]; then
nixErrorLog "second arugment actualArrayRef must be an array reference"
if ! isDeclaredArray actualArrayRef; then
nixErrorLog "second arugment actualArrayRef must be an array reference to a declared array"
exit 1
fi

View file

@ -1,5 +1,11 @@
# shellcheck shell=bash
# Tests if a map is declared.
isDeclaredMap() {
# shellcheck disable=SC2034
local -nr mapRef="$1" && [[ ${!mapRef@a} =~ A ]]
}
# Asserts that two maps are equal, printing out differences if they are not.
# Does not short circuit on the first difference.
assertEqualMap() {
@ -12,13 +18,13 @@ assertEqualMap() {
local -nr expectedMapRef="$1"
local -nr actualMapRef="$2"
if [[ ! ${expectedMapRef@a} =~ A ]]; then
nixErrorLog "first arugment expectedMapRef must be an associative array reference"
if ! isDeclaredMap expectedMapRef; then
nixErrorLog "first arugment expectedMapRef must be an associative array reference to a declared associative array"
exit 1
fi
if [[ ! ${actualMapRef@a} =~ A ]]; then
nixErrorLog "second arugment actualMapRef must be an associative array reference"
if ! isDeclaredMap actualMapRef; then
nixErrorLog "second arugment actualMapRef must be an associative array reference to a declared associative array"
exit 1
fi

View file

@ -0,0 +1,64 @@
# shellcheck shell=bash
set -eu
preScript() {
# If neither expectedArray nor expectedMap are declared, the test is meaningless.
if ! isDeclaredArray expectedArray && ! isDeclaredMap expectedMap; then
nixErrorLog "neither expectedArray nor expectedMap were set, so test is meaningless!"
exit 1
fi
if isDeclaredArray valuesArray; then
# shellcheck disable=SC2154
nixLog "using valuesArray: $(declare -p valuesArray)"
fi
if isDeclaredMap valuesMap; then
# shellcheck disable=SC2154
nixLog "using valuesMap: $(declare -p valuesMap)"
fi
if isDeclaredArray expectedArray; then
# shellcheck disable=SC2154
nixLog "using expectedArray: $(declare -p expectedArray)"
declare -ag actualArray=()
fi
if isDeclaredMap expectedMap; then
# shellcheck disable=SC2154
nixLog "using expectedMap: $(declare -p expectedMap)"
declare -Ag actualMap=()
fi
return 0
}
scriptPhase() {
runHook preScript
runHook script
runHook postScript
}
postScript() {
if isDeclaredArray expectedArray; then
nixLog "using actualArray: $(declare -p actualArray)"
nixLog "comparing actualArray against expectedArray"
assertEqualArray expectedArray actualArray
nixLog "actualArray matches expectedArray"
fi
if isDeclaredMap expectedMap; then
nixLog "using actualMap: $(declare -p actualMap)"
nixLog "comparing actualMap against expectedMap"
assertEqualMap expectedMap actualMap
nixLog "actualMap matches expectedMap"
fi
return 0
}
runHook scriptPhase
touch "${out:?}"

View file

@ -3,12 +3,10 @@
# our negative tests will not work. See ./tests.nix for more information.
{
lib,
runCommand,
stdenvNoCC,
}:
let
inherit (lib) maintainers;
inherit (lib.customisation) makeOverridable;
inherit (lib.strings) optionalString;
testEqualArrayOrMap =
{
@ -17,76 +15,25 @@ let
valuesMap ? null,
expectedArray ? null,
expectedMap ? null,
checkSetupScript ? ''
nixErrorLog "no checkSetupScript provided!"
exit 1
'',
script,
}:
runCommand name
{
__structuredAttrs = true;
strictDeps = true;
stdenvNoCC.mkDerivation (finalAttrs: {
__structuredAttrs = true;
strictDeps = true;
nativeBuildInputs = [
./assert-equal-array.sh
./assert-equal-map.sh
];
inherit name;
inherit valuesArray valuesMap;
inherit expectedArray expectedMap;
nativeBuildInputs = [
./assert-equal-array.sh
./assert-equal-map.sh
];
preCheckSetupScript =
optionalString (expectedArray == null && expectedMap == null) ''
nixErrorLog "neither expectedArray nor expectedMap were set, so test is meaningless!"
exit 1
''
+ optionalString (valuesArray != null) ''
nixLog "using valuesArray: $(declare -p valuesArray)"
''
+ optionalString (valuesMap != null) ''
nixLog "using valuesMap: $(declare -p valuesMap)"
''
+ optionalString (expectedArray != null) ''
nixLog "using expectedArray: $(declare -p expectedArray)"
declare -ag actualArray
''
+ optionalString (expectedMap != null) ''
nixLog "using expectedMap: $(declare -p expectedMap)"
declare -Ag actualMap
'';
inherit valuesArray valuesMap;
inherit expectedArray expectedMap;
# NOTE:
# The singular task of checkSetupScript is to populate actualArray or actualMap. To do this, checkSetupScript
# may access valuesArray, valuesMap, actualArray, and actualMap, but should *never* access or modify expectedArray,
# or expectedMap.
inherit checkSetupScript;
inherit script;
postCheckSetupScript =
optionalString (expectedArray != null) ''
nixLog "using actualArray: $(declare -p actualArray)"
nixLog "comparing actualArray against expectedArray"
assertEqualArray expectedArray actualArray
nixLog "actualArray matches expectedArray"
''
+ optionalString (expectedMap != null) ''
nixLog "using actualMap: $(declare -p actualMap)"
nixLog "comparing actualMap against expectedMap"
assertEqualMap expectedMap actualMap
nixLog "actualMap matches expectedMap"
'';
}
''
nixLog "running preCheckSetupScript"
runHook preCheckSetupScript
nixLog "running checkSetupScript"
runHook checkSetupScript
nixLog "running postCheckSetupScript"
runHook postCheckSetupScript
nixLog "test passed"
touch "$out"
'';
buildCommandPath = ./build-command.sh;
});
in
makeOverridable testEqualArrayOrMap

View file

@ -33,7 +33,7 @@ in
"cowbell"
"cowbell"
];
checkSetupScript = ''
script = ''
addCowbell() {
local -rn arrayNameRef="$1"
arrayNameRef+=( "cowbell" )
@ -61,7 +61,7 @@ in
"cat"
"dog"
];
checkSetupScript = ''
script = ''
${concatValuesArrayToActualArray}
actualArray+=( "dog" )
'';
@ -79,7 +79,7 @@ in
"bee"
"cat"
];
checkSetupScript = ''
script = ''
actualArray+=( "dog" )
${concatValuesArrayToActualArray}
'';
@ -92,7 +92,7 @@ in
"cat"
];
expectedArray = [ ];
checkSetupScript = ''
script = ''
# doing nothing
'';
};
@ -103,7 +103,7 @@ in
name = "${name}-failure";
valuesArray = [ "apple" ];
expectedArray = [ ];
checkSetupScript = concatValuesArrayToActualArray;
script = concatValuesArrayToActualArray;
};
in
runCommand name
@ -140,7 +140,7 @@ in
cat = "2";
dog = "3";
};
checkSetupScript = ''
script = ''
${concatValuesMapToActualMap}
actualMap["dog"]="3"
'';
@ -158,7 +158,7 @@ in
cat = "2";
dog = "3";
};
checkSetupScript = ''
script = ''
${concatValuesMapToActualMap}
unset 'actualMap[bee]'
'';
@ -179,7 +179,7 @@ in
cat = "2";
dog = "3";
};
checkSetupScript = concatValuesMapToActualMap;
script = concatValuesMapToActualMap;
};
in
runCommand name
@ -210,9 +210,7 @@ in
name = "${name}-failure";
valuesArray = [ ];
expectedMap.apple = 1;
checkSetupScript = ''
nixLog "doing nothing in checkSetupScript"
'';
script = "";
};
in
runCommand name
@ -252,7 +250,7 @@ in
bee = "1";
dog = "3";
};
checkSetupScript = concatValuesMapToActualMap;
script = concatValuesMapToActualMap;
};
in
runCommand