vyre-conform 0.1.0

Conformance suite for vyre backends — proves byte-identical output to CPU reference
Documentation
use std::collections::BTreeSet;
use std::fs;
use std::path::{Path, PathBuf};
use walkdir::WalkDir;

#[cfg(test)]
mod tests {
    use super::*;
    use std::io::Write;
    use tempfile::TempDir;

    fn write_file(dir: &TempDir, rel: &str, content: &str) -> PathBuf {
        let path = dir.path().join(rel);
        if let Some(parent) = path.parent() {
            fs::create_dir_all(parent).unwrap();
        }
        let mut file = std::fs::File::create(&path).unwrap();
        file.write_all(content.as_bytes()).unwrap();
        path
    }

    fn run(dir: &TempDir) -> StructuralReport {
        scan(&StructuralRulesConfig::with_root(dir.path())).expect("scan must not fail")
    }

    #[test]
    fn clean_tree_is_green() {
        let dir = TempDir::new().unwrap();
        write_file(&dir, "src/lib.rs", "pub fn a() {}\n");
        write_file(&dir, "src/a.rs", "pub fn a() {}\n");
        let report = run(&dir);
        assert!(report.is_green(), "{:?}", report);
    }

    #[test]
    fn mod_rs_is_detected() {
        let dir = TempDir::new().unwrap();
        write_file(&dir, "src/lib.rs", "pub mod foo;\n");
        write_file(&dir, "src/foo/mod.rs", "pub fn a() {}\n");
        let report = run(&dir);
        let kinds: Vec<_> = report
            .new_findings
            .iter()
            .map(|finding| finding.rule)
            .collect();
        assert!(kinds.contains(&StructuralRule::NoModRs), "{:?}", report);
    }

    #[test]
    fn six_entries_in_src_dir_fails_max_5() {
        let dir = TempDir::new().unwrap();
        for letter in 'a'..='f' {
            write_file(
                &dir,
                &format!("src/foo/item_{letter}.rs"),
                "pub fn a() {}\n",
            );
        }
        let report = run(&dir);
        let dir_findings: Vec<_> = report
            .new_findings
            .iter()
            .filter(|finding| finding.rule == StructuralRule::MaxFiveEntriesPerDir)
            .collect();
        assert_eq!(dir_findings.len(), 1, "{:?}", report);
    }

    #[test]
    fn max_5_ignores_tree_gen_artifacts() {
        let dir = TempDir::new().unwrap();
        write_file(&dir, "src/a.rs", "pub fn a() {}\n");
        write_file(&dir, "src/b.rs", "pub fn a() {}\n");
        write_file(&dir, "src/c.rs", "pub fn a() {}\n");
        write_file(&dir, "src/d.rs", "pub fn a() {}\n");
        write_file(&dir, "src/e.rs", "pub fn a() {}\n");
        write_file(&dir, "src/_tree.rs", "pub mod a;\n");
        write_file(&dir, "src/_deprecated_bridge.rs", "\n");
        let report = run(&dir);
        assert!(
            !report
                .new_findings
                .iter()
                .any(|finding| finding.rule == StructuralRule::MaxFiveEntriesPerDir),
            "tree-gen files must not count toward the 5-entry limit: {:?}",
            report
        );
    }

    #[test]
    fn use_super_is_detected_and_use_crate_is_not() {
        let dir = TempDir::new().unwrap();
        write_file(
            &dir,
            "src/lib.rs",
            "use super::parent;\nuse self::sibling;\nuse crate::elsewhere;\nfn f() {}\n",
        );
        let report = run(&dir);
        let count = report
            .new_findings
            .iter()
            .filter(|finding| finding.rule == StructuralRule::NoUseSuper)
            .count();
        assert_eq!(count, 2, "{:?}", report);
    }

    #[test]
    fn use_super_in_comment_is_ignored() {
        let dir = TempDir::new().unwrap();
        write_file(
            &dir,
            "src/lib.rs",
            "/// Historical example: `use super::parent;` is banned.\nfn f() {}\n",
        );
        let report = run(&dir);
        assert!(report.is_green(), "{:?}", report);
    }

    #[test]
    fn skip_dirs_default_excludes_target_and_docs() {
        let dir = TempDir::new().unwrap();
        write_file(
            &dir,
            "target/poison/src/mod.rs",
            "pub fn a() {}\n",
        );
        write_file(&dir, "src/good.rs", "pub fn a() {}\n");
        let report = run(&dir);
        assert!(report.is_green(), "{:?}", report);
    }

    #[test]
    fn waiver_suppresses_exactly_its_rule_on_its_path() {
        let dir = TempDir::new().unwrap();
        write_file(&dir, "src/a/mod.rs", "pub fn a() {}\n");
        write_file(
            &dir,
            "src/b.rs",
            "use super::x;\nfn f() {}\n",
        );
        let config = StructuralRulesConfig {
            roots: vec![dir.path().to_path_buf()],
            skip_dirs: default_skip_dirs(),
            waivers: [
                Waiver {
                    path: PathBuf::from("src/a/mod.rs"),
                    rule: StructuralRule::NoModRs,
                },
                Waiver {
                    path: PathBuf::from("src/b.rs"),
                    rule: StructuralRule::NoUseSuper,
                },
            ]
            .into_iter()
            .collect(),
        };
        let report = scan(&config).unwrap();
        assert!(
            report.is_green(),
            "all violations should be waived: {:?}",
            report
        );
    }

