kimun 0.35.0

Code metrics tool — health score, complexity, duplication, hotspots, ownership
use super::*;
use crate::cli::OutputMode;
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, OutputMode::Table, 1, 20, false, "total").unwrap();
}

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

#[test]
fn run_on_python_file() {
    let dir = tempfile::tempdir().unwrap();
    fs::write(
        dir.path().join("app.py"),
        "def main():\n    if True:\n        print(\"hi\")\n",
    )
    .unwrap();
    let filter = ExcludeFilter::default();
    let cfg = WalkConfig::new(dir.path(), false, &filter);
    run(&cfg, OutputMode::Table, 1, 20, false, "total").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, OutputMode::Table, 1, 20, false, "total").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);
    run(&cfg, OutputMode::Table, 1, 20, false, "total").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, OutputMode::Json, 1, 20, false, "total").unwrap();
}

#[test]
fn run_per_function_output() {
    let dir = tempfile::tempdir().unwrap();
    fs::write(
        dir.path().join("main.rs"),
        "fn foo() {\n    if x > 0 {\n        bar();\n    }\n}\nfn baz() {\n    quux();\n}\n",
    )
    .unwrap();
    let filter = ExcludeFilter::default();
    let cfg = WalkConfig::new(dir.path(), false, &filter);
    run(&cfg, OutputMode::Table, 1, 20, true, "total").unwrap();
}

#[test]
fn run_min_complexity_filter() {
    let dir = tempfile::tempdir().unwrap();
    fs::write(
        dir.path().join("simple.rs"),
        "fn main() {\n    let x = 1;\n}\n",
    )
    .unwrap();
    // min_complexity=5 should filter out simple functions
    let filter = ExcludeFilter::default();
    let cfg = WalkConfig::new(dir.path(), false, &filter);
    run(&cfg, OutputMode::Table, 5, 20, false, "total").unwrap();
}

#[test]
fn run_top_limit() {
    let dir = tempfile::tempdir().unwrap();
    fs::write(
        dir.path().join("a.rs"),
        "fn a() {\n    if x { bar(); }\n}\n",
    )
    .unwrap();
    fs::write(
        dir.path().join("b.rs"),
        "fn b() {\n    if y { baz(); }\n}\n",
    )
    .unwrap();
    let filter = ExcludeFilter::default();
    let cfg = WalkConfig::new(dir.path(), false, &filter);
    run(&cfg, OutputMode::Table, 1, 1, false, "total").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, OutputMode::Table, 1, 20, false, "total").unwrap();
}

#[test]
fn run_skips_non_code_languages() {
    let dir = tempfile::tempdir().unwrap();
    fs::write(dir.path().join("data.json"), "{\"key\": \"value\"}\n").unwrap();
    fs::write(dir.path().join("style.css"), "body { color: red; }\n").unwrap();
    // Should produce no results (JSON/CSS have no markers)
    let filter = ExcludeFilter::default();
    let cfg = WalkConfig::new(dir.path(), false, &filter);
    run(&cfg, OutputMode::Table, 1, 20, false, "total").unwrap();
}

#[test]
fn run_short_format() {
    let dir = tempfile::tempdir().unwrap();
    fs::write(
        dir.path().join("main.rs"),
        "fn foo() {\n    if true {\n        println!(\"hi\");\n    }\n}\n",
    )
    .unwrap();
    let filter = ExcludeFilter::default();
    let cfg = WalkConfig::new(dir.path(), false, &filter);
    run(&cfg, OutputMode::Short, 0, 20, false, "total").unwrap();
}

#[test]
fn run_terse_format() {
    let dir = tempfile::tempdir().unwrap();
    fs::write(
        dir.path().join("main.rs"),
        "fn foo() {\n    if true {\n        println!(\"hi\");\n    }\n}\n",
    )
    .unwrap();
    let filter = ExcludeFilter::default();
    let cfg = WalkConfig::new(dir.path(), false, &filter);
    run(&cfg, OutputMode::Terse, 0, 20, false, "total").unwrap();
}

#[test]
fn run_codeclimate_format() {
    let dir = tempfile::tempdir().unwrap();
    fs::write(
        dir.path().join("main.rs"),
        "fn foo() {\n    if x > 0 {\n        bar();\n    }\n}\n",
    )
    .unwrap();
    let filter = ExcludeFilter::default();
    let cfg = WalkConfig::new(dir.path(), false, &filter);
    run(&cfg, OutputMode::Codeclimate, 1, 20, false, "total").unwrap();
}

#[test]
fn run_github_format() {
    let dir = tempfile::tempdir().unwrap();
    fs::write(
        dir.path().join("main.rs"),
        "fn foo() {\n    if x > 0 {\n        bar();\n    }\n}\n",
    )
    .unwrap();
    let filter = ExcludeFilter::default();
    let cfg = WalkConfig::new(dir.path(), false, &filter);
    run(&cfg, OutputMode::Github, 1, 20, false, "total").unwrap();
}

#[test]
fn analyze_file_leaves_inline_tests_out_when_they_are_excluded() {
    let (_dir, path) = crate::test_blocks::tests::source_with_test_module();
    let spec = crate::loc::language::detect(&path).unwrap();
    let production = analyze_file(&path, spec, true).unwrap().unwrap();
    let everything = analyze_file(&path, spec, false).unwrap().unwrap();
    assert_eq!(production.function_count, 1);
    assert_eq!(everything.function_count, 2);
    assert!(production.total_complexity < everything.total_complexity);
}

#[test]
fn text_inside_raw_strings_is_no_code() {
    let dir = tempfile::tempdir().unwrap();
    let path = dir.path().join("lib.rs");
    let src = "\
fn text() -> &'static str {
    r#\"
fn fake(a: u32) {
    if a > 1 && a < 3 { }
}
\"#
}

fn json() -> &'static str {
    r#\"{ \"if\": \"while {\" }\"#
}

fn real(n: u32) -> u32 {
    if n > 1 { 2 } else { 3 }
}
";
    fs::write(&path, src).unwrap();
    let spec = crate::loc::language::detect(&path).unwrap();
    let file = analyze_file(&path, spec, true).unwrap().unwrap();
    let found: Vec<(&str, usize)> = file
        .functions
        .iter()
        .map(|f| (f.name.as_str(), f.complexity))
        .collect();
    assert_eq!(found, [("text", 1), ("json", 1), ("real", 2)]);
}