+ aptautotest 'testwarning' "$@"
+ msggroup
+}
+testfailure() {
+ msggroup 'testfailure'
+ if [ "$1" = '--nomsg' ]; then
+ shift
+ else
+ msgtest 'Test for failure in execution of' "$*"
+ fi
+ local OUTPUT="${TMPWORKINGDIRECTORY}/rootdir/tmp/testfailure.output"
+ if "$@" >"${OUTPUT}" 2>&1; then
+ local EXITCODE=$?
+ msgfailoutput "exitcode $EXITCODE" "$OUTPUT" "$@"
+ else
+ local EXITCODE=$?
+ if expr match "$1" '^apt.*' >/dev/null; then
+ if [ "$1" = 'aptkey' ]; then
+ if grep -q " Can't check signature:
+ BAD signature from
+ signature could not be verified" "$OUTPUT"; then
+ msgpass
+ else
+ msgfailoutput "run failed with exitcode ${EXITCODE}, but no signature error" "$OUTPUT" "$@"
+ fi
+ else
+ if grep -q -E ' runtime error: ' "$OUTPUT"; then
+ msgfailoutput 'compiler detected undefined behavior' "$OUTPUT" "$@"
+ elif grep -q -E '==ERROR' "$OUTPUT"; then
+ msgfailoutput 'compiler sanitizers reported errors' "$OUTPUT" "$@"
+ elif ! grep -q -E '^E: ' "$OUTPUT"; then
+ msgfailoutput "run failed with exitcode ${EXITCODE}, but with no errors" "$OUTPUT" "$@"
+ else
+ msgpass
+ fi
+ fi
+ else
+ msgpass
+ fi
+ fi
+ aptautotest 'testfailure' "$@"
+ msggroup
+}
+
+testreturnstateequal() {
+ local STATE="$1"
+ if [ "$STATE" = 'testsuccesswithglobalerror' ]; then
+ local STATE="$2"
+ local TYPE="$3"
+ shift 3
+ msggroup "${STATE}equal"
+ if [ "$1" != '--nomsg' ]; then
+ local CMP="$1"
+ shift
+ testsuccesswithglobalerror "$STATE" "$TYPE" "$@"
+ testfileequal "${TMPWORKINGDIRECTORY}/rootdir/tmp/${STATE}.output" "$CMP"
+ else
+ local CMP="$2"
+ shift 2
+ testsuccesswithglobalerror "$STATE" "$TYPE" "$@"
+ testfileequal "${TMPWORKINGDIRECTORY}/rootdir/tmp/${STATE}.output" "$CMP"
+ fi
+ else
+ msggroup "${STATE}equal"
+ if [ "$2" != '--nomsg' ]; then
+ local CMP="$2"
+ shift 2
+ "$STATE" "$@"
+ testfileequal "${TMPWORKINGDIRECTORY}/rootdir/tmp/${STATE}.output" "$CMP"
+ else
+ local CMP="$3"
+ shift 3
+ "$STATE" --nomsg "$@"
+ testfileequal "${TMPWORKINGDIRECTORY}/rootdir/tmp/${STATE}.output" "$CMP"
+ fi
+ fi
+ msggroup
+}
+testsuccessequal() {
+ # we compare output, so we know perfectly well about N:
+ testreturnstateequal 'testsuccesswithglobalerror' 'testsuccess' 'WE' "$@"
+}
+testwarningequal() {
+ testreturnstateequal 'testwarning' "$@"
+}
+testfailureequal() {
+ testreturnstateequal 'testfailure' "$@"
+}
+
+testfailuremsg() {
+ msggroup 'testfailuremsg'
+ local CMP="$1"
+ shift
+ testfailure "$@"
+ msgtest 'Check that the output of the previous failed command has expected' 'failures and warnings'
+ local COMPAREFILE="${TMPWORKINGDIRECTORY}/rootdir/tmp/testfailuremsg.comparefile"
+ grep '^\(W\|E\|N\):' "${TMPWORKINGDIRECTORY}/rootdir/tmp/testfailure.output" > "$COMPAREFILE" 2>&1 || true
+ testoutputequal "$COMPAREFILE" echo "$CMP"
+ msggroup
+}
+testwarningmsg() {
+ msggroup 'testwarningmsg'
+ local CMP="$1"
+ shift
+ testwarning "$@"
+ msgtest 'Check that the output of the previous warned command has expected' 'warnings'
+ local COMPAREFILE="${TMPWORKINGDIRECTORY}/rootdir/tmp/testwarningmsg.comparefile"
+ grep '^\(W\|E\|N\):' "${TMPWORKINGDIRECTORY}/rootdir/tmp/testwarning.output" > "$COMPAREFILE" 2>&1 || true
+ testoutputequal "$COMPAREFILE" echo "$CMP"
+ msggroup
+}
+
+testfilestats() {
+ msggroup 'testfilestats'
+ msgtest "Test that file $1 has $2 $3" "$4"
+ if [ "$4" "$3" "$(stat --format "$2" "$1")" ]; then
+ msgpass
+ else
+ local OUTPUT="${TMPWORKINGDIRECTORY}/rootdir/tmp/testfilestats.output"
+ {
+ ls -ld "$1" || true
+ echo -n "stat(1) reports for $2: "
+ stat --format "$2" "$1" || true
+ } >"$OUTPUT" 2>&1
+ msgfailoutput '' "$OUTPUT"
+ fi
+ msggroup
+}
+testaccessrights() {
+ msggroup 'testaccessrights'
+ testfilestats "$1" '%a' '=' "$2"
+ msggroup
+}
+
+testwebserverlaststatuscode() {
+ msggroup 'testwebserverlaststatuscode'
+ local DOWNLOG='rootdir/tmp/webserverstatus-testfile.log'
+ local STATUS='downloaded/webserverstatus-statusfile.log'
+ rm -f "$DOWNLOG" "$STATUS"
+ msgtest 'Test last status code from the webserver was' "$1"
+ if downloadfile "http://localhost:${APTHTTPPORT}/_config/find/aptwebserver::last-status-code" "$STATUS" > "$DOWNLOG" && [ "$(cat "$STATUS")" = "$1" ]; then
+ msgpass
+ else
+ local OUTPUT="${TMPWORKINGDIRECTORY}/rootdir/tmp/testwebserverlaststatuscode.output"
+ {
+ if [ -n "$2" ]; then
+ shift
+ echo >&2 '#### Additionally provided output files contain:'
+ cat >&2 "$@"
+ fi
+ echo >&2 '#### Download log of the status code:'
+ cat >&2 "$DOWNLOG"
+ } >"$OUTPUT" 2>&1
+ msgfailoutput "Status was $(cat "$STATUS")" "$OUTPUT"
+ fi
+ msggroup
+}
+
+mapkeynametokeyid() {
+ while [ -n "$1" ]; do
+ case "$1" in
+ *Joe*|*Sixpack*|newarchive) echo '5A90D141DBAC8DAE';;
+ *Rex*|*Expired*) echo '4BC0A39C27CE74F9';;
+ *Marvin*|*Paranoid*) echo 'E8525D47528144E2';;
+ oldarchive) echo 'FDD2DB85F68C85A3';;
+ *) echo 'UNKNOWN KEY';;
+ esac
+ shift
+ done
+}
+testaptkeys() {
+ local OUTPUT="${TMPWORKINGDIRECTORY}/rootdir/tmp/aptkeylist.output"
+ if ! aptkey list --with-colon | grep '^pub' | cut -d':' -f 5 > "$OUTPUT"; then
+ echo -n > "$OUTPUT"
+ fi
+ testfileequal "$OUTPUT" "$(mapkeynametokeyid "$@")"
+}
+
+pause() {
+ echo "STOPPED execution. Press enter to continue"
+ local IGNORE
+ read IGNORE
+}
+
+logcurrentarchivedirectory() {
+ find "${TMPWORKINGDIRECTORY}/aptarchive/dists" -type f | while read line; do
+ stat --format '%U:%G:%a:%n' "$line"
+ done | sort > "${TMPWORKINGDIRECTORY}/rootdir/var/log/aptgetupdate.before.lst"
+}
+listcurrentlistsdirectory() {
+ {
+ find rootdir/var/lib/apt/lists -maxdepth 1 -type d | while read line; do
+ stat --format '%U:%G:%a:%n' "$line"
+ done
+ find rootdir/var/lib/apt/lists -maxdepth 1 \! -type d | while read line; do
+ stat --format '%U:%G:%a:%s:%y:%n' "$line"
+ done
+ } | sort
+}
+forallsupportedcompressors() {
+ rm -f "${TMPWORKINGDIRECTORY}/rootdir/etc/apt/apt.conf.d/00force-compressor"
+ for COMP in $(aptconfig dump 'APT::Compressor' --format '%f%n' | cut -d':' -f 5 | uniq); do
+ if [ -z "$COMP" -o "$COMP" = '.' ]; then continue; fi
+ "$@" "$COMP"
+ done
+}
+
+### convenience hacks ###
+mkdir() {
+ # creating some directories by hand is a tedious task, so make it look simple
+ local PARAMS="$*"
+ if [ "$PARAMS" != "${PARAMS#*rootdir/var/lib/apt/lists}" ]; then
+ # only the last directory created by mkdir is effected by the -m !
+ command mkdir -m 755 -p "${TMPWORKINGDIRECTORY}/rootdir/var/lib/apt"
+ command mkdir -m 755 -p "${TMPWORKINGDIRECTORY}/rootdir/var/lib/apt/lists"
+ command mkdir -m 700 -p "${TMPWORKINGDIRECTORY}/rootdir/var/lib/apt/lists/partial"
+ touch "${TMPWORKINGDIRECTORY}/rootdir/var/lib/apt/lists/lock"
+ if [ "$(id -u)" = '0' ]; then
+ chown _apt:$(id -gn) "${TMPWORKINGDIRECTORY}/rootdir/var/lib/apt/lists/partial"
+ fi
+ else
+ command mkdir "$@"
+ fi
+}
+
+### The following tests are run by most test methods automatically to check
+### general things about commands executed without writing the test every time.
+
+aptautotest() {
+ if [ $# -lt 3 ]; then return; fi
+ local TESTCALL="$1"
+ local CMD="$2"
+ local FIRSTOPT="$3"
+ shift 2
+ for i in "$@"; do
+ if ! expr match "$i" '^-' >/dev/null 2>&1; then
+ FIRSTOPT="$i"
+ break
+ fi
+ done
+ shift
+ local AUTOTEST="aptautotest_$(echo "${CMD##*/}_${FIRSTOPT}" | tr -d -c 'A-za-z0-9')"
+ if command -v $AUTOTEST >/dev/null; then
+ # save and restore the *.output files from other tests
+ # as we might otherwise override them in these automatic tests
+ rm -rf "${TMPWORKINGDIRECTORY}/rootdir/tmp-before"
+ mv "${TMPWORKINGDIRECTORY}/rootdir/tmp" "${TMPWORKINGDIRECTORY}/rootdir/tmp-before"
+ mkdir "${TMPWORKINGDIRECTORY}/rootdir/tmp"
+ $AUTOTEST "$TESTCALL" "$@"
+ rm -rf "${TMPWORKINGDIRECTORY}/rootdir/tmp-aptautotest"
+ mv "${TMPWORKINGDIRECTORY}/rootdir/tmp" "${TMPWORKINGDIRECTORY}/rootdir/tmp-aptautotest"
+ mv "${TMPWORKINGDIRECTORY}/rootdir/tmp-before" "${TMPWORKINGDIRECTORY}/rootdir/tmp"
+ fi
+}
+
+aptautotest_aptget_update() {
+ local TESTCALL="$1"
+ while [ -n "$2" ]; do
+ if [ "$2" = '--print-uris' ]; then return; fi # simulation mode
+ shift
+ done
+ if ! test -d "${TMPWORKINGDIRECTORY}/rootdir/var/lib/apt/lists"; then return; fi
+ testfilestats "${TMPWORKINGDIRECTORY}/rootdir/var/lib/apt" '%U:%G:%a' '=' "${TEST_DEFAULT_USER}:${TEST_DEFAULT_GROUP}:755"
+ testfilestats "${TMPWORKINGDIRECTORY}/rootdir/var/lib/apt/lists" '%U:%G:%a' '=' "${TEST_DEFAULT_USER}:${TEST_DEFAULT_GROUP}:755"
+ # all copied files are properly chmodded
+ local backupIFS="$IFS"
+ IFS="$(printf "\n\b")"
+ for file in $(find "${TMPWORKINGDIRECTORY}/rootdir/var/lib/apt/lists" -type f ! -name 'lock'); do
+ testfilestats "$file" '%U:%G:%a' '=' "${TEST_DEFAULT_USER}:${TEST_DEFAULT_GROUP}:644"
+ done
+ IFS="$backupIFS"
+ if [ "$TESTCALL" = 'testsuccess' ]; then
+ # failure cases can retain partial files and such
+ testempty find "${TMPWORKINGDIRECTORY}/rootdir/var/lib/apt/lists/partial" -mindepth 1 ! \( -name 'lock' -o -name '*.FAILED' \)
+ fi
+ if [ -s "${TMPWORKINGDIRECTORY}/rootdir/var/log/aptgetupdate.before.lst" ]; then
+ testfileequal "${TMPWORKINGDIRECTORY}/rootdir/var/log/aptgetupdate.before.lst" \
+ "$(find "${TMPWORKINGDIRECTORY}/aptarchive/dists" -type f | while read line; do stat --format '%U:%G:%a:%n' "$line"; done | sort)"
+ fi
+}
+aptautotest_apt_update() { aptautotest_aptget_update "$@"; }
+aptautotest_aptcdrom_add() { aptautotest_aptget_update "$@"; }
+
+testaptautotestnodpkgwarning() {
+ local TESTCALL="$1"
+ while [ -n "$2" ]; do
+ if expr match "$2" '^-[a-z]*s' >/dev/null 2>&1; then return; fi # simulation mode
+ if expr match "$2" '^-dy\?' >/dev/null 2>&1; then return; fi # download-only mode
+ shift
+ done
+ testfailure grep '^dpkg: warning:.*\(ignor\|unknown\).*' "${TMPWORKINGDIRECTORY}/rootdir/tmp-before/${TESTCALL}.output"
+}
+
+aptautotest_aptget_install() { testaptautotestnodpkgwarning "$@"; }
+aptautotest_aptget_remove() { testaptautotestnodpkgwarning "$@"; }
+aptautotest_aptget_purge() { testaptautotestnodpkgwarning "$@"; }
+aptautotest_apt_install() { testaptautotestnodpkgwarning "$@"; }
+aptautotest_apt_remove() { testaptautotestnodpkgwarning "$@"; }
+aptautotest_apt_purge() { testaptautotestnodpkgwarning "$@"; }
+
+testaptmarknodefaultsections() {
+ testfailure grep '^Auto-Installed: 0$' "${TMPWORKINGDIRECTORY}/rootdir/var/lib/apt/extended_states"