diff --git a/doc/build-helpers/testers.chapter.md b/doc/build-helpers/testers.chapter.md index 904ee76a0d95..d3c8a7a78bb6 100644 --- a/doc/build-helpers/testers.chapter.md +++ b/doc/build-helpers/testers.chapter.md @@ -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} diff --git a/pkgs/build-support/testers/default.nix b/pkgs/build-support/testers/default.nix index bdb1726e37ee..a0ebc7061488 100644 --- a/pkgs/build-support/testers/default.nix +++ b/pkgs/build-support/testers/default.nix @@ -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 diff --git a/pkgs/build-support/testers/testEqualArrayOrMap/assert-equal-array.sh b/pkgs/build-support/testers/testEqualArrayOrMap/assert-equal-array.sh index 4e68b3b1488c..92fa981e44ee 100644 --- a/pkgs/build-support/testers/testEqualArrayOrMap/assert-equal-array.sh +++ b/pkgs/build-support/testers/testEqualArrayOrMap/assert-equal-array.sh @@ -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 diff --git a/pkgs/build-support/testers/testEqualArrayOrMap/assert-equal-map.sh b/pkgs/build-support/testers/testEqualArrayOrMap/assert-equal-map.sh index 1e07b3aaff02..f66fab4f8468 100644 --- a/pkgs/build-support/testers/testEqualArrayOrMap/assert-equal-map.sh +++ b/pkgs/build-support/testers/testEqualArrayOrMap/assert-equal-map.sh @@ -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 diff --git a/pkgs/build-support/testers/testEqualArrayOrMap/build-command.sh b/pkgs/build-support/testers/testEqualArrayOrMap/build-command.sh new file mode 100644 index 000000000000..b1733692d1e9 --- /dev/null +++ b/pkgs/build-support/testers/testEqualArrayOrMap/build-command.sh @@ -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:?}" diff --git a/pkgs/build-support/testers/testEqualArrayOrMap/tester.nix b/pkgs/build-support/testers/testEqualArrayOrMap/tester.nix index 73f8972c0eb6..34373b01db3c 100644 --- a/pkgs/build-support/testers/testEqualArrayOrMap/tester.nix +++ b/pkgs/build-support/testers/testEqualArrayOrMap/tester.nix @@ -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 diff --git a/pkgs/build-support/testers/testEqualArrayOrMap/tests.nix b/pkgs/build-support/testers/testEqualArrayOrMap/tests.nix index 92888151d43e..bcb2a176a424 100644 --- a/pkgs/build-support/testers/testEqualArrayOrMap/tests.nix +++ b/pkgs/build-support/testers/testEqualArrayOrMap/tests.nix @@ -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