lol
fork

Configure Feed

Select the types of activity you want to include in your feed.

at 23.11-pre 179 lines 5.8 kB view raw
1#!/usr/bin/env bash 2 3# Property tests for the `lib.path` library 4# 5# It generates random path-like strings and runs the functions on 6# them, checking that the expected laws of the functions hold 7 8set -euo pipefail 9shopt -s inherit_errexit 10 11# https://stackoverflow.com/a/246128 12SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) 13 14if test -z "${TEST_LIB:-}"; then 15 TEST_LIB=$SCRIPT_DIR/../.. 16fi 17 18tmp="$(mktemp -d)" 19clean_up() { 20 rm -rf "$tmp" 21} 22trap clean_up EXIT 23mkdir -p "$tmp/work" 24cd "$tmp/work" 25 26# Defaulting to a random seed but the first argument can override this 27seed=${1:-$RANDOM} 28echo >&2 "Using seed $seed, use \`lib/path/tests/prop.sh $seed\` to reproduce this result" 29 30# The number of random paths to generate. This specific number was chosen to 31# be fast enough while still generating enough variety to detect bugs. 32count=500 33 34debug=0 35# debug=1 # print some extra info 36# debug=2 # print generated values 37 38# Fine tuning parameters to balance the number of generated invalid paths 39# to the variance in generated paths. 40extradotweight=64 # Larger value: more dots 41extraslashweight=64 # Larger value: more slashes 42extranullweight=16 # Larger value: shorter strings 43 44die() { 45 echo >&2 "test case failed: " "$@" 46 exit 1 47} 48 49if [[ "$debug" -ge 1 ]]; then 50 echo >&2 "Generating $count random path-like strings" 51fi 52 53# Read stream of null-terminated strings entry-by-entry into bash, 54# write it to a file and the `strings` array. 55declare -a strings=() 56mkdir -p "$tmp/strings" 57while IFS= read -r -d $'\0' str; do 58 printf "%s" "$str" > "$tmp/strings/${#strings[@]}" 59 strings+=("$str") 60done < <(awk \ 61 -f "$SCRIPT_DIR"/generate.awk \ 62 -v seed="$seed" \ 63 -v count="$count" \ 64 -v extradotweight="$extradotweight" \ 65 -v extraslashweight="$extraslashweight" \ 66 -v extranullweight="$extranullweight") 67 68if [[ "$debug" -ge 1 ]]; then 69 echo >&2 "Trying to normalise the generated path-like strings with Nix" 70fi 71 72# Precalculate all normalisations with a single Nix call. Calling Nix for each 73# string individually would take way too long 74nix-instantiate --eval --strict --json \ 75 --argstr libpath "$TEST_LIB" \ 76 --argstr dir "$tmp/strings" \ 77 "$SCRIPT_DIR"/prop.nix \ 78 >"$tmp/result.json" 79 80# Uses some jq magic to turn the resulting attribute set into an associative 81# bash array assignment 82declare -A normalised_result="($(jq ' 83 to_entries 84 | map("[\(.key | @sh)]=\(.value | @sh)") 85 | join(" \n")' -r < "$tmp/result.json"))" 86 87# Looks up a normalisation result for a string 88# Checks that the normalisation is only failing iff it's an invalid subpath 89# For valid subpaths, returns 0 and prints the normalisation result 90# For invalid subpaths, returns 1 91normalise() { 92 local str=$1 93 # Uses the same check for validity as in the library implementation 94 if [[ "$str" == "" || "$str" == /* || "$str" =~ ^(.*/)?\.\.(/.*)?$ ]]; then 95 valid= 96 else 97 valid=1 98 fi 99 100 normalised=${normalised_result[$str]} 101 # An empty string indicates failure, this is encoded in ./prop.nix 102 if [[ -n "$normalised" ]]; then 103 if [[ -n "$valid" ]]; then 104 echo "$normalised" 105 else 106 die "For invalid subpath \"$str\", lib.path.subpath.normalise returned this result: \"$normalised\"" 107 fi 108 else 109 if [[ -n "$valid" ]]; then 110 die "For valid subpath \"$str\", lib.path.subpath.normalise failed" 111 else 112 if [[ "$debug" -ge 2 ]]; then 113 echo >&2 "String \"$str\" is not a valid subpath" 114 fi 115 # Invalid and it correctly failed, we let the caller continue if they catch the exit code 116 return 1 117 fi 118 fi 119} 120 121# Intermediate result populated by test_idempotency_realpath 122# and used in test_normalise_uniqueness 123# 124# Contains a mapping from a normalised subpath to the realpath result it represents 125declare -A norm_to_real 126 127test_idempotency_realpath() { 128 if [[ "$debug" -ge 1 ]]; then 129 echo >&2 "Checking idempotency of each result and making sure the realpath result isn't changed" 130 fi 131 132 # Count invalid subpaths to display stats 133 invalid=0 134 for str in "${strings[@]}"; do 135 if ! result=$(normalise "$str"); then 136 ((invalid++)) || true 137 continue 138 fi 139 140 # Check the law that it doesn't change the result of a realpath 141 mkdir -p -- "$str" "$result" 142 real_orig=$(realpath -- "$str") 143 real_norm=$(realpath -- "$result") 144 145 if [[ "$real_orig" != "$real_norm" ]]; then 146 die "realpath of the original string \"$str\" (\"$real_orig\") is not the same as realpath of the normalisation \"$result\" (\"$real_norm\")" 147 fi 148 149 if [[ "$debug" -ge 2 ]]; then 150 echo >&2 "String \"$str\" gets normalised to \"$result\" and file path \"$real_orig\"" 151 fi 152 norm_to_real["$result"]="$real_orig" 153 done 154 if [[ "$debug" -ge 1 ]]; then 155 echo >&2 "$(bc <<< "scale=1; 100 / $count * $invalid")% of the total $count generated strings were invalid subpath strings, and were therefore ignored" 156 fi 157} 158 159test_normalise_uniqueness() { 160 if [[ "$debug" -ge 1 ]]; then 161 echo >&2 "Checking for the uniqueness law" 162 fi 163 164 for norm_p in "${!norm_to_real[@]}"; do 165 real_p=${norm_to_real["$norm_p"]} 166 for norm_q in "${!norm_to_real[@]}"; do 167 real_q=${norm_to_real["$norm_q"]} 168 # Checks normalisation uniqueness law for each pair of values 169 if [[ "$norm_p" != "$norm_q" && "$real_p" == "$real_q" ]]; then 170 die "Normalisations \"$norm_p\" and \"$norm_q\" are different, but the realpath of them is the same: \"$real_p\"" 171 fi 172 done 173 done 174} 175 176test_idempotency_realpath 177test_normalise_uniqueness 178 179echo >&2 tests ok