use decy_codegen::CodeGenerator;
use decy_hir::{BinaryOperator, HirExpression, HirFunction, HirStatement, HirType};
fn create_function(name: &str, body: Vec<HirStatement>) -> HirFunction {
HirFunction::new_with_body(name.to_string(), HirType::Int, vec![], body)
}
#[test]
fn test_exec_generates_command_new() {
let func = create_function(
"run_ls",
vec![HirStatement::Expression(HirExpression::FunctionCall {
function: "execl".to_string(),
arguments: vec![
HirExpression::StringLiteral("/bin/ls".to_string()),
HirExpression::StringLiteral("ls".to_string()),
HirExpression::NullLiteral,
],
})],
);
let generator = CodeGenerator::new();
let code = generator.generate_function(&func);
assert!(code.contains("Command::new"), "Should generate Command::new, got:\n{}", code);
assert!(code.contains("/bin/ls"), "Should include command path:\n{}", code);
}
#[test]
fn test_exec_with_args_generates_arg_chain() {
let func = create_function(
"run_ls_la",
vec![HirStatement::Expression(HirExpression::FunctionCall {
function: "execl".to_string(),
arguments: vec![
HirExpression::StringLiteral("/bin/ls".to_string()),
HirExpression::StringLiteral("ls".to_string()),
HirExpression::StringLiteral("-la".to_string()),
HirExpression::NullLiteral,
],
})],
);
let generator = CodeGenerator::new();
let code = generator.generate_function(&func);
assert!(code.contains(".arg("), "Should generate .arg() for arguments:\n{}", code);
assert!(code.contains("-la"), "Should include -la argument:\n{}", code);
}
#[test]
fn test_fork_exec_generates_spawn() {
let func = create_function(
"spawn_process",
vec![
HirStatement::VariableDeclaration {
name: "pid".to_string(),
var_type: HirType::Int,
initializer: Some(HirExpression::FunctionCall {
function: "fork".to_string(),
arguments: vec![],
}),
},
HirStatement::If {
condition: HirExpression::BinaryOp {
op: BinaryOperator::Equal,
left: Box::new(HirExpression::Variable("pid".to_string())),
right: Box::new(HirExpression::IntLiteral(0)),
},
then_block: vec![HirStatement::Expression(HirExpression::FunctionCall {
function: "execl".to_string(),
arguments: vec![
HirExpression::StringLiteral("/bin/echo".to_string()),
HirExpression::StringLiteral("echo".to_string()),
HirExpression::StringLiteral("hello".to_string()),
HirExpression::NullLiteral,
],
})],
else_block: None,
},
],
);
let generator = CodeGenerator::new();
let code = generator.generate_function(&func);
assert!(!code.contains("= fork();"), "Should NOT generate raw fork() assignment:\n{}", code);
assert!(code.contains("Command::new"), "Should generate Command::new:\n{}", code);
}
#[test]
fn test_fork_exec_wait_generates_wait() {
let func = create_function(
"spawn_and_wait",
vec![
HirStatement::VariableDeclaration {
name: "pid".to_string(),
var_type: HirType::Int,
initializer: Some(HirExpression::FunctionCall {
function: "fork".to_string(),
arguments: vec![],
}),
},
HirStatement::If {
condition: HirExpression::BinaryOp {
op: BinaryOperator::Equal,
left: Box::new(HirExpression::Variable("pid".to_string())),
right: Box::new(HirExpression::IntLiteral(0)),
},
then_block: vec![HirStatement::Expression(HirExpression::FunctionCall {
function: "execl".to_string(),
arguments: vec![
HirExpression::StringLiteral("/bin/true".to_string()),
HirExpression::StringLiteral("true".to_string()),
HirExpression::NullLiteral,
],
})],
else_block: Some(vec![HirStatement::Expression(HirExpression::FunctionCall {
function: "waitpid".to_string(),
arguments: vec![
HirExpression::Variable("pid".to_string()),
HirExpression::AddressOf(Box::new(HirExpression::Variable(
"status".to_string(),
))),
HirExpression::IntLiteral(0),
],
})]),
},
],
);
let generator = CodeGenerator::new();
let code = generator.generate_function(&func);
assert!(
code.contains(".wait()") || code.contains(".status()"),
"Should generate .wait() or .status():\n{}",
code
);
}
#[test]
fn test_execlp_uses_path() {
let func = create_function(
"run_from_path",
vec![HirStatement::Expression(HirExpression::FunctionCall {
function: "execlp".to_string(),
arguments: vec![
HirExpression::StringLiteral("ls".to_string()),
HirExpression::StringLiteral("ls".to_string()),
HirExpression::NullLiteral,
],
})],
);
let generator = CodeGenerator::new();
let code = generator.generate_function(&func);
assert!(
code.contains("Command::new(\"ls\")"),
"Should use simple command name for PATH lookup:\n{}",
code
);
}
#[test]
fn test_no_subprocess_no_command() {
let func = create_function(
"regular_func",
vec![HirStatement::Return(Some(HirExpression::IntLiteral(42)))],
);
let generator = CodeGenerator::new();
let code = generator.generate_function(&func);
assert!(
!code.contains("Command"),
"Should NOT generate Command for non-subprocess code:\n{}",
code
);
}