From 97652362fa4a00178a4c0c363e089670b3412042 Mon Sep 17 00:00:00 2001 From: nrq6993-a11y Date: Sun, 25 Oct 2026 09:55:09 +0300 Subject: [PATCH] G8: EPS import regression tests for operator type checks ## What and why Hostile EPS programs that give an operator the wrong operand type (a non-integer count, a non-procedure body, a non-boolean condition) must stop the import with a `typecheck` error instead of misbehaving. One test per operator: copy, for, idiv, if, ifelse, index, loop, mod, repeat, roll and srand. The tests only: the limits and checks they cover are already on main, so the tests pin that behaviour so a later change can't silently drop it. ## How it was tested The new tests pass, along with the full `cargo xtask ci` on the branch. Combines the tests from: stream/vc-featD-g8-eps-copy-typecheck-count, stream/vc-featD-g8-eps-for-typecheck-procedure, stream/vc-featD-g8-eps-idiv-typecheck-operand, stream/vc-featD-g8-eps-if-typecheck-condition, stream/vc-featD-g8-eps-if-typecheck-procedure, stream/vc-featD-g8-eps-ifelse-typecheck-condition, stream/vc-featD-g8-eps-ifelse-typecheck-procedure, stream/vc-featD-g8-eps-index-typecheck-count, stream/vc-featD-g8-eps-loop-typecheck-procedure, stream/vc-featD-g8-eps-mod-typecheck-operand, stream/vc-featD-g8-eps-repeat-typecheck-count, stream/vc-featD-g8-eps-repeat-typecheck-procedure, stream/vc-featD-g8-eps-roll-typecheck-j, stream/vc-featD-g8-eps-roll-typecheck-n, stream/vc-featD-g8-eps-srand-typecheck-operand --- crates/eps/src/import/tests.rs | 90 ++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/crates/eps/src/import/tests.rs b/crates/eps/src/import/tests.rs index 7a503a905..d74446f2b 100644 --- a/crates/eps/src/import/tests.rs +++ b/crates/eps/src/import/tests.rs @@ -19,6 +19,96 @@ pub(super) fn read(body: &str) -> crate::Imported { import(&eps(body)).unwrap() } +#[test] +fn postscript_srand_non_integer_operand_is_refused() { + let err = import(&eps("1.5 srand")).unwrap_err(); + assert!(err.contains("typecheck") && err.contains("srand")); +} + +#[test] +fn postscript_roll_non_integer_count_is_refused() { + let err = import(&eps("1.5 1 roll")).unwrap_err(); + assert!(err.contains("typecheck")); +} + +#[test] +fn postscript_roll_non_integer_displacement_is_refused() { + let err = import(&eps("1 1.5 roll")).unwrap_err(); + assert!(err.contains("typecheck")); +} + +#[test] +fn postscript_repeat_non_procedure_operand_is_refused() { + let err = import(&eps("1 1 repeat")).unwrap_err(); + assert!(err.contains("typecheck") && err.contains("repeat")); +} + +#[test] +fn postscript_repeat_non_integer_count_is_refused() { + let err = import(&eps("1.5 { pop } repeat")).unwrap_err(); + assert!(err.contains("typecheck")); +} + +#[test] +fn postscript_mod_non_integer_operand_is_refused() { + let err = import(&eps("1.5 2 mod")).unwrap_err(); + assert!(err.contains("typecheck") && err.contains("mod")); +} + +#[test] +fn postscript_loop_non_procedure_operand_is_refused() { + let err = import(&eps("1 loop")).unwrap_err(); + assert!(err.contains("typecheck") && err.contains("loop")); +} + +#[test] +fn postscript_index_non_integer_count_is_refused() { + let err = import(&eps("1 1.5 index pop")).unwrap_err(); + assert!(err.contains("typecheck")); +} + +#[test] +fn postscript_ifelse_non_procedure_branch_is_refused() { + let err = import(&eps("true { pop } 1 ifelse")).unwrap_err(); + assert!(err.contains("typecheck") && err.contains("ifelse")); +} + +#[test] +fn postscript_ifelse_non_boolean_condition_is_refused() { + let err = import(&eps("1 { pop } { pop } ifelse")).unwrap_err(); + assert!(err.contains("typecheck") && err.contains("ifelse")); +} + +#[test] +fn postscript_if_non_procedure_body_is_refused() { + let err = import(&eps("true 1 if")).unwrap_err(); + assert!(err.contains("typecheck") && err.contains("if")); +} + +#[test] +fn postscript_if_non_boolean_condition_is_refused() { + let err = import(&eps("1 { pop } if")).unwrap_err(); + assert!(err.contains("typecheck") && err.contains("if")); +} + +#[test] +fn postscript_idiv_non_integer_operand_is_refused() { + let err = import(&eps("1.5 2 idiv")).unwrap_err(); + assert!(err.contains("typecheck") && err.contains("idiv")); +} + +#[test] +fn postscript_for_non_procedure_operand_is_refused() { + let err = import(&eps("1 1 10 1 for")).unwrap_err(); + assert!(err.contains("typecheck") && err.contains("for")); +} + +#[test] +fn postscript_copy_non_integer_operand_count_is_refused() { + let err = import(&eps("1 1.5 copy pop")).unwrap_err(); + assert!(err.contains("typecheck") && err.contains("copy")); +} + /// #1714: arrays and dictionaries a program links into themselves or each other are freed with /// the interpreter, also the ones it no longer reaches and when it stops on an error. #[test]