use super::*;
use crate::adapters::analyzers::coupling::{CouplingAnalysis, CouplingMetrics, ModuleGraph};
use crate::config::sections::CouplingConfig;
fn coupling_config() -> CouplingConfig {
CouplingConfig {
max_instability: 0.5,
max_fan_in: 5,
max_fan_out: 5,
..CouplingConfig::default()
}
}
fn metric(afferent: usize, efferent: usize, instability: f64) -> CouplingMetrics {
CouplingMetrics {
module_name: "m".to_string(),
afferent,
efferent,
instability,
incoming: vec![],
outgoing: vec![],
suppressed: false,
warning: false,
}
}
fn coupling_warnings(metrics: Vec<CouplingMetrics>) -> usize {
let mut analysis = CouplingAnalysis {
metrics,
cycles: vec![],
sdp_violations: vec![],
graph: ModuleGraph::default(),
};
let config = coupling_config();
let mut summary = Summary::from_results(&[]);
count_coupling_warnings(Some(&mut analysis), &config, &mut summary);
summary.coupling_warnings
}
#[test]
fn coupling_afferent_exactly_at_threshold_does_not_warn() {
assert_eq!(coupling_warnings(vec![metric(5, 0, 0.0)]), 0);
}
#[test]
fn coupling_afferent_above_threshold_warns() {
assert_eq!(coupling_warnings(vec![metric(6, 0, 0.0)]), 1);
}
#[test]
fn coupling_efferent_exactly_at_threshold_does_not_warn() {
assert_eq!(coupling_warnings(vec![metric(0, 5, 0.0)]), 0);
}
#[test]
fn coupling_efferent_above_threshold_warns() {
assert_eq!(coupling_warnings(vec![metric(0, 6, 0.0)]), 1);
}
#[test]
fn coupling_instability_exactly_at_threshold_does_not_warn() {
assert_eq!(coupling_warnings(vec![metric(1, 0, 0.5)]), 0);
}
#[test]
fn coupling_instability_above_threshold_warns() {
assert_eq!(coupling_warnings(vec![metric(1, 0, 0.6)]), 1);
}
#[test]
fn compute_coupling_returns_some_when_enabled() {
let analysis = compute_coupling(&[], &crate::config::Config::default());
assert!(analysis.is_some(), "coupling analysis runs when enabled");
}
#[test]
fn count_srp_warnings_counts_unsuppressed_struct_warnings() {
let mut srp = make_srp();
srp.struct_warnings
.push(crate::adapters::analyzers::srp::SrpWarning {
struct_name: "S".to_string(),
file: "test.rs".to_string(),
line: 1,
lcom4: 2,
field_count: 3,
method_count: 4,
fan_out: 1,
composite_score: 0.9,
clusters: vec![],
suppressed: false,
});
let mut summary = Summary::from_results(&[]);
count_srp_warnings(Some(&srp), &mut summary);
assert_eq!(summary.srp_struct_warnings, 1);
}
#[test]
fn count_sdp_violations_excludes_suppressed_with_distinct_counts() {
use crate::adapters::analyzers::coupling::sdp::SdpViolation;
let sdp = |suppressed: bool| SdpViolation {
from_module: "a".into(),
to_module: "b".into(),
from_instability: 0.2,
to_instability: 0.8,
suppressed,
};
let analysis = CouplingAnalysis {
metrics: vec![],
cycles: vec![],
sdp_violations: vec![sdp(true), sdp(true), sdp(false)],
graph: ModuleGraph::default(),
};
let config = CouplingConfig::default();
let mut summary = Summary::from_results(&[]);
count_sdp_violations(Some(&analysis), &config, &mut summary);
assert_eq!(
summary.sdp_violations, 1,
"only the one unsuppressed violation counts"
);
}
use crate::adapters::analyzers::dry::wildcards::WildcardImportWarning;
use crate::findings::Dimension;
use std::collections::HashMap;
fn sups_at(line: usize, dim: Dimension) -> HashMap<String, Vec<Suppression>> {
[(
"test.rs".to_string(),
vec![Suppression {
line,
dimensions: vec![dim],
reason: None,
}],
)]
.into()
}
fn wildcard_suppressed(w_line: usize, sup_line: usize, dim: Dimension) -> bool {
let mut warnings = vec![WildcardImportWarning {
file: "test.rs".to_string(),
line: w_line,
module_path: "crate::x::*".to_string(),
suppressed: false,
}];
mark_wildcard_suppressions(&mut warnings, &sups_at(sup_line, dim));
warnings[0].suppressed
}
#[test]
fn wildcard_suppression_same_line_suppresses() {
assert!(wildcard_suppressed(5, 5, Dimension::Dry));
}
#[test]
fn wildcard_suppression_one_line_above_suppresses() {
assert!(wildcard_suppressed(2, 1, Dimension::Dry));
}
#[test]
fn wildcard_suppression_two_lines_above_does_not_suppress() {
assert!(!wildcard_suppressed(3, 1, Dimension::Dry));
}
#[test]
fn wildcard_suppression_below_warning_does_not_suppress() {
assert!(!wildcard_suppressed(2, 5, Dimension::Dry));
}
#[test]
fn wildcard_suppression_wrong_dimension_does_not_suppress() {
assert!(!wildcard_suppressed(5, 5, Dimension::Complexity));
}
fn one_entry_dup(suppressed: bool) -> DuplicateGroup {
DuplicateGroup {
entries: vec![DuplicateEntry {
name: "a".into(),
qualified_name: "a".into(),
file: "test.rs".into(),
line: 1,
}],
kind: DuplicateKind::Exact,
suppressed,
}
}
fn one_entry_frag(suppressed: bool) -> FragmentGroup {
FragmentGroup {
entries: vec![FragmentEntry {
function_name: "f".into(),
qualified_name: "f".into(),
file: "test.rs".into(),
start_line: 1,
end_line: 3,
}],
statement_count: 3,
suppressed,
}
}
fn one_bp(suppressed: bool) -> BoilerplateFind {
BoilerplateFind {
pattern_id: "BP-001".into(),
file: "test.rs".into(),
line: 1,
struct_name: None,
description: String::new(),
suggestion: String::new(),
suppressed,
}
}
fn one_wildcard(suppressed: bool) -> WildcardImportWarning {
WildcardImportWarning {
file: "test.rs".into(),
line: 1,
module_path: "crate::x::*".into(),
suppressed,
}
}
fn one_entry_rep(suppressed: bool) -> RepeatedMatchGroup {
RepeatedMatchGroup {
enum_name: "E".into(),
entries: vec![RepeatedMatchEntry {
file: "test.rs".into(),
line: 1,
function_name: "h".into(),
arm_count: 4,
}],
suppressed,
}
}
#[test]
fn count_dry_findings_counts_only_unsuppressed() {
let dry = DryResults {
duplicates: vec![
one_entry_dup(false),
one_entry_dup(false),
one_entry_dup(true),
],
dead_code: vec![],
fragments: vec![
one_entry_frag(false),
one_entry_frag(false),
one_entry_frag(true),
],
boilerplate: vec![one_bp(false), one_bp(false), one_bp(true)],
wildcard_warnings: vec![one_wildcard(false), one_wildcard(false), one_wildcard(true)],
};
let repeated = vec![
one_entry_rep(false),
one_entry_rep(false),
one_entry_rep(true),
];
let mut summary = Summary::from_results(&[]);
count_dry_findings(&dry, &repeated, &mut summary);
assert_eq!(summary.duplicate_groups, 2, "duplicates");
assert_eq!(summary.fragment_groups, 2, "fragments");
assert_eq!(summary.boilerplate_warnings, 2, "boilerplate");
assert_eq!(summary.wildcard_import_warnings, 2, "wildcards");
assert_eq!(summary.repeated_match_groups, 2, "repeated matches");
}