use super::*;
#[test]
fn test_FUNC_SHELL_001_detect_shell_date_basic() {
assert!(detect_shell_date("$(shell date +%s)"));
}
#[test]
fn test_FUNC_SHELL_001_detect_shell_date_with_format() {
assert!(detect_shell_date("$(shell date +%Y%m%d-%H%M%S)"));
}
#[test]
fn test_FUNC_SHELL_001_no_false_positive() {
assert!(!detect_shell_date("VERSION := 1.0.0"));
}
#[test]
fn test_FUNC_SHELL_001_detect_in_variable_context() {
let value = "RELEASE := $(shell date +%s)";
assert!(detect_shell_date(value));
}
#[test]
fn test_FUNC_SHELL_001_empty_string() {
assert!(!detect_shell_date(""));
}
#[test]
fn test_FUNC_SHELL_001_no_shell_command() {
assert!(!detect_shell_date("$(CC) -o output"));
}
#[test]
fn test_FUNC_SHELL_001_shell_but_not_date() {
assert!(!detect_shell_date("$(shell pwd)"));
}
#[test]
fn test_FUNC_SHELL_001_multiple_shell_commands() {
assert!(detect_shell_date("A=$(shell pwd) B=$(shell date +%s)"));
}
#[test]
fn test_FUNC_SHELL_001_date_without_shell() {
assert!(!detect_shell_date("# Update date: 2025-10-16"));
}
#[test]
fn test_FUNC_SHELL_001_case_sensitive() {
assert!(!detect_shell_date("$(SHELL DATE)"));
}
#[test]
fn test_FUNC_SHELL_001_mut_contains_must_check_substring() {
assert!(detect_shell_date("prefix $(shell date +%s) suffix"));
}
#[test]
fn test_FUNC_SHELL_001_mut_exact_pattern() {
assert!(!detect_shell_date("datestamp"));
}
#[test]
fn test_FUNC_SHELL_001_mut_non_empty_check() {
let result = detect_shell_date("");
assert!(!result);
}
#[cfg(test)]
mod property_tests {
use super::*;
use proptest::prelude::*;
proptest! {
#[test]
fn prop_FUNC_SHELL_001_any_string_no_panic(s in "\\PC*") {
let _ = detect_shell_date(&s);
}
#[test]
fn prop_FUNC_SHELL_001_shell_date_always_detected(
format in "[+%a-zA-Z0-9-]*"
) {
let input = format!("$(shell date {})", format);
prop_assert!(detect_shell_date(&input));
}
#[test]
fn prop_FUNC_SHELL_001_no_shell_never_detected(
s in "[^$]*"
) {
prop_assert!(!detect_shell_date(&s));
}
#[test]
fn prop_FUNC_SHELL_001_deterministic(s in "\\PC*") {
let result1 = detect_shell_date(&s);
let result2 = detect_shell_date(&s);
prop_assert_eq!(result1, result2);
}
#[test]
fn prop_FUNC_SHELL_001_shell_without_date_not_detected(
cmd in "[a-z]{3,10}"
) {
if cmd != "date" {
let input = format!("$(shell {})", cmd);
prop_assert!(!detect_shell_date(&input));
}
}
}
}
#[test]
fn test_FUNC_SHELL_001_analyze_detects_shell_date() {
use crate::make_parser::parse_makefile;
let makefile = "RELEASE := $(shell date +%s)";
let ast = parse_makefile(makefile).unwrap();
let issues = analyze_makefile(&ast);
assert_eq!(issues.len(), 1);
assert_eq!(issues[0].rule, "NO_TIMESTAMPS");
assert_eq!(issues[0].severity, IssueSeverity::Critical);
assert!(issues[0].message.contains("RELEASE"));
assert!(issues[0].suggestion.is_some());
}
#[test]
fn test_FUNC_SHELL_001_analyze_no_issues_clean_makefile() {
use crate::make_parser::parse_makefile;
let makefile = "VERSION := 1.0.0\nCC := gcc";
let ast = parse_makefile(makefile).unwrap();
let issues = analyze_makefile(&ast);
assert_eq!(issues.len(), 0);
}
#[test]
fn test_FUNC_SHELL_001_analyze_multiple_issues() {
use crate::make_parser::parse_makefile;
let makefile = r#"RELEASE := $(shell date +%s)
VERSION := 1.0.0
BUILD_TIME := $(shell date +%Y%m%d)"#;
let ast = parse_makefile(makefile).unwrap();
let issues = analyze_makefile(&ast);
assert_eq!(issues.len(), 2);
assert!(issues[0].message.contains("RELEASE"));
assert!(issues[1].message.contains("BUILD_TIME"));
}
#[test]
fn test_FUNC_SHELL_001_analyze_suggestion_format() {
use crate::make_parser::parse_makefile;
let makefile = "TIMESTAMP := $(shell date +%s)";
let ast = parse_makefile(makefile).unwrap();
let issues = analyze_makefile(&ast);
assert_eq!(issues.len(), 1);
let suggestion = issues[0].suggestion.as_ref().unwrap();
assert!(suggestion.contains("TIMESTAMP"));
assert!(suggestion.contains(":="));
}
#[test]
fn test_FUNC_SHELL_001_analyze_ignores_targets() {
use crate::make_parser::parse_makefile;
let makefile = "build:\n\techo $(shell date +%s)";
let ast = parse_makefile(makefile).unwrap();
let issues = analyze_makefile(&ast);
assert_eq!(issues.len(), 1);
assert_eq!(issues[0].rule, "AUTO_PHONY");
}
#[cfg(test)]
mod semantic_tests_extracted_FUNC {
use super::*;
include!("semantic_tests_extracted_FUNC.rs");
}