Editor test suite ships in tst/ (from dev 4e8c3db2882d)

This commit is contained in:
2026-07-27 01:59:29 +02:00
parent 01b1cc959a
commit 73ed7f3d5d
31 changed files with 419 additions and 2 deletions

View File

@@ -39,7 +39,7 @@ are regenerated on each release — patches cannot be merged directly.
Report problems (or send patches) to the author; accepted changes are
applied to the development tree and appear in a following snapshot.
This snapshot was assembled from development commit `6d767149c26c`.
This snapshot was assembled from development commit `4e8c3db2882d`.
## License

View File

@@ -1,10 +1,12 @@
# Klammertext distribution test suite (subset).
#
# Runs the four shell regression suites:
# Runs the five shell regression suites:
# cond_test.sh — @cond argument delimitation
# deftype_test.sh — the four klammer definition modes + redefinition table
# escape_test.sh — target character escaping and quoted specials
# filename_test.sh — filenames with spaces (quoting, " / " lists, rescue)
# editor_test.sh — editor support (doc/edit): indentation and table
# alignment; needs python3, uses Emacs when installed
#
# Requires KLAMMERTEXT_HOME set and `ktext` on PATH (build it with `make -C com`).
@@ -14,3 +16,4 @@ test:
./deftype_test.sh
./escape_test.sh
./filename_test.sh
./editor_test.sh

View File

@@ -0,0 +1,5 @@
@table
123456789012345678901234567890 | x ||
abcdefghijklmnopqrstuvwxyzabcde | y ||
short | z ||
@

View File

@@ -0,0 +1,5 @@
@table
123456789012345678901234567890 | x ||
abcdefghijklmnopqrstuvwxyzabcde | y ||
short | z ||
@

View File

@@ -0,0 +1,4 @@
@table
one | two | three ||
spanning | wide ||
@

View File

@@ -0,0 +1,4 @@
@table
one | two | three ||
spanning | wide ||
@

View File

@@ -0,0 +1,4 @@
@table
a | | b ||
cc | dd | ee ||
@

View File

@@ -0,0 +1,4 @@
@table
a | | b ||
cc | dd | ee ||
@

View File

@@ -0,0 +1,4 @@
@table
a ^| b | c ||
dd | ee ||
@

View File

@@ -0,0 +1,4 @@
@table
a ^| b | c ||
dd | ee ||
@

12
tst/editor/align_mixed.kt Normal file
View File

@@ -0,0 +1,12 @@
Some text before.
@table :caption Test
First item | Second | A third item that's longer ||
Row 2 | Text | Not as long ||
:column_width f f f
A cell that is way way too long to align nicely here | x | y ||
Multi | line cell
continues here | z ||
@b bold @ | @frac 1 | 2 @ ||
Last | row | here
@
After.

View File

@@ -0,0 +1,12 @@
Some text before.
@table :caption Test
First item | Second | A third item that's longer ||
Row 2 | Text | Not as long ||
:column_width f f f
A cell that is way way too long to align nicely here | x | y ||
Multi | line cell
continues here | z ||
@b bold @ | @frac 1 | 2 @ ||
Last | row | here
@
After.

View File

@@ -0,0 +1,4 @@
@table
xxxxxxxxxxxxxxxxxxxxxxxxxxxx | xxxxxxxxxxxxxxxxxxxxxxxxxxxx | xxxxxxxxxxxxxxxxxxxxxxxxxxxx | xxxxxxxxxxxxxxxxxxxxxxxxxxxx ||
xxxxxxxxxxxxxxxxxxxxxxxxxxxx | xxxxxxxxxxxxxxxxxxxxxxxxxxxx | xxxxxxxxxxxxxxxxxxxxxxxxxxxx | xxxxxxxxxxxxxxxxxxxxxxxxxxxx ||
@

View File

