Target coverage: a klammer states the targets it serves
kdesc gains --coverage, which reports for every klammer the set of targets it can render to, and — the point of it — which klammers' coverage cannot be derived and must therefore be declared. Three rules: coverage is DERIVED where the definitions determine it (a general body of klammer calls covers the intersection of what those klammers cover, by a greatest fixpoint after loading), DECLARED where the engine cannot interpret what decides it (an @eval body, whose targets are undecidable), and UNKNOWN where nothing is written — which never means "deliberately unavailable". Two new spellings in a definition's name. A comma-separated target list, "@@table.html,tex :: ...", gives one body several targets; it is surface syntax, expanded at registration, and each member goes through the redefinition rules on its own. And "@@date.* :: ..." writes the general target out, asserting that the klammer works for EVERY target including ones not yet defined — a stronger claim than a list of the targets defined today, and the one target declaration that could be mechanically falsified. The Standard Klammer Set was swept accordingly: it now has no general definitions at all, every klammer names the targets it serves, six use ".*", and tex and pdf are at zero undecided. kdesc's flags are reorganised on two rules: a flag reached for often gets a single letter (-k klammers, -t targets, -c characters, -i input), a more specialised topic a multi-letter name (--argtypes, --katoms, --rewrite, --optionsets, --coverage, --klammerset, --font); and -v says how much to show about PROCESSING, never what the RESULT contains — so the katom regex column is "--katoms full" and the coverage detail "--coverage all". NOTE: "-k" now lists klammers (optionally filtered by a name/description search); the katom table moved to "--katoms". Fixes carried along: an option written with no value crashed the command with SIGSEGV instead of reporting the mistake; two required positional arguments never parsed; kdesc and kdiag printed an error and exited 0; and definition diagnostics counted registrations rather than what was written, so one line could be reported as two definitions and then printed twice. Four new test suites: target_list, coverage, command_option, kdesc. (from dev 46f54080bd9a)
This commit is contained in:
@@ -19,4 +19,8 @@ test:
|
||||
./klammerset_test.sh
|
||||
./option_set_test.sh
|
||||
./signature_test.sh
|
||||
./target_list_test.sh
|
||||
./coverage_test.sh
|
||||
./command_option_test.sh
|
||||
./kdesc_test.sh
|
||||
./editor_test.sh
|
||||
|
||||
120
tst/command_option_test.sh
Executable file
120
tst/command_option_test.sh
Executable file
@@ -0,0 +1,120 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# command_option_test.sh — Command-line option parsing (mac/argv.cpp).
|
||||
#
|
||||
# The commands' own argument handling, which no suite covered: argv_test.cpp
|
||||
# exercises Argv but asserts nothing, so two defects lived there unnoticed.
|
||||
#
|
||||
# * An option declared with opt() takes a value. Written LAST with nothing
|
||||
# after it -- "kdesc -v", "ktext doc.kt -t" -- parse_optional() read one
|
||||
# past the end of the word vector and the command died with SIGSEGV,
|
||||
# naming nothing. Two bounds checks sat commented out at that spot; they
|
||||
# would have returned a half-parsed option instead of reporting the
|
||||
# mistake. It is now a located argument error.
|
||||
# * kdesc and kdiag printed an error and then exited 0, so a script could
|
||||
# not tell a failed run from a successful one. Both return 1 now, as
|
||||
# ktext already did.
|
||||
#
|
||||
# Also here: two required positional arguments. parse_positional() consumes
|
||||
# one per required name, but regex_split_prefix() requires its match at
|
||||
# position 0 and returned the remainder with the separating space intact, so
|
||||
# the second was always "not found". Latent, because no shipped command
|
||||
# declares two -- tst/argv_test.cpp is the only program that does.
|
||||
#
|
||||
# Usage: ./command_option_test.sh (needs KLAMMERTEXT_HOME set; commands on PATH)
|
||||
# Exit code: 0 if all tests pass, 1 otherwise.
|
||||
|
||||
PASS=0
|
||||
FAIL=0
|
||||
K=${KLAMMERTEXT_HOME:?KLAMMERTEXT_HOME must be set}
|
||||
TSTDIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
|
||||
red=$'\033[31m'
|
||||
green=$'\033[32m'
|
||||
bold=$'\033[1m'
|
||||
reset=$'\033[0m'
|
||||
|
||||
# A signal death is not an error exit: 128+n, and the point of these tests is
|
||||
# the difference. Report it distinctly so a crash can never read as a pass.
|
||||
died_by_signal() { [ "$1" -gt 128 ]; }
|
||||
|
||||
# error NAME PATTERN CMD... — nonzero exit, no signal, PATTERN in the output.
|
||||
error() {
|
||||
local name="$1" pattern="$2"; shift 2
|
||||
local out status
|
||||
out=$("$@" 2>&1); status=$?
|
||||
if died_by_signal $status; then
|
||||
echo "${red}FAIL${reset} $name — died by signal $((status-128))"; FAIL=$((FAIL+1)); return
|
||||
fi
|
||||
if [ $status -eq 0 ]; then
|
||||
echo "${red}FAIL${reset} $name — reported an error but exited 0"; FAIL=$((FAIL+1)); return
|
||||
fi
|
||||
if printf '%s' "$out" | grep -qF -- "$pattern"; then
|
||||
echo "${green}PASS${reset} $name"; PASS=$((PASS+1))
|
||||
else
|
||||
echo "${red}FAIL${reset} $name — expected [$pattern]"
|
||||
echo " got: $(printf '%s' "$out" | head -2)"; FAIL=$((FAIL+1))
|
||||
fi
|
||||
}
|
||||
|
||||
# ok NAME CMD... — exits 0 and does not die by signal.
|
||||
ok() {
|
||||
local name="$1"; shift
|
||||
local status
|
||||
"$@" >/dev/null 2>&1; status=$?
|
||||
if died_by_signal $status; then
|
||||
echo "${red}FAIL${reset} $name — died by signal $((status-128))"; FAIL=$((FAIL+1))
|
||||
elif [ $status -ne 0 ]; then
|
||||
echo "${red}FAIL${reset} $name — exit $status"; FAIL=$((FAIL+1))
|
||||
else
|
||||
echo "${green}PASS${reset} $name"; PASS=$((PASS+1))
|
||||
fi
|
||||
}
|
||||
|
||||
echo "${bold}Command option tests${reset}"
|
||||
echo "===================="
|
||||
echo
|
||||
|
||||
echo "-- an option written last, with no value --"
|
||||
error " 1. kdesc -v" "needs a value" kdesc -v
|
||||
error " 2. kdesc with a flag first" "needs a value" kdesc --katoms -v
|
||||
error " 3. kdiag -v" "needs a value" kdiag -v
|
||||
error " 4. ktext -t" "needs a value" ktext -s '@i-x' -t
|
||||
error " 5. the message names the option" "-v <level>" kdesc -v
|
||||
error " 6. ... and how to get the usage" "for the list of arguments" kdesc -v
|
||||
|
||||
echo
|
||||
echo "-- the same options WITH a value still work --"
|
||||
# NOT "kdesc -v 1": show_usage() treats exactly "<command> -v <n>" as a
|
||||
# request for the usage text, which exits 1 by design. Give it another flag.
|
||||
ok " 7. kdesc -c -v 1" kdesc -c -v 1
|
||||
ok " 8. kdesc --katoms -v 1" kdesc --katoms -v 1
|
||||
ok " 9. kdiag -v 1 '@i-x'" kdiag -v 1 '@i-x'
|
||||
ok "10. ktext -t html" ktext -s '@i-x' -t html -d
|
||||
|
||||
echo
|
||||
echo "-- an error exit is nonzero, not a printed message and exit 0 --"
|
||||
error "11. kdesc reports failure" "needs a value" kdesc -v
|
||||
error "12. kdiag reports failure" "needs a value" kdiag -v
|
||||
|
||||
echo
|
||||
echo "-- two required positional arguments --"
|
||||
# argv_test is the only program declaring two; it prints what it parsed.
|
||||
if [ -x "$TSTDIR/argv_test" ]; then
|
||||
out=$("$TSTDIR/argv_test" first second --bool 2>&1 | sed 's/\x1b\[[0-9;]*m//g')
|
||||
for expect in "<input> : first" "<input2> : second"; do
|
||||
if printf '%s' "$out" | grep -qF "$expect"; then
|
||||
echo "${green}PASS${reset} 13/14. positional [$expect]"; PASS=$((PASS+1))
|
||||
else
|
||||
echo "${red}FAIL${reset} 13/14. positional [$expect] not parsed"; FAIL=$((FAIL+1))
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo "SKIP 13/14. two positionals (argv_test not built; run make -C tst)"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "===================="
|
||||
echo "Results: ${PASS} passed, ${FAIL} failed"
|
||||
[ "$FAIL" -eq 0 ] || exit 1
|
||||
exit 0
|
||||
29
tst/coverage/basic.k
Normal file
29
tst/coverage/basic.k
Normal file
@@ -0,0 +1,29 @@
|
||||
# Fixture for coverage_test.sh. Engine tier: it declares its own targets,
|
||||
# so the suite runs with no klammer set.
|
||||
@@@target ta | Target A @@@
|
||||
@@@target tb | Target B @@@
|
||||
@@@target tc | Target C @@@
|
||||
|
||||
# Defined per target: coverage is what the definitions say.
|
||||
@@ab.k s : defined for ta and tb @@
|
||||
@@ab.ta :: A*s* @@
|
||||
@@ab.tb :: B*s* @@
|
||||
|
||||
# A comma list is the same statement written once (step 1).
|
||||
@@cd.k s : defined for ta and tc by a list @@
|
||||
@@cd.ta,tc :: C*s* @@
|
||||
|
||||
# General body of plain text: every target.
|
||||
@@plain : just text @@
|
||||
|
||||
# General body calling one klammer: that klammer's coverage.
|
||||
@@calls_ab s : @ab *s* @ @@
|
||||
|
||||
# Intersection of two klammers with different coverage: only ta is in both.
|
||||
@@calls_both s : @ab *s* @ @cd *s* @ @@
|
||||
|
||||
# ".*" is an assertion: every target, including targets not yet defined.
|
||||
# Distinct from a bare definition, which asserts nothing -- that is the
|
||||
# writer's macro form.
|
||||
@@universal.k s : works for any target @@
|
||||
@@universal.* :: U*s* @@
|
||||
8
tst/coverage/clean.k
Normal file
8
tst/coverage/clean.k
Normal file
@@ -0,0 +1,8 @@
|
||||
# A klammerset with nothing outstanding: every klammer declared and defined.
|
||||
# Used to check that a report with no problems says nothing about problems --
|
||||
# no banner, no categories.
|
||||
@@@target ta | Target A @@@
|
||||
@@@target tb | Target B @@@
|
||||
|
||||
@@x.k s : declared and defined @@
|
||||
@@x.ta,tb :: [*s*] @@
|
||||
22
tst/coverage/cycle.k
Normal file
22
tst/coverage/cycle.k
Normal file
@@ -0,0 +1,22 @@
|
||||
@@@target ta | Target A @@@
|
||||
@@@target tb | Target B @@@
|
||||
|
||||
# Mutual reference between two general klammers. A recursive analysis would
|
||||
# not terminate; the greatest fixpoint does.
|
||||
#
|
||||
# These take a parameter deliberately. A general klammer with NO parameters
|
||||
# is a CONSTANT, and a constant's body is spliced into later definitions at
|
||||
# definition time -- so a parameterless pair never reaches the fixpoint as a
|
||||
# cycle: the expansion has already turned the second body into a
|
||||
# self-reference. Parameters keep the calls in the stored body.
|
||||
@@ping s : @pong *s* @ @@
|
||||
@@pong s : @ping *s* @ @@
|
||||
|
||||
# A cycle that reaches a constrained klammer keeps the constraint.
|
||||
@@only_ta.ta s : A*s* @@
|
||||
@@loop_a s : @loop_b *s* @ @only_ta *s* @ @@
|
||||
@@loop_b s : @loop_a *s* @ @@
|
||||
|
||||
# Disjoint coverage: nothing is in both, so this can never be applied.
|
||||
@@only_tb.tb s : B*s* @@
|
||||
@@impossible s : @only_ta *s* @ @only_tb *s* @ @@
|
||||
6
tst/coverage/split.k
Normal file
6
tst/coverage/split.k
Normal file
@@ -0,0 +1,6 @@
|
||||
@@@target ta | Target A @@@
|
||||
@@@target tb | Target B @@@
|
||||
|
||||
@@x.k s : declared here, defined in two files @@
|
||||
@@x.ta :: A*s* @@
|
||||
@read split_more.k @
|
||||
5
tst/coverage/split_more.k
Normal file
5
tst/coverage/split_more.k
Normal file
@@ -0,0 +1,5 @@
|
||||
# The second half of the split fixture: one klammer's targets, written in a
|
||||
# different file from its declaration. Andy's policy keeps a klammer's
|
||||
# targets together, so this is the exception the file column exists to make
|
||||
# visible.
|
||||
@@x.tb :: B*s* @@
|
||||
10
tst/coverage/undecidable.k
Normal file
10
tst/coverage/undecidable.k
Normal file
@@ -0,0 +1,10 @@
|
||||
@@@target ta | Target A @@@
|
||||
@@@target tb | Target B @@@
|
||||
|
||||
# The three bodies whose coverage the engine cannot work out.
|
||||
@@evaluated : @eval 1 + 1 @ @@
|
||||
@@literally : ^'\raw{markup}'^ @@
|
||||
@@included : @read /dev/null @ @@
|
||||
|
||||
# A declaration with no definition anywhere.
|
||||
@@orphan.k s : declared and never defined @@
|
||||
258
tst/coverage_test.sh
Executable file
258
tst/coverage_test.sh
Executable file
@@ -0,0 +1,258 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# coverage_test.sh — Target coverage: which targets a klammer can render to.
|
||||
#
|
||||
# "kdesc --coverage" computes the coverage FACT and reports it. Three rules
|
||||
# (mac/coverage.{h,cpp}, notes/target_coverage.md):
|
||||
#
|
||||
# DERIVED a general body of plain text covers every target; a general body
|
||||
# of klammer calls covers the INTERSECTION of what those klammers
|
||||
# cover, computed as a greatest fixpoint after loading.
|
||||
# DECLARED a general body holding @eval, @read or a ^'...'^ literal span
|
||||
# cannot be interpreted, so its targets must be written down.
|
||||
# UNKNOWN no definition at all. Absence never means "deliberately
|
||||
# unavailable" -- the SKS is incomplete on schedule, not by design.
|
||||
#
|
||||
# The analysis modifies nothing. These tests therefore assert only what is
|
||||
# REPORTED, and a companion case checks that rendering is unaffected.
|
||||
#
|
||||
# Engine tier: the fixtures in tst/coverage/ declare their own targets with
|
||||
# @@@target, so no klammer set is involved.
|
||||
#
|
||||
# Usage: ./coverage_test.sh (needs KLAMMERTEXT_HOME set; kdesc on PATH)
|
||||
# Exit code: 0 if all tests pass, 1 otherwise.
|
||||
|
||||
PASS=0
|
||||
FAIL=0
|
||||
KDESC=kdesc
|
||||
KTEXT=ktext
|
||||
K=${KLAMMERTEXT_HOME:?KLAMMERTEXT_HOME must be set}
|
||||
DIR="$(cd "$(dirname "$0")" && pwd)/coverage"
|
||||
|
||||
red=$'\033[31m'
|
||||
green=$'\033[32m'
|
||||
bold=$'\033[1m'
|
||||
reset=$'\033[0m'
|
||||
|
||||
# Each fixture is analysed once; the tests match against the saved report.
|
||||
declare -A REPORT VREPORT
|
||||
for f in basic undecidable cycle split clean; do
|
||||
REPORT[$f]=$(timeout 30 "$KDESC" -i "$DIR/$f.k" --coverage 2>&1 |
|
||||
sed 's/\x1b\[[0-9;]*m//g')
|
||||
# "all" adds the source-file column AND the empty problem categories.
|
||||
VREPORT[$f]=$(timeout 30 "$KDESC" -i "$DIR/$f.k" --coverage all 2>&1 |
|
||||
sed 's/\x1b\[[0-9;]*m//g')
|
||||
if [ -z "${REPORT[$f]}" ] || [ -z "${VREPORT[$f]}" ]; then
|
||||
echo "${red}FAIL${reset} $f.k produced no report"; FAIL=$((FAIL+1))
|
||||
fi
|
||||
done
|
||||
|
||||
# vlacks NAME FIXTURE REGEX — the VERBOSE report does NOT match REGEX.
|
||||
vlacks() {
|
||||
local name="$1" fixture="$2" rgx="$3"
|
||||
if printf '%s\n' "${VREPORT[$fixture]}" | grep -Eq "$rgx"; then
|
||||
echo "${red}FAIL${reset} $name — unexpected match: $rgx"; FAIL=$((FAIL+1))
|
||||
else
|
||||
echo "${green}PASS${reset} $name"; PASS=$((PASS+1))
|
||||
fi
|
||||
}
|
||||
|
||||
# vhas NAME FIXTURE REGEX — the VERBOSE report matches REGEX.
|
||||
vhas() {
|
||||
local name="$1" fixture="$2" rgx="$3"
|
||||
if printf '%s\n' "${VREPORT[$fixture]}" | grep -Eq "$rgx"; then
|
||||
echo "${green}PASS${reset} $name"; PASS=$((PASS+1))
|
||||
else
|
||||
echo "${red}FAIL${reset} $name"
|
||||
echo " no line matching: $rgx"; FAIL=$((FAIL+1))
|
||||
fi
|
||||
}
|
||||
|
||||
# has NAME FIXTURE REGEX — the report matches REGEX.
|
||||
has() {
|
||||
local name="$1" fixture="$2" rgx="$3"
|
||||
if printf '%s\n' "${REPORT[$fixture]}" | grep -Eq "$rgx"; then
|
||||
echo "${green}PASS${reset} $name"; PASS=$((PASS+1))
|
||||
else
|
||||
echo "${red}FAIL${reset} $name"
|
||||
echo " no line matching: $rgx"; FAIL=$((FAIL+1))
|
||||
fi
|
||||
}
|
||||
|
||||
# section_lacks NAME FIXTURE HEADER REGEX — REGEX does not appear within the
|
||||
# named section. Needed wherever the same klammer legitimately appears in a
|
||||
# LATER section: a whole-report "lacks" would match there and report a failure
|
||||
# that is not one. A section runs from its header to the next blank line.
|
||||
section_lacks() {
|
||||
local name="$1" fixture="$2" header="$3" rgx="$4"
|
||||
local body
|
||||
body=$(printf '%s\n' "${REPORT[$fixture]}" |
|
||||
awk -v h="$header" 'index($0, h) == 1 { f = 1; next } f && /^$/ { exit } f')
|
||||
if [ -z "$body" ]; then
|
||||
echo "${red}FAIL${reset} $name — section [$header] not found"; FAIL=$((FAIL+1)); return
|
||||
fi
|
||||
if printf '%s\n' "$body" | grep -Eq "$rgx"; then
|
||||
echo "${red}FAIL${reset} $name — unexpected match in [$header]: $rgx"; FAIL=$((FAIL+1))
|
||||
else
|
||||
echo "${green}PASS${reset} $name"; PASS=$((PASS+1))
|
||||
fi
|
||||
}
|
||||
|
||||
# lacks NAME FIXTURE REGEX — the report does NOT match REGEX.
|
||||
lacks() {
|
||||
local name="$1" fixture="$2" rgx="$3"
|
||||
if printf '%s\n' "${REPORT[$fixture]}" | grep -Eq "$rgx"; then
|
||||
echo "${red}FAIL${reset} $name — unexpected match: $rgx"; FAIL=$((FAIL+1))
|
||||
else
|
||||
echo "${green}PASS${reset} $name"; PASS=$((PASS+1))
|
||||
fi
|
||||
}
|
||||
|
||||
echo "${bold}Target coverage tests${reset}"
|
||||
echo "====================="
|
||||
echo
|
||||
|
||||
echo "-- derived coverage --"
|
||||
has " 1. a general text body covers every target" basic '^All targets, derived \(1\)'
|
||||
has " 2. ... and that body is @plain" basic '@plain'
|
||||
has " 3. a body calling one klammer takes its targets" basic '@calls_ab +ta tb +from @ab'
|
||||
has " 4. two klammers intersect" basic '@calls_both +ta +from @ab @cd'
|
||||
lacks " 5. the intersection drops the target only one has" basic '@calls_both +ta tb'
|
||||
|
||||
echo
|
||||
echo "-- what the definitions themselves say --"
|
||||
has " 6. per-target definitions are not derived" basic 'Defined per target \(2\)'
|
||||
has " 7. a comma list covers each target it names" basic '^ ta +6 covered'
|
||||
has " 8. a target named by no definition is undecided" basic '^ tc +3 covered +3 undecided'
|
||||
|
||||
echo
|
||||
echo "-- coverage that cannot be derived --"
|
||||
has " 9. an @eval general body must be declared" undecidable '@evaluated +@eval body'
|
||||
has "10. a @read general body must be declared" undecidable '@included +@read body'
|
||||
has "11. a literal span must be declared" undecidable "@literally +\\^'\\.\\.\\.'\\^ literal span"
|
||||
has "12. all three are counted together" undecidable '^Must be declared \(3\)'
|
||||
has "13. the report says they are offered everywhere" undecidable 'currently offered by an underivable general body'
|
||||
|
||||
echo
|
||||
echo "-- declared and never defined --"
|
||||
has "14. a .k with no definitions is reported" undecidable '^Declared but never defined \(1\)'
|
||||
has "15. ... and named" undecidable '^ @orphan$'
|
||||
|
||||
echo
|
||||
echo "-- \".*\": every target, asserted --"
|
||||
# The distinction the report exists to keep: a bare definition asserts
|
||||
# nothing (the writer's macro form), while ".*" states that the klammer works
|
||||
# for any target, including targets that do not exist yet. A list of the
|
||||
# targets defined today cannot say that.
|
||||
has "37. an explicit .* is its own category" basic '^All targets, declared \(1\)'
|
||||
has "38. ... and covers every target" basic '^ @universal +ta tb tc'
|
||||
lacks "39. it is not counted as derived" basic '^ @universal +ta tb tc$(.*)from'
|
||||
section_lacks "40. nor as a bare general body" basic 'All targets, derived' '@universal'
|
||||
# ".*" outranks the body scan: the point of writing it is to assert what an
|
||||
# @eval body cannot be read to mean.
|
||||
has "41. an @eval body under .* is not flagged" basic '^All targets, declared'
|
||||
|
||||
echo
|
||||
echo "-- klammers with no \".k\" declaration --"
|
||||
# The mirror image of the section above: that one has a declaration and no
|
||||
# definitions, this one definitions and no declaration. Such a klammer works
|
||||
# but has no DESCRIPTION, so kdesc can say nothing about it and "-k <text>"
|
||||
# can only find it by name. Orthogonal to the coverage kinds -- a klammer can
|
||||
# be defined for every target and still have none.
|
||||
has "32. the section counts them" basic '^No "\.k" declaration \(3\)'
|
||||
has "33. a general klammer has none" basic '^ @plain +ta tb tc$'
|
||||
has "34. ... nor one defined without .k" basic '^ @calls_ab +ta tb$'
|
||||
section_lacks "35. a declared klammer is not listed" basic 'No ".k" declaration' '^ @ab '
|
||||
# @orphan has a .k and no definitions, so it belongs to the OTHER section.
|
||||
lacks "36. the two sections do not overlap" undecidable '^No "\.k" declaration \(1\)\n @orphan'
|
||||
|
||||
echo
|
||||
echo "-- the fixpoint --"
|
||||
# A recursive analysis would not terminate on these; the report existing at
|
||||
# all is most of the assertion.
|
||||
has "16. mutual reference terminates" cycle '@ping +ta tb +from @pong'
|
||||
has "17. ... in both directions" cycle '@pong +ta tb +from @ping'
|
||||
has "18. a constraint propagates around a cycle" cycle '@loop_a +ta +from @loop_b @only_ta'
|
||||
has "19. ... to the klammer that does not name it" cycle '@loop_b +ta +from @loop_a'
|
||||
has "20. disjoint coverage is its own category" cycle '^Covers no target \(1\)'
|
||||
has "21. ... naming the klammers to look at" cycle '^ @impossible +from @only_ta @only_tb'
|
||||
section_lacks "42. and it is not listed as derived" cycle 'Derived from the klammers' '@impossible'
|
||||
|
||||
echo
|
||||
echo "-- the source-file column (-v only) --"
|
||||
# The file is the LAST column -- anchored, so a change of position is caught.
|
||||
vhas "23. a klammer's file is shown, last" undecidable '@evaluated +@eval body +tst/coverage/undecidable\.k$'
|
||||
vhas "24. ... in the derived section too" basic '@calls_ab +ta tb +from @ab +tst/coverage/basic\.k$'
|
||||
vhas "25. ... and in the per-target listing" basic '@ab +ta tb +tst/coverage/basic\.k$'
|
||||
vhas "26. a declaration with no definition" undecidable '^ @orphan +tst/coverage/undecidable\.k$'
|
||||
lacks "27. no file column without -v" basic '@calls_ab +tst/coverage'
|
||||
# Andy's policy keeps a klammer's targets together, so several files is the
|
||||
# exception -- which is the case the column exists to make visible.
|
||||
vhas "28. definitions in two files, comma-separated" split \
|
||||
'@x +ta tb +tst/coverage/split\.k, tst/coverage/split_more\.k$'
|
||||
# The "All targets" section lists many names on one line, so there is nothing
|
||||
# for a file to attach to; it must not sprout a column.
|
||||
vhas "29. the many-names row keeps its shape" basic '^ @plain$'
|
||||
# A continuation line belongs to the row above it and takes no file.
|
||||
lacks "30. the old continuation line is gone" cycle '^ +\^ covers no target'
|
||||
|
||||
echo
|
||||
echo "-- the problems come last, and are grouped --"
|
||||
# A terminal is read from the bottom: an 80-klammer listing scrolls a
|
||||
# three-line warning off the screen, so the actionable part must be last.
|
||||
has "43. a banner counts the distinct klammers" basic '^Needs attention: [0-9]+ klammers?$'
|
||||
# An empty category is hidden, but the two halves hide for different reasons.
|
||||
#
|
||||
# A REPORTING category describes the shape of the klammer set, so an empty one
|
||||
# still says something and "all" shows it as a designer's checklist.
|
||||
lacks "44. an empty reporting category is hidden" cycle '^All targets, declared \(0\)'
|
||||
vhas "45. ... and shown by \"all\"" cycle '^All targets, declared \(0\)'
|
||||
# A PROBLEM category sits under a banner reading "Needs attention", and an
|
||||
# empty one does not. Printing it there would state the opposite of the
|
||||
# heading above it, so it stays hidden even under "all".
|
||||
lacks "47. an empty problem category is hidden" basic '^Covers no target \(0\)'
|
||||
vlacks "48. ... and stays hidden under \"all\"" basic '^Covers no target \(0\)'
|
||||
# A category WITH entries is always shown, with or without "all".
|
||||
has "49. a non-empty category needs no \"all\"" cycle '^Derived from the klammers the body calls \(4\)'
|
||||
# The banner still counts, so "all" is not silent about the problems.
|
||||
vhas "50. the banner survives under \"all\"" basic '^Needs attention: 3 klammers$'
|
||||
# "No .k" is orthogonal to the others, so summing the counts would overstate.
|
||||
has "46. the banner counts distinct klammers" basic '^Needs attention: 3 klammers$'
|
||||
|
||||
echo
|
||||
echo "-- the category explanations --"
|
||||
# The prose under a heading teaches the categories; a reader who knows them
|
||||
# wants headings and rows. So it appears only under "all", with the empty
|
||||
# categories and the file column.
|
||||
lacks "51. no explanation by default" basic 'the author states that these work'
|
||||
vhas "52. ... and one under \"all\"" basic 'the author states that these work'
|
||||
has "53. the heading is always there" basic '^All targets, declared \(1\)'
|
||||
|
||||
echo
|
||||
echo "-- a klammer set with nothing outstanding --"
|
||||
# Nothing to attend to, nothing said: no banner and no problem categories,
|
||||
# with or without "all". A banner reading "0" would contradict itself in the
|
||||
# same way an empty category under it would.
|
||||
lacks "54. no banner when nothing needs attention" clean '^Needs attention'
|
||||
vlacks "55. ... not even under \"all\"" clean '^Needs attention'
|
||||
vlacks "56. ... and no problem categories" clean '^No "\.k" declaration'
|
||||
# The reporting categories still describe the set.
|
||||
vhas "57. the reporting categories remain" clean '^Defined per target \(1\)'
|
||||
|
||||
echo
|
||||
echo "-- the analysis changes nothing --"
|
||||
# Coverage is a report, not a policy: a klammer whose general body cannot be
|
||||
# interpreted is still offered to every target, exactly as before.
|
||||
out=$("$KTEXT" -k none -s '@@@target ta | Target A @@@
|
||||
@@e : @eval 6 * 7 @ @@ x @e@' -t ta -d 2>&1 | tr -d '\n ')
|
||||
if [ "$out" = "x42" ]; then
|
||||
echo "${green}PASS${reset} 22. an underivable klammer still renders"; PASS=$((PASS+1))
|
||||
else
|
||||
echo "${red}FAIL${reset} 22. an underivable klammer still renders — got [$out]"; FAIL=$((FAIL+1))
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "====================="
|
||||
echo "Results: ${PASS} passed, ${FAIL} failed"
|
||||
[ "$FAIL" -eq 0 ] || exit 1
|
||||
exit 0
|
||||
158
tst/kdesc_test.sh
Executable file
158
tst/kdesc_test.sh
Executable file
@@ -0,0 +1,158 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# kdesc_test.sh — the kdesc command's interface.
|
||||
#
|
||||
# The flag structure was reorganised 2026-08-11
|
||||
# (notes/modifying_the_kdesc_arguments.md) on two rules:
|
||||
#
|
||||
# * a flag a user reaches for often gets a single letter (-k klammers,
|
||||
# -t targets, -c character codes, -i input); a more specialised topic gets
|
||||
# a multi-letter name (--argtypes, --katoms, --rewrite, --optionsets,
|
||||
# --coverage, --klammerset, --font);
|
||||
# * -v says how much to show about the command's PROCESSING and never what
|
||||
# its RESULT contains. So the katom regex column is "--katoms full" and
|
||||
# the coverage file column is "--coverage all", not verbosity levels.
|
||||
#
|
||||
# "-k" is the one rename that changes a name's MEANING rather than retiring
|
||||
# it: it used to show katom types (now --katoms). A name-based guard cannot
|
||||
# catch that -- the old spelling still works and does something else -- so
|
||||
# these tests pin the new meaning down.
|
||||
#
|
||||
# Usage: ./kdesc_test.sh (needs KLAMMERTEXT_HOME set; kdesc on PATH)
|
||||
# Exit code: 0 if all tests pass, 1 otherwise.
|
||||
|
||||
PASS=0
|
||||
FAIL=0
|
||||
KDESC=kdesc
|
||||
K=${KLAMMERTEXT_HOME:?KLAMMERTEXT_HOME must be set}
|
||||
DIR="$(cd "$(dirname "$0")" && pwd)/coverage"
|
||||
|
||||
red=$'\033[31m'
|
||||
green=$'\033[32m'
|
||||
bold=$'\033[1m'
|
||||
reset=$'\033[0m'
|
||||
|
||||
plain() { sed 's/\x1b\[[0-9;]*m//g'; }
|
||||
|
||||
# shows NAME PATTERN CMD... — exits 0 and the output matches PATTERN.
|
||||
shows() {
|
||||
local name="$1" pattern="$2"; shift 2
|
||||
local out status
|
||||
out=$(timeout 60 "$@" 2>&1 | plain); status=$?
|
||||
if [ $status -gt 128 ]; then
|
||||
echo "${red}FAIL${reset} $name — died by signal $((status-128))"; FAIL=$((FAIL+1)); return
|
||||
fi
|
||||
if printf '%s' "$out" | grep -Eq -- "$pattern"; then
|
||||
echo "${green}PASS${reset} $name"; PASS=$((PASS+1))
|
||||
else
|
||||
echo "${red}FAIL${reset} $name — no line matching [$pattern]"
|
||||
echo " got: $(printf '%s' "$out" | head -2)"; FAIL=$((FAIL+1))
|
||||
fi
|
||||
}
|
||||
|
||||
# absent NAME PATTERN CMD... — the output does NOT match PATTERN.
|
||||
absent() {
|
||||
local name="$1" pattern="$2"; shift 2
|
||||
if timeout 60 "$@" 2>&1 | plain | grep -Eq -- "$pattern"; then
|
||||
echo "${red}FAIL${reset} $name — unexpected match [$pattern]"; FAIL=$((FAIL+1))
|
||||
else
|
||||
echo "${green}PASS${reset} $name"; PASS=$((PASS+1))
|
||||
fi
|
||||
}
|
||||
|
||||
echo "${bold}kdesc interface tests${reset}"
|
||||
echo "====================="
|
||||
echo
|
||||
|
||||
echo "-- the single-letter flags --"
|
||||
shows " 1. -k lists klammers" '@table' "$KDESC" -k
|
||||
shows " 2. -t lists targets" 'html' "$KDESC" -t
|
||||
shows " 3. -c lists character codes" '.' "$KDESC" -c
|
||||
shows " 4. -i reads the named input" '@x' "$KDESC" -i "$DIR/split.k" -k
|
||||
|
||||
echo
|
||||
echo "-- the multi-letter topics --"
|
||||
shows " 5. --katoms lists katom types" 'katom' "$KDESC" --katoms
|
||||
shows " 6. --argtypes lists argtypes" 'fraction' "$KDESC" --argtypes
|
||||
shows " 7. --rewrite lists rewrites" '.' "$KDESC" --rewrite
|
||||
shows " 8. --optionsets lists sets" 'caption_args' "$KDESC" --optionsets
|
||||
|
||||
echo
|
||||
echo "-- result detail is a word, not a verbosity level --"
|
||||
shows " 9. --katoms full adds the regex column" 'Regex' "$KDESC" --katoms full
|
||||
absent "10. --katoms alone omits it" 'Regex' "$KDESC" --katoms
|
||||
shows "11. --coverage all adds the file column" 'sks/table/table\.k' "$KDESC" --coverage all
|
||||
absent "12. --coverage alone omits it" 'sks/table/table\.k' "$KDESC" --coverage
|
||||
# "all" is one word for both kinds of missing information: if you want some of
|
||||
# it you probably want all of it. It shows an empty REPORTING category -- a
|
||||
# designer's checklist of the shapes a klammer set can have -- but not an
|
||||
# empty PROBLEM one, which would state the opposite of the "Needs attention"
|
||||
# banner above it.
|
||||
# Against a FIXTURE, not the SKS: which categories are empty there changes as
|
||||
# the klammer set is worked on, and a test pinned to that state fails for a
|
||||
# reason that has nothing to do with what it is testing. (It did: adopting
|
||||
# ".*" in the SKS made "All targets, declared" non-empty.)
|
||||
shows "12a. --coverage all shows an empty reporting category" \
|
||||
'All targets, declared \(0\)' "$KDESC" -i "$DIR/cycle.k" --coverage all
|
||||
absent "12b. ... hidden without it" \
|
||||
'All targets, declared \(0\)' "$KDESC" -i "$DIR/cycle.k" --coverage
|
||||
absent "12c. an empty problem category stays hidden" \
|
||||
'Declared but never defined \(0\)' "$KDESC" -i "$DIR/cycle.k" --coverage all
|
||||
absent "13. -v adds no result detail" 'Regex' "$KDESC" --katoms -v 3
|
||||
|
||||
echo
|
||||
echo "-- -v is about processing only --"
|
||||
shows "14. -v names the input it read" 'input_filenames' "$KDESC" -t -v 1
|
||||
absent "15. ... and is silent without it" 'input_filenames' "$KDESC" -t
|
||||
|
||||
echo
|
||||
echo "-- the klammer search --"
|
||||
shows "16. a name matches" '@table' "$KDESC" -k table
|
||||
absent "17. ... and others are excluded" '@document' "$KDESC" -k table
|
||||
shows "18. matching is case-insensitive" '@table' "$KDESC" -k TABLE
|
||||
shows "19. a description word matches" '@' "$KDESC" -k verbatim
|
||||
# The words after -k are one phrase, and whitespace is collapsed on both
|
||||
# sides -- a description written across several lines in a .k file must still
|
||||
# match a phrase typed on one.
|
||||
shows "20. several words are one phrase" '@' "$KDESC" -k displayed verbatim
|
||||
shows "21. ... with runs of space collapsed" '@' "$KDESC" -k displayed verbatim
|
||||
shows "22. no match says so" 'No klammer names or descriptions contained "zzqq"\.' "$KDESC" -k zzqq
|
||||
absent "23. ... and lists nothing" '^ @' "$KDESC" -k zzqq
|
||||
|
||||
echo
|
||||
echo "-- the subcommand words --"
|
||||
shows "24. --coverage help explains" 'Coverage commands' "$KDESC" --coverage help
|
||||
shows "25. --katoms help explains" 'Katom commands' "$KDESC" --katoms help
|
||||
shows "26. an unknown coverage word" 'Unrecognized coverage command' "$KDESC" --coverage nonsense
|
||||
shows "27. an unknown katom word" 'Unrecognized katom command' "$KDESC" --katoms nonsense
|
||||
|
||||
echo
|
||||
echo "-- the usage text --"
|
||||
shows "28. -k shows its optional argument" '\-k \[<text>\]' "$KDESC"
|
||||
shows "29. -i shows its filename" '\-i <filename>' "$KDESC"
|
||||
# Ordered by likely use: the single letters come before the long names.
|
||||
# Line numbers, not a multi-line pattern -- grep is line-oriented.
|
||||
usage=$(timeout 60 "$KDESC" 2>&1 | plain)
|
||||
k_line=$(printf '%s\n' "$usage" | grep -n -- '-k \[<text>\]' | head -1 | cut -d: -f1)
|
||||
katoms_line=$(printf '%s\n' "$usage" | grep -n -- '--katoms' | head -1 | cut -d: -f1)
|
||||
if [ -n "$k_line" ] && [ -n "$katoms_line" ] && [ "$k_line" -lt "$katoms_line" ]; then
|
||||
echo "${green}PASS${reset} 30. -k is listed before --katoms"; PASS=$((PASS+1))
|
||||
else
|
||||
echo "${red}FAIL${reset} 30. usage order: -k at line $k_line, --katoms at $katoms_line"
|
||||
FAIL=$((FAIL+1))
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "-- a no-result search is not an error --"
|
||||
timeout 60 "$KDESC" -k zzqq >/dev/null 2>&1
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "${green}PASS${reset} 31. finding nothing exits 0"; PASS=$((PASS+1))
|
||||
else
|
||||
echo "${red}FAIL${reset} 31. finding nothing exits nonzero"; FAIL=$((FAIL+1))
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "====================="
|
||||
echo "Results: ${PASS} passed, ${FAIL} failed"
|
||||
[ "$FAIL" -eq 0 ] || exit 1
|
||||
exit 0
|
||||
@@ -135,7 +135,7 @@ accepted " 8. a .k declaration with :: instances" \
|
||||
rejected " 9. a .k declaration plus a parameterized definition" \
|
||||
'@@k9.k s : a declaration @@
|
||||
@@k9.ta s2 :other : [*s2*] @@
|
||||
@k9 x @' 'both a declaration'
|
||||
@k9 x @' 'both a ".k" declaration'
|
||||
|
||||
echo
|
||||
echo "============================="
|
||||
|
||||
215
tst/target_list_test.sh
Executable file
215
tst/target_list_test.sh
Executable file
@@ -0,0 +1,215 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# target_list_test.sh — A klammer definition may name several targets.
|
||||
#
|
||||
# @@table.html,tex :: <body> @@
|
||||
#
|
||||
# One body, several targets. The list is SURFACE SYNTAX: the registry makes
|
||||
# one definition per target named, so nothing downstream of registration knows
|
||||
# a list was written. That is what these tests pin down — in particular that
|
||||
# each member goes through the redefinition transition table on its own, so a
|
||||
# list overlapping an existing definition is decided per target rather than
|
||||
# all-or-nothing.
|
||||
#
|
||||
# Why the feature exists: a klammer whose body is an `@eval` cannot have its
|
||||
# coverage derived (deciding which targets a Python function answers for is
|
||||
# undecidable), so its targets must be DECLARED. Writing one definition per
|
||||
# target would then duplicate the body. See notes/target_coverage.md.
|
||||
#
|
||||
# Two patterns are involved and must not be confused (mac/ktype.h):
|
||||
# definition_begin_name — runs through commas; used ONLY for "@@<name>"
|
||||
# definition_name — no comma; klammer applications, option names,
|
||||
# "*arg*" variables, and the closing delimiters
|
||||
# Case 14 guards the second: a comma next to an application is writer text.
|
||||
#
|
||||
# These are engine tests, so they use -k none and define their own targets
|
||||
# inline: a target is a Machine construct (@@@target), not owned by any
|
||||
# klammer set.
|
||||
#
|
||||
# Usage: ./target_list_test.sh (needs KLAMMERTEXT_HOME set; ktext on PATH)
|
||||
# Exit code: 0 if all tests pass, 1 otherwise.
|
||||
|
||||
PASS=0
|
||||
FAIL=0
|
||||
KTEXT=ktext
|
||||
K=${KLAMMERTEXT_HOME:?KLAMMERTEXT_HOME must be set}
|
||||
ERR=/tmp/target_list_test_err.$$
|
||||
|
||||
red=$'\033[31m'
|
||||
green=$'\033[32m'
|
||||
bold=$'\033[1m'
|
||||
reset=$'\033[0m'
|
||||
|
||||
# ta, tb, tc are independent; td INCLUDES tc, so tc "provides" td and a
|
||||
# definition for tc is copied to td (mac/klammer_registry.cpp).
|
||||
TARGETS='@@@target ta | Target A @@@
|
||||
@@@target tb | Target B @@@
|
||||
@@@target tc | Target C @@@
|
||||
@@@target td | Target D :includes tc @@@
|
||||
'
|
||||
|
||||
trim() { awk '{ sub(/[ \t\r]+$/, "") } { line[NR]=$0 } END { f=1; while (f<=NR && line[f]=="") f++; l=NR; while (l>=1 && line[l]=="") l--; for (i=f;i<=l;i++) print line[i] }'; }
|
||||
|
||||
# check_eq NAME TARGET EXPECTED SRC — exit 0, stdout==EXPECTED, no warning.
|
||||
check_eq() {
|
||||
local name="$1" target="$2" expected="$3" src="$4"
|
||||
local out status err
|
||||
out=$("$KTEXT" -k none -s "$TARGETS$src" -t "$target" -d 2>"$ERR"); status=$?
|
||||
err=$(cat "$ERR")
|
||||
out=$(printf '%s' "$out" | trim)
|
||||
if [ $status -ne 0 ]; then
|
||||
echo "${red}FAIL${reset} $name — ktext exited $status"
|
||||
echo " stderr: $(echo "$err" | head -2)"; FAIL=$((FAIL+1)); return
|
||||
fi
|
||||
if printf '%s' "$err" | grep -qiF "warning"; then
|
||||
echo "${red}FAIL${reset} $name — unexpected warning"
|
||||
echo " stderr: $(echo "$err" | head -2)"; FAIL=$((FAIL+1)); return
|
||||
fi
|
||||
if [ "$out" = "$expected" ]; then
|
||||
echo "${green}PASS${reset} $name"; PASS=$((PASS+1))
|
||||
else
|
||||
echo "${red}FAIL${reset} $name"
|
||||
echo " expected: [$expected]"; echo " got: [$out]"; FAIL=$((FAIL+1))
|
||||
fi
|
||||
}
|
||||
|
||||
# check_warn NAME TARGET EXPECTED SRC — exit 0, stdout==EXPECTED, AND a warning.
|
||||
check_warn() {
|
||||
local name="$1" target="$2" expected="$3" src="$4"
|
||||
local out status err
|
||||
out=$("$KTEXT" -k none -s "$TARGETS$src" -t "$target" -d 2>"$ERR"); status=$?
|
||||
err=$(cat "$ERR")
|
||||
out=$(printf '%s' "$out" | trim)
|
||||
if [ $status -ne 0 ]; then
|
||||
echo "${red}FAIL${reset} $name — ktext exited $status"; FAIL=$((FAIL+1)); return
|
||||
fi
|
||||
if ! printf '%s' "$err" | grep -qiF "warning"; then
|
||||
echo "${red}FAIL${reset} $name — expected a warning, got none"; FAIL=$((FAIL+1)); return
|
||||
fi
|
||||
if [ "$out" = "$expected" ]; then
|
||||
echo "${green}PASS${reset} $name"; PASS=$((PASS+1))
|
||||
else
|
||||
echo "${red}FAIL${reset} $name"
|
||||
echo " expected: [$expected]"; echo " got: [$out]"; FAIL=$((FAIL+1))
|
||||
fi
|
||||
}
|
||||
|
||||
# check_error NAME TARGET PATTERN SRC — nonzero exit and PATTERN in the message.
|
||||
check_error() {
|
||||
local name="$1" target="$2" pattern="$3" src="$4"
|
||||
local out status
|
||||
out=$("$KTEXT" -k none -s "$TARGETS$src" -t "$target" -d 2>&1); status=$?
|
||||
if [ $status -eq 0 ]; then
|
||||
echo "${red}FAIL${reset} $name — expected an error but ktext succeeded"; FAIL=$((FAIL+1)); return
|
||||
fi
|
||||
if echo "$out" | grep -qF "$pattern"; then
|
||||
echo "${green}PASS${reset} $name"; PASS=$((PASS+1))
|
||||
else
|
||||
echo "${red}FAIL${reset} $name — expected error to contain [$pattern]"
|
||||
echo " output: $(echo "$out" | head -3)"; FAIL=$((FAIL+1))
|
||||
fi
|
||||
}
|
||||
|
||||
echo "${bold}Klammer definition target-list tests${reset}"
|
||||
echo "===================================="
|
||||
echo
|
||||
|
||||
# --- One body, several targets ---
|
||||
echo "-- the list defines each target named --"
|
||||
check_eq " 1. first member gets the body" ta "x B y" \
|
||||
'@@f.k : a test @@ @@f.ta,tb :: B @@ x @f@ y'
|
||||
check_eq " 2. second member gets the body" tb "x B y" \
|
||||
'@@f.k : a test @@ @@f.ta,tb :: B @@ x @f@ y'
|
||||
check_error " 3. a target NOT in the list is undefined" tc "not defined for target" \
|
||||
'@@f.k : a test @@ @@f.ta,tb :: B @@ x @f@ y'
|
||||
check_eq " 4. three members, last one" tc "x B y" \
|
||||
'@@f.k : a test @@ @@f.ta,tb,tc :: B @@ x @f@ y'
|
||||
check_eq " 5. parameters are inherited from .k for every member" tb "x [q] y" \
|
||||
'@@f.k s : a test @@ @@f.ta,tb :: [*s*] @@ x @f q @ y'
|
||||
check_eq " 6. a list member propagates to a target that includes it" td "x B y" \
|
||||
'@@f.k : a test @@ @@f.ta,tc :: B @@ x @f@ y'
|
||||
|
||||
# --- Malformed lists ---
|
||||
echo
|
||||
echo "-- malformed lists --"
|
||||
check_error " 7. an unknown target in the list" ta "is not defined" \
|
||||
'@@f.ta,nosuch : B @@'
|
||||
check_error " 8. a target named twice" ta "more than once" \
|
||||
'@@f.ta,ta : B @@'
|
||||
check_error " 9. \".k\" in a list" ta "in a list of targets" \
|
||||
'@@f.k,ta : a test @@'
|
||||
check_error "10. \".o\" in a list" ta "in a list of targets" \
|
||||
'@@f.o,ta : an option set @@'
|
||||
# "*" is the general target written out: an assertion that the klammer works
|
||||
# for EVERY target, including ones not yet defined. In a list it is either
|
||||
# redundant or a misunderstanding.
|
||||
check_error "10a. \"*\" in a list" ta 'names "*" in a list of targets' \
|
||||
'@@f.*,ta : b @@'
|
||||
check_eq "10b. \"*\" alone defines every target" tb "x body" \
|
||||
'@@f.* : body @@ x @f@'
|
||||
check_eq "10c. ... including one it does not name" ta "x body" \
|
||||
'@@f.* : body @@ x @f@'
|
||||
check_error "11. a trailing comma" ta "is not correctly defined" \
|
||||
'@@f.ta, : B @@'
|
||||
check_error "12. an empty member" ta "is not correctly defined" \
|
||||
'@@f.ta,,tb : B @@'
|
||||
|
||||
# --- The transition table applies per member, not to the list ---
|
||||
echo
|
||||
echo "-- redefinition, decided per member --"
|
||||
check_error "13. create + list create collides on the shared member" ta "already defined" \
|
||||
'@@f.ta : one @@ @@f.ta,tb : two @@ x @f@'
|
||||
# The list's default is silently ignored for ta (which already has a create)
|
||||
# and creates tb. This is the case a whole-list decision would get wrong.
|
||||
check_eq "14. existing create + list default: existing kept" ta "x one" \
|
||||
'@@f.ta : one @@ @@f.ta,tb :::: two @@ x @f@'
|
||||
check_eq "15. existing create + list default: other member defined" tb "x two" \
|
||||
'@@f.ta : one @@ @@f.ta,tb :::: two @@ x @f@'
|
||||
check_eq "16. list default + create for one member: replaced" ta "x real" \
|
||||
'@@f.ta,tb :::: def @@ @@f.ta : real @@ x @f@'
|
||||
check_eq "17. list default + create for one member: other keeps default" tb "x def" \
|
||||
'@@f.ta,tb :::: def @@ @@f.ta : real @@ x @f@'
|
||||
check_warn "18. list override warns and replaces (first member)" ta "x over" \
|
||||
'@@f.k : a test @@ @@f.ta :: one @@ @@f.tb :: two @@ @@f.ta,tb ::: over @@ x @f@'
|
||||
check_warn "19. list override warns and replaces (second member)" tb "x over" \
|
||||
'@@f.k : a test @@ @@f.ta :: one @@ @@f.tb :: two @@ @@f.ta,tb ::: over @@ x @f@'
|
||||
|
||||
# --- The comma is a target separator ONLY after "@@" ---
|
||||
echo
|
||||
echo "-- a comma elsewhere is writer text --"
|
||||
check_eq "20. a comma in a body is text" ta "x a,b" \
|
||||
'@@f.ta : a,b @@ x @f@'
|
||||
check_eq "21. a comma after an argument substitution" ta "x q,tail" \
|
||||
'@@f.ta s : *s*,tail @@ x @f q @'
|
||||
check_eq "22. a comma after an application" ta "x q, y" \
|
||||
'@@f.ta s : *s* @@ x @f q @, y'
|
||||
check_eq "23. a general definition is unaffected" ta "x B" \
|
||||
'@@f : B @@ x @f@'
|
||||
|
||||
# --- Diagnostics count what was WRITTEN, not how often it registered ---
|
||||
echo
|
||||
echo "-- one definition, several registrations --"
|
||||
# tc provides td, so "@@f.tc : ..." registers twice from one line. Before
|
||||
# this was grouped, the message said "2 definitions" and then printed the one
|
||||
# line the author wrote twice, sending them to look for a second.
|
||||
check_error "24. the count is of written definitions" ta \
|
||||
"and a definition that declares its own parameters" \
|
||||
'@@f.k s : d @@ @@f.tc : [*s*] @@'
|
||||
check_error "25. ... and the location names its targets" ta "(targets tc, td)" \
|
||||
'@@f.k s : d @@ @@f.tc : [*s*] @@'
|
||||
# Two definitions in two places are still two.
|
||||
check_error "26. distinct places still count separately" ta \
|
||||
"2 definitions that declare their own parameters" \
|
||||
'@@f.k s : d @@ @@f.ta : a*s* @@ @@f.tb : b*s* @@'
|
||||
# The remedy, named in the message rather than implied.
|
||||
check_error "27. the message says what to write instead" ta \
|
||||
'Write "::" instead of ":"' \
|
||||
'@@f.k s : d @@ @@f.tc : [*s*] @@'
|
||||
|
||||
rm -f "$ERR"
|
||||
|
||||
echo
|
||||
echo "===================================="
|
||||
echo "Results: ${PASS} passed, ${FAIL} failed"
|
||||
[ "$FAIL" -eq 0 ] || exit 1
|
||||
exit 0
|
||||
Reference in New Issue
Block a user