use crate::compiler::CompilerOptions;
use super::harness::*;
#[test]
fn optimizer_differential_success_programs_match() {
let programs = [
r#"pipeline test(task) {
__io_println(2 + 3 * 4)
__io_println("ha" * 2)
__io_println(([1] + [2, 3])[2])
__io_println(({a: 1} + {b: 2}).b)
__io_println((true && false) || !false)
}"#,
r"pipeline test(task) {
fn add(a: int, b: int = 4) {
return a + b
}
const base = 3
__io_println(add(base))
__io_println(add(1 + 1, 2 + 2))
}",
];
for source in programs {
let optimized =
run_harn_result_display_with_options(source, CompilerOptions::optimized()).unwrap();
let unoptimized =
run_harn_result_display_with_options(source, CompilerOptions::without_optimizations())
.unwrap();
assert_eq!(optimized, unoptimized, "{source}");
}
}
#[test]
fn optimizer_differential_errors_match() {
let source = "pipeline test(task) { __io_println(1 / 0) }";
let optimized =
run_harn_result_display_with_options(source, CompilerOptions::optimized()).unwrap_err();
let unoptimized =
run_harn_result_display_with_options(source, CompilerOptions::without_optimizations())
.unwrap_err();
assert_eq!(optimized, unoptimized);
}
#[test]
fn inline_arithmetic_lambda_map_filter_optimization_path() {
let out = run_vm(
r"pipeline default(task) {
const evens = [1, 2, 3, 4, 5, 6].filter({ x -> x % 2 == 0 })
const doubled = evens.map({ x -> x * 2 })
log(doubled)
}",
);
assert_eq!(out, "[harn] [4, 8, 12]\n");
}
#[test]
fn self_recursive_named_fn_still_resolves_after_optimization() {
let out = run_vm(
r"pipeline default(task) {
fn fact(n) {
if n <= 1 { return 1 }
return n * fact(n - 1)
}
log(fact(6))
}",
);
assert_eq!(out, "[harn] 720\n");
}
#[test]
fn mutually_recursive_named_fns_still_resolve_after_optimization() {
let out = run_vm(
r"pipeline default(task) {
fn is_even(n) {
if n == 0 { return true }
return is_odd(n - 1)
}
fn is_odd(n) {
if n == 0 { return false }
return is_even(n - 1)
}
log(is_even(4))
log(is_even(5))
}",
);
assert_eq!(out, "[harn] true\n[harn] false\n");
}
#[test]
fn anonymous_lambda_calling_sibling_fn_via_call_builtin_flags() {
let out = run_vm(
r"pipeline default(task) {
fn helper(x) { return x + 100 }
const r = [1, 2, 3].map({ v -> helper(v) })
log(r)
}",
);
assert_eq!(out, "[harn] [101, 102, 103]\n");
}
#[test]
fn anonymous_lambda_with_get_var_capture_flags() {
let out = run_vm(
r"pipeline default(task) {
const bonus = 10
const r = [1, 2, 3].map({ v -> v + bonus })
log(r)
}",
);
assert_eq!(out, "[harn] [11, 12, 13]\n");
}
#[test]
fn pure_lambda_inside_pipeline_with_unrelated_locals_skips_walk() {
let out = run_vm(
r"pipeline default(task) {
fn helper_a(x) { return x + 1 }
fn helper_b(x) { return x + 2 }
const r = [10, 20, 30].map({ v -> v * 2 })
log(r)
log(helper_a(0))
log(helper_b(0))
}",
);
assert_eq!(out, "[harn] [20, 40, 60]\n[harn] 1\n[harn] 2\n");
}
#[test]
fn nested_map_lambdas_skip_walk_independently() {
let out = run_vm(
r"pipeline default(task) {
const grid = [[1, 2], [3, 4]]
const r = grid.map({ row -> row.map({ x -> x * 10 }) })
log(r)
}",
);
assert_eq!(out, "[harn] [[10, 20], [30, 40]]\n");
}
#[test]
fn typed_param_lambda_uses_check_type_and_walks() {
let out = run_vm(
r"pipeline default(task) {
const r = [1, 2, 3].map({ v: int -> v + 1 })
log(r)
}",
);
assert_eq!(out, "[harn] [2, 3, 4]\n");
}
#[test]
fn var_reassigned_via_any_matches_unoptimized() {
let source = r#"pipeline default(task) {
let x = 0
let sum = 0
let i = 0
const cell = shared_cell("k", 2.5)
while i < 3 {
sum = sum + x
if i == 0 { x = shared_get(cell) }
i = i + 1
}
log("${sum}")
}"#;
let optimized = run_harn_result_display_with_options(source, CompilerOptions::optimized())
.expect("optimized run should not spuriously type-error");
let baseline =
run_harn_result_display_with_options(source, CompilerOptions::without_optimizations())
.expect("unoptimized run is the ground truth");
assert_eq!(
optimized.0, baseline.0,
"stdout must match the generic path"
);
assert_eq!(optimized.0.trim_end(), "[harn] 5.0");
}
#[test]
fn for_item_reassigned_via_any_matches_unoptimized() {
let source = r#"pipeline default(task) {
let sum = 0
const cell = shared_cell("k", 2.5)
for n in [1, 2, 3] {
sum = sum + n
n = shared_get(cell)
sum = sum + n
}
log("${sum}")
}"#;
let optimized = run_harn_result_display_with_options(source, CompilerOptions::optimized())
.expect("optimized run should not spuriously type-error");
let baseline =
run_harn_result_display_with_options(source, CompilerOptions::without_optimizations())
.expect("unoptimized run is the ground truth");
assert_eq!(
optimized.0, baseline.0,
"stdout must match the generic path"
);
}
#[test]
fn monomorphic_counter_loop_result_is_correct() {
let source = r#"pipeline default(task) {
let i = 0
let total = 0
while i < 10 {
total = total + (i + 3) * 2 - 1
i = i + 1
}
log("${total}")
}"#;
let (out, _) = run_harn(source);
assert_eq!(out.trim_end(), "[harn] 140");
}
#[test]
fn inplace_concat_untyped_accumulator_builds_correctly() {
let source = r#"
fn seed() -> any { return [] }
pipeline t(task) {
let x = seed()
for i in [1, 2, 3] {
x = x + [i]
}
let y = seed()
for i in [4, 5] {
y += [i]
}
log("${x} ${y}")
}"#;
assert_eq!(run_output(source), "[harn] [1, 2, 3] [4, 5]");
}
#[test]
fn list_appending_assign_preserves_alias_immutability() {
let source = r#"
pipeline t(task) {
let x = []
x = x.appending(1)
const y = x
x = x.appending(2)
log("${x} ${y}")
}"#;
assert_eq!(run_output(source), "[harn] [1, 2] [1]");
}
#[test]
fn inplace_concat_preserves_binding_when_add_throws() {
let source = r#"
pipeline t(task) {
let x = 5
try {
x += [1]
} catch (e) {
log("caught")
}
log("${x}")
}"#;
assert_eq!(run_output(source), "[harn] caught\n[harn] 5");
}