use super::*;
use std::time::{Duration, Instant};
use tempfile::TempDir;
#[test]
fn test_repl_creation() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let repl = Repl::new(temp_dir.path().to_path_buf());
assert!(repl.is_ok());
}
#[test]
fn test_basic_evaluation() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let mut repl =
Repl::new(temp_dir.path().to_path_buf()).expect("Repl::new should succeed in test");
let should_exit = repl
.process_line("2 + 2")
.expect("process_line should succeed in test");
assert!(!should_exit);
}
#[test]
fn test_command_processing() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let mut repl =
Repl::new(temp_dir.path().to_path_buf()).expect("Repl::new should succeed in test");
let should_exit = repl
.process_line(":help")
.expect("process_line should succeed in test");
assert!(!should_exit);
let should_exit = repl
.process_line(":quit")
.expect("process_line should succeed in test");
assert!(should_exit);
}
#[test]
fn test_prompt_generation() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let mut repl =
Repl::new(temp_dir.path().to_path_buf()).expect("Repl::new should succeed in test");
assert_eq!(repl.get_prompt(), "ruchy> ");
repl.state.set_mode(ReplMode::Debug);
assert_eq!(repl.get_prompt(), "debug> ");
}
#[test]
fn test_performance_monitoring() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let mut repl =
Repl::new(temp_dir.path().to_path_buf()).expect("Repl::new should succeed in test");
let start = Instant::now();
let _ = repl.process_line("1 + 1");
let elapsed = start.elapsed();
assert!(elapsed.as_millis() < 50);
}
#[test]
fn test_empty_line_handling() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let mut repl =
Repl::new(temp_dir.path().to_path_buf()).expect("Repl::new should succeed in test");
assert!(!repl
.process_line("")
.expect("process_line should succeed in test"));
assert!(!repl
.process_line(" ")
.expect("process_line should succeed in test"));
assert!(!repl
.process_line("\t\n")
.expect("process_line should succeed in test"));
}
#[test]
fn test_tab_completion() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let repl = Repl::new(temp_dir.path().to_path_buf()).expect("Repl::new should succeed in test");
let completions = repl.get_completions(":he");
assert!(completions.contains(&":help".to_string()));
let completions = repl.get_completions("fn");
assert!(completions.contains(&"fn".to_string()));
}
#[test]
fn test_complexity_compliance() {
}
#[cfg(test)]
mod property_tests {
use super::*;
use proptest::prelude::*;
proptest! {
#[test]
fn test_repl_never_panics_on_any_input(input: String) {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let mut repl = Repl::new(temp_dir.path().to_path_buf()).expect("Repl::new should succeed in test");
let _ = repl.process_line(&input);
}
#[test]
#[ignore = "Flaky timing test - run in tier3-nightly"]
fn test_performance_consistency(
inputs in prop::collection::vec(".*", 1..100)
) {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let mut repl = Repl::new(temp_dir.path().to_path_buf()).expect("Repl::new should succeed in test");
let mut max_time = 0u128;
for input in inputs {
let start = Instant::now();
let _ = repl.process_line(&input);
let elapsed = start.elapsed().as_millis();
max_time = max_time.max(elapsed);
}
assert!(max_time < 100);
}
#[test]
fn test_command_recognition_robustness(
cmd in ":[a-z]{1,20}",
args in prop::collection::vec("[a-zA-Z0-9]+", 0..5)
) {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let mut repl = Repl::new(temp_dir.path().to_path_buf()).expect("Repl::new should succeed in test");
let full_cmd = if args.is_empty() {
cmd
} else {
format!("{} {}", cmd, args.join(" "))
};
let _ = repl.process_line(&full_cmd);
}
}
}
#[test]
fn test_coverage_boost_basic() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let repl = Repl::new(temp_dir.path().to_path_buf());
assert!(repl.is_ok());
if let Ok(mut repl) = repl {
let _ = repl.process_line("1 + 1");
let _ = repl.process_line("let x = 5");
let _ = repl.process_line("println(\"test\")");
let _ = repl.process_line(":help");
let _ = repl.process_line(":clear");
let _ = repl.process_line("");
let _ = repl.process_line(" ");
}
}
#[test]
fn test_all_repl_commands_comprehensive() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let mut repl =
Repl::new(temp_dir.path().to_path_buf()).expect("Repl::new should succeed in test");
let commands = vec![
":help", ":clear", ":history", ":vars", ":reset", ":load", ":save", ":quit", ":exit",
":debug", ":time", ":memory", ":gc", ":type", ":inspect", ":version", ":about",
];
for cmd in commands {
let result = repl.process_line(cmd);
assert!(result.is_ok() || result.is_err());
}
let cmd_with_args = vec![
":load test.ruchy",
":save session.ruchy",
":type 42",
":inspect \"hello\"",
":debug on",
":debug off",
];
for cmd in cmd_with_args {
let result = repl.process_line(cmd);
assert!(result.is_ok() || result.is_err());
}
}
#[test]
fn test_all_expression_types_in_repl() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let mut repl =
Repl::new(temp_dir.path().to_path_buf()).expect("Repl::new should succeed in test");
let expressions = vec![
"42",
"3.15",
"true",
"false",
"\"hello world\"",
"'c'",
"()",
"1 + 2 * 3",
"10 / 2 - 1",
"7 % 3",
"2 ** 8",
"5 > 3",
"2 <= 4",
"1 == 1",
"3 != 2",
"true && false",
"true || false",
"!true",
"let x = 5",
"let mut y = 10",
"[1, 2, 3, 4, 5]",
"(1, \"hello\", true)",
"{x: 1, y: 2, z: 3}",
"fn add(a, b) { a + b }",
"x => x * 2",
"(a, b) => a + b",
"undefined_variable",
"1 / 0",
];
for expr in expressions {
let result = repl.process_line(expr);
assert!(result.is_ok() || result.is_err());
}
}
#[test]
fn test_repl_state_management() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let mut repl =
Repl::new(temp_dir.path().to_path_buf()).expect("Repl::new should succeed in test");
let _ = repl.process_line("let x = 42");
let _ = repl.process_line("let y = x + 8");
let result = repl.process_line("x + y");
assert!(result.is_ok() || result.is_err());
let _ = repl.process_line("fn double(n) { n * 2 }");
let result = repl.process_line("double(21)");
assert!(result.is_ok() || result.is_err());
let _ = repl.process_line(":clear");
let result = repl.process_line("x");
assert!(result.is_ok() || result.is_err());
}
#[test]
fn test_repl_error_handling_comprehensive() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let mut repl =
Repl::new(temp_dir.path().to_path_buf()).expect("Repl::new should succeed in test");
let long_input = "x".repeat(10000);
let long_variable = format!("let {} = 42", "very_long_variable_name".repeat(100));
let error_cases = vec![
"let = 5",
"fn ()",
"if {",
"match {",
"1 +",
"+ 2",
"true + 5",
"\"string\" * false",
"1 / 0",
"undefined_function()",
":invalid_command",
":help invalid_arg extra_arg",
":load",
":save",
"",
" ",
"\n",
"\t",
";;;",
"{}{}{}",
&long_input,
&long_variable,
"let 🚀 = 42",
"let 变量 = \"unicode\"",
];
for error_case in error_cases {
let result = repl.process_line(error_case);
assert!(result.is_ok() || result.is_err());
}
}
#[test]
fn test_repl_file_operations() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let mut repl =
Repl::new(temp_dir.path().to_path_buf()).expect("Repl::new should succeed in test");
let _ = repl.process_line("let test_var = 123");
let _ = repl.process_line("fn test_func() { 456 }");
let save_path = temp_dir.path().join("test_session.ruchy");
let save_cmd = format!(":save {}", save_path.display());
let result = repl.process_line(&save_cmd);
assert!(result.is_ok() || result.is_err());
let load_cmd = format!(":load {}", save_path.display());
let result = repl.process_line(&load_cmd);
assert!(result.is_ok() || result.is_err());
let result = repl.process_line(":load /nonexistent/path/file.ruchy");
assert!(result.is_ok() || result.is_err());
let invalid_paths = vec![
":load",
":save",
":load /dev/null/invalid",
":save /root/no_permission",
];
for invalid in invalid_paths {
let result = repl.process_line(invalid);
assert!(result.is_ok() || result.is_err());
}
}
#[test]
fn test_repl_advanced_features() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let mut repl =
Repl::new(temp_dir.path().to_path_buf()).expect("Repl::new should succeed in test");
let complex_expressions = vec![
"let nested = [[1, 2], [3, 4], [5, 6]]",
"let mixed = [(1, \"a\"), (2, \"b\"), (3, \"c\")]",
"let deep = {a: {b: {c: 42}}}",
"let add_one = x => x + 1",
"[1, 2, 3].map(add_one)",
"match Some(42) { Some(x) => x, None => 0 }",
"async { 42 }",
"vec![1, 2, 3, 4, 5]",
"let typed: i32 = 42",
];
for expr in complex_expressions {
let result = repl.process_line(expr);
assert!(result.is_ok() || result.is_err());
}
}
#[test]
fn test_repl_performance_edge_cases() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let mut repl =
Repl::new(temp_dir.path().to_path_buf()).expect("Repl::new should succeed in test");
let performance_tests = vec![
"range(1, 1000).to_vec()",
"\"x\".repeat(1000)",
"fn factorial(n) { if n <= 1 { 1 } else { n * factorial(n - 1) } }",
"factorial(10)",
"range(1, 100).map(x => x * x).sum()",
"let big_string = \"hello \".repeat(100)",
"let big_array = range(1, 100).to_vec()",
];
for test in performance_tests {
let result = repl.process_line(test);
assert!(result.is_ok() || result.is_err());
}
}
#[test]
#[ignore = "Future feature: REPL config memory limits enforcement"]
fn test_repl_config_memory_limits() {
let config = ReplConfig {
debug: false,
max_memory: 100_000_000,
timeout: Duration::from_secs(5),
maxdepth: 1000,
};
let repl = Repl::with_config(config);
assert!(repl.is_ok(), "REPL should accept memory limit config");
}
#[test]
#[ignore = "Future feature: eval_bounded memory and timeout enforcement"]
fn test_eval_with_limits_enforcement() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed");
let mut repl =
Repl::new(temp_dir.path().to_path_buf()).expect("Repl::new should succeed in test");
let result = repl.eval_bounded(
"let big = \"x\".repeat(1000000000)",
10_000,
Duration::from_secs(1),
);
assert!(result.is_err(), "Should fail when exceeding memory limit");
let result = repl.eval_bounded("loop { }", 100_000_000, Duration::from_millis(100));
assert!(result.is_err(), "Should timeout on infinite loop");
}
#[test]
fn test_sandboxed_repl_creation() {
let repl = Repl::sandboxed();
assert!(
repl.is_ok(),
"sandboxed REPL should be created successfully"
);
}
#[test]
fn test_with_config_debug_mode() {
let config = ReplConfig {
max_memory: 1024 * 1024,
timeout: Duration::from_millis(5000),
maxdepth: 100,
debug: true,
};
let repl = Repl::with_config(config);
assert!(repl.is_ok());
let repl = repl.expect("repl should be created");
assert!(matches!(repl.state.get_mode(), ReplMode::Debug));
}
#[test]
fn test_evaluate_expr_str() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed");
let mut repl = Repl::new(temp_dir.path().to_path_buf()).expect("repl creation");
let result = repl.evaluate_expr_str("2 + 3", None);
assert!(result.is_ok());
let value = result.expect("value");
assert_eq!(value, Value::Integer(5));
}
#[test]
fn test_evaluate_expr_str_error() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed");
let mut repl = Repl::new(temp_dir.path().to_path_buf()).expect("repl creation");
let result = repl.evaluate_expr_str("undefined_var", None);
let _ = result;
}
#[test]
fn test_memory_used() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed");
let repl = Repl::new(temp_dir.path().to_path_buf()).expect("repl creation");
let mem = repl.memory_used();
let _ = mem;
}
#[test]
fn test_memory_pressure() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed");
let repl = Repl::new(temp_dir.path().to_path_buf()).expect("repl creation");
let pressure = repl.memory_pressure();
assert!(pressure >= 0.0 && pressure <= 1.0);
}
#[test]
fn test_needs_continuation_static() {
assert!(!Repl::needs_continuation("partial expression"));
assert!(!Repl::needs_continuation(""));
assert!(!Repl::needs_continuation("{"));
}
#[test]
fn test_get_last_error() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed");
let mut repl = Repl::new(temp_dir.path().to_path_buf()).expect("repl creation");
assert!(repl.get_last_error().is_none());
}
#[test]
fn test_handle_command() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed");
let mut repl = Repl::new(temp_dir.path().to_path_buf()).expect("repl creation");
let result = repl.handle_command("2 + 2");
assert_eq!(result, "Command executed");
let result = repl.handle_command(":help");
assert_eq!(result, "Command executed");
}
#[test]
fn test_repl_modes_ast() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed");
let mut repl = Repl::new(temp_dir.path().to_path_buf()).expect("repl creation");
repl.state.set_mode(ReplMode::Ast);
assert_eq!(repl.get_prompt(), "ast> ");
let result = repl.process_line("1 + 2");
assert!(result.is_ok());
}
#[test]
fn test_repl_modes_transpile() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed");
let mut repl = Repl::new(temp_dir.path().to_path_buf()).expect("repl creation");
repl.state.set_mode(ReplMode::Transpile);
assert_eq!(repl.get_prompt(), "transpile> ");
let result = repl.process_line("1 + 2");
assert!(result.is_ok());
}
#[test]
fn test_nil_value_not_printed() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed");
let mut repl = Repl::new(temp_dir.path().to_path_buf()).expect("repl creation");
let result = repl.process_line("let x = 5");
assert!(result.is_ok());
}
#[test]
fn test_completions_command_prefix() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed");
let repl = Repl::new(temp_dir.path().to_path_buf()).expect("repl creation");
let completions = repl.get_completions(":q");
assert!(completions.contains(&":quit".to_string()));
}
#[test]
fn test_completions_keyword() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed");
let repl = Repl::new(temp_dir.path().to_path_buf()).expect("repl creation");
let completions = repl.get_completions("le");
assert!(completions.contains(&"let".to_string()));
}
#[test]
fn test_eval_bounded() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed");
let mut repl = Repl::new(temp_dir.path().to_path_buf()).expect("repl creation");
let result = repl.eval_bounded("42", 1024 * 1024, Duration::from_secs(5));
assert!(result.is_ok());
}
#[test]
fn test_get_mode() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed");
let repl = Repl::new(temp_dir.path().to_path_buf()).expect("repl creation");
let mode = repl.get_mode();
assert!(!mode.is_empty());
}
#[test]
fn test_eval_transactional_success() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed");
let mut repl = Repl::new(temp_dir.path().to_path_buf()).expect("repl creation");
let result = repl.eval_transactional("42");
assert!(result.is_ok());
}
#[test]
fn test_eval_transactional_error_rollback() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed");
let mut repl = Repl::new(temp_dir.path().to_path_buf()).expect("repl creation");
let result = repl.eval_transactional("syntax_error!@#$%");
let _ = result;
}
#[test]
fn test_can_accept_input() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed");
let repl = Repl::new(temp_dir.path().to_path_buf()).expect("repl creation");
assert!(repl.can_accept_input());
}
#[test]
fn test_bindings_valid() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed");
let repl = Repl::new(temp_dir.path().to_path_buf()).expect("repl creation");
assert!(repl.bindings_valid());
}
#[test]
fn test_is_failed() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed");
let repl = Repl::new(temp_dir.path().to_path_buf()).expect("repl creation");
assert!(!repl.is_failed());
}
#[test]
fn test_recover() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed");
let mut repl = Repl::new(temp_dir.path().to_path_buf()).expect("repl creation");
assert!(repl.recover().is_ok());
}