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. 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} :::{.example #ex-testEqualArrayOrMap-test-function-add-cowbell}
@ -369,7 +369,7 @@ testers.testEqualArrayOrMap {
"cowbell" "cowbell"
"cowbell" "cowbell"
]; ];
checkSetupScript = '' script = ''
addCowbell() { addCowbell() {
local -rn arrayNameRef="$1" local -rn arrayNameRef="$1"
arrayNameRef+=( "cowbell" ) arrayNameRef+=( "cowbell" )
@ -390,8 +390,8 @@ testers.testEqualArrayOrMap {
### Inputs {#tester-testEqualArrayOrMap-inputs} ### Inputs {#tester-testEqualArrayOrMap-inputs}
NOTE: Internally, this tester uses `__structuredAttrs` to handle marhsalling between Nix expressions and shell variables. 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-coercible. 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. 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. : The name of the test.
`checkSetupScript` (string) `script` (string)
: The singular task of `checkSetupScript` is to populate `actualArray` or `actualMap` (it may populate both). : The singular task of `script` is to populate `actualArray` or `actualMap` (it may populate both).
To do this, checkSetupScript may access the following shell variables: To do this, `script` may access the following shell variables:
- `valuesArray` - `valuesArray` (available when `valuesArray` is provided to the tester)
- `valuesMap` - `valuesMap` (available when `valuesMap` is provided to the tester)
- `actualArray` - `actualArray` (available when `expectedArray` is provided to the tester)
- `actualMap` - `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) `valuesArray` (array of string-like values, optional)
: An array of string-coercible values. : An array of string-like values.
This array may be used within `checkSetupScript`. This array may be used within `script`.
`valuesMap` (attribute set of string-like values, optional) `valuesMap` (attribute set of string-like values, optional)
: An attribute set of string-coercible values. : An attribute set of string-like values.
This attribute set may be used within `checkSetupScript`. This attribute set may be used within `script`.
`expectedArray` (array of string-like values, optional) `expectedArray` (array of string-like values, optional)
: An array of string-coercible values. : An array of string-like values.
This array *must not* be accessed or modified from within `checkSetupScript`. This array *must not* be accessed or modified from within `script`.
When provided, `checkSetupScript` is expected to populate `actualArray`. When provided, `script` is expected to populate `actualArray`.
`expectedMap` (attribute set of string-like values, optional) `expectedMap` (attribute set of string-like values, optional)
: An attribute set of string-coercible values. : An attribute set of string-like values.
This attribute set *must not* be accessed or modified from within `checkSetupScript`. This attribute set *must not* be accessed or modified from within `script`.
When provided, `checkSetupScript` is expected to populate `actualMap`. When provided, `script` is expected to populate `actualMap`.
### Return value {#tester-testEqualArrayOrMap-return} ### Return value {#tester-testEqualArrayOrMap-return}

View file

@ -72,7 +72,7 @@
# See https://nixos.org/manual/nixpkgs/unstable/#tester-testEqualArrayOrMap # See https://nixos.org/manual/nixpkgs/unstable/#tester-testEqualArrayOrMap
# or doc/build-helpers/testers.chapter.md # or doc/build-helpers/testers.chapter.md
# NOTE: Must be `import`-ed rather than `callPackage`-d to preserve the `override` attribute. # 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 # See https://nixos.org/manual/nixpkgs/unstable/#tester-testVersion
# or doc/build-helpers/testers.chapter.md # or doc/build-helpers/testers.chapter.md

View file

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

View file

@ -1,5 +1,11 @@
# shellcheck shell=bash # 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. # Asserts that two maps are equal, printing out differences if they are not.
# Does not short circuit on the first difference. # Does not short circuit on the first difference.
assertEqualMap() { assertEqualMap() {
@ -12,13 +18,13 @@ assertEqualMap() {
local -nr expectedMapRef="$1" local -nr expectedMapRef="$1"
local -nr actualMapRef="$2" local -nr actualMapRef="$2"
if [[ ! ${expectedMapRef@a} =~ A ]]; then if ! isDeclaredMap expectedMapRef; then
nixErrorLog "first arugment expectedMapRef must be an associative array reference" nixErrorLog "first arugment expectedMapRef must be an associative array reference to a declared associative array"
exit 1 exit 1
fi fi
if [[ ! ${actualMapRef@a} =~ A ]]; then if ! isDeclaredMap actualMapRef; then
nixErrorLog "second arugment actualMapRef must be an associative array reference" nixErrorLog "second arugment actualMapRef must be an associative array reference to a declared associative array"
exit 1 exit 1
fi 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. # our negative tests will not work. See ./tests.nix for more information.
{ {
lib, lib,
runCommand, stdenvNoCC,
}: }:
let let
inherit (lib) maintainers;
inherit (lib.customisation) makeOverridable; inherit (lib.customisation) makeOverridable;
inherit (lib.strings) optionalString;
testEqualArrayOrMap = testEqualArrayOrMap =
{ {
@ -17,76 +15,25 @@ let
valuesMap ? null, valuesMap ? null,
expectedArray ? null, expectedArray ? null,
expectedMap ? null, expectedMap ? null,
checkSetupScript ? '' script,
nixErrorLog "no checkSetupScript provided!"
exit 1
'',
}: }:
runCommand name stdenvNoCC.mkDerivation (finalAttrs: {
{ __structuredAttrs = true;
__structuredAttrs = true; strictDeps = true;
strictDeps = true;
nativeBuildInputs = [ inherit name;
./assert-equal-array.sh
./assert-equal-map.sh
];
inherit valuesArray valuesMap; nativeBuildInputs = [
inherit expectedArray expectedMap; ./assert-equal-array.sh
./assert-equal-map.sh
];
preCheckSetupScript = inherit valuesArray valuesMap;
optionalString (expectedArray == null && expectedMap == null) '' inherit expectedArray expectedMap;
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
'';
# NOTE: inherit script;
# 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;
postCheckSetupScript = buildCommandPath = ./build-command.sh;
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"
'';
in in
makeOverridable testEqualArrayOrMap makeOverridable testEqualArrayOrMap

View file

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