Extends the test framework to prepare for compilation tests

Fix: it2 would have different working for emit eof
This commit is contained in:
Johan B.W. de Vries 2025-05-04 12:40:40 +02:00
parent 1b8333fcfa
commit 7672da6ad3
18 changed files with 170 additions and 33 deletions

View File

@ -2,14 +2,14 @@ import os
import sys
def emit(string):
sys.stdout.write(string)
sys.stdout.buffer.write(string.encode('latin_1'))
sys.stdout.flush()
def trace(header, value):
if os.environ.get('TRACE'):
sys.stderr.write(f'{header}={value!r}\n')
eof = chr(0)
eof = chr(0xFF)
eol = chr(10)
quote = chr(34)
PEEK = None
@ -354,14 +354,14 @@ def emitheader():
emitln(" return a + b[0]")
emitln("")
emitln("def emit(string):")
emitln(" sys.stdout.write(string)")
emitln(" sys.stdout.buffer.write(string.encode('latin_1'))")
emitln(" sys.stdout.flush()")
emitln("")
emitln("def trace(header, value):")
emitln(" if os.environ.get('TRACE'):")
emitln(" sys.stderr.write(f'{header}={value!r}\\n')")
emitln("")
emitln("eof = chr(0)")
emitln("eof = chr(0xFF)")
emitln("eol = chr(10)")
emitln("quote = chr(34)")
emitln("")

View File

@ -431,14 +431,14 @@ emitheader:
emitln " return a + b[0]"
emitln ""
emitln "def emit(string):"
emitln " sys.stdout.write(string)"
emitln " sys.stdout.buffer.write(string.encode('latin_1'))"
emitln " sys.stdout.flush()"
emitln ""
emitln "def trace(header, value):"
emitln " if os.environ.get('TRACE'):"
emitln " sys.stderr.write(f'{header}={value!r}\\n')"
emitln ""
emitln "eof = chr(0)"
emitln "eof = chr(0xFF)"
emitln "eol = chr(10)"
emitln "quote = chr(34)"
emitln ""

View File

@ -532,7 +532,7 @@ emitheader:
emitln "#include <string.h>"
emitln ""
emitln ""
emitln "char var_eof[2] = {-1, 0};"
emitln "char var_eof[2] = {0xFF, 0};"
emitln "char var_eol[2] = {10, 0};"
emitln "char var_quote[2] = {34, 0};"
emitln "char var_false[1] = {0};"

View File

@ -121,6 +121,20 @@ Calls the given function with the given arguments. If the function's return valu
Writes the name and value of the variable passed to stderr if the TRACE environment variable is set.
### Standard library constants
#### eof
End of file character, zero byte.
#### eol
End of line character.
#### quote
Double quote character.
### Standard library functions
#### addstringchar a b

3
tests/.gitignore vendored
View File

@ -1 +1,2 @@
/*.results
/*/*.act.*
/Makefile.mk

View File

