tsrun 0.1.23

A TypeScript interpreter designed for embedding in applications
Documentation
//! Test for cycle memory leak

use super::run;
use tsrun::Interpreter;

/// Get baseline object count (builtins only, no user code)
fn get_baseline_live_count() -> usize {
    let interp = Interpreter::new();
    interp.collect();
    interp.gc_stats().live_objects
}

#[test]
fn test_cycle_leak_detailed() {
    let baseline = get_baseline_live_count();
    println!("Baseline live count: {}", baseline);

    // Create many cycles and measure memory after full GC
    let source = r#"
        let sum = 0;
        for (let i = 0; i < 1000; i++) {
            const a = { id: i, other: null };
            const b = { id: i + 1, other: null };
            a.other = b;
            b.other = a;
            sum = sum + a.id + b.id;
        }
        sum
    "#;

    let mut interp = Interpreter::new();
    interp.set_gc_threshold(50); // Trigger GC frequently
    let result_step = run(&mut interp, source, None).unwrap();
    let result = if let tsrun::StepResult::Complete(rv) = result_step {
        rv
    } else {
        panic!("Expected Complete, got {:?}", result_step);
    };

    // Force final GC
    interp.collect();
    let stats = interp.gc_stats();

    println!("Result: {:?}", result);
    println!(
        "After cycles: total={}, pooled={}, live={}",
        stats.total_objects, stats.pooled_objects, stats.live_objects
    );

    // If cycles were properly collected, live count should be close to baseline
    // 1000 iterations * 2 objects per cycle = 2000 objects potentially leaked
    let overhead = stats.live_objects.saturating_sub(baseline);
    println!("Overhead over baseline: {}", overhead);

    // The test: if cycles are leaking, we'll have 2000+ extra objects
    assert!(
        overhead < 200,
        "Too many objects retained after GC: {} over baseline (cycles may be leaking)",
        overhead
    );
}