use stet::Interpreter;
fn probe(source: &str) -> bool {
let mut interp = Interpreter::builder().suppress_output().build();
let ctx = interp.context();
if stet_engine::eval::parse_and_exec(ctx, source.as_bytes()).is_err() {
return false;
}
matches!(
ctx.o_stack.peek(0).map(|o| o.value),
Ok(stet_core::object::PsValue::Bool(true))
)
}
fn raises(source: &str, errorname: &str) -> bool {
probe(&format!(
"{{ {source} }} stopped {{ $error /errorname get /{errorname} eq }} {{ false }} ifelse"
))
}
fn assert_undefined_result(source: &str) {
assert!(
raises(source, "undefinedresult"),
"expected undefinedresult from `{source}`"
);
}
fn assert_ok_and_finite(source: &str) {
let mut interp = Interpreter::builder().suppress_output().build();
let ctx = interp.context();
assert!(
stet_engine::eval::parse_and_exec(ctx, source.as_bytes()).is_ok(),
"`{source}` should succeed"
);
assert_stack_is_finite(&mut interp, source);
}
fn assert_stack_is_finite(interp: &mut Interpreter, source: &str) {
let ctx = interp.context();
for i in 0..ctx.o_stack.len() {
if let Ok(obj) = ctx.o_stack.peek(i)
&& let stet_core::object::PsValue::Real(v) = obj.value
{
assert!(v.is_finite(), "`{source}` left {v} on the stack");
}
}
}
#[test]
fn idiv_of_int_min_by_minus_one_does_not_panic() {
assert_undefined_result("-9223372036854775808 -1 idiv");
}
#[test]
fn mod_of_int_min_by_minus_one_does_not_panic() {
assert_undefined_result("-9223372036854775808 -1 mod");
}
#[test]
fn ordinary_integer_division_still_works() {
for source in [
"7 2 idiv 3 eq { } { (wrong) = } ifelse",
"7 2 mod 1 eq { } { (wrong) = } ifelse",
"-9223372036854775808 1 idiv",
"-9223372036854775808 2 idiv",
"-9223372036854775808 -2 idiv",
] {
assert_ok_and_finite(source);
}
}
#[test]
fn abs_and_neg_promote_at_the_integer_boundary() {
assert_ok_and_finite("-9223372036854775808 abs");
assert_ok_and_finite("-9223372036854775808 neg");
}
#[test]
fn arithmetic_overflow_raises_undefined_result() {
for source in [
"1e308 1e308 mul",
"1e308 10 mul",
"1e308 1e308 add",
"-1e308 -1e308 add",
"1e308 -1e308 sub",
"1e308 1e-308 div",
"2 10000 exp",
] {
assert_undefined_result(source);
}
}
#[test]
fn division_by_zero_still_raises_undefined_result() {
assert_undefined_result("1 0 div");
assert_undefined_result("1 0 idiv");
assert_undefined_result("1 0 mod");
}
#[test]
fn ordinary_arithmetic_is_unaffected() {
for source in [
"1e308 2 div",
"1e-308 1e308 mul",
"1.5 2.5 mul",
"2 1000 exp",
"3 4 add 7 eq { } { (wrong) = } ifelse",
] {
assert_ok_and_finite(source);
}
}
#[test]
fn out_of_range_literal_does_not_reach_the_stack() {
for source in ["1e999", "-1e999", "1.5e400", "1e999 1 add"] {
let mut interp = Interpreter::builder().suppress_output().build();
let ctx = interp.context();
assert!(
stet_engine::eval::parse_and_exec(ctx, source.as_bytes()).is_err(),
"`{source}` should not run"
);
}
}
#[test]
fn representable_literals_still_scan() {
for source in ["1e308", "-1e308", "1e-308", "1e38", "0.0", "123456789"] {
assert_ok_and_finite(source);
}
}
#[test]
fn cvr_of_an_out_of_range_literal_does_not_yield_infinity() {
let mut interp = Interpreter::builder().suppress_output().build();
let ctx = interp.context();
let _ = stet_engine::eval::parse_and_exec(ctx, b"(1e999) cvr");
assert_stack_is_finite(&mut interp, "(1e999) cvr");
}
#[test]
fn path_operators_reject_non_finite_coordinates() {
let blow = "1e300 1e300 scale 1e300 1e300 scale";
for op in [
"1 1 moveto",
"0 0 moveto 1 1 lineto",
"0 0 moveto 1 1 1 1 1 1 curveto",
"0 0 1 0 360 arc",
"0 0 1 360 0 arcn",
"0 0 moveto 1 1 2 2 1 arcto",
"0 0 moveto 1 1 2 2 1 arct",
] {
assert_undefined_result(&format!("{blow} {op}"));
}
for op in ["1 1 rlineto", "1 1 1 1 1 1 rcurveto"] {
assert_undefined_result(&format!("0 0 moveto {blow} {op}"));
}
}
#[test]
fn ordinary_and_extreme_finite_geometry_still_works() {
for source in [
"100 100 moveto 200 200 lineto 250 100 150 50 100 100 curveto closepath",
"100 100 50 0 360 arc",
"100 100 50 360 0 arcn",
"100 100 moveto 200 100 200 200 50 arcto pop pop pop pop",
"100 100 moveto 200 100 200 200 50 arct",
"1e6 1e6 scale 1 1 moveto 2 2 lineto",
"1e-6 1e-6 scale 1 1 moveto 2 2 lineto",
] {
assert_ok_and_finite(source);
}
}
#[test]
fn arc_with_non_finite_angle_terminates() {
let mut interp = Interpreter::builder().suppress_output().build();
let ctx = interp.context();
assert!(stet_engine::eval::parse_and_exec(ctx, b"0 0 1 0 1e999 arc").is_err());
assert_undefined_result("0 0 1 1e308 1e308 mul 360 arc");
}