mirror of
https://github.com/imjasonh/nescript
synced 2026-07-08 08:55:38 +00:00
Add fuzz testing and parser edge case regression tests
Fuzz targets (cargo +nightly fuzz):
- fuzz_lexer: lexer must never panic on arbitrary input
- fuzz_parser: parser must never panic on arbitrary input
- fuzz_compile: full pipeline (parse → analyze → IR → optimize →
codegen → assemble → link) must never panic
Results: 3.3M iterations across all targets, zero crashes.
Seed corpus from all 7 examples + crafted edge cases.
11 new parser edge case tests:
- draw_followed_by_statement: regression for keyword-arg lookahead bug
- draw_with_frame_followed_by_statement: same with frame: arg
- nested_if_else_chain: 3-deep else-if chain
- deeply_nested_blocks: 4-deep nested if blocks
- empty_function_body: fun noop() {}
- empty_state_handlers: on enter/exit/frame {}
- button_start_in_condition: "start" keyword as button name
- button_select_in_condition: "select" keyword as button name
- multiple_draws_in_sequence: 3 draws followed by assignment
- parser_no_panic_on_garbage: 25 malformed inputs
- lexer_no_panic_on_garbage: 10 malformed inputs
221 tests total, all pre-commit checks pass.
https://claude.ai/code/session_01W6eQFStA66EuMKHUFo2rx3
This commit is contained in:
parent
d7092f703d
commit
07dd5bd7e8
8 changed files with 781 additions and 0 deletions
33
fuzz/fuzz_targets/fuzz_compile.rs
Normal file
33
fuzz/fuzz_targets/fuzz_compile.rs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#![no_main]
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
// The full compilation pipeline must never panic — errors are reported
|
||||
// via diagnostics, not panics.
|
||||
if let Ok(source) = std::str::from_utf8(data) {
|
||||
let (program, parse_diags) = nescript::parser::parse(source);
|
||||
if parse_diags.iter().any(|d| d.is_error()) {
|
||||
return;
|
||||
}
|
||||
let Some(program) = program else { return };
|
||||
|
||||
let analysis = nescript::analyzer::analyze(&program);
|
||||
if analysis.diagnostics.iter().any(|d| d.is_error()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// IR lowering + optimization
|
||||
let mut ir = nescript::ir::lower(&program, &analysis);
|
||||
nescript::optimizer::optimize(&mut ir);
|
||||
|
||||
// Codegen + assembly
|
||||
let codegen =
|
||||
nescript::codegen::CodeGen::new(&analysis.var_allocations, &program.constants);
|
||||
let instructions = codegen.generate(&program);
|
||||
|
||||
// Linking
|
||||
let linker = nescript::linker::Linker::new(program.game.mirroring);
|
||||
let _rom = linker.link(&instructions);
|
||||
}
|
||||
});
|
||||
10
fuzz/fuzz_targets/fuzz_lexer.rs
Normal file
10
fuzz/fuzz_targets/fuzz_lexer.rs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#![no_main]
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
// The lexer must never panic on any input — always produce tokens or errors.
|
||||
if let Ok(source) = std::str::from_utf8(data) {
|
||||
let _ = nescript::lexer::lex(source);
|
||||
}
|
||||
});
|
||||
10
fuzz/fuzz_targets/fuzz_parser.rs
Normal file
10
fuzz/fuzz_targets/fuzz_parser.rs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#![no_main]
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
// The parser must never panic — always produce an AST or diagnostics.
|
||||
if let Ok(source) = std::str::from_utf8(data) {
|
||||
let _ = nescript::parser::parse(source);
|
||||
}
|
||||
});
|
||||
7
fuzz/fuzz_targets/fuzz_target_1.rs
Normal file
7
fuzz/fuzz_targets/fuzz_target_1.rs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#![no_main]
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
// fuzzed code goes here
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue