#![cfg_attr(coverage_nightly, coverage(off))]
use crate::cli::enums::ProvabilityOutputFormat;
use crate::cli::handlers::provability_handler::ProvabilityConfig;
use crate::cli::provability_helpers::{
filter_summaries, format_provability_detailed, format_provability_json,
format_provability_sarif, format_provability_summary, parse_function_spec,
};
use crate::services::lightweight_provability_analyzer::{
AliasLattice, FunctionId, IntervalLattice, LightweightProvabilityAnalyzer, NullabilityLattice,
ProofSummary, PropertyDomain, PropertyType, PurityLattice, VerifiedProperty,
};
use std::path::PathBuf;
fn strip_ansi(s: &str) -> String {
let re = regex::Regex::new(r"\x1b\[[0-9;]*m").unwrap();
re.replace_all(s, "").to_string()
}
#[test]
fn test_provability_config_all_fields() {
let config = ProvabilityConfig {
project_path: PathBuf::from("/home/user/project"),
functions: vec!["main".to_string(), "helper".to_string()],
analysis_depth: 10,
format: ProvabilityOutputFormat::Json,
high_confidence_only: true,
include_evidence: true,
output: Some(PathBuf::from("/tmp/output.json")),
top_files: 20,
};
assert_eq!(config.project_path, PathBuf::from("/home/user/project"));
assert_eq!(config.functions.len(), 2);
assert_eq!(config.analysis_depth, 10);
assert_eq!(config.format, ProvabilityOutputFormat::Json);
assert!(config.high_confidence_only);
assert!(config.include_evidence);
assert_eq!(config.output, Some(PathBuf::from("/tmp/output.json")));
assert_eq!(config.top_files, 20);
}
#[test]
fn test_provability_config_minimal() {
let config = ProvabilityConfig {
project_path: PathBuf::from("."),
functions: vec![],
analysis_depth: 1,
format: ProvabilityOutputFormat::Summary,
high_confidence_only: false,
include_evidence: false,
output: None,
top_files: 0,
};
assert!(config.functions.is_empty());
assert_eq!(config.analysis_depth, 1);
assert!(!config.high_confidence_only);
assert!(!config.include_evidence);
assert!(config.output.is_none());
}
#[test]
fn test_provability_config_all_formats() {
let formats = vec![
ProvabilityOutputFormat::Json,
ProvabilityOutputFormat::Summary,
ProvabilityOutputFormat::Full,
ProvabilityOutputFormat::Sarif,
ProvabilityOutputFormat::Markdown,
];
for format in formats {
let config = ProvabilityConfig {
project_path: PathBuf::from("/test"),
functions: vec![],
analysis_depth: 5,
format: format.clone(),
high_confidence_only: false,
include_evidence: false,
output: None,
top_files: 5,
};
assert_eq!(config.format, format);
}
}
#[test]
fn test_provability_config_clone() {
let original = ProvabilityConfig {
project_path: PathBuf::from("/original"),
functions: vec!["fn1".to_string(), "fn2".to_string()],
analysis_depth: 7,
format: ProvabilityOutputFormat::Sarif,
high_confidence_only: true,
include_evidence: true,
output: Some(PathBuf::from("/output.sarif")),
top_files: 15,
};
let cloned = original.clone();
assert_eq!(original.project_path, cloned.project_path);
assert_eq!(original.functions, cloned.functions);
assert_eq!(original.analysis_depth, cloned.analysis_depth);
assert_eq!(original.format, cloned.format);
assert_eq!(original.high_confidence_only, cloned.high_confidence_only);
assert_eq!(original.include_evidence, cloned.include_evidence);
assert_eq!(original.output, cloned.output);
assert_eq!(original.top_files, cloned.top_files);
}
#[test]
fn test_provability_config_debug() {
let config = ProvabilityConfig {
project_path: PathBuf::from("/debug/test"),
functions: vec!["test_fn".to_string()],
analysis_depth: 3,
format: ProvabilityOutputFormat::Full,
high_confidence_only: false,
include_evidence: true,
output: None,
top_files: 10,
};
let debug_str = format!("{:?}", config);
assert!(debug_str.contains("ProvabilityConfig"));
assert!(debug_str.contains("/debug/test"));
assert!(debug_str.contains("test_fn"));
}
#[test]
fn test_parse_function_spec_simple_name() {
let result = parse_function_spec("main", &PathBuf::from("/project")).unwrap();
assert_eq!(result.function_name, "main");
assert!(result.file_path.is_empty());
assert_eq!(result.line_number, 0);
}
#[test]
fn test_parse_function_spec_with_path() {
let result = parse_function_spec("src/lib.rs:helper", &PathBuf::from("/project")).unwrap();
assert_eq!(result.function_name, "helper");
assert!(result.file_path.contains("src/lib.rs"));
assert_eq!(result.line_number, 0);
}
#[test]
fn test_parse_function_spec_nested_path() {
let result = parse_function_spec(
"src/services/analyzer.rs:analyze",
&PathBuf::from("/project"),
)
.unwrap();
assert_eq!(result.function_name, "analyze");
assert!(result.file_path.contains("services"));
}
#[test]
fn test_parse_function_spec_empty_function_name() {
let result = parse_function_spec("src/main.rs:", &PathBuf::from("/project")).unwrap();
assert!(result.function_name.is_empty());
}
#[test]
fn test_parse_function_spec_windows_style_path() {
let result = parse_function_spec("src\\lib.rs:func", &PathBuf::from("/project")).unwrap();
assert_eq!(result.function_name, "func");
}
fn create_summary(score: f64) -> ProofSummary {
ProofSummary {
provability_score: score,
analysis_time_us: 100,
verified_properties: vec![],
version: 1,
}
}
#[test]
fn test_filter_summaries_no_filter() {
let summaries = vec![
create_summary(0.9),
create_summary(0.5),
create_summary(0.3),
];
let filtered = filter_summaries(&summaries, false);
assert_eq!(filtered.len(), 3);
}
#[test]
fn test_filter_summaries_high_confidence_only() {
let summaries = vec![
create_summary(0.95),
create_summary(0.8),
create_summary(0.79),
create_summary(0.5),
];
let filtered = filter_summaries(&summaries, true);
assert_eq!(filtered.len(), 2);
assert!(filtered.iter().all(|s| s.provability_score >= 0.8));
}
#[test]
fn test_filter_summaries_empty() {
let summaries: Vec<ProofSummary> = vec![];
let filtered = filter_summaries(&summaries, true);
assert!(filtered.is_empty());
}
#[test]
fn test_filter_summaries_all_below_threshold() {
let summaries = vec![
create_summary(0.7),
create_summary(0.6),
create_summary(0.5),
];
let filtered = filter_summaries(&summaries, true);
assert!(filtered.is_empty());
}
#[test]
fn test_filter_summaries_boundary_exact() {
let summaries = vec![create_summary(0.8), create_summary(0.7999)];
let filtered = filter_summaries(&summaries, true);
assert_eq!(filtered.len(), 1);
assert_eq!(filtered[0].provability_score, 0.8);
}
fn create_function_id(file: &str, name: &str, line: usize) -> FunctionId {
FunctionId {
file_path: file.to_string(),
function_name: name.to_string(),
line_number: line,
}
}
#[test]
fn test_function_id_creation() {
let func_id = create_function_id("src/main.rs", "main", 10);
assert_eq!(func_id.file_path, "src/main.rs");
assert_eq!(func_id.function_name, "main");
assert_eq!(func_id.line_number, 10);
}
#[test]
fn test_function_id_clone() {
let func_id = create_function_id("src/lib.rs", "helper", 42);
let cloned = func_id.clone();
assert_eq!(func_id.file_path, cloned.file_path);
assert_eq!(func_id.function_name, cloned.function_name);
assert_eq!(func_id.line_number, cloned.line_number);
}
#[test]
fn test_function_id_debug() {
let func_id = create_function_id("test.rs", "test_fn", 1);
let debug_str = format!("{:?}", func_id);
assert!(debug_str.contains("FunctionId"));
assert!(debug_str.contains("test.rs"));
assert!(debug_str.contains("test_fn"));
}
#[test]
fn test_function_id_equality() {
let func_id1 = create_function_id("src/main.rs", "main", 10);
let func_id2 = create_function_id("src/main.rs", "main", 10);
let func_id3 = create_function_id("src/main.rs", "main", 20);
assert_eq!(func_id1, func_id2);
assert_ne!(func_id1, func_id3);
}
#[test]
fn test_function_id_hash() {
use std::collections::HashSet;
let mut set = HashSet::new();
set.insert(create_function_id("a.rs", "fn1", 1));
set.insert(create_function_id("b.rs", "fn2", 2));
set.insert(create_function_id("a.rs", "fn1", 1)); assert_eq!(set.len(), 2);
}
#[test]
fn test_proof_summary_creation() {
let summary = ProofSummary {
provability_score: 0.85,
analysis_time_us: 500,
verified_properties: vec![],
version: 1,
};
assert_eq!(summary.provability_score, 0.85);
assert_eq!(summary.analysis_time_us, 500);
assert!(summary.verified_properties.is_empty());
assert_eq!(summary.version, 1);
}
#[test]
fn test_proof_summary_with_properties() {
let summary = ProofSummary {
provability_score: 0.95,
analysis_time_us: 200,
verified_properties: vec![
VerifiedProperty {
property_type: PropertyType::NullSafety,
confidence: 0.9,
evidence: "No null refs".to_string(),
},
VerifiedProperty {
property_type: PropertyType::BoundsCheck,
confidence: 0.85,
evidence: "Bounds verified".to_string(),
},
],
version: 2,
};
assert_eq!(summary.verified_properties.len(), 2);
}
#[test]
fn test_proof_summary_clone() {
let summary = create_summary(0.75);
let cloned = summary.clone();
assert_eq!(summary.provability_score, cloned.provability_score);
}
#[test]
fn test_proof_summary_debug() {
let summary = create_summary(0.5);
let debug_str = format!("{:?}", summary);
assert!(debug_str.contains("ProofSummary"));
assert!(debug_str.contains("0.5"));
}
#[test]
fn test_property_type_variants() {
let types = vec![
PropertyType::NullSafety,
PropertyType::BoundsCheck,
PropertyType::NoAliasing,
PropertyType::PureFunction,
PropertyType::MemorySafety,
PropertyType::ThreadSafety,
];
for prop_type in &types {
let debug_str = format!("{:?}", prop_type);
assert!(!debug_str.is_empty());
}
}
#[test]
fn test_property_type_equality() {
assert_eq!(PropertyType::NullSafety, PropertyType::NullSafety);
assert_ne!(PropertyType::NullSafety, PropertyType::BoundsCheck);
}
#[test]
fn test_property_type_clone() {
let prop_type = PropertyType::MemorySafety;
let cloned = prop_type.clone();
assert_eq!(prop_type, cloned);
}
#[test]
fn test_verified_property_creation() {
let prop = VerifiedProperty {
property_type: PropertyType::PureFunction,
confidence: 0.95,
evidence: "No side effects detected".to_string(),
};
assert_eq!(prop.property_type, PropertyType::PureFunction);
assert_eq!(prop.confidence, 0.95);
assert_eq!(prop.evidence, "No side effects detected");
}
#[test]
fn test_verified_property_clone() {
let prop = VerifiedProperty {
property_type: PropertyType::ThreadSafety,
confidence: 0.88,
evidence: "Sync + Send bounds verified".to_string(),
};
let cloned = prop.clone();
assert_eq!(prop.property_type, cloned.property_type);
assert_eq!(prop.confidence, cloned.confidence);
assert_eq!(prop.evidence, cloned.evidence);
}
#[test]
fn test_verified_property_debug() {
let prop = VerifiedProperty {
property_type: PropertyType::NoAliasing,
confidence: 0.8,
evidence: "No mutable aliasing".to_string(),
};
let debug_str = format!("{:?}", prop);
assert!(debug_str.contains("VerifiedProperty"));
assert!(debug_str.contains("NoAliasing"));
}
#[test]
fn test_nullability_lattice_join_not_null() {
assert_eq!(
NullabilityLattice::NotNull.join(&NullabilityLattice::NotNull),
NullabilityLattice::NotNull
);
}
#[test]
fn test_nullability_lattice_join_with_top() {
assert_eq!(
NullabilityLattice::NotNull.join(&NullabilityLattice::Top),
NullabilityLattice::Top
);
assert_eq!(
NullabilityLattice::Top.join(&NullabilityLattice::Null),
NullabilityLattice::Top
);
}
#[test]
fn test_nullability_lattice_join_with_bottom() {
assert_eq!(
NullabilityLattice::Bottom.join(&NullabilityLattice::NotNull),
NullabilityLattice::NotNull
);
assert_eq!(
NullabilityLattice::Null.join(&NullabilityLattice::Bottom),
NullabilityLattice::Null
);
}
#[test]
fn test_nullability_lattice_join_contradiction() {
let result = NullabilityLattice::NotNull.join(&NullabilityLattice::Null);
assert_eq!(result, NullabilityLattice::Bottom);
}
#[test]
fn test_nullability_lattice_join_maybe_null() {
assert_eq!(
NullabilityLattice::NotNull.join(&NullabilityLattice::MaybeNull),
NullabilityLattice::MaybeNull
);
}
#[test]
fn test_nullability_lattice_equality() {
assert_eq!(NullabilityLattice::NotNull, NullabilityLattice::NotNull);
assert_ne!(NullabilityLattice::NotNull, NullabilityLattice::Null);
}
#[test]
fn test_interval_lattice_widen_both_bounds() {
let interval1 = IntervalLattice {
lower: Some(0),
upper: Some(100),
};
let interval2 = IntervalLattice {
lower: Some(0),
upper: Some(100),
};
let result = interval1.widen(&interval2);
assert_eq!(result.lower, Some(0));
assert_eq!(result.upper, Some(100));
}
#[test]
fn test_interval_lattice_widen_to_infinity_upper() {
let interval1 = IntervalLattice {
lower: Some(0),
upper: Some(100),
};
let interval2 = IntervalLattice {
lower: Some(0),
upper: Some(200),
}; let result = interval1.widen(&interval2);
assert_eq!(result.lower, Some(0));
assert_eq!(result.upper, None); }
#[test]
fn test_interval_lattice_widen_to_infinity_lower() {
let interval1 = IntervalLattice {
lower: Some(10),
upper: Some(100),
};
let interval2 = IntervalLattice {
lower: Some(5),
upper: Some(100),
}; let result = interval1.widen(&interval2);
assert_eq!(result.lower, None); assert_eq!(result.upper, Some(100));
}
#[test]
fn test_interval_lattice_is_equal() {
let interval1 = IntervalLattice {
lower: Some(0),
upper: Some(100),
};
let interval2 = IntervalLattice {
lower: Some(0),
upper: Some(100),
};
let interval3 = IntervalLattice {
lower: Some(0),
upper: Some(50),
};
assert!(interval1.is_equal(&interval2));
assert!(!interval1.is_equal(&interval3));
}
#[test]
fn test_interval_lattice_none_bounds() {
let interval1 = IntervalLattice {
lower: None,
upper: None,
};
let interval2 = IntervalLattice {
lower: Some(0),
upper: Some(100),
};
let result = interval1.widen(&interval2);
assert_eq!(result.lower, None);
assert_eq!(result.upper, None);
}
#[test]
fn test_alias_lattice_join_no_alias() {
assert_eq!(
AliasLattice::NoAlias.join(&AliasLattice::NoAlias),
AliasLattice::NoAlias
);
}
#[test]
fn test_alias_lattice_join_with_top() {
assert_eq!(
AliasLattice::NoAlias.join(&AliasLattice::Top),
AliasLattice::Top
);
}
#[test]
fn test_alias_lattice_join_with_bottom() {
assert_eq!(
AliasLattice::Bottom.join(&AliasLattice::NoAlias),
AliasLattice::NoAlias
);
}
#[test]
fn test_alias_lattice_join_must_alias() {
assert_eq!(
AliasLattice::MustAlias.join(&AliasLattice::MustAlias),
AliasLattice::MustAlias
);
}
#[test]
fn test_alias_lattice_join_mixed() {
assert_eq!(
AliasLattice::NoAlias.join(&AliasLattice::MustAlias),
AliasLattice::MayAlias
);
}
#[test]
fn test_alias_lattice_equality() {
assert_eq!(AliasLattice::MayAlias, AliasLattice::MayAlias);
assert_ne!(AliasLattice::NoAlias, AliasLattice::MayAlias);
}
#[test]
fn test_purity_lattice_meet_pure() {
assert_eq!(
PurityLattice::Pure.meet(&PurityLattice::Pure),
PurityLattice::Pure
);
}
#[test]
fn test_purity_lattice_meet_readonly() {
assert_eq!(
PurityLattice::ReadOnly.meet(&PurityLattice::ReadOnly),
PurityLattice::ReadOnly
);
}
#[test]
fn test_purity_lattice_meet_write_local() {
assert_eq!(
PurityLattice::Pure.meet(&PurityLattice::WriteLocal),
PurityLattice::WriteLocal
);
}
#[test]
fn test_purity_lattice_meet_write_global() {
assert_eq!(
PurityLattice::Pure.meet(&PurityLattice::WriteGlobal),
PurityLattice::WriteGlobal
);
}
#[test]
fn test_purity_lattice_meet_bottom() {
assert_eq!(
PurityLattice::Bottom.meet(&PurityLattice::Pure),
PurityLattice::Bottom
);
}
#[test]
fn test_purity_lattice_meet_top() {
let result = PurityLattice::Pure.meet(&PurityLattice::Top);
assert_eq!(result, PurityLattice::Top);
}
#[test]
fn test_purity_lattice_equality() {
assert_eq!(PurityLattice::WriteLocal, PurityLattice::WriteLocal);
assert_ne!(PurityLattice::ReadOnly, PurityLattice::WriteLocal);
}
#[test]
fn test_property_domain_top() {
let domain = PropertyDomain::top();
assert_eq!(domain.nullability, NullabilityLattice::Top);
assert_eq!(domain.aliasing, AliasLattice::Top);
assert_eq!(domain.purity, PurityLattice::Top);
assert!(domain.bounds.lower.is_none());
assert!(domain.bounds.upper.is_none());
}
#[test]
fn test_property_domain_join() {
let domain1 = PropertyDomain::top();
let domain2 = PropertyDomain {
nullability: NullabilityLattice::NotNull,
bounds: IntervalLattice {
lower: Some(0),
upper: Some(100),
},
aliasing: AliasLattice::NoAlias,
purity: PurityLattice::Pure,
};
let joined = domain1.join(&domain2);
assert_eq!(joined.nullability, NullabilityLattice::Top);
}
#[test]
fn test_property_domain_widen() {
let domain1 = PropertyDomain {
nullability: NullabilityLattice::NotNull,
bounds: IntervalLattice {
lower: Some(0),
upper: Some(50),
},
aliasing: AliasLattice::NoAlias,
purity: PurityLattice::Pure,
};
let domain2 = PropertyDomain {
nullability: NullabilityLattice::NotNull,
bounds: IntervalLattice {
lower: Some(0),
upper: Some(100),
},
aliasing: AliasLattice::NoAlias,
purity: PurityLattice::Pure,
};
let widened = domain1.widen(&domain2);
assert_eq!(widened.bounds.upper, None);
}
#[test]
fn test_property_domain_is_equal() {
let domain1 = PropertyDomain {
nullability: NullabilityLattice::NotNull,
bounds: IntervalLattice {
lower: Some(0),
upper: Some(100),
},
aliasing: AliasLattice::NoAlias,
purity: PurityLattice::Pure,
};
let domain2 = domain1.clone();
assert!(domain1.is_equal(&domain2));
}
#[test]
fn test_property_domain_is_not_equal() {
let domain1 = PropertyDomain::top();
let domain2 = PropertyDomain {
nullability: NullabilityLattice::NotNull,
bounds: IntervalLattice {
lower: Some(0),
upper: Some(100),
},
aliasing: AliasLattice::NoAlias,
purity: PurityLattice::Pure,
};
assert!(!domain1.is_equal(&domain2));
}
#[test]
fn test_property_domain_clone() {
let domain = PropertyDomain {
nullability: NullabilityLattice::MaybeNull,
bounds: IntervalLattice {
lower: Some(10),
upper: Some(50),
},
aliasing: AliasLattice::MayAlias,
purity: PurityLattice::ReadOnly,
};
let cloned = domain.clone();
assert!(domain.is_equal(&cloned));
}
#[test]
fn test_analyzer_new() {
let analyzer = LightweightProvabilityAnalyzer::new();
let _ = format!("{:p}", &analyzer);
}
#[test]
fn test_analyzer_default() {
let analyzer = LightweightProvabilityAnalyzer::default();
let _ = format!("{:p}", &analyzer);
}
#[tokio::test]
async fn test_analyzer_analyze_incrementally_empty() {
let analyzer = LightweightProvabilityAnalyzer::new();
let functions: Vec<FunctionId> = vec![];
let summaries = analyzer.analyze_incrementally(&functions).await;
assert!(summaries.is_empty());
}
#[tokio::test]
async fn test_analyzer_analyze_incrementally_single() {
let analyzer = LightweightProvabilityAnalyzer::new();
let functions = vec![create_function_id("nonexistent.rs", "test_fn", 1)];
let summaries = analyzer.analyze_incrementally(&functions).await;
assert_eq!(summaries.len(), 1);
assert!(summaries[0].provability_score <= 1.0);
}
#[tokio::test]
async fn test_analyzer_analyze_incrementally_multiple() {
let analyzer = LightweightProvabilityAnalyzer::new();
let functions = vec![
create_function_id("a.rs", "fn1", 1),
create_function_id("b.rs", "fn2", 2),
create_function_id("c.rs", "fn3", 3),
];
let summaries = analyzer.analyze_incrementally(&functions).await;
assert_eq!(summaries.len(), 3);
}
#[test]
fn test_analyzer_calculate_provability_factor_high_score() {
let analyzer = LightweightProvabilityAnalyzer::new();
let summary = ProofSummary {
provability_score: 0.9,
analysis_time_us: 100,
verified_properties: vec![],
version: 1,
};
let factor = analyzer.calculate_provability_factor(&summary);
assert!(factor < 1.0);
}
#[test]
fn test_analyzer_calculate_provability_factor_low_score() {
let analyzer = LightweightProvabilityAnalyzer::new();
let summary = ProofSummary {
provability_score: 0.1,
analysis_time_us: 100,
verified_properties: vec![],
version: 1,
};
let factor = analyzer.calculate_provability_factor(&summary);
assert!(factor > 4.0);
}
#[test]
fn test_analyzer_calculate_provability_factor_with_critical_properties() {
let analyzer = LightweightProvabilityAnalyzer::new();
let summary = ProofSummary {
provability_score: 0.5,
analysis_time_us: 100,
verified_properties: vec![VerifiedProperty {
property_type: PropertyType::MemorySafety,
confidence: 0.9,
evidence: "Memory safe".to_string(),
}],
version: 1,
};
let factor = analyzer.calculate_provability_factor(&summary);
assert!(factor < 2.5 * 0.8); }
#[test]
fn test_analyzer_calculate_provability_factor_with_thread_safety() {
let analyzer = LightweightProvabilityAnalyzer::new();
let summary = ProofSummary {
provability_score: 0.5,
analysis_time_us: 100,
verified_properties: vec![VerifiedProperty {
property_type: PropertyType::ThreadSafety,
confidence: 0.95,
evidence: "Thread safe".to_string(),
}],
version: 1,
};
let factor = analyzer.calculate_provability_factor(&summary);
assert!(factor < 2.5);
}
#[test]
fn test_format_provability_json_basic() {
let function_ids = vec![create_function_id("src/main.rs", "main", 10)];
let summaries = vec![create_summary(0.85)];
let result = format_provability_json(&function_ids, &summaries, false).unwrap();
assert!(result.contains("provability_analysis"));
assert!(result.contains("\"total_functions\": 1"));
assert!(result.contains("main"));
}
#[test]
fn test_format_provability_json_with_evidence() {
let function_ids = vec![create_function_id("test.rs", "test_fn", 5)];
let summaries = vec![ProofSummary {
provability_score: 0.9,
analysis_time_us: 200,
verified_properties: vec![VerifiedProperty {
property_type: PropertyType::NullSafety,
confidence: 0.95,
evidence: "No null refs".to_string(),
}],
version: 1,
}];
let result = format_provability_json(&function_ids, &summaries, true).unwrap();
assert!(result.contains("properties"));
assert!(result.contains("NullSafety"));
assert!(result.contains("No null refs"));
}
#[test]
fn test_format_provability_json_empty() {
let function_ids: Vec<FunctionId> = vec![];
let summaries: Vec<ProofSummary> = vec![];
let result = format_provability_json(&function_ids, &summaries, false).unwrap();
assert!(result.contains("\"total_functions\": 0"));
}
#[test]
fn test_format_provability_summary_basic() {
let function_ids = vec![
create_function_id("src/main.rs", "main", 10),
create_function_id("src/lib.rs", "helper", 20),
];
let summaries = vec![create_summary(0.9), create_summary(0.6)];
let result = strip_ansi(&format_provability_summary(&function_ids, &summaries, 5).unwrap());
assert!(result.contains("Provability Analysis Summary"));
assert!(result.contains("Total functions analyzed: 2"));
assert!(result.contains("Score Distribution"));
}
#[test]
fn test_format_provability_summary_empty() {
let function_ids: Vec<FunctionId> = vec![];
let summaries: Vec<ProofSummary> = vec![];
let result = strip_ansi(&format_provability_summary(&function_ids, &summaries, 5).unwrap());
assert!(result.contains("Total functions analyzed: 0"));
}
#[test]
fn test_format_provability_summary_score_distribution() {
let function_ids = vec![
create_function_id("a.rs", "high", 1),
create_function_id("b.rs", "medium", 2),
create_function_id("c.rs", "low", 3),
];
let summaries = vec![
create_summary(0.9), create_summary(0.65), create_summary(0.3), ];
let result = format_provability_summary(&function_ids, &summaries, 5).unwrap();
assert!(result.contains("High"));
assert!(result.contains("Medium"));
assert!(result.contains("Low"));
}
#[test]
fn test_format_provability_detailed_basic() {
let function_ids = vec![create_function_id("src/main.rs", "main", 10)];
let summaries = vec![create_summary(0.85)];
let result = format_provability_detailed(&function_ids, &summaries, false).unwrap();
assert!(result.contains("Detailed Provability Analysis"));
assert!(result.contains("main"));
}
#[test]
fn test_format_provability_detailed_with_evidence() {
let function_ids = vec![create_function_id("test.rs", "secure_fn", 42)];
let summaries = vec![ProofSummary {
provability_score: 0.95,
analysis_time_us: 150,
verified_properties: vec![
VerifiedProperty {
property_type: PropertyType::MemorySafety,
confidence: 0.98,
evidence: "All memory safe".to_string(),
},
VerifiedProperty {
property_type: PropertyType::ThreadSafety,
confidence: 0.92,
evidence: "No data races".to_string(),
},
],
version: 1,
}];
let result = format_provability_detailed(&function_ids, &summaries, true).unwrap();
assert!(result.contains("Verified Properties"));
assert!(result.contains("MemorySafety"));
assert!(result.contains("ThreadSafety"));
}
#[test]
fn test_format_provability_detailed_empty() {
let function_ids: Vec<FunctionId> = vec![];
let summaries: Vec<ProofSummary> = vec![];
let result = format_provability_detailed(&function_ids, &summaries, true).unwrap();
assert!(result.contains("Detailed Provability Analysis"));
}
#[test]
fn test_format_provability_sarif_basic() {
let function_ids = vec![create_function_id("src/main.rs", "main", 10)];
let summaries = vec![create_summary(0.85)];
let result = format_provability_sarif(&function_ids, &summaries).unwrap();
assert!(result.contains("sarif-schema-2.1.0"));
assert!(result.contains("paiml-provability-analyzer"));
}
#[test]
fn test_format_provability_sarif_all_levels() {
let function_ids = vec![
create_function_id("a.rs", "high_fn", 1),
create_function_id("b.rs", "medium_fn", 2),
create_function_id("c.rs", "low_fn", 3),
];
let summaries = vec![
create_summary(0.9), create_summary(0.65), create_summary(0.3), ];
let result = format_provability_sarif(&function_ids, &summaries).unwrap();
assert!(result.contains("high-provability"));
assert!(result.contains("medium-provability"));
assert!(result.contains("low-provability"));
assert!(result.contains("\"level\": \"none\""));
assert!(result.contains("\"level\": \"note\""));
assert!(result.contains("\"level\": \"warning\""));
}
#[test]
fn test_format_provability_sarif_empty() {
let function_ids: Vec<FunctionId> = vec![];
let summaries: Vec<ProofSummary> = vec![];
let result = format_provability_sarif(&function_ids, &summaries).unwrap();
assert!(result.contains("sarif-schema"));
assert!(result.contains("\"results\": []"));
}
#[test]
fn test_boundary_scores() {
let summaries = vec![
create_summary(0.0),
create_summary(0.5),
create_summary(0.8),
create_summary(1.0),
];
let filtered_all = filter_summaries(&summaries, false);
assert_eq!(filtered_all.len(), 4);
let filtered_high = filter_summaries(&summaries, true);
assert_eq!(filtered_high.len(), 2); }
#[test]
fn test_large_function_list() {
let function_ids: Vec<FunctionId> = (0..100)
.map(|i| create_function_id(&format!("file{}.rs", i), &format!("fn{}", i), i))
.collect();
let summaries: Vec<ProofSummary> = (0..100).map(|i| create_summary(i as f64 / 100.0)).collect();
let result = strip_ansi(&format_provability_summary(&function_ids, &summaries, 10).unwrap());
assert!(result.contains("Total functions analyzed: 100"));
}
#[test]
fn test_special_characters_in_function_names() {
let function_ids = vec![
create_function_id("test.rs", "test_fn_with_underscore", 1),
create_function_id("test.rs", "fn123", 2),
create_function_id("test.rs", "__internal__", 3),
];
let summaries = vec![
create_summary(0.8),
create_summary(0.7),
create_summary(0.6),
];
let result = format_provability_json(&function_ids, &summaries, false).unwrap();
assert!(result.contains("test_fn_with_underscore"));
assert!(result.contains("fn123"));
assert!(result.contains("__internal__"));
}
#[test]
fn test_unicode_paths() {
let function_ids = vec![create_function_id("src/\u{00e9}dition.rs", "main", 1)];
let summaries = vec![create_summary(0.75)];
let result = format_provability_json(&function_ids, &summaries, false).unwrap();
assert!(result.contains("provability_analysis"));
}
#[test]
fn test_very_long_evidence() {
let long_evidence = "a".repeat(10000);
let summaries = vec![ProofSummary {
provability_score: 0.9,
analysis_time_us: 100,
verified_properties: vec![VerifiedProperty {
property_type: PropertyType::PureFunction,
confidence: 0.95,
evidence: long_evidence.clone(),
}],
version: 1,
}];
let function_ids = vec![create_function_id("test.rs", "fn", 1)];
let result = format_provability_json(&function_ids, &summaries, true).unwrap();
assert!(result.len() > 10000);
}
#[test]
fn test_zero_analysis_time() {
let summary = ProofSummary {
provability_score: 0.5,
analysis_time_us: 0,
verified_properties: vec![],
version: 0,
};
assert_eq!(summary.analysis_time_us, 0);
}
#[test]
fn test_max_version() {
let summary = ProofSummary {
provability_score: 0.5,
analysis_time_us: 100,
verified_properties: vec![],
version: u64::MAX,
};
assert_eq!(summary.version, u64::MAX);
}