use super::parallel_safety::{
is_automatic_variable, target_has_prerequisite, try_extract_cat_input,
try_extract_output_redirect,
};
use super::*;
#[test]
fn test_find_matching_paren_simple() {
let s = "$(wildcard *.c)";
assert_eq!(find_matching_paren(s, 0), Some(14));
}
#[test]
fn test_find_matching_paren_nested() {
let s = "$(filter %.o, $(wildcard *.c))";
assert_eq!(find_matching_paren(s, 15), Some(28));
}
#[test]
fn test_wrap_pattern_with_sort_simple() {
let value = "$(wildcard *.c)";
let result = wrap_pattern_with_sort(value, "$(wildcard");
assert_eq!(result, "$(sort $(wildcard *.c))");
}
#[test]
fn test_wrap_pattern_with_sort_nested() {
let value = "$(filter %.o, $(wildcard *.c))";
let result = wrap_pattern_with_sort(value, "$(wildcard");
assert_eq!(result, "$(filter %.o, $(sort $(wildcard *.c)))");
}
#[test]
fn test_extract_variable_name() {
let message = "Variable 'FILES' uses non-deterministic $(wildcard)";
assert_eq!(extract_variable_name(message), "FILES");
}
#[test]
fn test_PARALLEL_SAFETY_001_parallel_safety_analysis() {
let makefile = r#"
all: build
build:
gcc -o app main.c
"#;
let ast = crate::make_parser::parser::parse_makefile(makefile).unwrap();
let result = purify_makefile(&ast);
let _ = result.transformations_applied; }
#[test]
fn test_PARALLEL_SAFETY_002_detect_race_condition() {
let makefile = r#"
target1:
echo "output1" > shared.txt
target2:
echo "output2" > shared.txt
all: target1 target2
"#;
let ast = crate::make_parser::parser::parse_makefile(makefile).unwrap();
let result = purify_makefile(&ast);
assert!(
result
.report
.iter()
.any(|r| r.contains("race") || r.contains("parallel") || r.contains(".NOTPARALLEL")),
"Should detect race condition in shared file write and recommend .NOTPARALLEL"
);
}
#[test]
fn test_PARALLEL_SAFETY_003_add_order_only_prereq() {
let makefile = r#"
build: | output_dir
gcc -o output_dir/app main.c
output_dir:
mkdir -p output_dir
"#;
let ast = crate::make_parser::parser::parse_makefile(makefile).unwrap();
let result = purify_makefile(&ast);
let _ = result.transformations_applied; }
#[test]
fn test_PARALLEL_SAFETY_004_missing_dependency() {
let makefile = r#"
generate:
echo "data" > data.txt
process:
cat data.txt > output.txt
all: generate process
"#;
let ast = crate::make_parser::parser::parse_makefile(makefile).unwrap();
let result = purify_makefile(&ast);
assert!(
result
.report
.iter()
.any(|r| r.contains("dependency") || r.contains("data.txt")),
"Should detect missing dependency"
);
}
#[test]
fn test_PARALLEL_SAFETY_005_preserve_notparallel() {
let makefile = r#"
.NOTPARALLEL:
all: build
build:
gcc -o app main.c
"#;
let ast = crate::make_parser::parser::parse_makefile(makefile).unwrap();
let result = purify_makefile(&ast);
let notparallel_count = result
.report
.iter()
.filter(|r| r.contains(".NOTPARALLEL"))
.count();
assert!(
notparallel_count <= 1,
"Should not add duplicate .NOTPARALLEL"
);
}
#[test]
fn test_PARALLEL_SAFETY_006_phony_target_safety() {
let makefile = r#"
.PHONY: clean test
clean:
rm -f *.o
test:
./run_tests.sh
all: clean test
"#;
let ast = crate::make_parser::parser::parse_makefile(makefile).unwrap();
let result = purify_makefile(&ast);
let _ = result.transformations_applied; }
#[test]
fn test_PARALLEL_SAFETY_007_multiple_targets_same_output() {
let makefile = r#"
debug: main.c
gcc -g -o app main.c
release: main.c
gcc -O2 -o app main.c
"#;
let ast = crate::make_parser::parser::parse_makefile(makefile).unwrap();
let result = purify_makefile(&ast);
assert!(
result
.report
.iter()
.any(|r| r.contains("conflict") || r.contains("same output")),
"Should detect multiple targets with same output"
);
}
#[test]
fn test_PARALLEL_SAFETY_008_recursive_make_serialization() {
let makefile = r#"
subdirs:
$(MAKE) -C subdir1
$(MAKE) -C subdir2
all: subdirs
"#;
let ast = crate::make_parser::parser::parse_makefile(makefile).unwrap();
let result = purify_makefile(&ast);
assert!(
result
.report
.iter()
.any(|r| r.contains("recursive") || r.contains("$(MAKE)")),
"Should handle recursive make calls"
);
}
#[test]
fn test_PARALLEL_SAFETY_009_pattern_rule_safety() {
let makefile = r#"
%.o: %.c
gcc -c $< -o $@
all: main.o util.o
"#;
let ast = crate::make_parser::parser::parse_makefile(makefile).unwrap();
let result = purify_makefile(&ast);
let _ = result.transformations_applied; }
#[test]
fn test_PARALLEL_SAFETY_010_shared_directory_race() {
let makefile = r#"
obj/main.o: main.c
mkdir -p obj
gcc -c main.c -o obj/main.o
obj/util.o: util.c
mkdir -p obj
gcc -c util.c -o obj/util.o
all: obj/main.o obj/util.o
"#;
let ast = crate::make_parser::parser::parse_makefile(makefile).unwrap();
let result = purify_makefile(&ast);
assert!(
result
.report
.iter()
.any(|r| r.contains("directory") || r.contains("mkdir")),
"Should detect shared directory creation race"
);
}
#[test]
fn test_REPRODUCIBLE_001_detect_shell_date() {
let makefile = r#"
VERSION := $(shell date +%Y%m%d)
build:
echo "Building version $(VERSION)"
"#;
let ast = crate::make_parser::parser::parse_makefile(makefile).unwrap();
let result = purify_makefile(&ast);
assert!(
result.report.iter().any(|r| r.contains("timestamp")
|| r.contains("date")
|| r.contains("SOURCE_DATE_EPOCH")),
"Should detect non-deterministic timestamp $(shell date)"
);
}
#[test]
fn test_REPRODUCIBLE_002_detect_unix_timestamp() {
let makefile = r#"
RELEASE := release-$(shell date +%s)
deploy:
tar -czf $(RELEASE).tar.gz src/
"#;
let ast = crate::make_parser::parser::parse_makefile(makefile).unwrap();
let result = purify_makefile(&ast);
assert!(
result
.report
.iter()
.any(|r| r.contains("timestamp") || r.contains("date")),
"Should detect non-deterministic unix timestamp"
);
}
include!("tests_tests_REPRODUCIBLE.rs");