kimun 0.20.0

Code metrics tool — health score, complexity, duplication, hotspots, ownership
use super::*;
use crate::loc::language::detect;
use crate::walk::{ExcludeFilter, WalkConfig};
use std::fs;

#[test]
fn run_on_empty_dir() {
    let dir = tempfile::tempdir().unwrap();
    let filter = ExcludeFilter::default();
    let cfg = WalkConfig::new(dir.path(), false, &filter);
    run(&cfg, false).unwrap();
}

#[test]
fn run_on_rust_file() {
    let dir = tempfile::tempdir().unwrap();
    fs::write(
        dir.path().join("main.rs"),
        "fn main() {\n    let x = 1;\n    if x > 0 {\n        println!(\"hi\");\n    }\n}\n",
    )
    .unwrap();
    let filter = ExcludeFilter::default();
    let cfg = WalkConfig::new(dir.path(), false, &filter);
    run(&cfg, false).unwrap();
}

#[test]
fn run_json_output() {
    let dir = tempfile::tempdir().unwrap();
    fs::write(
        dir.path().join("main.rs"),
        "fn main() {\n    let x = 1;\n}\n",
    )
    .unwrap();
    let filter = ExcludeFilter::default();
    let cfg = WalkConfig::new(dir.path(), false, &filter);
    run(&cfg, true).unwrap();
}

#[test]
fn run_skips_binary() {
    let dir = tempfile::tempdir().unwrap();
    fs::write(dir.path().join("data.c"), b"hello\x00world").unwrap();
    let filter = ExcludeFilter::default();
    let cfg = WalkConfig::new(dir.path(), false, &filter);
    run(&cfg, false).unwrap();
}

#[test]
fn run_excludes_tests_by_default() {
    let dir = tempfile::tempdir().unwrap();
    fs::create_dir(dir.path().join("tests")).unwrap();
    fs::write(
        dir.path().join("tests/integration.rs"),
        "fn test() {\n    assert!(true);\n}\n",
    )
    .unwrap();
    let filter = ExcludeFilter::default();
    let cfg = WalkConfig::new(dir.path(), false, &filter);
    // No source files outside tests/ → prints "No recognized source files"
    run(&cfg, false).unwrap();
}

#[test]
fn run_includes_tests_with_flag() {
    let dir = tempfile::tempdir().unwrap();
    fs::create_dir(dir.path().join("tests")).unwrap();
    fs::write(
        dir.path().join("tests/integration.rs"),
        "fn test() {\n    assert!(true);\n}\n",
    )
    .unwrap();
    let filter = ExcludeFilter::default();
    let cfg = WalkConfig::new(dir.path(), true, &filter);
    run(&cfg, false).unwrap();
}

#[test]
fn run_excludes_test_files_by_name() {
    let dir = tempfile::tempdir().unwrap();
    fs::write(
        dir.path().join("parser_test.rs"),
        "fn test() {\n    assert!(true);\n}\n",
    )
    .unwrap();
    fs::write(
        dir.path().join("parser.rs"),
        "fn parse() {\n    let x = 1;\n}\n",
    )
    .unwrap();
    let filter = ExcludeFilter::default();
    let cfg = WalkConfig::new(dir.path(), false, &filter);
    // parser_test.rs excluded, only parser.rs analyzed
    run(&cfg, false).unwrap();
}

#[test]
fn run_sorts_by_stddev_descending() {
    let dir = tempfile::tempdir().unwrap();
    // Flat file → low stddev
    fs::write(
        dir.path().join("flat.rs"),
        "fn a() {}\nfn b() {}\nfn c() {}\n",
    )
    .unwrap();
    // Nested file → high stddev
    fs::write(
        dir.path().join("nested.rs"),
        "fn main() {\n    if true {\n        if true {\n            if true {\n                x();\n            }\n        }\n    }\n}\n",
    )
    .unwrap();
    let filter = ExcludeFilter::default();
    let cfg = WalkConfig::new(dir.path(), false, &filter);
    run(&cfg, false).unwrap();
}

#[test]
fn analyze_file_returns_none_for_comment_only_file() {
    let dir = tempfile::tempdir().unwrap();
    let path = dir.path().join("comments.rs");
    // A file with only comments has no code lines → analyze returns None
    fs::write(&path, "// This is a comment\n// Another comment\n").unwrap();
    let spec = detect(&path).unwrap();
    let result = analyze_file(&path, spec).unwrap();
    assert!(result.is_none(), "comment-only file should return None");
}

#[test]
fn run_multiple_languages() {
    let dir = tempfile::tempdir().unwrap();
    fs::write(
        dir.path().join("main.rs"),
        "fn main() {\n    println!(\"hi\");\n}\n",
    )
    .unwrap();
    fs::write(
        dir.path().join("app.py"),
        "def main():\n    print(\"hi\")\n",
    )
    .unwrap();
    let filter = ExcludeFilter::default();
    let cfg = WalkConfig::new(dir.path(), true, &filter);
    run(&cfg, false).unwrap();
}