|
#!/usr/bin/env bash |
|
# Minimal assertion library for the markdown.awk test suites. |
|
# Plain bash on purpose: no dev dependency, runs anywhere awk runs. |
|
|
|
TESTS_RUN=0 |
|
TESTS_FAILED=0 |
|
CURRENT_TEST="" |
|
|
|
# ANSI colours, disabled when not a tty or when NO_COLOR is set. |
|
if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then |
|
C_RED=$'\033[31m'; C_GREEN=$'\033[32m'; C_YELLOW=$'\033[33m' |
|
C_DIM=$'\033[2m'; C_OFF=$'\033[0m' |
|
else |
|
C_RED=""; C_GREEN=""; C_YELLOW=""; C_DIM=""; C_OFF="" |
|
fi |
|
|
|
|
|
# ---------------------------------------------------------------- test runner |
|
|
|
# it <description> -- starts a new test case |
|
it() { |
|
CURRENT_TEST="$1" |
|
TESTS_RUN=$((TESTS_RUN + 1)) |
|
} |
|
|
|
pass() { |
|
printf ' %sok%s %s\n' "$C_GREEN" "$C_OFF" "$CURRENT_TEST" |
|
} |
|
|
|
fail() { |
|
TESTS_FAILED=$((TESTS_FAILED + 1)) |
|
printf ' %sFAIL%s %s\n' "$C_RED" "$C_OFF" "$CURRENT_TEST" |
|
local line |
|
while IFS= read -r line; do |
|
printf ' %s%s%s\n' "$C_DIM" "$line" "$C_OFF" |
|
done <<< "$1" |
|
} |
|
|
|
skip() { |
|
printf ' %sskip%s %s %s(%s)%s\n' \ |
|
"$C_YELLOW" "$C_OFF" "$CURRENT_TEST" "$C_DIM" "$1" "$C_OFF" |
|
} |
|
|
|
suite() { |
|
printf '\n%s\n' "$1" |
|
} |
|
|
|
# Call at the end of a test file. Exits non-zero if anything failed. |
|
summary() { |
|
printf '\n' |
|
if [ "$TESTS_FAILED" -eq 0 ]; then |
|
printf '%s%d passed%s\n' "$C_GREEN" "$TESTS_RUN" "$C_OFF" |
|
return 0 |
|
fi |
|
printf '%s%d of %d failed%s\n' "$C_RED" "$TESTS_FAILED" "$TESTS_RUN" "$C_OFF" |
|
return 1 |
|
} |
|
|
|
# ----------------------------------------------------------------- assertions |
|
|
|
assert_eq() { |
|
# assert_eq <actual> <expected> [label] |
|
if [ "$1" = "$2" ]; then |
|
pass |
|
else |
|
fail "${3:+$3 |
|
}expected: $2 |
|
actual: $1" |
|
fi |
|
} |
|
|
|
assert_contains() { |
|
# assert_contains <haystack> <needle> [label] |
|
case "$1" in |
|
*"$2"*) pass ;; |
|
*) fail "${3:+$3 |
|
}expected to contain: $2 |
|
actual: $1" ;; |
|
esac |
|
} |
|
|
|
assert_not_contains() { |
|
case "$1" in |
|
*"$2"*) fail "${3:+$3 |
|
}expected NOT to contain: $2 |
|
actual: $1" ;; |
|
*) pass ;; |
|
esac |
|
} |
|
|
|
assert_file_contains() { |
|
# assert_file_contains <file> <fixed-string> [label] |
|
if [ ! -f "$1" ]; then |
|
fail "${3:+$3 |
|
}no such file: $1" |
|
return |
|
fi |
|
if grep -qF -- "$2" "$1"; then |
|
pass |
|
else |
|
fail "${3:+$3 |
|
}$1 does not contain: $2" |
|
fi |
|
} |
|
|
|
assert_file_not_contains() { |
|
if [ ! -f "$1" ]; then |
|
fail "${3:+$3 |
|
}no such file: $1" |
|
return |
|
fi |
|
if grep -qF -- "$2" "$1"; then |
|
fail "${3:+$3 |
|
}$1 unexpectedly contains: $2" |
|
else |
|
pass |
|
fi |
|
} |
|
|
|
assert_file_exists() { |
|
if [ -f "$1" ]; then pass; else fail "${2:+$2 |
|
}expected file to exist: $1"; fi |
|
} |
|
|
|
assert_file_absent() { |
|
if [ ! -e "$1" ]; then pass; else fail "${2:+$2 |
|
}expected file to be absent: $1"; fi |
|
} |
|
|
|
assert_success() { |
|
# assert_success <status> [output] |
|
if [ "$1" -eq 0 ]; then pass; else fail "expected exit 0, got $1 |
|
${2:-}"; fi |
|
} |
|
|
|
assert_failure() { |
|
# assert_failure <status> [output] |
|
if [ "$1" -ne 0 ]; then pass; else fail "expected non-zero exit, got 0 |
|
${2:-}"; fi |
|
} |
|
|
|
# --------------------------------------------------------------- blog fixture |
|
|
|
# make_blog <dir> [posts_per_page] |
|
# Builds the standard repro blog from the handoff document. |
|
|
|
# make_blog_min <dir> <count> [posts_per_page] |
|
# A blog with exactly <count> plain posts and nothing tricky. |
|
|
|
# copy_fixture <name> <dest> |
|
# Copies a committed fixture blog to a scratch directory. Fixtures are |
|
# committed rather than generated so the legacy layouts they carry stay |
|
# byte-identical to what shipped before the record format changed. |
|
|
|
|
|
# Same, but keeps stderr separate in $stderr_output. |
|
|
|
# ---------------------------------------------------------------- link checker |
|
|
|
# check_links <dist_dir> |
|
# Extracts every href/src from every .html file under <dist_dir> and verifies |
|
# that site-relative and document-relative targets resolve to a real file, |
|
# treating <dist_dir> as the document root. Prints one line per broken link. |
|
# Returns 0 when everything resolves. |
|
|
|
# feed_item_count <feed.xml> |
|
|
|
# index_post_count <index.html> -- number of real post links (excludes break marker) |