#![allow(clippy::unwrap_used)]
#![allow(clippy::expect_used)]
use super::makefile::emit_makefile;
use crate::ast::restricted::{Function, Literal, Type};
use crate::ast::{Expr, RestrictedAst, Stmt};
fn make_ast(stmts: Vec<Stmt>) -> RestrictedAst {
RestrictedAst {
functions: vec![Function {
name: "main".to_string(),
params: vec![],
return_type: Type::Void,
body: stmts,
}],
entry_point: "main".to_string(),
}
}
#[test]
fn test_MCOV2_001_line_with_hash_name_not_target() {
let ast = make_ast(vec![Stmt::Expr(Expr::FunctionCall {
name: "exec".to_string(),
args: vec![Expr::Literal(Literal::Str("#comment: value".to_string()))],
})]);
let result = emit_makefile(&ast).unwrap();
assert!(
!result.contains("#comment:") || result.contains("#comment"),
"Result: {result}"
);
}
#[test]
fn test_MCOV2_002_recursive_var_dot_prefix_skipped() {
let ast = make_ast(vec![Stmt::Expr(Expr::FunctionCall {
name: "exec".to_string(),
args: vec![Expr::Literal(Literal::Str(".hidden = value".to_string()))],
})]);
let result = emit_makefile(&ast).unwrap();
assert!(result.contains(".hidden"), "Result: {result}");
}
#[test]
fn test_MCOV2_003_recursive_var_hash_prefix_skipped() {
let ast = make_ast(vec![Stmt::Expr(Expr::FunctionCall {
name: "exec".to_string(),
args: vec![Expr::Literal(Literal::Str("# VAR = value".to_string()))],
})]);
let result = emit_makefile(&ast).unwrap();
assert!(result.contains("# VAR"), "Result: {result}");
}
#[test]
fn test_MCOV2_004_line_with_space_in_name_not_var() {
let ast = make_ast(vec![Stmt::Expr(Expr::FunctionCall {
name: "exec".to_string(),
args: vec![Expr::Literal(Literal::Str(
"multi word = value".to_string(),
))],
})]);
let result = emit_makefile(&ast).unwrap();
assert!(result.contains("multi word"), "Result: {result}");
}
#[test]
fn test_MCOV2_005_simple_assignment_empty_name_not_var() {
let ast = make_ast(vec![Stmt::Expr(Expr::FunctionCall {
name: "exec".to_string(),
args: vec![Expr::Literal(Literal::Str(":= value".to_string()))],
})]);
let result = emit_makefile(&ast).unwrap();
assert!(
result.contains(":=") || result.contains("value"),
"Result: {result}"
);
}
#[test]
fn test_MCOV2_006_simple_assignment_space_in_name_not_var() {
let ast = make_ast(vec![Stmt::Expr(Expr::FunctionCall {
name: "exec".to_string(),
args: vec![Expr::Literal(Literal::Str(
"multi word := value".to_string(),
))],
})]);
let result = emit_makefile(&ast).unwrap();
assert!(result.contains("multi word"), "Result: {result}");
}
#[test]
fn test_MCOV2_007_target_name_with_space_not_target() {
let ast = make_ast(vec![Stmt::Expr(Expr::FunctionCall {
name: "exec".to_string(),
args: vec![Expr::Literal(Literal::Str("multi word: deps".to_string()))],
})]);
let result = emit_makefile(&ast).unwrap();
assert!(result.contains("multi word"), "Result: {result}");
}
#[test]
fn test_MCOV2_008_bash_fallback_for_raw_code() {
let ast = make_ast(vec![Stmt::Expr(Expr::FunctionCall {
name: "unknown_func".to_string(),
args: vec![Expr::Literal(Literal::Str("arg".to_string()))],
})]);
let result = emit_makefile(&ast).unwrap();
assert!(
result.contains("all") || result.contains(".PHONY") || !result.is_empty(),
"Result: {result}"
);
}
#[test]
fn test_MCOV2_009_mixed_output_types_raw_mode() {
let ast = make_ast(vec![
Stmt::Let {
name: "cc".to_string(),
value: Expr::Literal(Literal::Str("gcc".to_string())),
declaration: true,
},
Stmt::Expr(Expr::FunctionCall {
name: "rash_println".to_string(),
args: vec![Expr::FunctionCall {
name: "__format_concat".to_string(),
args: vec![
Expr::Variable("cc".to_string()),
Expr::Literal(Literal::Str(" -Wall".to_string())),
],
}],
}),
Stmt::Expr(Expr::FunctionCall {
name: "rash_print".to_string(),
args: vec![Expr::Literal(Literal::Str("no newline".to_string()))],
}),
Stmt::Expr(Expr::FunctionCall {
name: "exec".to_string(),
args: vec![Expr::Literal(Literal::Str("LDFLAGS := -lm".to_string()))],
}),
]);
let result = emit_makefile(&ast).unwrap();
assert!(
result.contains("gcc") || result.contains("no newline") || result.contains("LDFLAGS"),
"Result: {result}"
);
}
#[test]
fn test_MCOV2_010_nested_format_concat() {
let ast = make_ast(vec![Stmt::Expr(Expr::FunctionCall {
name: "rash_println".to_string(),
args: vec![Expr::FunctionCall {
name: "__format_concat".to_string(),
args: vec![
Expr::Literal(Literal::Str("a".to_string())),
Expr::FunctionCall {
name: "__format_concat".to_string(),
args: vec![
Expr::Literal(Literal::Str("b".to_string())),
Expr::Literal(Literal::Str("c".to_string())),
],
},
],
}],
})]);
let result = emit_makefile(&ast).unwrap();
assert!(result.contains("abc"), "Nested concat: {result}");
}
#[test]
fn test_MCOV2_011_phony_line_without_colon() {
let ast = make_ast(vec![Stmt::Expr(Expr::FunctionCall {
name: "exec".to_string(),
args: vec![Expr::Literal(Literal::Str(".PHONY".to_string()))],
})]);
let result = emit_makefile(&ast).unwrap();
assert!(result.contains(".PHONY"), "Result: {result}");
}
#[test]
fn test_MCOV2_012_target_empty_name_not_created() {
let ast = make_ast(vec![Stmt::Expr(Expr::FunctionCall {
name: "exec".to_string(),
args: vec![Expr::Literal(Literal::Str(": deps".to_string()))],
})]);
let result = emit_makefile(&ast).unwrap();
assert!(
result.contains(": deps") || !result.is_empty(),
"Result: {result}"
);
}
#[test]
fn test_MCOV2_013_exec_no_args() {
let ast = make_ast(vec![Stmt::Expr(Expr::FunctionCall {
name: "exec".to_string(),
args: vec![],
})]);
let result = emit_makefile(&ast);
assert!(result.is_ok(), "Err: {:?}", result.err());
}
#[test]
fn test_MCOV2_014_rash_eprintln_in_dsl() {
let ast = make_ast(vec![
Stmt::Let {
name: "x".to_string(),
value: Expr::Literal(Literal::Str("val".to_string())),
declaration: true,
},
Stmt::Expr(Expr::FunctionCall {
name: "rash_eprintln".to_string(),
args: vec![Expr::Literal(Literal::Str("warning message".to_string()))],
}),
]);
let result = emit_makefile(&ast).unwrap();
assert!(
result.contains("warning") || result.contains("X") || !result.is_empty(),
"Result: {result}"
);
}
#[test]
fn test_MCOV2_015_phony_target_3_args() {
let ast = make_ast(vec![Stmt::Expr(Expr::FunctionCall {
name: "phony_target".to_string(),
args: vec![
Expr::Literal(Literal::Str("test".to_string())),
Expr::Array(vec![Expr::Literal(Literal::Str("build".to_string()))]),
Expr::Array(vec![Expr::Literal(Literal::Str("cargo test".to_string()))]),
],
})]);
let result = emit_makefile(&ast).unwrap();
assert!(result.contains("test"), "Result: {result}");
assert!(result.contains(".PHONY"), "PHONY in: {result}");
}
#[test]
fn test_MCOV2_016_target_2_args_no_recipes() {
let ast = make_ast(vec![Stmt::Expr(Expr::FunctionCall {
name: "target".to_string(),
args: vec![
Expr::Literal(Literal::Str("all".to_string())),
Expr::Array(vec![
Expr::Literal(Literal::Str("build".to_string())),
Expr::Literal(Literal::Str("test".to_string())),
]),
],
})]);
let result = emit_makefile(&ast).unwrap();
assert!(result.contains("all"), "Result: {result}");
}
#[test]
fn test_MCOV2_017_recursive_var_tab_prefix_skipped() {
let ast = make_ast(vec![Stmt::Expr(Expr::FunctionCall {
name: "exec".to_string(),
args: vec![Expr::Literal(Literal::Str("\tVAR = value".to_string()))],
})]);
let result = emit_makefile(&ast).unwrap();
assert!(result.contains("VAR"), "Result: {result}");
}
#[test]
fn test_MCOV2_018_resolve_all_literal_types_in_concat() {
let ast = make_ast(vec![Stmt::Expr(Expr::FunctionCall {
name: "rash_println".to_string(),
args: vec![Expr::FunctionCall {
name: "__format_concat".to_string(),
args: vec![
Expr::Literal(Literal::Bool(true)),
Expr::Literal(Literal::Str(" ".to_string())),
Expr::Literal(Literal::U16(42)),
Expr::Literal(Literal::Str(" ".to_string())),
Expr::Literal(Literal::U32(100)),
Expr::Literal(Literal::Str(" ".to_string())),
Expr::Literal(Literal::I32(-5)),
],
}],
})]);
let result = emit_makefile(&ast).unwrap();
assert!(result.contains("true"), "Bool in concat: {result}");
assert!(result.contains("42"), "U16 in concat: {result}");
assert!(result.contains("100"), "U32 in concat: {result}");
assert!(result.contains("-5"), "I32 in concat: {result}");
}
#[test]
fn test_MCOV2_019_non_main_fn_echo_non_string_arg() {
let ast = RestrictedAst {
functions: vec![
Function {
name: "main".to_string(),
params: vec![],
return_type: Type::Void,
body: vec![],
},
Function {
name: "report".to_string(),
params: vec![],
return_type: Type::Void,
body: vec![Stmt::Expr(Expr::FunctionCall {
name: "echo".to_string(),
args: vec![Expr::Variable("status".to_string())],
})],
},
],
entry_point: "main".to_string(),
};
let result = emit_makefile(&ast).unwrap();
assert!(
!result.contains("report:") || result.contains("report"),
"Result: {result}"
);
}
#[test]
fn test_MCOV2_020_empty_main_body_fallback() {
let ast = make_ast(vec![]);
let result = emit_makefile(&ast).unwrap();
assert!(
result.contains("all") || result.contains(".PHONY") || result.contains("main"),
"Result: {result}"
);
}