use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::PathBuf;
#[path = "../src/style.rs"]
mod style;
#[path = "../src/enhanced_regex.rs"]
mod enhanced_regex;
#[path = "../src/grc.rs"]
mod grc;
use grc::{GrcConfigReader, GrcatConfigEntry, GrcatConfigReader};
fn get_project_root() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
}
fn get_share_dir() -> PathBuf {
get_project_root().join("share")
}
fn get_etc_dir() -> PathBuf {
get_project_root().join("etc")
}
#[cfg(test)]
mod grc_config_reader_tests {
use super::*;
#[test]
fn test_grc_conf_parsing() {
let conf_path = get_etc_dir().join("rgrc.conf");
assert!(
conf_path.exists(),
"grc.conf should exist at {:?}",
conf_path
);
let file = File::open(&conf_path).expect("Failed to open grc.conf");
let reader = BufReader::new(file);
let grc_reader = GrcConfigReader::new(reader.lines());
let configs: Vec<_> = grc_reader.collect();
assert!(
!configs.is_empty(),
"grc.conf should contain configuration entries"
);
for (regex, config_file) in &configs {
assert!(
!config_file.is_empty(),
"Config file path should not be empty"
);
let _ = regex.is_match("test"); }
println!(
"Successfully parsed {} configuration entries from grc.conf",
configs.len()
);
}
#[test]
fn test_grc_conf_specific_commands() {
let conf_path = get_etc_dir().join("rgrc.conf");
let file = File::open(&conf_path).expect("Failed to open grc.conf");
let reader = BufReader::new(file);
let grc_reader = GrcConfigReader::new(reader.lines());
let configs: Vec<_> = grc_reader.collect();
let command_configs: Vec<_> = configs
.iter()
.map(|(regex, config)| (regex.as_str(), config.as_str()))
.collect();
let has_ping = command_configs
.iter()
.any(|(_, config)| config.contains("ping"));
let has_ls = command_configs
.iter()
.any(|(_, config)| config.contains("ls"));
let has_diff = command_configs
.iter()
.any(|(_, config)| config.contains("diff"));
assert!(
has_ping || has_ls || has_diff,
"grc.conf should contain at least one common command configuration"
);
}
#[test]
fn test_grc_conf_regex_patterns() {
let conf_path = get_etc_dir().join("rgrc.conf");
let file = File::open(&conf_path).expect("Failed to open grc.conf");
let reader = BufReader::new(file);
let grc_reader = GrcConfigReader::new(reader.lines());
for (regex, _config_file) in grc_reader {
let _ = regex.is_match(""); }
}
#[test]
fn test_grc_conf_skip_comments() {
let conf_path = get_etc_dir().join("rgrc.conf");
let file = File::open(&conf_path).expect("Failed to open grc.conf");
let reader = BufReader::new(file);
let grc_reader = GrcConfigReader::new(reader.lines());
let configs: Vec<_> = grc_reader.collect();
for (_, config_file) in &configs {
assert!(
!config_file.starts_with('#'),
"Config file paths should not start with #: {}",
config_file
);
}
}
}
#[cfg(test)]
mod grcat_config_reader_tests {
use super::*;
fn get_all_conf_files() -> Vec<PathBuf> {
let share_dir = get_share_dir();
let mut conf_files = Vec::new();
if let Ok(entries) = std::fs::read_dir(&share_dir) {
for entry in entries.flatten() {
let path = entry.path();
if let Some(filename) = path.file_name()
&& filename.to_string_lossy().starts_with("conf.")
{
conf_files.push(path);
}
}
}
conf_files.sort();
conf_files
}
#[test]
fn test_all_conf_files_exist() {
let conf_files = get_all_conf_files();
assert!(
!conf_files.is_empty(),
"Share directory should contain conf.* files"
);
println!("Found {} configuration files:", conf_files.len());
for file in &conf_files {
println!(" - {:?}", file.file_name().unwrap());
}
}
#[test]
fn test_parse_all_conf_files() {
let conf_files = get_all_conf_files();
let mut successful_parses = 0;
let mut total_entries = 0;
let mut files_with_issues = Vec::new();
for conf_file in &conf_files {
let filename = conf_file.file_name().unwrap().to_string_lossy();
match File::open(conf_file) {
Ok(file) => {
let reader = BufReader::new(file);
let grcat_reader = GrcatConfigReader::new(reader.lines());
let entries: Vec<GrcatConfigEntry> = grcat_reader.collect();
successful_parses += 1;
total_entries += entries.len();
if entries.is_empty() {
files_with_issues.push(filename.to_string());
}
println!("{}: {} entries", filename, entries.len());
}
Err(e) => {
panic!("Failed to open {}: {}", filename, e);
}
}
}
assert_eq!(
successful_parses,
conf_files.len(),
"All conf files should be parseable"
);
if !files_with_issues.is_empty() {
println!("\nFiles with no parsed entries (may use unsupported styles):");
for file in &files_with_issues {
println!(" - {}", file);
}
}
println!(
"\nTotal: {} files, {} entries",
successful_parses, total_entries
);
assert!(
total_entries > 0,
"Should have parsed at least some configuration entries"
);
}
#[test]
fn test_conf_ls_specific_patterns() {
let conf_path = get_share_dir().join("conf.ls");
let file = File::open(&conf_path).expect("conf.ls should exist");
let reader = BufReader::new(file);
let grcat_reader = GrcatConfigReader::new(reader.lines());
let entries: Vec<GrcatConfigEntry> = grcat_reader.collect();
assert!(
!entries.is_empty(),
"conf.ls should contain at least one entry"
);
for entry in &entries {
let _ = entry.regex.is_match(""); }
println!("conf.ls contains {} pattern entries", entries.len());
}
#[test]
fn test_conf_ping_specific_patterns() {
let conf_path = get_share_dir().join("conf.ping");
let file = File::open(&conf_path).expect("conf.ping should exist");
let reader = BufReader::new(file);
let grcat_reader = GrcatConfigReader::new(reader.lines());
let entries: Vec<GrcatConfigEntry> = grcat_reader.collect();
assert!(
!entries.is_empty(),
"conf.ping should contain at least one entry"
);
println!("conf.ping contains {} pattern entries", entries.len());
}
#[test]
fn test_conf_diff_specific_patterns() {
let conf_path = get_share_dir().join("conf.diff");
let file = File::open(&conf_path).expect("conf.diff should exist");
let reader = BufReader::new(file);
let grcat_reader = GrcatConfigReader::new(reader.lines());
let entries: Vec<GrcatConfigEntry> = grcat_reader.collect();
assert!(
!entries.is_empty(),
"conf.diff should contain at least one entry"
);
println!("conf.diff contains {} pattern entries", entries.len());
}
#[test]
fn test_conf_netstat_patterns() {
let conf_path = get_share_dir().join("conf.netstat");
if conf_path.exists() {
let file = File::open(&conf_path).expect("conf.netstat should be readable");
let reader = BufReader::new(file);
let grcat_reader = GrcatConfigReader::new(reader.lines());
let entries: Vec<GrcatConfigEntry> = grcat_reader.collect();
assert!(
!entries.is_empty(),
"conf.netstat should contain at least one entry"
);
println!("conf.netstat contains {} pattern entries", entries.len());
}
}
#[test]
fn test_all_conf_files_have_valid_regexes() {
let conf_files = get_all_conf_files();
let mut files_tested = 0;
let mut total_regexes_tested = 0;
for conf_file in &conf_files {
let _filename = conf_file.file_name().unwrap().to_string_lossy();
if let Ok(file) = File::open(conf_file) {
let reader = BufReader::new(file);
let grcat_reader = GrcatConfigReader::new(reader.lines());
for entry in grcat_reader {
let _ = entry.regex.is_match(""); total_regexes_tested += 1;
}
files_tested += 1;
}
}
println!(
"Validated {} regexes across {} files",
total_regexes_tested, files_tested
);
assert_eq!(files_tested, conf_files.len());
println!("Note: Files with unsupported styles (like 'reverse') may have fewer entries");
}
#[test]
fn test_all_conf_files_color_definitions() {
let conf_files = get_all_conf_files();
for conf_file in &conf_files {
let _filename = conf_file.file_name().unwrap().to_string_lossy();
if let Ok(file) = File::open(conf_file) {
let reader = BufReader::new(file);
let grcat_reader = GrcatConfigReader::new(reader.lines());
for entry in grcat_reader {
let _ = entry.regex.is_match("");
let _colors = &entry.colors;
}
}
}
}
#[test]
fn test_specific_conf_files() {
let test_files = vec![
"conf.ant",
"conf.blkid",
"conf.configure",
"conf.curl",
"conf.cvs",
"conf.df",
"conf.dig",
"conf.dnf",
"conf.docker-machinels",
"conf.dockerimages",
"conf.dockerinfo",
"conf.dockernetwork",
"conf.dockerps",
"conf.dockerpull",
"conf.dockersearch",
"conf.dockerversion",
"conf.du",
"conf.env",
"conf.fdisk",
"conf.findmnt",
"conf.free",
"conf.gcc",
"conf.getfacl",
"conf.getsebool",
"conf.go-test",
"conf.id",
"conf.ifconfig",
"conf.iostat_sar",
"conf.ip",
"conf.ipaddr",
"conf.ipneighbor",
"conf.iproute",
"conf.iptables",
"conf.irclog",
"conf.iwconfig",
"conf.jobs",
"conf.kubectl",
"conf.last",
"conf.ldap",
"conf.log",
"conf.lolcat",
"conf.lsattr",
"conf.lsblk",
"conf.lsmod",
"conf.lsof",
"conf.lspci",
"conf.lsusb",
"conf.mount",
"conf.mtr",
"conf.mvn",
"conf.nmap",
"conf.ntpdate",
"conf.php",
"conf.ping2",
"conf.proftpd",
"conf.ps",
"conf.pv",
"conf.semanageboolean",
"conf.semanagefcontext",
"conf.semanageuser",
"conf.sensors",
"conf.showmount",
"conf.sockstat",
"conf.sql",
"conf.ss",
"conf.stat",
"conf.sysctl",
"conf.systemctl",
"conf.tcpdump",
"conf.traceroute",
"conf.tune2fs",
"conf.ulimit",
"conf.uptime",
"conf.vmstat",
"conf.wdiff",
"conf.whois",
"conf.yaml",
];
let share_dir = get_share_dir();
let mut found_count = 0;
let mut parsed_count = 0;
for filename in &test_files {
let conf_path = share_dir.join(filename);
if conf_path.exists() {
found_count += 1;
if let Ok(file) = File::open(&conf_path) {
let reader = BufReader::new(file);
let grcat_reader = GrcatConfigReader::new(reader.lines());
let entries: Vec<_> = grcat_reader.collect();
if !entries.is_empty() {
parsed_count += 1;
}
}
}
}
println!(
"Found {}/{} test files, successfully parsed {} with entries",
found_count,
test_files.len(),
parsed_count
);
assert!(
found_count > 0,
"Should find at least some test configuration files"
);
}
#[test]
fn test_conf_log_count_field_parsing() {
let conf_path = get_share_dir().join("conf.log");
let file = File::open(&conf_path).expect("conf.log should exist");
let reader = BufReader::new(file);
let grcat_reader = GrcatConfigReader::new(reader.lines());
let entries: Vec<GrcatConfigEntry> = grcat_reader.collect();
assert!(
!entries.is_empty(),
"conf.log should contain at least one entry"
);
let mut found_once = false;
let mut found_more = false;
let mut found_stop = false;
for entry in &entries {
match entry.count {
grc::GrcatConfigEntryCount::Once => found_once = true,
grc::GrcatConfigEntryCount::More => found_more = true,
grc::GrcatConfigEntryCount::Stop => found_stop = true,
}
}
assert!(found_once, "Should find at least one 'once' count entry");
assert!(found_more, "Should find at least one 'more' count entry");
assert!(found_stop, "Should find at least one 'stop' count entry");
println!(
"conf.log count field parsing: once={}, more={}, stop={}",
found_once, found_more, found_stop
);
}
#[test]
fn test_conf_ping2_replace_field_parsing() {
let conf_path = get_share_dir().join("conf.ping2");
let file = File::open(&conf_path).expect("conf.ping2 should exist");
let reader = BufReader::new(file);
let grcat_reader = GrcatConfigReader::new(reader.lines());
let entries: Vec<GrcatConfigEntry> = grcat_reader.collect();
assert!(
!entries.is_empty(),
"conf.ping2 should contain at least one entry"
);
let mut found_replace = false;
let mut replace_value = String::new();
for entry in &entries {
if !entry.replace.is_empty() {
found_replace = true;
replace_value = entry.replace.clone();
break;
}
}
assert!(
found_replace,
"Should find at least one entry with replace field"
);
assert_eq!(
replace_value, "TIMEOUT \\1",
"Replace value should match expected pattern"
);
println!(
"conf.ping2 replace field parsing: found replace='{}'",
replace_value
);
}
#[test]
fn test_count_and_replace_default_values() {
let config_content = r#"
regexp=test pattern
colours=red
======
"#;
let reader = BufReader::new(config_content.as_bytes());
let grcat_reader = GrcatConfigReader::new(reader.lines());
let entries: Vec<GrcatConfigEntry> = grcat_reader.collect();
assert_eq!(entries.len(), 1, "Should parse one entry");
let entry = &entries[0];
assert!(
matches!(entry.count, grc::GrcatConfigEntryCount::More),
"Default count should be More"
);
assert!(
entry.replace.is_empty(),
"Default replace should be empty string"
);
println!("Default values test passed: count=More, replace=''");
}
#[test]
fn test_conf_netstat_skip_field_parsing() {
let conf_path = get_share_dir().join("conf.netstat");
let file = File::open(&conf_path).expect("conf.netstat should exist");
let reader = BufReader::new(file);
let grcat_reader = GrcatConfigReader::new(reader.lines());
let entries: Vec<GrcatConfigEntry> = grcat_reader.collect();
assert!(
!entries.is_empty(),
"conf.netstat should contain at least one entry"
);
let mut found_skip_true = false;
let mut found_skip_false = false;
for entry in &entries {
if entry.skip {
found_skip_true = true;
} else {
found_skip_false = true;
}
}
assert!(
found_skip_true,
"Should find at least one entry with skip=true"
);
assert!(
found_skip_false,
"Should find at least one entry with skip=false"
);
println!(
"conf.netstat skip field parsing: skip_true={}, skip_false={}",
found_skip_true, found_skip_false
);
}
#[test]
fn test_conf_sockstat_skip_field_parsing() {
let conf_path = get_share_dir().join("conf.sockstat");
let file = File::open(&conf_path).expect("conf.sockstat should exist");
let reader = BufReader::new(file);
let grcat_reader = GrcatConfigReader::new(reader.lines());
let entries: Vec<GrcatConfigEntry> = grcat_reader.collect();
assert!(
!entries.is_empty(),
"conf.sockstat should contain at least one entry"
);
let mut found_skip_true = false;
let mut found_skip_false = false;
for entry in &entries {
if entry.skip {
found_skip_true = true;
} else {
found_skip_false = true;
}
}
assert!(
found_skip_true,
"Should find at least one entry with skip=true"
);
assert!(
found_skip_false,
"Should find at least one entry with skip=false"
);
println!(
"conf.sockstat skip field parsing: skip_true={}, skip_false={}",
found_skip_true, found_skip_false
);
}
#[test]
fn test_conf_ss_skip_field_parsing() {
let conf_path = get_share_dir().join("conf.ss");
let file = File::open(&conf_path).expect("conf.ss should exist");
let reader = BufReader::new(file);
let grcat_reader = GrcatConfigReader::new(reader.lines());
let entries: Vec<GrcatConfigEntry> = grcat_reader.collect();
assert!(
!entries.is_empty(),
"conf.ss should contain at least one entry"
);
let mut found_skip_true = false;
let mut found_skip_false = false;
for entry in &entries {
if entry.skip {
found_skip_true = true;
} else {
found_skip_false = true;
}
}
assert!(
found_skip_true,
"Should find at least one entry with skip=true"
);
assert!(
found_skip_false,
"Should find at least one entry with skip=false"
);
println!(
"conf.ss skip field parsing: skip_true={}, skip_false={}",
found_skip_true, found_skip_false
);
}
#[test]
fn test_skip_default_values() {
let config_content = r#"
regexp=test pattern
colours=red
======
"#;
let reader = BufReader::new(config_content.as_bytes());
let grcat_reader = GrcatConfigReader::new(reader.lines());
let entries: Vec<GrcatConfigEntry> = grcat_reader.collect();
assert_eq!(entries.len(), 1, "Should parse one entry");
assert!(
!entries[0].skip,
"Skip should default to false when not specified"
);
println!("Skip default value test passed: skip=false");
}
}
#[cfg(test)]
mod integration_tests {
use super::*;
#[test]
fn test_grc_conf_references_valid_conf_files() {
let grc_conf_path = get_etc_dir().join("rgrc.conf");
let file = File::open(&grc_conf_path).expect("Failed to open grc.conf");
let reader = BufReader::new(file);
let grc_reader = GrcConfigReader::new(reader.lines());
let share_dir = get_share_dir();
let mut missing_files = Vec::new();
let mut found_files = 0;
for (_regex, config_file) in grc_reader {
let conf_path = share_dir.join(&config_file);
if conf_path.exists() {
found_files += 1;
} else {
missing_files.push(config_file.clone());
}
}
if !missing_files.is_empty() {
println!(
"Warning: grc.conf references {} missing files:",
missing_files.len()
);
for file in &missing_files {
println!(" - {}", file);
}
}
println!(
"Found {} referenced configuration files in share/",
found_files
);
assert!(
found_files > 0,
"At least some referenced files should exist"
);
}
#[test]
fn test_complete_workflow_grc_conf_to_grcat() {
let grc_conf_path = get_etc_dir().join("rgrc.conf");
let file = File::open(&grc_conf_path).expect("Failed to open grc.conf");
let reader = BufReader::new(file);
let grc_reader = GrcConfigReader::new(reader.lines());
let share_dir = get_share_dir();
let mut workflows_tested = 0;
let mut total_workflows = 0;
for (_regex, config_file) in grc_reader {
let conf_path = share_dir.join(&config_file);
if conf_path.exists() {
total_workflows += 1;
if let Ok(file) = File::open(&conf_path) {
let reader = BufReader::new(file);
let grcat_reader = GrcatConfigReader::new(reader.lines());
let _entries: Vec<_> = grcat_reader.collect();
workflows_tested += 1;
}
}
}
println!(
"Successfully tested {}/{} complete workflows (grc.conf -> grcat config)",
workflows_tested, total_workflows
);
assert!(
workflows_tested > 0,
"Should test at least one complete workflow"
);
}
#[test]
fn test_all_conf_files_in_share_are_valid() {
let conf_files = get_all_conf_files();
let mut valid_files = 0;
let mut invalid_files = Vec::new();
for conf_file in &conf_files {
let filename = conf_file.file_name().unwrap().to_string_lossy().to_string();
match File::open(conf_file) {
Ok(file) => {
let reader = BufReader::new(file);
let grcat_reader = GrcatConfigReader::new(reader.lines());
let entries: Vec<_> = grcat_reader.collect();
valid_files += 1;
if entries.is_empty() {
println!(" {} - no entries (possibly all comments)", filename);
}
}
Err(e) => {
invalid_files.push((filename.clone(), e.to_string()));
}
}
}
if !invalid_files.is_empty() {
println!("Invalid files:");
for (file, error) in &invalid_files {
println!(" - {}: {}", file, error);
}
}
println!(
"Valid configuration files: {}/{}",
valid_files,
conf_files.len()
);
assert_eq!(
valid_files,
conf_files.len(),
"All configuration files should be valid"
);
}
fn get_all_conf_files() -> Vec<PathBuf> {
let share_dir = get_share_dir();
let mut conf_files = Vec::new();
if let Ok(entries) = std::fs::read_dir(&share_dir) {
for entry in entries.flatten() {
let path = entry.path();
if let Some(filename) = path.file_name()
&& filename.to_string_lossy().starts_with("conf.")
{
conf_files.push(path);
}
}
}
conf_files.sort();
conf_files
}
}
#[cfg(test)]
mod edge_case_tests {
use super::*;
#[test]
fn test_empty_colors_handling() {
let conf_path = get_share_dir().join("conf.dummy");
if conf_path.exists() {
let file = File::open(&conf_path).expect("Should open conf.dummy");
let reader = BufReader::new(file);
let grcat_reader = GrcatConfigReader::new(reader.lines());
for entry in grcat_reader {
let _colors = &entry.colors;
}
}
}
#[test]
fn test_complex_regex_patterns() {
let test_files = vec!["conf.ls", "conf.ping", "conf.netstat", "conf.iptables"];
let share_dir = get_share_dir();
for filename in &test_files {
let conf_path = share_dir.join(filename);
if conf_path.exists() {
let file =
File::open(&conf_path).unwrap_or_else(|_| panic!("Should open {}", filename));
let reader = BufReader::new(file);
let grcat_reader = GrcatConfigReader::new(reader.lines());
for entry in grcat_reader {
let _ = entry.regex.is_match("test"); }
}
}
}
#[test]
fn test_multiple_color_definitions() {
let conf_path = get_share_dir().join("conf.ls");
if conf_path.exists() {
let file = File::open(&conf_path).expect("Should open conf.ls");
let reader = BufReader::new(file);
let grcat_reader = GrcatConfigReader::new(reader.lines());
let entries: Vec<_> = grcat_reader.collect();
let has_multiple_colors = entries.iter().any(|e| e.colors.len() > 1);
if has_multiple_colors {
println!("conf.ls has entries with multiple color definitions");
}
}
}
}