/home/noah/src/ruchy/src/runtime/repl_function_tests.rs
Line | Count | Source |
1 | | //! Unit tests for function call evaluation in REPL |
2 | | |
3 | | #[cfg(test)] |
4 | | #[allow(clippy::panic)] // Tests may panic on failure |
5 | | #[allow(clippy::expect_used)] // Tests use expect for error messages |
6 | | mod tests { |
7 | | use super::super::*; |
8 | | use anyhow::Result; |
9 | | |
10 | | /// Test basic println function call |
11 | | #[test] |
12 | 1 | fn test_println_basic() -> Result<()> { |
13 | 1 | let mut repl = Repl::new()?0 ; |
14 | 1 | let result = repl.eval(r#"println("Hello, World!")"#)?0 ; |
15 | 1 | assert_eq!(result, ""); |
16 | 1 | Ok(()) |
17 | 1 | } |
18 | | |
19 | | /// Test println with multiple arguments |
20 | | #[test] |
21 | 1 | fn test_println_multiple_args() -> Result<()> { |
22 | 1 | let mut repl = Repl::new()?0 ; |
23 | 1 | let result = repl.eval(r#"println("Hello", "World", "!")"#)?0 ; |
24 | 1 | assert_eq!(result, ""); |
25 | 1 | Ok(()) |
26 | 1 | } |
27 | | |
28 | | /// Test println with variables |
29 | | #[test] |
30 | 1 | fn test_println_with_variables() -> Result<()> { |
31 | 1 | let mut repl = Repl::new()?0 ; |
32 | 1 | repl.eval("let x = 42")?0 ; |
33 | 1 | let result = repl.eval("println(x)")?0 ; |
34 | 1 | assert_eq!(result, ""); |
35 | 1 | Ok(()) |
36 | 1 | } |
37 | | |
38 | | /// Test println with expressions |
39 | | #[test] |
40 | 1 | fn test_println_with_expressions() -> Result<()> { |
41 | 1 | let mut repl = Repl::new()?0 ; |
42 | 1 | let result = repl.eval("println(2 + 3)")?0 ; |
43 | 1 | assert_eq!(result, ""); |
44 | 1 | Ok(()) |
45 | 1 | } |
46 | | |
47 | | /// Test println with different value types |
48 | | #[test] |
49 | 1 | fn test_println_different_types() -> Result<()> { |
50 | 1 | let mut repl = Repl::new()?0 ; |
51 | | |
52 | | // Integer |
53 | 1 | let result = repl.eval("println(42)")?0 ; |
54 | 1 | assert_eq!(result, ""); |
55 | | |
56 | | // Float |
57 | 1 | let result = repl.eval("println(3.14)")?0 ; |
58 | 1 | assert_eq!(result, ""); |
59 | | |
60 | | // Boolean |
61 | 1 | let result = repl.eval("println(true)")?0 ; |
62 | 1 | assert_eq!(result, ""); |
63 | | |
64 | | // String |
65 | 1 | let result = repl.eval(r#"println("test")"#)?0 ; |
66 | 1 | assert_eq!(result, ""); |
67 | | |
68 | 1 | Ok(()) |
69 | 1 | } |
70 | | |
71 | | /// Test print function (without newline) |
72 | | #[test] |
73 | 1 | fn test_print_function() -> Result<()> { |
74 | 1 | let mut repl = Repl::new()?0 ; |
75 | 1 | let result = repl.eval(r#"print("Hello")"#)?0 ; |
76 | 1 | assert_eq!(result, ""); |
77 | 1 | Ok(()) |
78 | 1 | } |
79 | | |
80 | | /// Test print with multiple arguments |
81 | | #[test] |
82 | 1 | fn test_print_multiple_args() -> Result<()> { |
83 | 1 | let mut repl = Repl::new()?0 ; |
84 | 1 | let result = repl.eval(r#"print("A", "B", "C")"#)?0 ; |
85 | 1 | assert_eq!(result, ""); |
86 | 1 | Ok(()) |
87 | 1 | } |
88 | | |
89 | | /// Test unknown function error |
90 | | #[test] |
91 | 1 | fn test_unknown_function_error() { |
92 | 1 | let Ok(mut repl) = Repl::new() else { |
93 | 0 | panic!("REPL creation should succeed"); |
94 | | }; |
95 | 1 | let result = repl.eval("unknown_function()"); |
96 | 1 | assert!(result.is_err()); |
97 | 1 | if let Err(error) = result { |
98 | 1 | let error_msg = error.to_string(); |
99 | 1 | assert!(error_msg.contains("Unknown function")); |
100 | 0 | } |
101 | 1 | } |
102 | | |
103 | | /// Test complex function calls with nested expressions |
104 | | #[test] |
105 | 1 | fn test_complex_function_calls() -> Result<()> { |
106 | 1 | let mut repl = Repl::new()?0 ; |
107 | 1 | repl.eval("let x = 10")?0 ; |
108 | 1 | repl.eval("let y = 20")?0 ; |
109 | 1 | let result = repl.eval("println(x + y, x * y)")?0 ; |
110 | 1 | assert_eq!(result, ""); |
111 | 1 | Ok(()) |
112 | 1 | } |
113 | | |
114 | | /// Test function calls with string interpolation-like behavior |
115 | | #[test] |
116 | 1 | fn test_function_call_string_formatting() -> Result<()> { |
117 | 1 | let mut repl = Repl::new()?0 ; |
118 | 1 | repl.eval("let name = \"World\"")?0 ; |
119 | 1 | let result = repl.eval(r#"println("Hello,", name, "!")"#)?0 ; |
120 | 1 | assert_eq!(result, ""); |
121 | 1 | Ok(()) |
122 | 1 | } |
123 | | |
124 | | /// Test function calls return unit type |
125 | | #[test] |
126 | 1 | fn test_function_calls_return_unit() -> Result<()> { |
127 | 1 | let mut repl = Repl::new()?0 ; |
128 | 1 | let result = repl.eval(r#"println("test")"#)?0 ; |
129 | 1 | assert_eq!(result, ""); |
130 | | |
131 | | // Test that we can assign function call results |
132 | 1 | let result = repl.eval(r#"let result = println("assign test")"#)?0 ; |
133 | 1 | assert_eq!(result, ""); |
134 | 1 | Ok(()) |
135 | 1 | } |
136 | | |
137 | | /// Test function calls work in expressions |
138 | | #[test] |
139 | 1 | fn test_function_calls_in_expressions() -> Result<()> { |
140 | 1 | let mut repl = Repl::new()?0 ; |
141 | | // Function calls in if expressions |
142 | 1 | let result = |
143 | 1 | repl.eval(r#"if true { println("true branch") } else { println("false branch") }"#)?0 ; |
144 | 1 | assert_eq!(result, ""); |
145 | 1 | Ok(()) |
146 | 1 | } |
147 | | |
148 | | /// Property-based test: all function calls should return unit |
149 | | #[test] |
150 | 1 | fn test_property_all_builtin_calls_return_unit() -> Result<()> { |
151 | 1 | let mut repl = Repl::new()?0 ; |
152 | 1 | let builtin_calls = vec![ |
153 | | r#"println("test")"#, |
154 | 1 | r#"print("test")"#, |
155 | 1 | r"println(42)", |
156 | 1 | r"print(3.14)", |
157 | 1 | r"println(true)", |
158 | 1 | r"print(false)", |
159 | | ]; |
160 | | |
161 | 7 | for call6 in builtin_calls { |
162 | 6 | let result = repl.eval(call)?0 ; |
163 | 6 | assert_eq!(result, "", "Function call {call} should return unit"0 ); |
164 | | } |
165 | 1 | Ok(()) |
166 | 1 | } |
167 | | |
168 | | /// Test memory bounds with function calls |
169 | | #[test] |
170 | 1 | fn test_function_call_memory_bounds() -> Result<()> { |
171 | 1 | let mut repl = Repl::new()?0 ; |
172 | | // Test with large string arguments |
173 | 1 | let large_string = "x".repeat(1000); |
174 | 1 | let call = format!(r#"println("{large_string}")"#); |
175 | 1 | let result = repl.eval(&call)?0 ; |
176 | 1 | assert_eq!(result, ""); |
177 | 1 | Ok(()) |
178 | 1 | } |
179 | | } |
180 | | |
181 | | #[cfg(test)] |
182 | | #[allow(clippy::panic)] // Tests may panic on failure |
183 | | #[allow(clippy::expect_used)] // Tests use expect for error messages |
184 | | mod property_tests { |
185 | | use super::super::*; |
186 | | use anyhow::Result; |
187 | | |
188 | | /// Property-like test: All builtin function calls should return unit type |
189 | | #[test] |
190 | 1 | fn test_builtin_calls_always_return_unit() -> Result<()> { |
191 | 1 | let mut repl = Repl::new()?0 ; |
192 | | |
193 | 1 | let test_cases = vec![ |
194 | | "println()", |
195 | 1 | "println(42)", |
196 | 1 | "println(\"test\")", |
197 | 1 | "println(true)", |
198 | 1 | "println(1, 2, 3)", |
199 | 1 | "print(\"hello\")", |
200 | 1 | "print(42, \"world\")", |
201 | | ]; |
202 | | |
203 | 8 | for call7 in test_cases { |
204 | 7 | let result = repl.eval(call)?0 ; |
205 | 7 | assert_eq!(result, "", "Function call '{call}' should return unit (suppressed)"0 ); |
206 | | } |
207 | 1 | Ok(()) |
208 | 1 | } |
209 | | |
210 | | /// Property-like test: Function calls work with various expression types |
211 | | #[test] |
212 | 1 | fn test_function_calls_with_different_expressions() -> Result<()> { |
213 | 1 | let mut repl = Repl::new()?0 ; |
214 | 1 | repl.eval("let x = 10")?0 ; |
215 | 1 | repl.eval("let y = 20")?0 ; |
216 | | |
217 | 1 | let test_cases = vec![ |
218 | | "println(x + y)", |
219 | 1 | "println(x * y, x - y)", |
220 | 1 | "println(x > y, x < y)", |
221 | 1 | "println(x == 10 && y == 20)", |
222 | 1 | "print(\"Result: \", x + y * 2)", |
223 | | ]; |
224 | | |
225 | 6 | for call5 in test_cases { |
226 | 5 | let result = repl.eval(call)?0 ; |
227 | 5 | assert_eq!(result, "", "Expression call '{call}' should return unit"0 ); |
228 | | } |
229 | 1 | Ok(()) |
230 | 1 | } |
231 | | |
232 | | /// Property-like test: Function calls handle edge cases correctly |
233 | | #[test] |
234 | 1 | fn test_function_call_edge_cases() -> Result<()> { |
235 | 1 | let mut repl = Repl::new()?0 ; |
236 | | |
237 | | // Empty arguments |
238 | 1 | let result = repl.eval("println()")?0 ; |
239 | 1 | assert_eq!(result, ""); |
240 | | |
241 | | // Single arguments of each type |
242 | 1 | let result = repl.eval("println(42)")?0 ; |
243 | 1 | assert_eq!(result, ""); |
244 | | |
245 | 1 | let result = repl.eval("println(3.14)")?0 ; |
246 | 1 | assert_eq!(result, ""); |
247 | | |
248 | 1 | let result = repl.eval(r#"println("string")"#)?0 ; |
249 | 1 | assert_eq!(result, ""); |
250 | | |
251 | 1 | let result = repl.eval("println(true)")?0 ; |
252 | 1 | assert_eq!(result, ""); |
253 | | |
254 | 1 | Ok(()) |
255 | 1 | } |
256 | | |
257 | | /// Property-like test: print and println have consistent return types |
258 | | #[test] |
259 | 1 | fn test_print_vs_println_consistency() -> Result<()> { |
260 | 1 | let mut repl = Repl::new()?0 ; |
261 | | |
262 | 1 | let args_list = vec![r#""hello""#, "42", "true", r#""a", "b", "c""#, "1, 2, 3"]; |
263 | | |
264 | 6 | for args5 in args_list { |
265 | 5 | let print_call = format!("print({args})"); |
266 | 5 | let println_call = format!("println({args})"); |
267 | | |
268 | 5 | let print_result = repl.eval(&print_call)?0 ; |
269 | 5 | let println_result = repl.eval(&println_call)?0 ; |
270 | | |
271 | 5 | assert_eq!(print_result, "", "print({args}) should return unit"0 ); |
272 | 5 | assert_eq!(println_result, "", "println({args}) should return unit"0 ); |
273 | 5 | assert_eq!( |
274 | | print_result, println_result, |
275 | 0 | "print and println should have same return type" |
276 | | ); |
277 | | } |
278 | | |
279 | 1 | Ok(()) |
280 | 1 | } |
281 | | } |