@@ -0,0 +1,4 @@
@table
xxxxxxxxxxxxxxxxxxxxxxxxxxxx | xxxxxxxxxxxxxxxxxxxxxxxxxxxx | xxxxxxxxxxxxxxxxxxxxxxxxxxxx | xxxxxxxxxxxxxxxxxxxxxxxxxxxx ||
xxxxxxxxxxxxxxxxxxxxxxxxxxxx | xxxxxxxxxxxxxxxxxxxxxxxxxxxx | xxxxxxxxxxxxxxxxxxxxxxxxxxxx | xxxxxxxxxxxxxxxxxxxxxxxxxxxx ||
@

View File

@@ -0,0 +1,30 @@
;;; editor_driver.el --- drive the Emacs editor units over fixtures -*- lexical-binding: t; -*-
;; Usage:
;; emacs --batch -L EMACSDIR -l klammertext-mode -l klammertext-indent
;; -l klammertext-align -l editor_driver.el MODE INFILE OUTFILE ...
;;
;; MODE is "indent" or "align"; each triple applies that tool to INFILE and
;; writes the result to OUTFILE. Called by editor_test.sh.
(let ((args command-line-args-left))
(setq command-line-args-left nil) ; keep batch Emacs from visiting them
(while args
(let ((mode (pop args))
(in (pop args))
(out (pop args)))
(with-temp-buffer
(insert-file-contents in)
(klammertext-mode)
(cond
((string= mode "indent")
(indent-region (point-min) (point-max)))
((string= mode "align")
(goto-char (point-min))
(when (search-forward "|" nil t)
(goto-char (1- (point))))
(klammertext-align-table))
(t (error "editor_driver.el: unknown mode %s" mode)))
(write-region (point-min) (point-max) out nil 'silent)))))
;;; editor_driver.el ends here

View File

@@ -0,0 +1,63 @@
#!/usr/bin/env python3
"""Drive the Sublime Text editor cores over fixture files, outside Sublime.
Usage: editor_driver.py SUBLIME_DIR (MODE INFILE OUTFILE)...
MODE is `indent` or `align`; each triple applies that tool to INFILE and
writes the result to OUTFILE. The Sublime plugin files import without the
`sublime` module (their try/except guard), so the pure cores run under plain
python3. Also asserts the built-in error path (no enclosing table).
Called by editor_test.sh; exits nonzero on an internal error.
"""
import sys
def apply_indent(KI, s):
bols = [0] + [i + 1 for i, ch in enumerate(s)
if ch == '\n' and i + 1 < len(s)]
out = s
for a, b, new in sorted(KI.reindent_lines(s, bols), reverse=True):
out = out[:a] + new + out[b:]
return out
def apply_align(KA, s):
caret = s.index('|') if '|' in s else 0
span = KA.enclosing_span(s, caret, KA.ALIGN_KLAMMERS)
if span is None:
return s
_name, cs, ce = span
edits, _msg = KA.compute_edits(s[cs:ce])
out = s
for a, b, new in sorted(edits, reverse=True):
out = out[:cs + a] + new + out[cs + b:]
return out
def main():
sublime_dir = sys.argv[1]
sys.path.insert(0, sublime_dir)
import Klammertext_indent as KI
import Klammertext_align as KA
args = sys.argv[2:]
for k in range(0, len(args), 3):
mode, infile, outfile = args[k:k + 3]
with open(infile) as f:
s = f.read()
if mode == 'indent':
out = apply_indent(KI, s)
elif mode == 'align':
out = apply_align(KA, s)
else:
sys.exit("editor_driver.py: unknown mode: " + mode)
with open(outfile, 'w') as f:
f.write(out)
# Error path: no enclosing table klammer.
assert KA.enclosing_span("no table here\n", 3, KA.ALIGN_KLAMMERS) is None
if __name__ == '__main__':
main()

12
tst/editor/indent_defs.kt Normal file
View File

@@ -0,0 +1,12 @@
@@note.k s :label Note :color 1.0,1.0,0.9
:level.int 0
: Rectangular block for a special note @@
@@quote.txt ::
@eval block.block_indent(K) eval@
@@
@@@state Word_max
:desc Maximum words
:value 7
@@@

View File

@@ -0,0 +1,12 @@
@@note.k s :label Note :color 1.0,1.0,0.9
:level.int 0
: Rectangular block for a special note @@
@@quote.txt ::
@eval block.block_indent(K) eval@
@@
@@@state Word_max
:desc Maximum words
:value 7
@@@

View File

@@ -0,0 +1,10 @@
@document :structure plain :text
This is a normal paragraph, at the left margin.
@ol
One
| Two
@
More text after the list.
@

View File

@@ -0,0 +1,10 @@
@document :structure plain :text
This is a normal paragraph, at the left margin.
@ol
One
| Two
@
More text after the list.
@

View File

@@ -0,0 +1,5 @@
@ol
Item one
| ^| a literal bar item
^@ a literal at line
@

View File

@@ -0,0 +1,5 @@
@ol
Item one
| ^| a literal bar item
^@ a literal at line
@

11
tst/editor/indent_list.kt Normal file
View File

@@ -0,0 +1,11 @@
@ol
Item one
| Item two
| Item three
@ol
Embedded item one
| Embedded item two
@
| Item four
| Item five
@

View File

@@ -0,0 +1,11 @@
@ol
Item one
| Item two
| Item three
@ol
Embedded item one
| Embedded item two
@
| Item four
| Item five
@

View File

@@ -0,0 +1,10 @@
@document :structure plain :text
Text at the margin.
@quote
Quoted text.
quote@
document@
@@mydef arg :
Body of the definition.
mydef@@

View File

@@ -0,0 +1,10 @@
@document :structure plain :text
Text at the margin.
@quote
Quoted text.
quote@
document@
@@mydef arg :
Body of the definition.
mydef@@

View File

@@ -0,0 +1,4 @@
@table
First item | Second | A third item that's longer ||
Row 2 | Text | Not as long ||
@

View File

@@ -0,0 +1,4 @@
@table
First item | Second | A third item that's longer ||
Row 2 | Text | Not as long ||
@

View File

@@ -0,0 +1,14 @@
@code
def f(x):
if x:
return 1
code@
@eval
def g(y):
return y + 1
g(2)
@
@@@state Word_max
:desc A description line
:value 7
@@@

View File

@@ -0,0 +1,14 @@
@code
def f(x):
if x:
return 1
code@
@eval
def g(y):
return y + 1
g(2)
@
@@@state Word_max
:desc A description line
:value 7
@@@

123
tst/editor_test.sh Executable file
View File

@@ -0,0 +1,123 @@
#!/bin/bash
#
# editor_test.sh — Regression tests for the Klammertext editor support
# (the Emacs mode's indentation/alignment units and their Sublime Text ports).
#
# Why this lives in tst/: the editor code implements the LANGUAGE's structural
# layer — @-run length, bar-run dimension, ^-escapes, # removal, literal
# spans, nesting depth — independently of both the SKS and the Klammermachine.
# tst/ is the SKS-independent tier, and these suites check that the
# independent implementations of Klammertext's structure agree with the
# canonical formatting conventions AND with each other. The klammer names
# that appear in the fixtures (@ol, @table, @document, @code) are seeded
# configuration of the editor tools, not SKS dependencies; nothing here runs
# ktext or loads a klammer set.
#
# What is checked, for every fixture pair <name>.kt / <name>_expected.kt in
# tst/editor/:
# * the Sublime Python core's output equals the expected file
# * idempotence: the tool applied to the expected file leaves it unchanged
# * when Emacs is installed: the Emacs unit's output equals the expected
# file, and equals the Python output byte for byte (the sync check across
# the SYNC-noted policy lists) — skipped gracefully otherwise
#
# The editor code is located automatically: doc/emacs + doc/sublime in the
# development tree, doc/edit/emacs + doc/edit/sublime in the distribution.
#
# Usage: ./editor_test.sh Requires python3; Emacs is optional.
# Exit code: 0 if all tests pass, 1 otherwise.
PASS=0
FAIL=0
red=$'\033[31m'
green=$'\033[32m'
bold=$'\033[1m'
reset=$'\033[0m'
HERE="$(cd "$(dirname "$0")" && pwd)"
ROOT="$(cd "$HERE/.." && pwd)"
if [ -d "$ROOT/doc/emacs" ]; then
EMACS_DIR="$ROOT/doc/emacs"
SUBLIME_DIR="$ROOT/doc/sublime"
elif [ -d "$ROOT/doc/edit/emacs" ]; then
EMACS_DIR="$ROOT/doc/edit/emacs"
SUBLIME_DIR="$ROOT/doc/edit/sublime"
else
echo "editor_test.sh: cannot locate the editor support (doc/emacs or doc/edit/emacs)" >&2
exit 1
fi
FIX="$HERE/editor"
OUT="$(mktemp -d)"
trap 'rm -rf "$OUT"' EXIT
INDENT_FIXTURES="indent_list indent_document indent_table indent_untouched
indent_defs indent_escapes indent_named_close"
ALIGN_FIXTURES="align_mixed align_empty_cells align_boundary align_colspan
align_escapes align_too_wide"
# Build the (MODE INFILE OUTFILE) triples: each fixture is run from its input
# and from its expected file (the idempotence check).
py_args=()
el_args=()
add_fixture() { # add_fixture MODE NAME
local mode="$1" name="$2"
py_args+=("$mode" "$FIX/$name.kt" "$OUT/$name.py.out")
py_args+=("$mode" "$FIX/${name}_expected.kt" "$OUT/$name.py.idem")
el_args+=("$mode" "$FIX/$name.kt" "$OUT/$name.el.out")
el_args+=("$mode" "$FIX/${name}_expected.kt" "$OUT/$name.el.idem")
}
for f in $INDENT_FIXTURES; do add_fixture indent "$f"; done
for f in $ALIGN_FIXTURES; do add_fixture align "$f"; done
# --- run the Sublime Python cores (required) -------------------------------
if ! command -v python3 >/dev/null 2>&1; then
echo "editor_test.sh: python3 not found" >&2
exit 1
fi
if ! PYTHONDONTWRITEBYTECODE=1 python3 "$FIX/editor_driver.py" "$SUBLIME_DIR" "${py_args[@]}"; then
echo "editor_test.sh: the Python driver failed" >&2
exit 1
fi
# --- run the Emacs units (optional) ----------------------------------------
HAVE_EMACS=0
if command -v emacs >/dev/null 2>&1; then
if emacs --batch -L "$EMACS_DIR" \
-l klammertext-mode -l klammertext-indent -l klammertext-align \
-l "$FIX/editor_driver.el" "${el_args[@]}" 2>"$OUT/emacs.log"; then
HAVE_EMACS=1
else
echo "${red}FAIL${reset} the Emacs driver failed:" >&2
cat "$OUT/emacs.log" >&2
FAIL=$((FAIL + 1))
fi
else
echo "(Emacs not installed — the Emacs half is skipped; the Python cores still run)"
fi
# --- compare ---------------------------------------------------------------
check() { # check NAME FILE_A FILE_B
if diff -q "$2" "$3" >/dev/null 2>&1; then
echo "${green}PASS${reset} $1"
PASS=$((PASS + 1))
else
echo "${red}FAIL${reset} $1"
diff "$2" "$3" | head -10
FAIL=$((FAIL + 1))
fi
}
for f in $INDENT_FIXTURES $ALIGN_FIXTURES; do
check "$f (python)" "$OUT/$f.py.out" "$FIX/${f}_expected.kt"
check "$f (python idempotent)" "$OUT/$f.py.idem" "$FIX/${f}_expected.kt"
if [ "$HAVE_EMACS" = 1 ]; then
check "$f (emacs)" "$OUT/$f.el.out" "$FIX/${f}_expected.kt"
check "$f (emacs idempotent)" "$OUT/$f.el.idem" "$FIX/${f}_expected.kt"
check "$f (emacs == python)" "$OUT/$f.el.out" "$OUT/$f.py.out"
fi
done
echo
echo "${bold}editor_test: $PASS passed, $FAIL failed${reset}"
[ "$FAIL" -eq 0 ]