vyre-conform 0.1.0

Conformance suite for vyre backends — proves byte-identical output to CPU reference
Documentation
//! Meta-test: every test file under conform/tests/ that contains assertions
//! or #[test] attributes must be listed in conform/.append-only-paths or in
//! the bootstrap allowlist conform/tests/.append-only-allowlist.txt.

use std::fs;
use std::path::{Path, PathBuf};

fn repo_root() -> PathBuf {
    // CARGO_MANIFEST_DIR for integration tests in vyre-conform is the conform/ dir.
    PathBuf::from(std::env!("CARGO_MANIFEST_DIR"))
        .parent()
        .expect("conform is inside workspace")
        .to_path_buf()
}

fn read_lines_filtered(path: &Path) -> Vec<String> {
    let content = fs::read_to_string(path).unwrap_or_default();
    content
        .lines()
        .map(|line| {
            let trimmed = line.split('#').next().unwrap_or(line);
            trimmed.trim().to_string()
        })
        .filter(|line| !line.is_empty())
        .collect()
}

fn is_covered_by_pattern(file: &str, pattern: &str) -> bool {
    if pattern.ends_with("/**") {
        let prefix = &pattern[..pattern.len() - 3];
        if file == prefix || file.starts_with(&format!("{}/", prefix)) {
            return true;
        }
    } else if pattern.ends_with("*.rs") {
        let prefix = &pattern[..pattern.len() - 4];
        if file.starts_with(prefix) && file.ends_with(".rs") {
            return true;
        }
    } else if file == pattern {
        return true;
    }
    false
}

fn is_covered(file: &str, patterns: &[String]) -> bool {
    patterns.iter().any(|p| is_covered_by_pattern(file, p))
}

fn walk_tests(dir: &Path, files: &mut Vec<PathBuf>) {
    if !dir.is_dir() {
        return;
    }
    for entry in fs::read_dir(dir).unwrap() {
        let entry = entry.unwrap();
        let path = entry.path();
        if path.is_dir() {
            walk_tests(&path, files);
        } else if path.extension().and_then(|s| s.to_str()) == Some("rs") {
            files.push(path);
        }
    }
}

#[test]
fn append_only_paths_cover_all_test_files() {
    let root = repo_root();
    let patterns_file = root.join("conform/.append-only-paths");
    let allowlist_file = root.join("conform/tests/.append-only-allowlist.txt");
    let tests_dir = root.join("conform/tests");

    let patterns = read_lines_filtered(&patterns_file);
    let allowlist: Vec<String> = read_lines_filtered(&allowlist_file);

    let mut files = Vec::new();
    walk_tests(&tests_dir, &mut files);

    let mut failures = Vec::new();

    for path in files {
        let rel = path
            .strip_prefix(&root)
            .unwrap()
            .to_string_lossy()
            .replace("\\", "/");
        let content = fs::read_to_string(&path).unwrap_or_default();

        let has_assert = content.contains("assert_") || content.contains("#[test]");
        if !has_assert {
            continue;
        }

        if is_covered(&rel, &patterns) {
            continue;
        }

        if allowlist.contains(&rel) {
            continue;
        }

        failures.push(rel);
    }

    if !failures.is_empty() {
        let mut msg = String::from(
            "append_only_paths_completeness: FAILED — the following test files are not append-only:\n",
        );
        for f in &failures {
            msg.push_str(&format!(
                "  - Fix: add {} to conform/.append-only-paths or move to conform/tests/regression/\n",
                f
            ));
        }
        panic!("{}", msg);
    }
}