use std::fs;
use std::path::Path;
use std::process::Command;
fn compile_rust_code(rust_code: &str, output_name: &str) -> Result<(), String> {
let temp_dir = std::env::temp_dir();
let temp_file = temp_dir.join(format!("{}.rs", output_name));
let output_file = temp_dir.join(output_name);
fs::write(&temp_file, rust_code).expect("Failed to write temp file");
let compile_output = Command::new("rustc")
.arg(&temp_file)
.arg("--crate-type")
.arg("lib")
.arg("-o")
.arg(&output_file)
.output()
.expect("Failed to run rustc");
let _ = fs::remove_file(&temp_file);
let _ = fs::remove_file(&output_file);
if compile_output.status.success() {
Ok(())
} else {
Err(String::from_utf8_lossy(&compile_output.stderr).to_string())
}
}
#[test]
fn test_transpile_compound_assignments() {
let example_path = "../../examples/pointer_arithmetic/compound_assignments.c";
assert!(Path::new(example_path).exists(), "Example file {} should exist", example_path);
let c_code = fs::read_to_string(example_path).expect("Failed to read example file");
assert!(c_code.contains("+="), "Should contain += operator");
assert!(c_code.contains("-="), "Should contain -= operator");
assert!(c_code.contains("*="), "Should contain *= operator");
assert!(c_code.contains("/="), "Should contain /= operator");
assert!(c_code.contains("%="), "Should contain %= operator");
let result = decy_core::transpile(&c_code);
assert!(result.is_ok(), "Should transpile compound assignments, got error: {:?}", result.err());
let rust_code = result.unwrap();
assert!(rust_code.contains("fn increment_by"), "Should contain increment_by function");
assert!(rust_code.contains("fn decrement_by"), "Should contain decrement_by function");
assert!(rust_code.contains("fn multiply_by"), "Should contain multiply_by function");
assert!(rust_code.contains("fn divide_by"), "Should contain divide_by function");
assert!(rust_code.contains("fn modulo_by"), "Should contain modulo_by function");
assert!(rust_code.contains("fn advance_pointer"), "Should contain advance_pointer function");
assert!(rust_code.contains("i32"), "Should use i32 for int types, got: {}", rust_code);
println!("Generated Rust code:\n{}", rust_code);
match compile_rust_code(&rust_code, "test_compound_assignments") {
Ok(_) => (),
Err(e) => panic!("Generated Rust code should compile. Errors:\n{}", e),
}
}
#[test]
#[ignore = "DECY-TBD: Codegen produces non-mut parameters that are then mutated (n = n - 1 with immutable n)"]
fn test_transpile_increment_decrement() {
let example_path = "../../examples/pointer_arithmetic/increment_decrement.c";
assert!(Path::new(example_path).exists(), "Example file {} should exist", example_path);
let c_code = fs::read_to_string(example_path).expect("Failed to read example file");
assert!(c_code.contains("++"), "Should contain ++ operator");
assert!(c_code.contains("--"), "Should contain -- operator");
let result = decy_core::transpile(&c_code);
assert!(result.is_ok(), "Should transpile increment/decrement, got error: {:?}", result.err());
let rust_code = result.unwrap();
assert!(
rust_code.contains("fn post_increment_test"),
"Should contain post_increment_test function"
);
assert!(
rust_code.contains("fn pre_increment_test"),
"Should contain pre_increment_test function"
);
assert!(
rust_code.contains("fn post_decrement_test"),
"Should contain post_decrement_test function"
);
assert!(
rust_code.contains("fn pre_decrement_test"),
"Should contain pre_decrement_test function"
);
assert!(rust_code.contains("fn sum_to_n"), "Should contain sum_to_n function with for loop");
assert!(rust_code.contains("fn countdown_sum"), "Should contain countdown_sum function");
println!("Generated Rust code:\n{}", rust_code);
match compile_rust_code(&rust_code, "test_increment_decrement") {
Ok(_) => (),
Err(e) => panic!("Generated Rust code should compile. Errors:\n{}", e),
}
}
#[test]
#[ignore = "DECY-TBD: Codegen produces invalid pointer arithmetic on slices (arr + arr.len())"]
fn test_transpile_real_world_patterns() {
let example_path = "../../examples/pointer_arithmetic/real_world_patterns.c";
assert!(Path::new(example_path).exists(), "Example file {} should exist", example_path);
let c_code = fs::read_to_string(example_path).expect("Failed to read example file");
assert!(c_code.contains("arr + size"), "Should contain pointer arithmetic");
assert!(c_code.contains("arr++"), "Should contain pointer increment");
assert!(c_code.contains("break"), "Should contain break statement");
assert!(c_code.contains("continue"), "Should contain continue statement");
let result = decy_core::transpile(&c_code);
assert!(result.is_ok(), "Should transpile real-world patterns, got error: {:?}", result.err());
let rust_code = result.unwrap();
assert!(rust_code.contains("fn sum_array"), "Should contain sum_array function");
assert!(rust_code.contains("fn find_first"), "Should contain find_first function");
assert!(
rust_code.contains("fn count_even"),
"Should contain count_even function with continue"
);
assert!(
rust_code.contains("fn linear_search"),
"Should contain linear_search function with break"
);
assert!(rust_code.contains("fn string_length"), "Should contain string_length function");
assert!(rust_code.contains("break"), "Should preserve break statements");
assert!(rust_code.contains("continue"), "Should preserve continue statements");
println!("Generated Rust code:\n{}", rust_code);
match compile_rust_code(&rust_code, "test_real_world_patterns") {
Ok(_) => (),
Err(e) => panic!("Generated Rust code should compile. Errors:\n{}", e),
}
}
#[test]
fn test_for_loop_with_increment() {
let c_code = r#"
int sum_range(int n) {
int sum = 0;
int i;
for (i = 0; i < n; i++) {
sum += i;
}
return sum;
}
"#;
let result = decy_core::transpile(c_code);
assert!(result.is_ok(), "Should transpile for loop with i++, got error: {:?}", result.err());
let rust_code = result.unwrap();
assert!(rust_code.contains("while"), "For loop should convert to while loop");
println!("Generated Rust code:\n{}", rust_code);
match compile_rust_code(&rust_code, "test_for_loop_increment") {
Ok(_) => (),
Err(e) => panic!("Generated Rust code should compile. Errors:\n{}", e),
}
}
#[test]
fn test_nested_loops_with_break_continue() {
let c_code = r#"
int find_pair_sum(int* arr, int size, int target) {
int i;
int j;
for (i = 0; i < size; i++) {
for (j = i + 1; j < size; j++) {
if (arr[i] + arr[j] == target) {
return 1; // Found
}
}
}
return 0; // Not found
}
int count_valid(int* arr, int size) {
int count = 0;
int i;
for (i = 0; i < size; i++) {
if (arr[i] < 0) {
continue; // Skip negative
}
count++;
}
return count;
}
"#;
let result = decy_core::transpile(c_code);
assert!(result.is_ok(), "Should transpile nested loops, got error: {:?}", result.err());
let rust_code = result.unwrap();
println!("Generated Rust code:\n{}", rust_code);
match compile_rust_code(&rust_code, "test_nested_loops") {
Ok(_) => (),
Err(e) => panic!("Generated Rust code should compile. Errors:\n{}", e),
}
}