    #[test]
    fn stale_waiver_is_a_regression() {
        let dir = TempDir::new().unwrap();
        write_file(&dir, "src/ok.rs", "pub fn a() {}\n");
        let config = StructuralRulesConfig {
            roots: vec![dir.path().to_path_buf()],
            skip_dirs: default_skip_dirs(),
            waivers: [Waiver {
                path: PathBuf::from("src/gone.rs"),
                rule: StructuralRule::NoModRs,
            }]
            .into_iter()
            .collect(),
        };
        let report = scan(&config).unwrap();
        assert!(
            !report.is_green(),
            "unused waivers should fail the gate: {:?}",
            report
        );
        assert_eq!(report.unused_waivers.len(), 1);
    }

    #[test]
    fn waiver_does_not_bleed_across_rules() {
        let dir = TempDir::new().unwrap();
        write_file(&dir, "src/foo/mod.rs", "use super::parent;\n");
        let config = StructuralRulesConfig {
            roots: vec![dir.path().to_path_buf()],
            skip_dirs: default_skip_dirs(),
            waivers: [Waiver {
                path: PathBuf::from("src/foo/mod.rs"),
                rule: StructuralRule::NoModRs,
            }]
            .into_iter()
            .collect(),
        };
        let report = scan(&config).unwrap();
        // mod.rs is waived; use super must still fire.
        assert_eq!(
            report
                .new_findings
                .iter()
                .filter(|finding| finding.rule == StructuralRule::NoUseSuper)
                .count(),
            1,
            "{:?}",
            report
        );
    }

    #[test]
    fn violation_count_counts_all_findings_regardless_of_waivers() {
        let dir = TempDir::new().unwrap();
        write_file(&dir, "src/a/mod.rs", "use super::x;\n");
        let config = StructuralRulesConfig {
            roots: vec![dir.path().to_path_buf()],
            skip_dirs: default_skip_dirs(),
            waivers: [
                Waiver {
                    path: PathBuf::from("src/a/mod.rs"),
                    rule: StructuralRule::NoModRs,
                },
                Waiver {
                    path: PathBuf::from("src/a/mod.rs"),
                    rule: StructuralRule::NoUseSuper,
                },
            ]
            .into_iter()
            .collect(),
        };
        let report = scan(&config).unwrap();
        assert_eq!(violation_count(&report), 2);
        assert!(report.is_green());
    }

    #[test]
    fn findings_are_deterministically_sorted() {
        let dir = TempDir::new().unwrap();
        write_file(&dir, "src/z/mod.rs", "\n");
        write_file(&dir, "src/a/mod.rs", "\n");
        write_file(&dir, "src/m/mod.rs", "\n");
        let report = run(&dir);
        let paths: Vec<_> = report
            .all_findings
            .iter()
            .map(|finding| finding.path.to_string_lossy().to_string())
            .collect();
        let mut sorted = paths.clone();
        sorted.sort();
        assert_eq!(paths, sorted, "{:?}", paths);
    }

    #[test]
    fn missing_root_returns_actionable_error() {
        let config = StructuralRulesConfig::with_root("/nope/does/not/exist");
        let error = scan(&config).unwrap_err();
        assert!(
            error.contains("structural rules gate root does not exist"),
            "{error}"
        );
        assert!(error.contains("Fix:"), "{error}");
    }

    #[test]
    fn rule_names_are_unique() {
        let mut seen = std::collections::BTreeSet::new();
        for rule in [
            StructuralRule::NoModRs,
            StructuralRule::MaxFiveEntriesPerDir,
            StructuralRule::NoUseSuper,
        ] {
            assert!(seen.insert(rule.name()), "duplicate name {}", rule.name());
        }
    }

    #[test]
    fn display_finding_includes_rule_name_and_fix() {
        let finding = StructuralFinding {
            path: PathBuf::from("src/a/mod.rs"),
            rule: StructuralRule::NoModRs,
            line: None,
            detail: "mod.rs is forbidden".to_string(),
        };
        let rendered = format!("{finding}");
        assert!(rendered.contains("src/a/mod.rs"), "{rendered}");
        assert!(rendered.contains("no_mod_rs"), "{rendered}");
    }

    #[test]
    fn dir_rule_ignores_non_src_directories() {
        let dir = TempDir::new().unwrap();
        // 6 entries under `tests/` must not fire — only `src/` is in scope.
        for letter in 'a'..='f' {
            write_file(&dir, &format!("tests/{letter}.rs"), "\n");
        }
        let report = run(&dir);
        assert!(report.is_green(), "{:?}", report);
    }
}