Connor Baker 4b80e5995e testers.testEqualArrayOrMap: use arrayUtilities where possible
Signed-off-by: Connor Baker <ConnorBaker01@gmail.com>
2025-06-11 17:08:13 +00:00

92 lines
3.3 KiB
Bash

# shellcheck shell=bash
# Asserts that two maps are equal, printing out differences if they are not.
# Does not short circuit on the first difference.
assertEqualMap() {
if (($# != 2)); then
nixErrorLog "expected two arguments!"
nixErrorLog "usage: assertEqualMap expectedMapRef actualMapRef"
exit 1
fi
local -nr expectedMapRef="$1"
local -nr actualMapRef="$2"
if ! isDeclaredMap "${!expectedMapRef}"; then
nixErrorLog "first argument expectedMapRef must be a reference to an associative array"
exit 1
fi
if ! isDeclaredMap "${!actualMapRef}"; then
nixErrorLog "second argument actualMapRef must be a reference to an associative array"
exit 1
fi
local -ir expectedLength=${#expectedMapRef[@]}
local -ir actualLength=${#actualMapRef[@]}
local -i hasDiff=0
if ((expectedLength != actualLength)); then
nixErrorLog "maps differ in length: expectedMap has length $expectedLength but actualMap has length $actualLength"
hasDiff=1
fi
local -a sortedExpectedKeys=()
getSortedMapKeys "${!expectedMapRef}" sortedExpectedKeys
local -a sortedActualKeys=()
getSortedMapKeys "${!actualMapRef}" sortedActualKeys
local -i expectedKeyIdx=0
local expectedKey
local expectedValue
local -i actualKeyIdx=0
local actualKey
local actualValue
# We iterate so long as at least one map has keys we've not considered.
while ((expectedKeyIdx < expectedLength || actualKeyIdx < actualLength)); do
# Update values for variables which are still in range/valid.
if ((expectedKeyIdx < expectedLength)); then
expectedKey="${sortedExpectedKeys["$expectedKeyIdx"]}"
expectedValue="${expectedMapRef["$expectedKey"]}"
fi
if ((actualKeyIdx < actualLength)); then
actualKey="${sortedActualKeys["$actualKeyIdx"]}"
actualValue="${actualMapRef["$actualKey"]}"
fi
# In the case actualKeyIdx is valid and expectedKey comes after actualKey or expectedKeyIdx is invalid, actualMap
# has an extra key relative to expectedMap.
# NOTE: In Bash, && and || have the same precedence, so use the fact they're left-associative to enforce groups.
if ((actualKeyIdx < actualLength)) && [[ $expectedKey > $actualKey ]] || ((expectedKeyIdx >= expectedLength)); then
nixErrorLog "maps differ at key ${actualKey@Q}: expectedMap has no such key but actualMap has value ${actualValue@Q}"
hasDiff=1
actualKeyIdx+=1
# In the case actualKeyIdx is invalid or expectedKey comes before actualKey, expectedMap has an extra key relative
# to actualMap.
# NOTE: By virtue of the previous condition being false, we know the negation is true. Namely, expectedKeyIdx is
# valid AND (actualKeyIdx is invalid OR expectedKey <= actualKey).
elif ((actualKeyIdx >= actualLength)) || [[ $expectedKey < $actualKey ]]; then
nixErrorLog "maps differ at key ${expectedKey@Q}: expectedMap has value ${expectedValue@Q} but actualMap has no such key"
hasDiff=1
expectedKeyIdx+=1
# In the case where both key indices are valid and the keys are equal.
else
if [[ $expectedValue != "$actualValue" ]]; then
nixErrorLog "maps differ at key ${expectedKey@Q}: expectedMap has value ${expectedValue@Q} but actualMap has value ${actualValue@Q}"
hasDiff=1
fi
expectedKeyIdx+=1
actualKeyIdx+=1
fi
done
((hasDiff)) && exit 1 || return 0
}