debtmap 0.23.0

Code complexity and technical debt analyzer
Documentation
//! Performance benchmarks for call graph operations
//!
//! This ensures that the refactoring hasn't introduced performance regressions

use criterion::{Criterion, criterion_group, criterion_main};
use debtmap::analyzers::call_graph::debug::{CallGraphDebugger, DebugConfig, DebugFormat};
use debtmap::analyzers::call_graph::validation::CallGraphValidator;
use debtmap::priority::call_graph::{CallGraph, CallType, FunctionCall, FunctionId};
use std::hint::black_box;
use std::path::PathBuf;

fn create_test_graph(size: usize) -> CallGraph {
    let mut graph = CallGraph::new();

    // Create functions
    for i in 0..size {
        let func_id = FunctionId::new(
            PathBuf::from(format!("file{}.rs", i % 10)),
            format!("func_{}", i),
            i * 10,
        );
        graph.add_function(func_id, i == 0, false, (i % 10) as u32, i * 5);
    }

    // Create call relationships
    for i in 0..size - 1 {
        let caller = FunctionId::new(
            PathBuf::from(format!("file{}.rs", i % 10)),
            format!("func_{}", i),
            i * 10,
        );
        let callee = FunctionId::new(
            PathBuf::from(format!("file{}.rs", (i + 1) % 10)),
            format!("func_{}", i + 1),
            (i + 1) * 10,
        );
        graph.add_call(FunctionCall {
            caller,
            callee,
            call_type: CallType::Direct,
        });
    }

    graph
}

fn bench_add_function(c: &mut Criterion) {
    c.bench_function("add_function", |b| {
        b.iter(|| {
            let mut graph = CallGraph::new();
            for i in 0..100 {
                let func_id =
                    FunctionId::new(PathBuf::from("test.rs"), format!("func_{}", i), i * 10);
                graph.add_function(black_box(func_id), false, false, 5, 50);
            }
        });
    });
}

fn bench_add_call(c: &mut Criterion) {
    c.bench_function("add_call", |b| {
        let mut graph = create_test_graph(100);
        b.iter(|| {
            let call = FunctionCall {
                caller: FunctionId::new(PathBuf::from("test.rs"), "caller".to_string(), 10),
                callee: FunctionId::new(PathBuf::from("test.rs"), "callee".to_string(), 20),
                call_type: CallType::Direct,
            };
            graph.add_call(black_box(call));
        });
    });
}

fn bench_get_callees(c: &mut Criterion) {
    let graph = create_test_graph(1000);
    let func_id = FunctionId::new(PathBuf::from("file0.rs"), "func_0".to_string(), 0);

    c.bench_function("get_callees", |b| {
        b.iter(|| {
            graph.get_callees(black_box(&func_id));
        });
    });
}

fn bench_transitive_callees(c: &mut Criterion) {
    let graph = create_test_graph(100);
    let func_id = FunctionId::new(PathBuf::from("file0.rs"), "func_0".to_string(), 0);

    c.bench_function("get_transitive_callees_depth_3", |b| {
        b.iter(|| {
            graph.get_transitive_callees(black_box(&func_id), 3);
        });
    });

    c.bench_function("get_transitive_callees_depth_10", |b| {
        b.iter(|| {
            graph.get_transitive_callees(black_box(&func_id), 10);
        });
    });
}

fn bench_criticality_calculation(c: &mut Criterion) {
    let graph = create_test_graph(500);
    let func_id = FunctionId::new(PathBuf::from("file5.rs"), "func_50".to_string(), 500);

    c.bench_function("calculate_criticality", |b| {
        b.iter(|| {
            graph.calculate_criticality(black_box(&func_id));
        });
    });
}

fn bench_delegation_detection(c: &mut Criterion) {
    let graph = create_test_graph(200);
    let func_id = FunctionId::new(PathBuf::from("file2.rs"), "func_20".to_string(), 200);

    c.bench_function("detect_delegation_pattern", |b| {
        b.iter(|| {
            graph.detect_delegation_pattern(black_box(&func_id));
        });
    });
}

fn bench_cross_file_resolution(c: &mut Criterion) {
    c.bench_function("resolve_cross_file_calls", |b| {
        b.iter(|| {
            let mut graph = create_test_graph(500);
            // Add some unresolved calls
            for i in 0..50 {
                let call = FunctionCall {
                    caller: FunctionId {
                        file: PathBuf::from(format!("file{}.rs", i % 10)),
                        name: format!("caller_{}", i),
                        line: i * 10,
                        module_path: String::new(),
                    },
                    callee: FunctionId {
                        file: PathBuf::from("unknown.rs"),
                        name: format!("unresolved_{}", i),
                        line: 0, // Line 0 indicates unresolved
                        module_path: String::new(),
                    },
                    call_type: CallType::Direct,
                };
                graph.add_call(call);
            }
            graph.resolve_cross_file_calls();
        });
    });
}

/// Benchmark debug mode overhead - verifies <20% overhead requirement
fn bench_debug_mode_overhead(c: &mut Criterion) {
    let graph = create_test_graph(500);

    // Baseline: validation without debug mode
    c.bench_function("validation_baseline", |b| {
        b.iter(|| {
            CallGraphValidator::validate(black_box(&graph));
        });
    });

    // With debug mode enabled
    c.bench_function("validation_with_debug", |b| {
        b.iter(|| {
            let debug_config = DebugConfig {
                show_successes: false,
                show_timing: true,
                max_candidates_shown: 5,
                format: DebugFormat::Text,
                filter_functions: None,
            };
            let mut debugger = CallGraphDebugger::new(debug_config);
            debugger.finalize_statistics();

            CallGraphValidator::validate(black_box(&graph));
        });
    });

    // Debug report generation
    c.bench_function("debug_report_generation_text", |b| {
        b.iter(|| {
            let debug_config = DebugConfig {
                show_successes: false,
                show_timing: true,
                max_candidates_shown: 5,
                format: DebugFormat::Text,
                filter_functions: None,
            };
            let mut debugger = CallGraphDebugger::new(debug_config);
            debugger.finalize_statistics();

            let mut output = Vec::new();
            let _ = debugger.write_report(&mut output);
        });
    });

    c.bench_function("debug_report_generation_json", |b| {
        b.iter(|| {
            let debug_config = DebugConfig {
                show_successes: false,
                show_timing: true,
                max_candidates_shown: 5,
                format: DebugFormat::Json,
                filter_functions: None,
            };
            let mut debugger = CallGraphDebugger::new(debug_config);
            debugger.finalize_statistics();

            let mut output = Vec::new();
            let _ = debugger.write_report(&mut output);
        });
    });
}

/// Benchmark validation operations
fn bench_validation_operations(c: &mut Criterion) {
    let graph = create_test_graph(1000);

    c.bench_function("validate_call_graph_1000_functions", |b| {
        b.iter(|| {
            CallGraphValidator::validate(black_box(&graph));
        });
    });

    // Test with smaller graph
    let small_graph = create_test_graph(100);
    c.bench_function("validate_call_graph_100_functions", |b| {
        b.iter(|| {
            CallGraphValidator::validate(black_box(&small_graph));
        });
    });
}

criterion_group!(
    benches,
    bench_add_function,
    bench_add_call,
    bench_get_callees,
    bench_transitive_callees,
    bench_criticality_calculation,
    bench_delegation_detection,
    bench_cross_file_resolution,
    bench_debug_mode_overhead,
    bench_validation_operations
);

criterion_main!(benches);