@ -1,6 +1,5 @@
.SUFFIXES:
.PHONY: all clean
.PRECIOUS: build/%.it0.py build/%.it0.c
.DELETE_ON_ERROR:
PYVERSION=3.12
@ -8,31 +7,23 @@ PYPREFIX=/usr
CYTHON=cython3
CC=gcc
# builtincheckfalse is separate since it's supposed to crash. It's a test for the test 'framework', if you will.
TESTLIST=$(shell ls *.lang0 | grep -v 'builtincheckfalse' | sed 's/.lang0//')
all: verify-results
all: check
Makefile.mk: generate-recipes.py $(wildcard test_stdlib_constants/*.exp.*) $(wildcard test_stdlib_functions/*.exp.*)
python3 generate-recipes.py Makefile.mk it0 it1 it2
check: all-it0.results all-it1.results all-it2.results
! grep -v 'Success' $^
all-it0.results: $(addprefix build/,$(addsuffix .it0, $(TESTLIST))) build/builtincheckfalse.it0
-rm -f $@
cat test-input.txt | build/builtincheckfalse.it0 2> /dev/null 2>&1 | grep 'Success'
$(foreach test,$(TESTLIST), echo -n "$(test) " >> $@; cat test-input.txt | ./build/$(test).it0 >> $@ ; echo "" >> $@ ;)
all-it1.results: $(addprefix build/,$(addsuffix .it1, $(TESTLIST))) build/builtincheckfalse.it1
-rm -f $@
cat test-input.txt | build/builtincheckfalse.it1 2> /dev/null 2>&1 | grep 'Success'
$(foreach test,$(TESTLIST), echo -n "$(test) " >> $@; cat test-input.txt | ./build/$(test).it1 >> $@ ; echo "" >> $@ ;)
all-it2.results: $(addprefix build/,$(addsuffix .it2, $(TESTLIST))) build/builtincheckfalse.it2
-rm -f $@
cat test-input.txt | build/builtincheckfalse.it2 2> /dev/null 2>&1 | grep 'Success'
$(foreach test,$(TESTLIST), echo -n "$(test) " >> $@; cat test-input.txt | ./build/$(test).it2 >> $@ ; echo "" >> $@ ;)
include Makefile.mk
clean:
-rm -f *.results build/*.it0* build/*.it1* build/*.it2*
find build -name '*.c' -delete
find build -name '*.it0' -delete
find build -name '*.it1' -delete
find build -name '*.it2' -delete
find build -name '*.py' -delete
find build -name '*.tmp' -delete
find . -name '*.act.*.result' -delete
find . -name '*.act.*.stdout' -delete
find . -name '*.act.*.stderr' -delete
###
# it0

View File

@ -0,0 +1,14 @@
/*.it0
/*.it0.c
/*.it0.o
/*.it0.py
/*.it0.py.tmp
/*.it1
/*.it1.c
/*.it1.o
/*.it1.py
/*.it1.py.tmp
/*.it2
/*.it2.c
/*.it2.c.tmp
/*.it2.o

103
tests/generate-recipes.py Normal file
View File

@ -0,0 +1,103 @@
import glob
import os
import sys
class Rule:
def __init__(self, target: str, prerequisites: list[str], recipe: list[str]) -> None:
self.target = target
self.prerequisites = prerequisites
self.recipe = recipe
def make_lines(self):
return [
self.target + ': ' + ' '.join(self.prerequisites),
] + [
'\t' + line
for line in self.recipe
] + ['']
def get_file_list():
return glob.glob('test_*/*.lang0')
def make_rules(file_path, lang0it):
result: list[Rule] = []
act_result = file_path.replace('.lang0', f'.act.{lang0it}.result')
act_stdout = file_path.replace('.lang0', f'.act.{lang0it}.stdout')
act_stderr = file_path.replace('.lang0', f'.act.{lang0it}.stderr')
stdin = file_path.replace('.lang0', '.stdin')
exp_stdout = file_path.replace('.lang0', '.exp.stdout')
exp_stderr = file_path.replace('.lang0', '.exp.stderr')
program = file_path.replace('.lang0', f'.{lang0it}')
if os.path.isfile(exp_stdout) or os.path.isfile(exp_stderr):
result.append(Rule(
act_stdout,
['build/' + program],
[
(f'cat {stdin} | ' if os.path.isfile(stdin) else 'echo x | ')
+ f'build/{program} 1> {act_stdout} 2> {act_stderr}',
]
))
exp_list = [exp_stdout, exp_stderr]
act_list = [act_stdout, act_stderr]
result.append(Rule(
act_result,
[
fil
for exp_file, act_file in zip(exp_list, act_list, strict=True)
if os.path.isfile(exp_file)
for fil in [exp_file, act_file]
],
[
'rm -f $@',
] + [
f'-diff {exp_file} {act_file} >> $@'
for exp_file, act_file in zip(exp_list, act_list, strict=True)
if os.path.isfile(exp_file)
]
))
assert result[-1].prerequisites, f'Missing expectations for {file_path}'
return result
def main(program, write_to, *lang0it):
rule_list_list = [
make_rules(file_path, it)
for file_path in get_file_list()
for it in lang0it
]
result_targets = [
rule.target
for rule_list in rule_list_list
for rule in rule_list
if rule.target.endswith('.result')
]
rule_list_list.append([Rule(
'verify-results',
result_targets,
[
'@echo Finding failed test results...',
'@find $^ -not -empty -print -exec false {} +',
'@echo All tests passed.'
]
)])
data = '\n'.join(
line
for rule_list in rule_list_list
for rule in rule_list
for line in rule.make_lines()
)
with open(write_to, 'wt', encoding='ASCII') as fil:
fil.write(data)
if __name__ == '__main__':
main(*sys.argv)

View File

@ -1,3 +0,0 @@
main:
emit "Success"
/

View File

@ -0,0 +1 @@
<EFBFBD>

View File

@ -0,0 +1,3 @@
main:
emit eof
/

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,3 @@
main:
emit eol
/

View File

@ -0,0 +1 @@
"

View File

@ -0,0 +1,3 @@
main:
emit quote
/

View File

@ -0,0 +1 @@
Hello, world!

View File

@ -0,0 +1,4 @@
main:
emit "Hello"
emit ", world!"
/