test-hermetic.sh
#!/bin/bash
## Hermeticity: hostile repo-local git config and attributes, in the subject
## or the target repo, must not change what patchette writes or replays.
##
## Nulling global and system config doesn't reach a repo's own .git/config,
## its info/attributes, or a worktree .gitattributes; git reads all of those.
## Each case plants one hostile setup into a copy of a repo, then checks:
## export bytes match a clean export, verify passes, and import reproduces exact tips.
set -euo pipefail
cd "$(dirname "$0")"
source ./lib.sh
work=$(mktemp -d)
trap 'rm -rf "$work"' EXIT
make_corpus "$work/corpus"
source "$work/corpus/FACTS"
branches=(topic/fancy docs-polish odd-bytes)
want_tips=$(printf '%s\n' "$tip_topic_fancy" "$tip_docs_polish" "$tip_odd_bytes")
"$PATCHETTE" export "$work/corpus/dev" "$work/clean" "$base" "${branches[@]}" >/dev/null
printf 'odd/trailing.txt\n' >"$work/orderfile"
# Each case is `|`-separated items:
# `key=value` sets repo-local config; `@line` appends to the repo's info/attributes;
# `%line` appends to an uncommitted worktree .gitattributes;
# `!line` commits a .gitattributes on a new branch and checks it out
# (export bytes must not vary with which branch happens to be checked out).
cases=(
diff.context=1
diff.context=8
diff.interHunkContext=10
diff.algorithm=histogram
diff.algorithm=minimal
diff.indentHeuristic=false
diff.renames=copies
diff.noprefix=true
diff.mnemonicPrefix=true
'diff.srcPrefix=old/|diff.dstPrefix=new/'
diff.relative=true
diff.suppressBlankEmpty=true
"diff.orderFile=$work/orderfile"
diff.wsErrorHighlight=all
color.ui=always
color.diff=always
core.abbrev=12
core.quotePath=false
core.bigFileThreshold=1
core.autocrlf=true
core.autocrlf=input
core.eol=crlf
'core.autocrlf=true|core.safecrlf=true'
core.fileMode=false
core.symlinks=false
core.ignoreCase=true
core.precomposeUnicode=true
core.whitespace=trailing-space,space-before-tab,cr-at-eol,blank-at-eof
core.splitIndex=true
index.version=4
apply.whitespace=fix
apply.whitespace=error
apply.ignoreWhitespace=change
i18n.logOutputEncoding=ISO-8859-1
log.showSignature=true
format.pretty=oneline
'@* -diff'
'@* binary'
'@*.c diff=cpp'
'@* diff=hostile|diff.hostile.xfuncname=^/\*.*$'
'@* diff=hostile|diff.hostile.textconv=tr a-z A-Z'
'@* diff=hostile|diff.hostile.binary=true'
'@* filter=hostile|filter.hostile.clean=tr a-z A-Z|filter.hostile.smudge=cat'
'@* text eol=crlf'
'@* text=auto'
'@* whitespace=trailing-space,cr-at-eol'
'%*.c diff=cpp'
'%* -diff'
'!* -diff'
'!*.c diff=cpp'
)
## Plant one hostile case into a repo.
##
## - @param $1 repo path (non-bare)
## - @param $2 case string, as described above the table
hostilize() {
local repo=$1 item
local -a items
IFS='|' read -ra items <<<"$2"
for item in "${items[@]}"; do
case $item in
@*) printf '%s\n' "${item#@}" >>"$repo/.git/info/attributes" ;;
%*) printf '%s\n' "${item#%}" >>"$repo/.gitattributes" ;;
'!'*)
printf '%s\n' "${item#!}" >>"$repo/.gitattributes"
git -C "$repo" switch -qc hostile-attrs
git -C "$repo" add .gitattributes
git -C "$repo" commit -qm "hostile attributes"
;;
*) git -C "$repo" config "${item%%=*}" "${item#*=}" ;;
esac
done
}
n=0
for c in "${cases[@]}"; do
n=$((n + 1))
d="$work/case$n"
mkdir "$d"
# Command failures here surface on stderr just above the FAIL they cause.
cp -a "$work/corpus/dev" "$d/subject"
hostilize "$d/subject" "$c"
"$PATCHETTE" export "$d/subject" "$d/out" "$base" "${branches[@]}" >/dev/null || true
EXPECT success "subject has $c: export bytes match a clean export" -- \
diff -r "$work/clean" "$d/out"
EXPECT success "subject has $c: verify passes" -- \
"$PATCHETTE" verify "$d/subject" "$work/clean"
git clone -q "$work/corpus/upstream" "$d/target"
hostilize "$d/target" "$c"
"$PATCHETTE" import "$d/target" "$work/clean" >/dev/null || true
EXPECT equal "target has $c: import reproduces exact tips" -- \
"$(git -C "$d/target" rev-parse "${branches[@]/#/refs/heads/}" 2>/dev/null || true)" "$want_tips"
done
exit "$RESULT"
tests/test-hermetic.sh · 4.0 KiB · 14dda3a13d