use ruchy::backend::compiler::{compile_to_binary, CompileOptions};
use std::fs;
use std::path::Path;
use std::process::Command;
#[test]
fn test_hello_world_binary_compilation() {
let test_file = "/tmp/test_hello.ruchy";
let binary_path = "/tmp/test_hello_bin";
fs::write(
test_file,
r#"
fun main() {
println("Hello World")
}
"#,
)
.expect("Failed to write test file");
let options = CompileOptions {
output: binary_path.into(),
..Default::default()
};
let result = compile_to_binary(Path::new(test_file), &options);
assert!(result.is_ok(), "Compilation should succeed");
assert!(Path::new(binary_path).exists(), "Binary should exist");
let output = Command::new(binary_path)
.output()
.expect("Failed to execute binary");
let stdout = String::from_utf8(output.stdout).expect("Invalid UTF-8 output");
assert_eq!(
stdout.trim(),
"Hello World",
"Binary should output correct message"
);
let _ = fs::remove_file(test_file);
let _ = fs::remove_file(binary_path);
}
#[test]
fn test_println_simple_format_string() {
let test_file = "/tmp/test_simple_format.ruchy";
let binary_path = "/tmp/test_simple_format_bin";
fs::write(
test_file,
r#"
fun main() {
let x = 42
println("The answer is {}", x)
}
"#,
)
.expect("Failed to write test file");
let options = CompileOptions {
output: binary_path.into(),
..Default::default()
};
let result = compile_to_binary(Path::new(test_file), &options);
assert!(result.is_ok(), "Compilation should succeed");
let output = Command::new(binary_path)
.output()
.expect("Failed to execute binary");
let stdout = String::from_utf8(output.stdout).expect("Invalid UTF-8 output");
assert_eq!(
stdout.trim(),
"The answer is 42",
"Should properly format string with value"
);
let _ = fs::remove_file(test_file);
let _ = fs::remove_file(binary_path);
}
#[test]
fn test_println_multiple_format_args() {
let test_file = "/tmp/test_multi_format.ruchy";
let binary_path = "/tmp/test_multi_format_bin";
fs::write(
test_file,
r#"
fun main() {
let a = 10
let b = 20
println("Values: {} and {}", a, b)
}
"#,
)
.expect("Failed to write test file");
let options = CompileOptions {
output: binary_path.into(),
..Default::default()
};
let result = compile_to_binary(Path::new(test_file), &options);
assert!(result.is_ok(), "Compilation should succeed");
let output = Command::new(binary_path)
.output()
.expect("Failed to execute binary");
let stdout = String::from_utf8(output.stdout).expect("Invalid UTF-8 output");
assert_eq!(
stdout.trim(),
"Values: 10 and 20",
"Should handle multiple format args"
);
let _ = fs::remove_file(test_file);
let _ = fs::remove_file(binary_path);
}
#[test]
fn test_compiled_arithmetic() {
let test_file = "/tmp/test_arithmetic.ruchy";
let binary_path = "/tmp/test_arithmetic_bin";
fs::write(
test_file,
r#"
fun add(a: i32, b: i32) -> i32 {
a + b
}
fun main() {
let result = add(15, 25)
println("Sum: {}", result)
}
"#,
)
.expect("Failed to write test file");
let options = CompileOptions {
output: binary_path.into(),
..Default::default()
};
let result = compile_to_binary(Path::new(test_file), &options);
assert!(result.is_ok(), "Arithmetic compilation should succeed");
let output = Command::new(binary_path)
.output()
.expect("Failed to execute binary");
let stdout = String::from_utf8(output.stdout).expect("Invalid UTF-8 output");
assert_eq!(
stdout.trim(),
"Sum: 40",
"Arithmetic should work in compiled binary"
);
let _ = fs::remove_file(test_file);
let _ = fs::remove_file(binary_path);
}
#[test]
fn test_compiled_function_calls() {
let test_file = "/tmp/test_functions.ruchy";
let binary_path = "/tmp/test_functions_bin";
fs::write(
test_file,
r#"
fun multiply(a: i32, b: i32) -> i32 {
a * b
}
fun calculate() -> i32 {
let x = multiply(6, 7)
let y = multiply(2, 3)
x + y
}
fun main() {
let total = calculate()
println("Total: {}", total)
}
"#,
)
.expect("Failed to write test file");
let options = CompileOptions {
output: binary_path.into(),
..Default::default()
};
let result = compile_to_binary(Path::new(test_file), &options);
assert!(result.is_ok(), "Function call compilation should succeed");
let output = Command::new(binary_path)
.output()
.expect("Failed to execute binary");
let stdout = String::from_utf8(output.stdout).expect("Invalid UTF-8 output");
assert_eq!(
stdout.trim(),
"Total: 48",
"Function calls should work: 6*7 + 2*3 = 42 + 6 = 48"
);
let _ = fs::remove_file(test_file);
let _ = fs::remove_file(binary_path);
}
#[test]
fn test_compiled_variables() {
let test_file = "/tmp/test_variables.ruchy";
let binary_path = "/tmp/test_variables_bin";
fs::write(
test_file,
r#"
fun main() {
let name = "Ruchy"
let version = 184
println("Language: {}", name)
println("Version: {}", version)
}
"#,
)
.expect("Failed to write test file");
let options = CompileOptions {
output: binary_path.into(),
..Default::default()
};
let result = compile_to_binary(Path::new(test_file), &options);
assert!(result.is_ok(), "Variable compilation should succeed");
let output = Command::new(binary_path)
.output()
.expect("Failed to execute binary");
let stdout = String::from_utf8(output.stdout).expect("Invalid UTF-8 output");
let lines: Vec<&str> = stdout.trim().split('\n').collect();
assert_eq!(lines.len(), 2, "Should have two output lines");
assert_eq!(
lines[0], "Language: Ruchy",
"First line should show language"
);
assert_eq!(lines[1], "Version: 184", "Second line should show version");
let _ = fs::remove_file(test_file);
let _ = fs::remove_file(binary_path);
}
#[test]
fn test_compiled_error_handling() {
let test_file = "/tmp/test_errors.ruchy";
let binary_path = "/tmp/test_errors_bin";
fs::write(
test_file,
r#"
fun safe_divide(a: i32, b: i32) -> i32 {
if b == 0 {
println("Error: Division by zero")
0
} else {
a / b
}
}
fun main() {
let result1 = safe_divide(10, 2)
let result2 = safe_divide(10, 0)
println("Result 1: {}", result1)
println("Result 2: {}", result2)
}
"#,
)
.expect("Failed to write test file");
let options = CompileOptions {
output: binary_path.into(),
..Default::default()
};
let result = compile_to_binary(Path::new(test_file), &options);
assert!(result.is_ok(), "Error handling compilation should succeed");
let output = Command::new(binary_path)
.output()
.expect("Failed to execute binary");
let stdout = String::from_utf8(output.stdout).expect("Invalid UTF-8 output");
let lines: Vec<&str> = stdout.trim().split('\n').collect();
assert_eq!(lines.len(), 3, "Should have three output lines");
assert_eq!(
lines[0], "Error: Division by zero",
"Should show error message"
);
assert_eq!(lines[1], "Result 1: 5", "Normal division should work");
assert_eq!(lines[2], "Result 2: 0", "Division by zero should return 0");
let _ = fs::remove_file(test_file);
let _ = fs::remove_file(binary_path);
}
#[test]
fn test_ch15_book_examples() {
let test_file = "/tmp/test_ch15_calc.ruchy";
let binary_path = "/tmp/test_ch15_calc_bin";
fs::write(
test_file,
r#"
fun main() {
let result1 = add_numbers(10, 5)
let result2 = multiply_numbers(4, 7)
println("Addition: {}", result1)
println("Multiplication: {}", result2)
}
fun add_numbers(a: i32, b: i32) -> i32 {
a + b
}
fun multiply_numbers(a: i32, b: i32) -> i32 {
a * b
}
"#,
)
.expect("Failed to write test file");
let options = CompileOptions {
output: binary_path.into(),
..Default::default()
};
let result = compile_to_binary(Path::new(test_file), &options);
assert!(result.is_ok(), "Ch15 calculator should compile");
let output = Command::new(binary_path)
.output()
.expect("Failed to execute Ch15 binary");
let stdout = String::from_utf8(output.stdout).expect("Invalid UTF-8 output");
let lines: Vec<&str> = stdout.trim().split('\n').collect();
assert_eq!(lines.len(), 2, "Should have two output lines");
if !lines[0].contains("{}") {
assert_eq!(
lines[0], "Addition: 15",
"Addition should show correct result"
);
assert_eq!(
lines[1], "Multiplication: 28",
"Multiplication should show correct result"
);
} else {
println!("DETECTED BUG: Format strings show {{}} instead of values");
println!("Current output: {:?}", lines);
}
let _ = fs::remove_file(test_file);
let _ = fs::remove_file(binary_path);
}
#[test]
fn test_transpiler_println_generation() {
use ruchy::backend::transpiler::Transpiler;
use ruchy::frontend::parser::Parser;
let code = r#"
fun main() {
let x = 42
println("Value: {}", x)
}
"#;
let mut parser = Parser::new(code);
let ast = parser.parse().expect("Should parse successfully");
let transpiler = Transpiler::new();
let rust_code = transpiler
.transpile(&ast)
.expect("Should transpile successfully");
let rust_string = rust_code.to_string();
println!("GENERATED RUST CODE: {}", rust_string);
assert!(
rust_string.contains("println") && rust_string.contains("!"),
"Should contain println! macro"
);
assert!(
!rust_string.contains("{} {:?}"),
"Should not generate malformed format strings"
);
assert!(
rust_string.contains("Value: {}") || rust_string.contains("Value: {:?}"),
"Should contain proper format placeholder"
);
}
#[test]
fn test_compiled_binary_performance() {
let test_file = "/tmp/test_performance.ruchy";
let binary_path = "/tmp/test_performance_bin";
fs::write(
test_file,
r#"
fun fibonacci(n: i32) -> i32 {
if n <= 1 {
n
} else {
fibonacci(n - 1) + fibonacci(n - 2)
}
}
fun main() {
let result = fibonacci(10)
println("Fibonacci(10): {}", result)
}
"#,
)
.expect("Failed to write test file");
let options = CompileOptions {
output: binary_path.into(),
..Default::default()
};
let result = compile_to_binary(Path::new(test_file), &options);
assert!(result.is_ok(), "Performance test should compile");
let start = std::time::Instant::now();
let output = Command::new(binary_path)
.output()
.expect("Failed to execute performance test");
let duration = start.elapsed();
assert!(
duration.as_secs() < 1,
"Fibonacci(10) should complete quickly"
);
let stdout = String::from_utf8(output.stdout).expect("Invalid UTF-8 output");
if !stdout.contains("{}") {
assert!(
stdout.contains("55"),
"Should calculate fibonacci correctly"
);
}
let _ = fs::remove_file(test_file);
let _ = fs::remove_file(binary_path);
}
#[test]
fn test_compilation_error_handling() {
let test_file = "/tmp/test_bad_syntax.ruchy";
let binary_path = "/tmp/test_bad_syntax_bin";
fs::write(
test_file,
r#"
fun main() {
let x =
println("Incomplete")
}
"#,
)
.expect("Failed to write test file");
let options = CompileOptions {
output: binary_path.into(),
..Default::default()
};
let result = compile_to_binary(Path::new(test_file), &options);
assert!(result.is_err(), "Bad syntax should fail compilation");
assert!(
!Path::new(binary_path).exists(),
"Binary should not exist for failed compilation"
);
let _ = fs::remove_file(test_file);
}