#!/bin/sh # run-tests.sh - test suite for bibutils ROOT=$(cd "$(dirname "$0")/.." && pwd) PATH=$ROOT:$PATH tmpd=$(mktemp -d) || exit 1 trap 'rm -rf "$tmpd"' EXIT INT TERM pass=0 fail=0 ok() { pass=$((pass + 1)) printf 'ok - %s\n' "$1" } not_ok() { fail=$((fail + 1)) printf 'FAIL - %s\n' "$1" } # check description command... (passes if the command succeeds) check() { desc=$1 shift if "$@" > /dev/null 2>&1; then ok "$desc" else not_ok "$desc" fi } entry='@ARTICLE{ junk-key , AUTHOR = "Donald E. Knuth", Title={Literate Programming}, JOURNAL = {The Computer Journal}, Year = 1984, volume={27}, pages = {97--111} }' # ---- bib-key ---------------------------------------------------------- out=$(printf '%s\n' "$entry" | bib-key) check "bib-key generates surname-year-word key" \ sh -c "printf '%s' '$out' | grep -q '^@article{knuth1984literate,'" # key collisions get letter suffixes out=$(printf '@inproceedings{a, author={J. Smith}, title={Fast Trees}, year=2020} @article{b, author={J. Smith}, title={Fast Trees Extended}, year=2020} @article{c, author={J. Smith}, title={Fast Tree Methods}, year=2020}\n' | bib-key) check "bib-key disambiguates colliding keys" \ sh -c "printf '%s' '$out' | grep -q '{smith2020fast,' && printf '%s' '$out' | grep -q '{smith2020fastb,' && printf '%s' '$out' | grep -q '{smith2020fastc,'" # ---- canonicalization via bib-add ------------------------------------- db=$tmpd/refs.bib printf '%s\n' "$entry" | bib-add "$db" check "bib-add creates database" test -s "$db" check "bib-add lowercases field names" grep -q ' author = {Donald E. Knuth},' "$db" check "bib-add collapses whitespace in values" \ grep -q ' title = {Literate Programming},' "$db" check "bib-add keeps bare numbers bare" grep -q ' year = 1984,' "$db" # duplicate detection if printf '%s\n' "$entry" | bib-add "$db" 2> /dev/null; then not_ok "bib-add rejects duplicate key" else ok "bib-add rejects duplicate key" fi # forced replacement printf '%s\n' "$entry" | sed 's/1984/1985/' | bib-add -f "$db" check "bib-add -f replaces entry" grep -q ' year = 1985,' "$db" n=$(grep -c '^@article{junk-key,' "$db") [ "$n" = 1 ] && ok "bib-add -f leaves one copy" || not_ok "bib-add -f leaves one copy" # ---- bib-add hardening -------------------------------------------------- check "bib-add writes a backup on modify" \ sh -c "cmp -s '$db.bak' /dev/null; [ -s '$db.bak' ]" # replacement must not disturb other bytes (comments, formatting) cat > "$tmpd/pres.bib" <<'EOF' % Encoding: UTF-8 % hand-maintained; do not reformat @ARTICLE{ keep , AUTHOR = "Stays Verbatim", YEAR = 1111 } @article{swap2000old, author = {Old One}, title = {Swap}, year = 2000} EOF printf '@article{swap2000old, author = {New One}, title = {Swap}, year = 2000}\n' \ | bib-add -f "$tmpd/pres.bib" check "bib-add -f preserves comments" grep -q '^% Encoding: UTF-8$' "$tmpd/pres.bib" check "bib-add -f preserves untouched entries verbatim" \ grep -q 'AUTHOR = "Stays Verbatim"' "$tmpd/pres.bib" check "bib-add -f swapped the entry" grep -q '{New One}' "$tmpd/pres.bib" n=$(grep -c 'swap2000old' "$tmpd/pres.bib") [ "$n" = 1 ] && ok "bib-add -f removed the old version" \ || not_ok "bib-add -f removed the old version" # bogus input must never modify the database cp "$db" "$tmpd/before" printf '@article{, author = {No Key}, year = 1}\n' | bib-add "$db" 2> /dev/null \ && not_ok "bib-add rejects empty keys" || ok "bib-add rejects empty keys" printf '@misc{same2, title={A}}\n@misc{same2, title={B}}\n' \ | bib-add "$db" 2> /dev/null \ && not_ok "bib-add rejects dup keys within input" \ || ok "bib-add rejects dup keys within input" check "database untouched after rejected input" cmp -s "$db" "$tmpd/before" # concurrent writers serialize; no entries lost, lock released i=0 while [ "$i" -lt 10 ]; do i=$((i + 1)) printf '@misc{lock%d, title = {L %d}}\n' "$i" "$i" \ | bib-add "$tmpd/lock.bib" 2> /dev/null & done wait n=$(bib-ls "$tmpd/lock.bib" | wc -l) [ "$n" -eq 10 ] && ok "concurrent bib-add loses no entries" \ || not_ok "concurrent bib-add loses no entries (got $n)" [ -e "$tmpd/lock.bib.lock" ] && not_ok "lock released after use" \ || ok "lock released after use" # a stale lock from a dead process is reaped echo 999999 > "$tmpd/lock.bib.lock" printf '@misc{lock11, title = {L 11}}\n' | bib-add "$tmpd/lock.bib" 2> /dev/null check "stale lock reaped" grep -q 'lock11' "$tmpd/lock.bib" mkdir "$tmpd/adir" printf '@misc{k, title={T}}\n' | bib-add "$tmpd/adir" 2> /dev/null \ && not_ok "bib-add refuses non-regular files" \ || ok "bib-add refuses non-regular files" # ---- bib-extract ------------------------------------------------------- cat > "$tmpd/all.bib" <<'EOF' @article{alpha2020one, author = {A. Alpha}, title = {One}, year = 2020} @article{beta2021two, author = {B. Beta}, title = {Two}, year = 2021} @article{gamma2022three, author = {C. Gamma}, title = {Three}, year = 2022} EOF cat > "$tmpd/doc.aux" <<'EOF' \relax \citation{alpha2020one} \citation{gamma2022three,alpha2020one} \bibstyle{plain} EOF out=$(bib-extract "$tmpd/doc.aux" "$tmpd/all.bib") check "bib-extract keeps cited entries" \ sh -c "printf '%s' '$out' | grep -q alpha2020one" check "bib-extract keeps all cited entries" \ sh -c "printf '%s' '$out' | grep -q gamma2022three" if printf '%s' "$out" | grep -q beta2021two; then not_ok "bib-extract drops uncited entries" else ok "bib-extract drops uncited entries" fi # ---- bib-convert ------------------------------------------------------- out=$(printf '%s\n' "$entry" | bib-convert) check "bib-convert emits refer author" \ sh -c "printf '%s' '$out' | grep -q '^%A Donald E. Knuth$'" check "bib-convert emits refer pages with single dash" \ sh -c "printf '%s' '$out' | grep -q '^%P 97-111$'" cat > "$tmpd/rec.ref" <<'EOF' %A Alan M. Turing %T Computing Machinery and Intelligence %J Mind %D 1950 %V 59 %P 433-460 EOF out=$(bib-convert "$tmpd/rec.ref") check "bib-convert refer->bibtex type guess" \ sh -c "printf '%s' '$out' | grep -q '^@article{turing1950computing,'" check "bib-convert refer->bibtex pages" \ sh -c "printf '%s' '$out' | grep -q ' pages = {433--460},'" # format detection is not fooled by a leading % comment (e.g. JabRef's # "% Encoding: UTF-8") in a bibtex file out=$(printf '%% Encoding: UTF-8\n%s\n' "$entry" | bib-convert) check "bib-convert detects bibtex behind %% comment" \ sh -c "printf '%s' '$out' | grep -q '^%A Donald E. Knuth$'" # ---- bib-gen ----------------------------------------------------------- out=$(bib-gen -t book author='Xavier Yu' title='Some Title' year=2001 publisher='Pub') check "bib-gen argument mode" \ sh -c "printf '%s' '$out' | grep -q '^@book{yu2001some,'" out=$(printf 'A. Author\tNeat Paper\tGood Journal\t1999\n' \ | bib-gen -F author,title,journal,year) check "bib-gen batch mode" \ sh -c "printf '%s' '$out' | grep -q '^@article{author1999neat,'" # ---- bib-ls ------------------------------------------------------------ out=$(bib-ls "$tmpd/all.bib") check "bib-ls lists keys" \ sh -c "[ \"\$(printf '%s\n' '$out' | wc -l)\" = 3 ]" out=$(bib-ls -l "$tmpd/all.bib") check "bib-ls -l shows details" \ sh -c "printf '%s' '$out' | grep -q 'beta2021two article B. Beta 2021 Two'" # ---- bib-check --------------------------------------------------------- cat > "$tmpd/bad.bib" <<'EOF' @article{good2020fine, author = {A. Good}, title = {Fine}, journal = {J}, year = 2020} @article{noj2020sad, author = {B. Sad}, title = {No Journal Here}, year = 2020} @misc{noj2020sad, title = {Dup Key}} @book{dup2021title, author = {C. Dup}, title = {FINE!}, publisher = {P}, year = 2021} EOF out=$(bib-check "$tmpd/bad.bib") if [ $? -ne 0 ]; then ok "bib-check exits nonzero on problems"; else not_ok "bib-check exits nonzero on problems"; fi check "bib-check finds missing field" \ sh -c "printf '%s' '$out' | grep -q 'noj2020sad: missing required field: journal'" check "bib-check finds duplicate key" \ sh -c "printf '%s' '$out' | grep -q 'noj2020sad: duplicate key'" check "bib-check finds duplicate title" \ sh -c "printf '%s' '$out' | grep -q 'dup2021title: title duplicates good2020fine'" cat > "$tmpd/clean.bib" <<'EOF' @article{a2020x, author = {A. A}, title = {X}, journal = {J}, year = 2020} @misc{b2021y, title = {Y}} EOF check "bib-check passes a clean db" bib-check "$tmpd/clean.bib" # ---- biblatex aux ------------------------------------------------------ cat > "$tmpd/bl.aux" <<'EOF' \abx@aux@refcontext{nty/global//global/global} \abx@aux@cite{0}{beta2021two} EOF out=$(bib-extract "$tmpd/bl.aux" "$tmpd/all.bib") check "bib-extract reads biblatex aux" \ sh -c "printf '%s' '$out' | grep -q beta2021two" # ---- bib-util ---------------------------------------------------------- out=$(printf '%s\n' "$entry" | bib-util key) check "bib-util dispatches" \ sh -c "printf '%s' '$out' | grep -q knuth1984literate" # ---- @string passthrough ----------------------------------------------- cat > "$tmpd/str.bib" <<'EOF' @string{cj = {The Computer Journal}} @article{knuth1984literate, author = {D. Knuth}, journal = cj, year = 1984} EOF out=$(printf '\\citation{knuth1984literate}\n' > "$tmpd/s.aux"; \ bib-extract "$tmpd/s.aux" "$tmpd/str.bib") check "bib-extract passes @string through" \ sh -c "printf '%s' '$out' | grep -q '@string{cj'" check "macro field stays raw" \ sh -c "printf '%s' '$out' | grep -q ' journal = cj,'" printf '\n%d passed, %d failed\n' "$pass" "$fail" [ "$fail" -eq 0 ]