Skip to main content

mumutest/
asserts.rs

1use crate::suite::mark_fail;
2use mumu::parser::types::Value;
3use mumu::parser::interpreter::Interpreter;
4
5pub fn expect_equal_bridge_fn(_interp: &mut Interpreter, mut args: Vec<Value>) -> Result<Value, String> {
6    if args.len() != 2 {
7        return Err(format!("expect_equal => expected 2 args, got {}", args.len()));
8    }
9    let actual = args.remove(0);
10    let expected = args.remove(0);
11
12    if let Err(msg) = values_equal(&actual, &expected) {
13        mark_fail(&format!("expect_equal fail: actual={:?}, expected={:?}\n{}", actual, expected, msg));
14    }
15    Ok(Value::Bool(true))
16}
17
18pub fn expect_not_equal_bridge_fn(_interp: &mut Interpreter, mut args: Vec<Value>) -> Result<Value, String> {
19    if args.len() != 2 {
20        return Err(format!("expect_not_equal => expected 2 args, got {}", args.len()));
21    }
22    let actual = args.remove(0);
23    let expected = args.remove(0);
24
25    if values_equal(&actual, &expected).is_ok() {
26        mark_fail(&format!("expect_not_equal fail: both are {:?}", actual));
27    }
28    Ok(Value::Bool(true))
29}
30
31pub fn has_key_bridge_fn(_interp: &mut Interpreter, mut args: Vec<Value>) -> Result<Value, String> {
32    if args.len() != 2 {
33        return Err(format!("has_key => expected 2 args, got {}", args.len()));
34    }
35    let subject = args.remove(0);
36    let key_val = args.remove(0);
37
38    let map = match subject {
39        Value::KeyedArray(m) => m,
40        other => {
41            mark_fail(&format!("has_key => subject not keyed array, got {:?}", other));
42            return Ok(Value::Bool(true));
43        }
44    };
45    let key_str = match key_val {
46        Value::SingleString(s) => s,
47        Value::StrArray(ss) if ss.len() == 1 => ss[0].clone(),
48        other => {
49            mark_fail(&format!("has_key => key must be single string, got {:?}", other));
50            return Ok(Value::Bool(true));
51        }
52    };
53
54    if !map.contains_key(&key_str) {
55        mark_fail(&format!("has_key => missing key '{}'", key_str));
56    }
57    Ok(Value::Bool(true))
58}
59
60pub fn prop_equals_bridge_fn(_interp: &mut Interpreter, mut args: Vec<Value>) -> Result<Value, String> {
61    if args.len() != 3 {
62        return Err(format!("prop_equals => expected 3 args, got {}", args.len()));
63    }
64    let subject = args.remove(0);
65    let key_val = args.remove(0);
66    let expected_val = args.remove(0);
67
68    let map = match subject {
69        Value::KeyedArray(m) => m,
70        other => {
71            mark_fail(&format!("prop_equals => subject is not keyed, got {:?}", other));
72            return Ok(Value::Bool(true));
73        }
74    };
75    let key_str = match key_val {
76        Value::SingleString(s) => s,
77        Value::StrArray(sa) if sa.len() == 1 => sa[0].clone(),
78        other => {
79            mark_fail(&format!("prop_equals => key must be single string, got {:?}", other));
80            return Ok(Value::Bool(true));
81        }
82    };
83
84    match map.get(&key_str) {
85        Some(actual_val) => {
86            if values_equal(actual_val, &expected_val).is_err() {
87                mark_fail(&format!(
88                    "prop_equals => mismatch for '{}': got={:?}, expected={:?}",
89                    key_str, actual_val, expected_val
90                ));
91            }
92        }
93        None => {
94            mark_fail(&format!("prop_equals => missing key '{}'", key_str));
95        }
96    }
97
98    Ok(Value::Bool(true))
99}
100
101// Recursive, float/array/deep tolerant
102pub fn values_equal(a: &Value, b: &Value) -> Result<(), String> {
103    match (a, b) {
104        (Value::Int(x), Value::Int(y)) => if x == y { Ok(()) } else { Err(format!("Int {} != {}", x, y)) },
105        (Value::Long(x), Value::Long(y)) => if x == y { Ok(()) } else { Err(format!("Long {} != {}", x, y)) },
106        (Value::Bool(x), Value::Bool(y)) => if x == y { Ok(()) } else { Err(format!("Bool {} != {}", x, y)) },
107        (Value::SingleString(x), Value::SingleString(y)) => if x == y { Ok(()) } else { Err(format!("String {:?} != {:?}", x, y)) },
108        (Value::Float(x), Value::Float(y)) => {
109            if (x - y).abs() <= 1e-9 || (x.is_nan() && y.is_nan()) {
110                Ok(())
111            } else {
112                Err(format!("Float {} != {} (eps=1e-9)", x, y))
113            }
114        }
115        (Value::IntArray(xs), Value::IntArray(ys)) => {
116            if xs.len() != ys.len() {
117                return Err(format!("IntArray length {} != {}", xs.len(), ys.len()));
118            }
119            for (i, (x, y)) in xs.iter().zip(ys).enumerate() {
120                if x != y {
121                    return Err(format!("IntArray[{}] {} != {}", i, x, y));
122                }
123            }
124            Ok(())
125        }
126        (Value::FloatArray(xs), Value::FloatArray(ys)) => {
127            if xs.len() != ys.len() {
128                return Err(format!("FloatArray length {} != {}", xs.len(), ys.len()));
129            }
130            for (i, (x, y)) in xs.iter().zip(ys).enumerate() {
131                if (x - y).abs() > 1e-9 && !(x.is_nan() && y.is_nan()) {
132                    return Err(format!("FloatArray[{}] {} != {} (eps=1e-9)", i, x, y));
133                }
134            }
135            Ok(())
136        }
137        (Value::StrArray(xs), Value::StrArray(ys)) => {
138            if xs.len() != ys.len() {
139                return Err(format!("StrArray length {} != {}", xs.len(), ys.len()));
140            }
141            for (i, (x, y)) in xs.iter().zip(ys).enumerate() {
142                if x != y {
143                    return Err(format!("StrArray[{}] {:?} != {:?}", i, x, y));
144                }
145            }
146            Ok(())
147        }
148        (Value::BoolArray(xs), Value::BoolArray(ys)) => {
149            if xs.len() != ys.len() {
150                return Err(format!("BoolArray length {} != {}", xs.len(), ys.len()));
151            }
152            for (i, (x, y)) in xs.iter().zip(ys).enumerate() {
153                if x != y {
154                    return Err(format!("BoolArray[{}] {} != {}", i, x, y));
155                }
156            }
157            Ok(())
158        }
159        (Value::MixedArray(xs), Value::MixedArray(ys)) => {
160            if xs.len() != ys.len() {
161                return Err(format!("MixedArray length {} != {}", xs.len(), ys.len()));
162            }
163            for (i, (x, y)) in xs.iter().zip(ys).enumerate() {
164                if let Err(e) = values_equal(x, y) {
165                    return Err(format!("MixedArray[{}] failed: {}", i, e));
166                }
167            }
168            Ok(())
169        }
170        (Value::KeyedArray(mx), Value::KeyedArray(my)) => {
171            if mx.len() != my.len() {
172                return Err(format!("KeyedArray len {} != {}", mx.len(), my.len()));
173            }
174            for (k, vx) in mx {
175                if let Some(vy) = my.get(k) {
176                    if let Err(e) = values_equal(vx, vy) {
177                        return Err(format!("KeyedArray[{:?}] failed: {}", k, e));
178                    }
179                } else {
180                    return Err(format!("KeyedArray missing key {:?}", k));
181                }
182            }
183            Ok(())
184        }
185        (Value::Int2DArray(xs), Value::Int2DArray(ys)) => {
186            if xs.len() != ys.len() {
187                return Err(format!("Int2DArray outer len {} != {}", xs.len(), ys.len()));
188            }
189            for (i, (r1, r2)) in xs.iter().zip(ys).enumerate() {
190                if r1.len() != r2.len() {
191                    return Err(format!("Int2DArray row {} len {} != {}", i, r1.len(), r2.len()));
192                }
193                for (j, (x, y)) in r1.iter().zip(r2).enumerate() {
194                    if x != y {
195                        return Err(format!("Int2DArray[{}][{}] {} != {}", i, j, x, y));
196                    }
197                }
198            }
199            Ok(())
200        }
201        (Value::Float2DArray(xs), Value::Float2DArray(ys)) => {
202            if xs.len() != ys.len() {
203                return Err(format!("Float2DArray outer len {} != {}", xs.len(), ys.len()));
204            }
205            for (i, (r1, r2)) in xs.iter().zip(ys).enumerate() {
206                if r1.len() != r2.len() {
207                    return Err(format!("Float2DArray row {} len {} != {}", i, r1.len(), r2.len()));
208                }
209                for (j, (x, y)) in r1.iter().zip(r2).enumerate() {
210                    if (x - y).abs() > 1e-9 && !(x.is_nan() && y.is_nan()) {
211                        return Err(format!("Float2DArray[{}][{}] {} != {}", i, j, x, y));
212                    }
213                }
214            }
215            Ok(())
216        }
217        _ => if a == b { Ok(()) } else { Err(format!("actual={:?}, expected={:?} (no deep match)", a, b)) },
218    }
219}
220