test-parse.sh
#!/bin/bash ## Parse rules, one edge per case: a document (as a printf format) and its expected dump or error. ## Most cases come from the conformance corpus, and expect what testmark-rs does, ## except those marked "diverges", where this library deliberately does otherwise. set -euo pipefail cd "$(dirname "$0")" # shellcheck source=tests/lib.sh source ./lib.sh MAL='malformed annotation; want "[testmark]:# (name)"' DANGLING='annotation not followed by a code block' # --- annotation line shape --- parse_case ann-basic 'prose\n\n[testmark]:# (x)\n```text\nhello\n```\n' 'hunk "x"' 'info "text"' 'body "hello\n"' parse_case ann-trailing-text '[testmark]:# (x reserved words)\n```\nhello\n```\n' 'hunk "x"' 'info ""' 'body "hello\n"' parse_case ann-space-before-close '[testmark]:# (x )\n```\nhello\n```\n' 'hunk "x"' 'info ""' 'body "hello\n"' parse_case ann-leading-tab-name '[testmark]:# (\tx)\n```\nhello\n```\n' 'hunk "x"' 'info ""' 'body "hello\n"' parse_case ann-paren-in-name '[testmark]:# (a(b))\n```\nhello\n```\n' 'hunk "a(b)"' 'info ""' 'body "hello\n"' parse_case ann-two-paren-groups '[testmark]:# (x) (y)\n```\nhello\n```\n' 'hunk "x)"' 'info ""' 'body "hello\n"' parse_case ann-trailing-space '[testmark]:# (x) \n```\nhello\n```\n' "error: testmark: in.md:1: $MAL" parse_case ann-no-space-after-hash '[testmark]:#(x)\n```\nhello\n```\n' "error: testmark: in.md:1: $MAL" parse_case ann-double-space '[testmark]:# (x)\n```\nhello\n```\n' "error: testmark: in.md:1: $MAL" parse_case ann-sigil-only 'prose\n[testmark]:# \nmore prose\n' "error: testmark: in.md:2: $MAL" parse_case ann-open-paren-only '[testmark]:# (\n```\nhello\n```\n' "error: testmark: in.md:1: $MAL" parse_case ann-empty-parens '[testmark]:# ()\n```\nhello\n```\n' "error: testmark: in.md:1: $MAL" parse_case ann-space-in-parens '[testmark]:# ( )\n```\nhello\n```\n' "error: testmark: in.md:1: $MAL" parse_case ann-indented ' [testmark]:# (x)\n```\nhello\n```\n' parse_case ann-uppercase '[Testmark]:# (x)\n```\nhello\n```\n' parse_case ann-in-codeblock '```\n[testmark]:# (x)\n```\n' parse_case ann-malformed-in-codeblock '```\n[testmark]:# x\n```\n' parse_case struct-error-after-hunk '[testmark]:# (a)\n```\none\n```\n\n[testmark]:# b\n' "error: testmark: in.md:6: $MAL" # --- names: whitespace is ASCII (SP HT VT FF CR) --- parse_case name-vt '[testmark]:# (a\x0bb)\n```\nhello\n```\n' 'hunk "a"' 'info ""' 'body "hello\n"' parse_case name-lone-cr '[testmark]:# (a\rb)\n```\nhello\n```\n' 'hunk "a"' 'info ""' 'body "hello\n"' parse_case name-em-space-diverges '[testmark]:# (a\xe2\x80\x83b)\n```\nhello\n```\n' 'hunk "a\xe2\x80\x83b"' 'info ""' 'body "hello\n"' parse_case name-invalid-utf8-diverges '[testmark]:# (a\xffb)\n```\nhello\n```\n' 'hunk "a\xffb"' 'info ""' 'body "hello\n"' parse_case name-dup '[testmark]:# (x)\n```\none\n```\n\n[testmark]:# (x)\n```\ntwo\n```\n' 'error: testmark: in.md:6: duplicate hunk name "x"' parse_case name-dup-dangling '[testmark]:# (x)\n```\none\n```\n[testmark]:# (x)\nprose\n' 'error: testmark: in.md:5: duplicate hunk name "x"' # --- what follows an annotation --- parse_case ann-last-line-nl 'prose\n[testmark]:# (x)\n' "error: testmark: in.md:2: $DANGLING" parse_case ann-last-line-no-nl 'prose\n[testmark]:# (x)' "error: testmark: in.md:2: $DANGLING" parse_case ann-blank-then-fence '[testmark]:# (x)\n\n```\nhello\n```\n' "error: testmark: in.md:1: $DANGLING" parse_case ann-then-ann '[testmark]:# (x)\n[testmark]:# (y)\n```\nhello\n```\n' "error: testmark: in.md:1: $DANGLING" parse_case ann-then-tilde-fence '[testmark]:# (x)\n~~~\nhello\n~~~\n' "error: testmark: in.md:1: $DANGLING" parse_case ann-then-table-diverges '[testmark]:# (x)\n| a | b |\n|---|---|\n| 1 | 2 |\n' "error: testmark: in.md:1: $DANGLING" # --- fences --- parse_case fence-four-backticks '[testmark]:# (x)\n````\nhello\n````\n' 'hunk "x"' 'info "`"' 'body "hello\n"' parse_case fence-four-backticks-nested '[testmark]:# (x)\n````md\n```inner\n````\n' 'error: testmark: in.md:4: code block not closed' parse_case fence-close-trailing-text '[testmark]:# (x)\n```\nhello\n``` trailing\nafter\n' 'hunk "x"' 'info ""' 'body "hello\n"' parse_case fence-indented-close '[testmark]:# (x)\n```\nhello\n ```\nstill body\n```\n' 'hunk "x"' 'info ""' 'body "hello\n ```\nstill body\n"' parse_case fence-midline-backticks '[testmark]:# (x)\n```\nhello ```\n```\n' 'hunk "x"' 'info ""' 'body "hello ```\n"' parse_case fence-close-cr-only-eof '[testmark]:# (x)\n```\nhello\n```\r' 'hunk "x"' 'info ""' 'body "hello\n"' parse_case fence-plain-unclosed-eof '[testmark]:# (x)\n```\nhello\n```\n\n```\nunclosed plain block\n' 'error: testmark: in.md:6: code block not closed' parse_case fence-hunk-unclosed-eof 'prose\n[testmark]:# (x)\n```\nhello\n' 'error: testmark: in.md:3: code block not closed' parse_case struct-adjacent-hunks '[testmark]:# (a)\n```\none\n```\n[testmark]:# (b)\n```\ntwo\n```\n' \ 'hunk "a"' 'info ""' 'body "one\n"' 'hunk "b"' 'info ""' 'body "two\n"' # --- info strings: after the first three backticks, trimmed of ASCII whitespace --- parse_case info-spaces '[testmark]:# (x)\n``` json \nhello\n```\n' 'hunk "x"' 'info "json"' 'body "hello\n"' parse_case info-tab '[testmark]:# (x)\n```\tjson\nhello\n```\n' 'hunk "x"' 'info "json"' 'body "hello\n"' parse_case info-words '[testmark]:# (x)\n```text with spaces\nhello\n```\n' 'hunk "x"' 'info "text with spaces"' 'body "hello\n"' parse_case info-lone-cr-mid '[testmark]:# (x)\n```a\rb\nhello\n```\n' 'hunk "x"' 'info "a\rb"' 'body "hello\n"' parse_case info-trailing-nbsp-diverges '[testmark]:# (x)\n```json\xc2\xa0\nhello\n```\n' 'hunk "x"' 'info "json\xc2\xa0"' 'body "hello\n"' # --- bodies --- parse_case body-empty '[testmark]:# (x)\n```\n```\n' 'hunk "x"' 'info ""' 'body ""' parse_case body-one-blank-diverges '[testmark]:# (x)\n```\n\n```\n' 'hunk "x"' 'info ""' 'body "\n"' parse_case body-two-blank '[testmark]:# (x)\n```\n\n\n```\n' 'hunk "x"' 'info ""' 'body "\n\n"' parse_case body-trailing-spaces '[testmark]:# (x)\n```\na \n\t\n```\n' 'hunk "x"' 'info ""' 'body "a \n\t\n"' parse_case body-contains-annotation '[testmark]:# (x)\n```\n[testmark]:# (y)\n```\n' 'hunk "x"' 'info ""' 'body "[testmark]:# (y)\n"' parse_case body-lone-cr '[testmark]:# (x)\n```\na\rb\n```\n' 'hunk "x"' 'info ""' 'body "a\rb\n"' parse_case body-invalid-utf8-diverges '[testmark]:# (x)\n```\na\xffb\n```\n' 'hunk "x"' 'info ""' 'body "a\xffb\n"' # --- NUL is an error, wherever it is (diverges: bash can't hold it) --- parse_case nul-in-body '[testmark]:# (x)\n```\na\000b\n```\n' 'error: testmark: in.md:3: contains a NUL byte' parse_case nul-in-prose-first 'a\000b\n[testmark]:# (x\n' 'error: testmark: in.md:1: contains a NUL byte' parse_case nul-last-line 'prose\na\000b\n' 'error: testmark: in.md:2: contains a NUL byte' parse_case nul-last-line-no-nl 'prose\na\000b' 'error: testmark: in.md:2: contains a NUL byte' parse_case nul-alone '\000' 'error: testmark: in.md:1: contains a NUL byte' # --- line endings and file edges: every CRLF becomes LF first --- parse_case eol-crlf-whole-doc 'prose\r\n\r\n[testmark]:# (x)\r\n```\r\na\r\nb\r\n```\r\n' 'hunk "x"' 'info ""' 'body "a\nb\n"' parse_case eol-mixed-body '[testmark]:# (x)\n```\na\r\nb\nc\r\n```\n' 'hunk "x"' 'info ""' 'body "a\nb\nc\n"' parse_case info-crlf '[testmark]:# (x)\r\n```json\r\nhello\r\n```\r\n' 'hunk "x"' 'info "json"' 'body "hello\n"' parse_case body-cr-cr-lf '[testmark]:# (x)\n```\na\r\r\n```\n' 'hunk "x"' 'info ""' 'body "a\r\n"' parse_case eol-ann-cr-cr-lf '[testmark]:# (x)\r\r\n```\nhello\n```\n' "error: testmark: in.md:1: $MAL" parse_case eol-lone-cr-as-newline '[testmark]:# (x)\r```\rhello\r```\r' "error: testmark: in.md:1: $MAL" parse_case eol-bom-first-line '\xef\xbb\xbf[testmark]:# (x)\n```\nhello\n```\n' parse_case eol-no-final-newline '[testmark]:# (x)\n```\nhello\n```' 'hunk "x"' 'info ""' 'body "hello\n"' parse_case eol-empty-file '' parse_case eol-only-newline '\n' exit "$RESULT"
tests/test-parse.sh · 7.8 KiB · f110c869d0