fix: stabilize learning path, reliability and legacy Windows builds
Unify beginner syntax while preserving compatibility, repair ownership and parser guards, add verified distribution tooling and VC6 Windows 98 build inputs, and clarify documentation and release status. Preserve DOS build-input line endings and exclude generated binaries/caches.
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Bounded malformed-expression regressions; sanitizer runs include leak checks."""
|
||||
from pathlib import Path
|
||||
import os
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
DRIVER = r'''#define main claro_cli_main
|
||||
#include "claro.c"
|
||||
#undef main
|
||||
int main(int argc,char **argv)
|
||||
{
|
||||
Runtime rt;
|
||||
Value result;
|
||||
int status;
|
||||
if(argc==3 && strcmp(argv[1],"check")==0) return check_file(argv[2]);
|
||||
if(argc!=2) return 2;
|
||||
rt_init(&rt);
|
||||
result=eval_expr(&rt,argv[1]);
|
||||
value_free(result);
|
||||
status=rt.error?1:0;
|
||||
if(rt.error) fprintf(stderr,"%s\n",rt.err_msg);
|
||||
runtime_free(&rt);
|
||||
return status;
|
||||
}
|
||||
'''
|
||||
CASES = [
|
||||
('!', 'NOT'),
|
||||
('(1 + 2', 'closing'),
|
||||
('1 +', 'value'),
|
||||
('"unfinished', 'quote'),
|
||||
('1 2', 'Unexpected'),
|
||||
('(' * 600 + '1' + ')' * 600, 'nesting'),
|
||||
('NOT ' * 600 + 'YES', 'nesting'),
|
||||
]
|
||||
|
||||
|
||||
def limited():
|
||||
import resource
|
||||
resource.setrlimit(resource.RLIMIT_AS, (128 * 1024 * 1024, 128 * 1024 * 1024))
|
||||
|
||||
|
||||
def main():
|
||||
if os.name == 'nt':
|
||||
print('SKIP: current parser sanitizer driver requires POSIX')
|
||||
return
|
||||
with tempfile.TemporaryDirectory(prefix='claro-parse-', dir=os.environ.get('TMPDIR')) as tmp:
|
||||
work = Path(tmp)
|
||||
(work / 'claro.c').write_bytes((ROOT / 'src/claro.c').read_bytes())
|
||||
driver = work / 'parse.c'
|
||||
driver.write_text(DRIVER)
|
||||
flags = ['gcc', '-std=c89', '-pedantic-errors', '-Werror=implicit-function-declaration', '-O0', '-g']
|
||||
plain, sanitized = work / 'parse', work / 'parse-asan'
|
||||
subprocess.run(flags + [str(driver), '-o', str(plain), '-lm'], check=True)
|
||||
for expression, expected in CASES:
|
||||
run = subprocess.run([str(plain), expression], cwd=work, capture_output=True, text=True, timeout=5, preexec_fn=limited)
|
||||
assert run.returncode == 1 and expected in run.stderr, (expression[:50], run.returncode, run.stderr[-1000:])
|
||||
subprocess.run(flags + ['-fsanitize=address,undefined', str(driver), '-o', str(sanitized), '-lm'], check=True)
|
||||
env = dict(os.environ, ASAN_OPTIONS='detect_leaks=1:halt_on_error=1', UBSAN_OPTIONS='halt_on_error=1')
|
||||
for expression, expected in CASES:
|
||||
run = subprocess.run([str(sanitized), expression], cwd=work, env=env, capture_output=True, text=True, timeout=10)
|
||||
assert run.returncode == 1 and expected in run.stderr, (expression[:50], run.returncode, run.stderr[-1000:])
|
||||
assert not any(marker in run.stderr for marker in ['AddressSanitizer', 'LeakSanitizer', 'runtime error:']), run.stderr
|
||||
for expression, expected in CASES[:1] + CASES[3:4]:
|
||||
script = work / 'bad.claro'
|
||||
script.write_text('SAY ' + expression + '\n')
|
||||
run = subprocess.run([str(sanitized), 'check', str(script)], cwd=work, env=env, capture_output=True, text=True, timeout=10)
|
||||
assert run.returncode == 1 and expected in run.stdout, ('check', expression, run.returncode, run.stdout, run.stderr[-1000:])
|
||||
assert not run.stderr, run.stderr
|
||||
for expression in ['(1 + 2) * 3', '(' * 64 + '1' + ')' * 64, 'NOT ' * 64 + 'YES', '"a" + "b"', '1 != 2']:
|
||||
run = subprocess.run([str(sanitized), expression], cwd=work, env=env, capture_output=True, text=True, timeout=10)
|
||||
assert run.returncode == 0 and not run.stderr, (expression[:50], run.returncode, run.stderr[-1000:])
|
||||
print('PASS: seven malformed expressions and five valid expressions terminate cleanly under ASan/UBSan/LSan')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user