1
//! DO / DO* / DOLIST edge-case coverage — static-eval branches,
2
//! non-terminating-loop fallback, multi-statement bodies, &c.
3

            
4
use super::common::{
5
    compile_and_validate, compile_expect_error, wrap_with_runtime_i32, wrap_with_runtime_ratio,
6
};
7

            
8
#[test]
9
1
fn do_with_bool_end_test() {
10
1
    compile_and_validate("(do ((i 0 (+ i 1))) (#t i))");
11
1
}
12

            
13
#[test]
14
1
fn do_static_evaluable_loop() {
15
    // Fully static; the eval-time loop runs to completion and the
16
    // result form is the final i.
17
1
    compile_and_validate("(do ((i 0 (+ i 1))) ((= i 5) i))");
18
1
}
19

            
20
#[test]
21
1
fn do_with_complex_step_expression() {
22
    // IDX is a runtime Index; the counter `i` and the complex step stay in the
23
    // Index stratum (mixing an index counter with a Scalar bound is refused).
24
1
    compile_and_validate(&wrap_with_runtime_i32(
25
1
        "(do ((i 0 (+ i (- IDX 1)))) ((>= i IDX)) (debug i))",
26
1
    ));
27
1
}
28

            
29
#[test]
30
1
fn do_star_with_dependent_step() {
31
    // IDX is a runtime Index bound; the counter `i`, the dependent var `j`, and
32
    // the end-test stay in the Index stratum (an index counter cannot be
33
    // compared against a Scalar — the implicit bridge is gone).
34
1
    compile_and_validate(&wrap_with_runtime_i32(
35
1
        "(do* ((i 0 (+ i 1)) (j i (- j 1))) ((>= i IDX) j))",
36
1
    ));
37
1
}
38

            
39
#[test]
40
1
fn dolist_no_body_errors() {
41
    // DOLIST requires at least one body form.
42
1
    let err = compile_expect_error("(dolist (x '(1 2 3)))");
43
1
    assert!(err.contains("DOLIST"), "got: {err}");
44
1
}
45

            
46
#[test]
47
1
fn dolist_with_multi_form_body() {
48
1
    compile_and_validate(
49
1
        "(let* ((acc 0)) \
50
1
           (dolist (x '(1 2 3)) \
51
1
             (debug \"step\") \
52
1
             (setf acc (+ acc x)) \
53
1
             (debug acc)) \
54
1
           acc)",
55
    );
56
1
}
57

            
58
#[test]
59
1
fn dolist_with_no_loop_var_init_errors() {
60
1
    let err = compile_expect_error("(dolist () 0)");
61
1
    assert!(err.contains("DOLIST"), "got: {err}");
62
1
}
63

            
64
#[test]
65
1
fn do_missing_var_list_errors() {
66
1
    let err = compile_expect_error("(do)");
67
1
    assert!(err.contains("DO"), "got: {err}");
68
1
}
69

            
70
#[test]
71
1
fn do_star_missing_end_clause_errors() {
72
1
    let err = compile_expect_error("(do* ((i 0 (+ i 1))))");
73
1
    assert!(err.contains("DO*"), "got: {err}");
74
1
}
75

            
76
#[test]
77
1
fn do_with_multi_result_form() {
78
1
    compile_and_validate("(do ((i 0 (+ i 1))) ((= i 3) (debug \"done\") i))");
79
1
}
80

            
81
/// DO* sequential init where the second var depends on the first
82
/// at runtime — exercises the runtime-init path in `compile_do_star`.
83
#[test]
84
1
fn do_star_runtime_dependent_init() {
85
1
    compile_and_validate(&wrap_with_runtime_ratio(
86
1
        "(do* ((a X) (b a (- b 1))) ((<= b 0) b))",
87
1
    ));
88
1
}