#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parser_new() {
let parser = MakefileParser::new("test input");
assert_eq!(parser.cursor, 0);
assert_eq!(parser.line, 1);
assert_eq!(parser.column, 1);
assert!(parser.errors.is_empty());
}
#[test]
fn test_parse_empty_file() {
let mut parser = MakefileParser::new("");
let result = parser.parse();
assert!(result.is_ok());
let ast = result.unwrap();
assert_eq!(ast.nodes.len(), 0);
}
#[test]
fn test_parse_simple_rule() {
let input = "test: dep1 dep2\n\techo hello";
let mut parser = MakefileParser::new(input);
let result = parser.parse();
assert!(result.is_ok());
let ast = result.unwrap();
assert!(!ast.nodes.is_empty());
let rule_node = &ast.nodes[0];
assert_eq!(rule_node.kind, MakefileNodeKind::Rule);
if let NodeData::Rule {
targets,
prerequisites,
..
} = &rule_node.data
{
assert_eq!(targets, &vec!["test".to_string()]);
assert_eq!(prerequisites, &vec!["dep1".to_string(), "dep2".to_string()]);
} else {
panic!("Expected Rule node data");
}
}
#[test]
fn test_parse_variable() {
let input = "CC = gcc\nCFLAGS := -Wall\nLDFLAGS += -lm";
let mut parser = MakefileParser::new(input);
let result = parser.parse();
assert!(result.is_ok());
let ast = result.unwrap();
let vars = ast.get_variables();
assert_eq!(vars.len(), 3);
assert_eq!(vars[0].0, "CC");
assert_eq!(vars[0].2, "gcc");
assert_eq!(vars[1].0, "CFLAGS");
assert_eq!(vars[1].2, "-Wall");
}
#[test]
fn test_parse_comment() {
let input = "# This is a comment\ntest:\n\techo test";
let mut parser = MakefileParser::new(input);
let result = parser.parse();
assert!(result.is_ok());
let ast = result.unwrap();
let comment_node = ast
.nodes
.iter()
.find(|n| n.kind == MakefileNodeKind::Comment);
assert!(comment_node.is_some());
if let NodeData::Text(text) = &comment_node.unwrap().data {
assert!(text.contains("This is a comment"));
}
}
#[test]
fn test_parse_pattern_rule() {
let input = "%.o: %.c\n\tgcc -c $< -o $@";
let mut parser = MakefileParser::new(input);
let result = parser.parse();
assert!(result.is_ok());
let ast = result.unwrap();
assert!(ast.has_pattern_rules());
}
#[test]
fn test_parse_phony_rule() {
let input = ".PHONY: clean test\nclean:\n\trm -f *.o";
let mut parser = MakefileParser::new(input);
let result = parser.parse();
assert!(result.is_ok());
let ast = result.unwrap();
let phony_targets = ast.get_phony_targets();
assert_eq!(phony_targets.len(), 2);
assert!(phony_targets.contains(&"clean".to_string()));
assert!(phony_targets.contains(&"test".to_string()));
}
#[test]
fn test_parse_double_colon_rule() {
let input = "all:: target1\nall:: target2";
let mut parser = MakefileParser::new(input);
let result = parser.parse();
assert!(result.is_ok());
let ast = result.unwrap();
let all_rules = ast.find_rules_by_target("all");
assert_eq!(all_rules.len(), 2);
}
#[test]
fn test_parse_include() {
let input = "include config.mk\n-include optional.mk";
let mut parser = MakefileParser::new(input);
let result = parser.parse();
assert!(result.is_ok());
let ast = result.unwrap();
let include_nodes: Vec<_> = ast
.nodes
.iter()
.filter(|n| n.kind == MakefileNodeKind::Include)
.collect();
assert_eq!(include_nodes.len(), 2);
}
#[test]
fn test_parse_recipe_with_prefixes() {
let input = "test:\n\t@echo Starting test\n\t-rm -f temp\n\t+make subtarget";
let mut parser = MakefileParser::new(input);
let result = parser.parse();
assert!(result.is_ok());
let ast = result.unwrap();
let recipe_node = ast
.nodes
.iter()
.find(|n| n.kind == MakefileNodeKind::Recipe);
assert!(recipe_node.is_some());
if let NodeData::Recipe { lines } = &recipe_node.unwrap().data {
assert_eq!(lines.len(), 3);
assert!(lines[0].prefixes.silent); assert!(lines[1].prefixes.ignore_error); assert!(lines[2].prefixes.always_exec); }
}
#[test]
fn test_parse_automatic_variables() {
let input = "%.o: %.c\n\tgcc -c $< -o $@\n\techo $^ $?";
let mut parser = MakefileParser::new(input);
let result = parser.parse();
assert!(result.is_ok());
let ast = result.unwrap();
assert!(ast.uses_automatic_variables());
}
#[test]
fn test_parse_errors() {
let input = "= value";
let mut parser = MakefileParser::new(input);
let result = parser.parse();
assert!(result.is_err());
let input2 = "unknown line";
let mut parser2 = MakefileParser::new(input2);
let result2 = parser2.parse();
assert!(result2.is_ok());
let ast = result2.unwrap();
assert_eq!(ast.nodes.len(), 0); }
#[test]
fn test_skip_functions() {
let mut parser = MakefileParser::new(" \t text after spaces");
parser.skip_spaces();
assert_eq!(parser.peek(), Some('\t'));
let mut parser2 = MakefileParser::new("line1\nline2");
parser2.skip_to_next_line();
assert_eq!(parser2.peek(), Some('l'));
assert_eq!(parser2.line, 2);
}
#[test]
fn test_at_end() {
let mut parser = MakefileParser::new("abc");
assert!(!parser.at_end());
parser.cursor = 3;
assert!(parser.at_end());
}
#[test]
fn test_advance() {
let mut parser = MakefileParser::new("a\nb");
assert_eq!(parser.line, 1);
assert_eq!(parser.column, 1);
parser.advance();
assert_eq!(parser.column, 2);
parser.advance(); assert_eq!(parser.line, 2);
assert_eq!(parser.column, 1);
}
#[test]
fn test_starts_with() {
let parser = MakefileParser::new("include file.mk");
assert!(parser.starts_with("include"));
assert!(!parser.starts_with("exclude"));
}
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod property_tests {
use proptest::prelude::*;
proptest! {
#[test]
fn basic_property_stability(_input in ".*") {
prop_assert!(true);
}
#[test]
fn module_consistency_check(_x in 0u32..1000) {
prop_assert!(_x < 1001);
}
}
}