#![cfg_attr(coverage_nightly, coverage(off))]
use super::*;
mod tests {
use super::*;
use std::fs;
use tempfile::TempDir;
#[tokio::test]
async fn test_complexity_analysis_uses_ast() {
let temp_dir = TempDir::new().unwrap();
let src_dir = temp_dir.path().join("src");
fs::create_dir_all(&src_dir).unwrap();
let test_file = src_dir.join("test.rs");
fs::write(
&test_file,
r#"
fn simple() {
println!("hello");
}
fn complex() {
if true {
if false {
match 5 {
1 => println!("one"),
2 => println!("two"),
_ => println!("other"),
}
}
}
}
"#,
)
.unwrap();
let analyzer = SimpleDeepContext::new();
let config = SimpleAnalysisConfig {
project_path: temp_dir.path().to_path_buf(),
include_features: vec!["all".to_string()],
include_patterns: vec![],
exclude_patterns: vec![],
enable_verbose: false,
};
let report = analyzer.analyze(config).await.unwrap();
assert_eq!(report.file_count, 1);
assert_eq!(report.complexity_metrics.total_functions, 2);
assert!(report.complexity_metrics.avg_complexity > 1.0);
assert!(report.complexity_metrics.avg_complexity < 10.0);
assert_eq!(report.file_complexity_details.len(), 1);
let file_detail = &report.file_complexity_details[0];
assert_eq!(file_detail.function_count, 2);
assert!(file_detail.avg_complexity > 1.0);
}
#[tokio::test]
async fn test_analyze_file_complexity_heuristic() {
let analyzer = SimpleDeepContext;
let temp_dir = TempDir::new().unwrap();
let py_file = temp_dir.path().join("test.py");
fs::write(
&py_file,
r#"
def simple_function():
return 42
def complex_function(x):
if x > 0:
for i in range(x):
if i % 2 == 0:
print(i)
else:
continue
elif x < 0:
while x < 0:
x += 1
else:
try:
return 1 / x
except:
return 0
"#,
)
.unwrap();
let (count, high, avg) = analyzer
.analyze_file_complexity_heuristic(&py_file, "py")
.await
.unwrap();
assert_eq!(count, 2);
assert!(high <= 1); assert!(avg >= 1.0);
let js_file = temp_dir.path().join("test.js");
fs::write(
&js_file,
r#"
function simpleFunc() {
return 42;
}
const complexFunc = (x) => {
if (x > 0) {
for (let i = 0; i < x; i++) {
if (i % 2 === 0) {
console.log(i);
}
}
}
return x;
};
"#,
)
.unwrap();
let (count, high, avg) = analyzer
.analyze_file_complexity_heuristic(&js_file, "js")
.await
.unwrap();
assert!(count >= 2); assert!(high <= count); assert!(avg >= 1.0); }
#[test]
fn test_estimate_complexity() {
let analyzer = SimpleDeepContext;
let py_code = r#"
if x > 0:
for i in range(10):
if i % 2 == 0:
print(i)
elif x < 0:
print("negative")
"#;
let complexity = analyzer.estimate_complexity(py_code, "py");
assert_eq!(complexity, 6);
let js_code = r#"
if (x > 0 && y < 10) {
for (let i = 0; i < 10; i++) {
if (i % 2 === 0 || i === 5) {
console.log(i);
}
}
}
"#;
let complexity = analyzer.estimate_complexity(js_code, "js");
assert_eq!(complexity, 6); }
#[test]
fn test_simple_deep_context_basic() {
assert_eq!(1 + 1, 2);
}
#[test]
fn test_simple_analysis_config_clone() {
let config = SimpleAnalysisConfig {
project_path: std::path::PathBuf::from("/test/path"),
include_features: vec!["feature1".to_string()],
include_patterns: vec!["**/*.rs".to_string()],
exclude_patterns: vec!["test".to_string()],
enable_verbose: true,
};
let cloned = config.clone();
assert_eq!(cloned.project_path, config.project_path);
assert_eq!(cloned.include_features, config.include_features);
assert_eq!(cloned.include_patterns, config.include_patterns);
assert_eq!(cloned.exclude_patterns, config.exclude_patterns);
assert_eq!(cloned.enable_verbose, config.enable_verbose);
}
#[test]
fn test_simple_analysis_config_debug() {
let config = SimpleAnalysisConfig {
project_path: std::path::PathBuf::from("/test"),
include_features: vec![],
include_patterns: vec![],
exclude_patterns: vec![],
enable_verbose: false,
};
let debug_str = format!("{:?}", config);
assert!(debug_str.contains("project_path"));
}
#[test]
fn test_complexity_metrics_debug() {
let metrics = ComplexityMetrics {
total_functions: 10,
high_complexity_count: 2,
avg_complexity: 3.5,
};
let debug_str = format!("{:?}", metrics);
assert!(debug_str.contains("total_functions"));
assert!(debug_str.contains("10"));
}
#[test]
fn test_file_complexity_detail_clone() {
let detail = FileComplexityDetail {
file_path: std::path::PathBuf::from("test.rs"),
function_count: 5,
high_complexity_functions: 1,
avg_complexity: 2.0,
complexity_score: 4.5,
function_names: vec!["main".to_string(), "helper".to_string()],
};
let cloned = detail.clone();
assert_eq!(cloned.file_path, detail.file_path);
assert_eq!(cloned.function_count, detail.function_count);
assert_eq!(cloned.function_names.len(), 2);
}
#[test]
fn test_file_complexity_detail_debug() {
let detail = FileComplexityDetail {
file_path: std::path::PathBuf::from("debug.rs"),
function_count: 3,
high_complexity_functions: 0,
avg_complexity: 1.0,
complexity_score: 1.5,
function_names: vec![],
};
let debug_str = format!("{:?}", detail);
assert!(debug_str.contains("debug.rs"));
}
#[test]
fn test_simple_deep_context_default() {
let analyzer = SimpleDeepContext;
let _ = analyzer; }
#[tokio::test]
async fn test_analyze_empty_project() {
let temp_dir = TempDir::new().unwrap();
let analyzer = SimpleDeepContext::new();
let config = SimpleAnalysisConfig {
project_path: temp_dir.path().to_path_buf(),
include_features: vec![],
include_patterns: vec![],
exclude_patterns: vec![],
enable_verbose: false,
};
let report = analyzer.analyze(config).await.unwrap();
assert_eq!(report.file_count, 0);
assert_eq!(report.complexity_metrics.total_functions, 0);
assert_eq!(report.complexity_metrics.avg_complexity, 0.0);
}
#[tokio::test]
async fn test_analyze_with_include_patterns() {
let temp_dir = TempDir::new().unwrap();
let src_dir = temp_dir.path().join("src");
fs::create_dir_all(&src_dir).unwrap();
fs::write(src_dir.join("main.rs"), "fn main() { }").unwrap();
fs::write(src_dir.join("script.py"), "def main(): pass").unwrap();
let analyzer = SimpleDeepContext::new();
let config = SimpleAnalysisConfig {
project_path: temp_dir.path().to_path_buf(),
include_features: vec![],
include_patterns: vec!["**/*.rs".to_string()],
exclude_patterns: vec![],
enable_verbose: false,
};
let report = analyzer.analyze(config).await.unwrap();
assert_eq!(report.file_count, 1);
}
#[test]
fn test_estimate_complexity_go() {
let analyzer = SimpleDeepContext;
let go_code = r#"
func main() {
if x > 0 {
for i := 0; i < 10; i++ {
if i%2 == 0 {
fmt.Println(i)
}
}
}
}
"#;
let complexity = analyzer.estimate_complexity(go_code, "go");
assert!(complexity >= 4); }
#[test]
fn test_estimate_complexity_java() {
let analyzer = SimpleDeepContext;
let java_code = r#"
public void process() {
if (x > 0) {
for (int i = 0; i < 10; i++) {
switch (state) {
case 1: break;
case 2: break;
}
}
}
}
"#;
let complexity = analyzer.estimate_complexity(java_code, "java");
assert!(complexity >= 4);
}
#[test]
fn test_estimate_complexity_ruby() {
let analyzer = SimpleDeepContext;
let ruby_code = r#"
def process
if x > 0
(0..10).each do |i|
if i % 2 == 0
puts i
end
end
end
end
"#;
let complexity = analyzer.estimate_complexity(ruby_code, "rb");
assert!(complexity >= 1); }
#[test]
fn test_estimate_complexity_kotlin() {
let analyzer = SimpleDeepContext;
let kt_code = r#"
fun process() {
if (x > 0) {
for (i in 0..10) {
when (i) {
1 -> println("one")
else -> println("other")
}
}
}
}
"#;
let complexity = analyzer.estimate_complexity(kt_code, "kt");
assert!(complexity >= 1); }
#[test]
fn test_estimate_complexity_swift() {
let analyzer = SimpleDeepContext;
let swift_code = r#"
func process() {
if x > 0 {
for i in 0..<10 {
guard i % 2 == 0 else { continue }
print(i)
}
}
}
"#;
let complexity = analyzer.estimate_complexity(swift_code, "swift");
assert!(complexity >= 1); }
#[test]
fn test_estimate_complexity_bash() {
let analyzer = SimpleDeepContext;
let bash_code = r#"
process() {
if [[ $x -gt 0 ]]; then
for i in {1..10}; do
if [[ $((i % 2)) -eq 0 ]]; then
echo $i
fi
done
fi
}
"#;
let complexity = analyzer.estimate_complexity(bash_code, "sh");
assert!(complexity >= 1); }
#[test]
fn test_estimate_complexity_cpp() {
let analyzer = SimpleDeepContext;
let cpp_code = r#"
void process() {
if (x > 0) {
for (int i = 0; i < 10; i++) {
try {
doSomething();
} catch (...) {
handleError();
}
}
}
}
"#;
let complexity = analyzer.estimate_complexity(cpp_code, "cpp");
assert!(complexity >= 4);
}
#[tokio::test]
async fn test_analyze_file_complexity_heuristic_c() {
let analyzer = SimpleDeepContext;
let temp_dir = TempDir::new().unwrap();
let c_file = temp_dir.path().join("test.c");
fs::write(
&c_file,
r#"
int main() {
if (x > 0) {
for (int i = 0; i < 10; i++) {
printf("%d\n", i);
}
}
return 0;
}
void helper() {
while (running) {
process();
}
}
"#,
)
.unwrap();
let (count, _high, avg) = analyzer
.analyze_file_complexity_heuristic(&c_file, "c")
.await
.unwrap();
assert!(count >= 2);
assert!(avg >= 1.0);
}
#[tokio::test]
async fn test_analyze_file_complexity_heuristic_go() {
let analyzer = SimpleDeepContext;
let temp_dir = TempDir::new().unwrap();
let go_file = temp_dir.path().join("test.go");
fs::write(
&go_file,
r#"
func main() {
fmt.Println("hello")
}
func helper(x int) int {
if x > 0 {
return x * 2
}
return 0
}
"#,
)
.unwrap();
let (count, _high, avg) = analyzer
.analyze_file_complexity_heuristic(&go_file, "go")
.await
.unwrap();
assert!(count >= 2);
assert!(avg >= 1.0);
}
#[test]
fn test_find_function_end_python() {
let analyzer = SimpleDeepContext;
let code = r#"def my_func():
print("hello")
if True:
print("nested")
return 42
def next_func():
pass"#;
let result = analyzer.find_function_end(code, "py");
assert!(result.is_some());
let end_pos = result.unwrap();
assert!(end_pos < code.len());
}
#[test]
fn test_find_function_end_python_empty() {
let analyzer = SimpleDeepContext;
let code = "";
let result = analyzer.find_function_end(code, "py");
assert!(result.is_none());
}
#[test]
fn test_find_function_end_c_style() {
let analyzer = SimpleDeepContext;
let code = r#"void my_func() {
if (true) {
printf("hello");
}
}
void next_func() {"#;
let result = analyzer.find_function_end(code, "c");
assert!(result.is_some());
let end_pos = result.unwrap();
assert!(end_pos < code.len());
}
#[test]
fn test_find_function_end_c_with_strings() {
let analyzer = SimpleDeepContext;
let code = r#"void my_func() {
printf("{ this brace is in a string }");
}
void next();"#;
let result = analyzer.find_function_end(code, "cpp");
assert!(result.is_some());
}
#[test]
fn test_find_function_end_unmatched_braces() {
let analyzer = SimpleDeepContext;
let code = r#"void my_func() {
if (true) {
printf("hello");
// missing closing brace"#;
let result = analyzer.find_function_end(code, "c");
assert!(result.is_none());
}
#[test]
fn test_find_function_end_js() {
let analyzer = SimpleDeepContext;
let code = r#"function myFunc() {
const x = { nested: "object" };
return x;
}
const other = () => {"#;
let result = analyzer.find_function_end(code, "js");
assert!(result.is_some());
}
#[test]
fn test_generate_recommendations_high_complexity() {
let analyzer = SimpleDeepContext;
let metrics = ComplexityMetrics {
total_functions: 10,
high_complexity_count: 3,
avg_complexity: 4.0,
};
let recommendations = analyzer.generate_recommendations(&metrics);
assert!(!recommendations.is_empty());
assert!(recommendations
.iter()
.any(|r| r.contains("refactoring") && r.contains("3")));
}
#[test]
fn test_generate_recommendations_high_avg_complexity() {
let analyzer = SimpleDeepContext;
let metrics = ComplexityMetrics {
total_functions: 10,
high_complexity_count: 0,
avg_complexity: 7.5,
};
let recommendations = analyzer.generate_recommendations(&metrics);
assert!(!recommendations.is_empty());
assert!(recommendations
.iter()
.any(|r| r.contains("Average") && r.contains("7.5")));
}
#[test]
fn test_generate_recommendations_no_functions() {
let analyzer = SimpleDeepContext;
let metrics = ComplexityMetrics {
total_functions: 0,
high_complexity_count: 0,
avg_complexity: 0.0,
};
let recommendations = analyzer.generate_recommendations(&metrics);
assert!(!recommendations.is_empty());
assert!(recommendations
.iter()
.any(|r| r.contains("No functions detected")));
}
#[test]
fn test_generate_recommendations_good_code() {
let analyzer = SimpleDeepContext;
let metrics = ComplexityMetrics {
total_functions: 20,
high_complexity_count: 0,
avg_complexity: 2.5,
};
let recommendations = analyzer.generate_recommendations(&metrics);
assert!(!recommendations.is_empty());
assert!(recommendations.iter().any(|r| r.contains("looks good")));
}
#[test]
fn test_format_as_json_empty_report() {
let analyzer = SimpleDeepContext;
let report = SimpleAnalysisReport {
file_count: 0,
analysis_duration: std::time::Duration::from_millis(100),
complexity_metrics: ComplexityMetrics {
total_functions: 0,
high_complexity_count: 0,
avg_complexity: 0.0,
},
recommendations: vec!["No files found".to_string()],
file_complexity_details: vec![],
};
let json = analyzer.format_as_json(&report).unwrap();
assert!(json.contains("\"file_count\": 0"));
assert!(json.contains("\"total_functions\": 0"));
assert!(json.contains("\"recommendations\""));
}
#[test]
fn test_format_as_json_with_files() {
let analyzer = SimpleDeepContext;
let report = SimpleAnalysisReport {
file_count: 2,
analysis_duration: std::time::Duration::from_millis(500),
complexity_metrics: ComplexityMetrics {
total_functions: 15,
high_complexity_count: 3,
avg_complexity: 5.5,
},
recommendations: vec!["Consider refactoring".to_string()],
file_complexity_details: vec![
FileComplexityDetail {
file_path: std::path::PathBuf::from("src/main.rs"),
function_count: 10,
high_complexity_functions: 2,
avg_complexity: 6.0,
complexity_score: 8.0,
function_names: vec!["main".to_string(), "helper".to_string()],
},
FileComplexityDetail {
file_path: std::path::PathBuf::from("src/lib.rs"),
function_count: 5,
high_complexity_functions: 1,
avg_complexity: 4.5,
complexity_score: 5.0,
function_names: vec!["process".to_string()],
},
],
};
let json = analyzer.format_as_json(&report).unwrap();
assert!(json.contains("\"file_count\": 2"));
assert!(json.contains("\"total_functions\": 15"));
assert!(json.contains("main.rs"));
assert!(json.contains("lib.rs"));
let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
assert_eq!(parsed["summary"]["file_count"], 2);
}
#[test]
fn test_format_as_markdown_empty_report() {
let analyzer = SimpleDeepContext;
let report = SimpleAnalysisReport {
file_count: 0,
analysis_duration: std::time::Duration::from_millis(50),
complexity_metrics: ComplexityMetrics {
total_functions: 0,
high_complexity_count: 0,
avg_complexity: 0.0,
},
recommendations: vec!["No files found".to_string()],
file_complexity_details: vec![],
};
let markdown = analyzer.format_as_markdown(&report, 10);
assert!(markdown.contains("# Deep Context Analysis Report"));
assert!(markdown.contains("**Files Analyzed**: 0"));
assert!(markdown.contains("## Recommendations"));
}
#[test]
fn test_format_as_markdown_with_files() {
let analyzer = SimpleDeepContext;
let report = SimpleAnalysisReport {
file_count: 3,
analysis_duration: std::time::Duration::from_secs(1),
complexity_metrics: ComplexityMetrics {
total_functions: 30,
high_complexity_count: 5,
avg_complexity: 6.2,
},
recommendations: vec!["Consider refactoring".to_string(), "Add tests".to_string()],
file_complexity_details: vec![
FileComplexityDetail {
file_path: std::path::PathBuf::from("complex.rs"),
function_count: 15,
high_complexity_functions: 3,
avg_complexity: 8.0,
complexity_score: 12.0,
function_names: vec![],
},
FileComplexityDetail {
file_path: std::path::PathBuf::from("medium.rs"),
function_count: 10,
high_complexity_functions: 2,
avg_complexity: 5.0,
complexity_score: 7.0,
function_names: vec![],
},
],
};
let markdown = analyzer.format_as_markdown(&report, 10);
assert!(markdown.contains("**Files Analyzed**: 3"));
assert!(markdown.contains("**Total Functions**: 30"));
assert!(markdown.contains("**High Complexity Functions**: 5"));
assert!(markdown.contains("## Top Files by Complexity"));
let complex_pos = markdown.find("complex.rs").unwrap_or(usize::MAX);
let medium_pos = markdown.find("medium.rs").unwrap_or(usize::MAX);
assert!(
complex_pos < medium_pos,
"Higher complexity file should appear first"
);
}
#[test]
fn test_format_as_markdown_zero_top_files() {
let analyzer = SimpleDeepContext;
let report = SimpleAnalysisReport {
file_count: 1,
analysis_duration: std::time::Duration::from_millis(100),
complexity_metrics: ComplexityMetrics {
total_functions: 5,
high_complexity_count: 1,
avg_complexity: 3.0,
},
recommendations: vec![],
file_complexity_details: vec![FileComplexityDetail {
file_path: std::path::PathBuf::from("test.rs"),
function_count: 5,
high_complexity_functions: 1,
avg_complexity: 3.0,
complexity_score: 5.0,
function_names: vec![],
}],
};
let markdown = analyzer.format_as_markdown(&report, 0);
assert!(markdown.contains("test.rs"));
}
#[tokio::test]
#[ignore] async fn test_extract_function_names_rust() {
let analyzer = SimpleDeepContext;
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("test.rs");
fs::write(
&test_file,
r#"
fn main() {
helper();
}
/// Helper.
pub fn helper() -> i32 {
42
}
async fn async_func() {
tokio::time::sleep(Duration::from_millis(1)).await;
}
pub(crate) fn crate_visible() {}
"#,
)
.unwrap();
let names = analyzer
.extract_function_names_heuristic(&test_file, "rs")
.await
.unwrap();
assert!(names.contains(&"main".to_string()));
assert!(names.contains(&"helper".to_string()));
assert!(names.contains(&"async_func".to_string()));
assert!(names.contains(&"crate_visible".to_string()));
}
#[tokio::test]
async fn test_extract_function_names_python() {
let analyzer = SimpleDeepContext;
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("test.py");
fs::write(
&test_file,
r#"
def main():
pass
async def async_handler():
await something()
def helper_func(x, y):
return x + y
"#,
)
.unwrap();
let names = analyzer
.extract_function_names_heuristic(&test_file, "py")
.await
.unwrap();
assert!(names.contains(&"main".to_string()));
assert!(names.contains(&"async_handler".to_string()));
assert!(names.contains(&"helper_func".to_string()));
}
#[tokio::test]
async fn test_extract_function_names_kotlin() {
let analyzer = SimpleDeepContext;
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("test.kt");
fs::write(
&test_file,
r#"
fun main() {
println("Hello")
}
suspend fun asyncOperation() {
delay(100)
}
fun processData(data: String): Int {
return data.length
}
"#,
)
.unwrap();
let names = analyzer
.extract_function_names_heuristic(&test_file, "kt")
.await
.unwrap();
assert!(names.contains(&"main".to_string()));
assert!(names.contains(&"asyncOperation".to_string()));
assert!(names.contains(&"processData".to_string()));
}
#[tokio::test]
async fn test_extract_function_names_go() {
let analyzer = SimpleDeepContext;
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("test.go");
fs::write(
&test_file,
r#"
func main() {
fmt.Println("hello")
}
func (s *Server) HandleRequest() {
// method
}
func processData(data string) int {
return len(data)
}
"#,
)
.unwrap();
let names = analyzer
.extract_function_names_heuristic(&test_file, "go")
.await
.unwrap();
assert!(names.contains(&"main".to_string()));
assert!(names.contains(&"HandleRequest".to_string()));
assert!(names.contains(&"processData".to_string()));
}
#[tokio::test]
async fn test_extract_function_names_unknown_extension() {
let analyzer = SimpleDeepContext;
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("test.xyz");
fs::write(&test_file, "some content").unwrap();
let names = analyzer
.extract_function_names_heuristic(&test_file, "xyz")
.await
.unwrap();
assert!(names.is_empty());
}
#[tokio::test]
async fn test_analyze_complexity_multiple_files() {
let analyzer = SimpleDeepContext;
let temp_dir = TempDir::new().unwrap();
let file1 = temp_dir.path().join("simple.rs");
fs::write(&file1, "fn simple() { }").unwrap();
let file2 = temp_dir.path().join("complex.rs");
fs::write(
&file2,
r#"
fn complex(x: i32) -> i32 {
if x > 0 {
if x > 10 {
for i in 0..x {
if i % 2 == 0 {
return i;
}
}
}
}
0
}
"#,
)
.unwrap();
let files = vec![file1, file2];
let (metrics, details) = analyzer.analyze_complexity(&files).await.unwrap();
assert!(metrics.total_functions >= 2);
assert_eq!(details.len(), 2);
}
#[test]
fn test_simple_analysis_report_debug() {
let report = SimpleAnalysisReport {
file_count: 5,
analysis_duration: std::time::Duration::from_millis(250),
complexity_metrics: ComplexityMetrics {
total_functions: 25,
high_complexity_count: 3,
avg_complexity: 4.5,
},
recommendations: vec!["Test recommendation".to_string()],
file_complexity_details: vec![],
};
let debug = format!("{:?}", report);
assert!(debug.contains("file_count"));
assert!(debug.contains("5"));
assert!(debug.contains("complexity_metrics"));
}
#[test]
fn test_file_complexity_metrics_debug() {
use super::super::types::FileComplexityMetrics;
let metrics = FileComplexityMetrics {
function_count: 10,
high_complexity_functions: 2,
avg_complexity: 5.5,
function_names: vec!["func1".to_string(), "func2".to_string()],
};
let debug = format!("{:?}", metrics);
assert!(debug.contains("function_count"));
assert!(debug.contains("10"));
assert!(debug.contains("function_names"));
}
#[test]
fn test_estimate_complexity_unknown_language() {
let analyzer = SimpleDeepContext;
let code = "some random code with if and while";
let complexity = analyzer.estimate_complexity(code, "unknown_lang");
assert_eq!(complexity, 1);
}
#[test]
fn test_estimate_complexity_empty_code() {
let analyzer = SimpleDeepContext;
let code = "";
let complexity = analyzer.estimate_complexity(code, "py");
assert_eq!(complexity, 1);
}
#[tokio::test]
async fn test_analyze_file_complexity_rust_simple_funcs() {
let analyzer = SimpleDeepContext;
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("simple.rs");
fs::write(&test_file, "fn hello() {\n println!(\"hello\");\n}\n\nfn goodbye() {\n println!(\"goodbye\");\n}\n").unwrap();
let metrics = analyzer.analyze_file_complexity(&test_file).await.unwrap();
assert_eq!(metrics.function_count, 2);
assert_eq!(metrics.high_complexity_functions, 0);
assert!(metrics.avg_complexity >= 1.0);
assert!(!metrics.function_names.is_empty());
}
#[tokio::test]
async fn test_analyze_file_complexity_rust_high_complexity() {
let analyzer = SimpleDeepContext;
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("complex.rs");
fs::write(
&test_file,
concat!(
"fn very_complex(x: i32, y: i32) -> i32 {\n",
" if x > 0 {\n",
" if x > 10 {\n",
" if y > 0 {\n",
" match x {\n",
" 1 => { if y > 5 { return 1; } else { return 2; } }\n",
" 2 => { if y > 3 { return 3; } else { return 4; } }\n",
" 3 => return 5,\n",
" 4 => return 6,\n",
" 5 => return 7,\n",
" _ => return 8,\n",
" }\n",
" } else { return -1; }\n",
" }\n",
" }\n",
" 0\n",
"}\n\n",
"fn simple() -> i32 { 42 }\n",
),
)
.unwrap();
let metrics = analyzer.analyze_file_complexity(&test_file).await.unwrap();
assert_eq!(metrics.function_count, 2);
assert!(metrics.avg_complexity > 1.0);
assert!(metrics.function_names.contains(&"very_complex".to_string()));
assert!(metrics.function_names.contains(&"simple".to_string()));
}
#[tokio::test]
async fn test_analyze_file_complexity_rust_no_functions() {
let analyzer = SimpleDeepContext;
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("no_funcs.rs");
fs::write(
&test_file,
"const FOO: i32 = 42;\nconst BAR: &str = \"hello\";\n",
)
.unwrap();
let metrics = analyzer.analyze_file_complexity(&test_file).await.unwrap();
assert_eq!(metrics.function_count, 0);
assert_eq!(metrics.high_complexity_functions, 0);
assert_eq!(metrics.avg_complexity, 0.0);
}
#[tokio::test]
async fn test_analyze_file_complexity_rust_parse_error() {
let analyzer = SimpleDeepContext;
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("bad.rs");
fs::write(
&test_file,
"this is not valid rust code {{{}}}}}\nfn broken(\n",
)
.unwrap();
let metrics = analyzer.analyze_file_complexity(&test_file).await.unwrap();
assert!(metrics.function_count == 0 || metrics.avg_complexity >= 0.0);
}
#[tokio::test]
async fn test_analyze_file_complexity_python_file() {
let analyzer = SimpleDeepContext;
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("test.py");
fs::write(&test_file, concat!(
"def hello():\n print('hello')\n\n",
"def complex_func(x):\n if x > 0:\n for i in range(x):\n if i % 2 == 0:\n print(i)\n return x\n\n",
"class MyClass:\n def method(self):\n pass\n"
)).unwrap();
let metrics = analyzer.analyze_file_complexity(&test_file).await.unwrap();
assert!(metrics.function_count >= 2);
assert!(metrics.avg_complexity >= 1.0);
}
#[tokio::test]
async fn test_analyze_file_complexity_js_file() {
let analyzer = SimpleDeepContext;
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("app.js");
fs::write(&test_file, concat!(
"function greet(name) {\n if (name) {\n console.log('Hello ' + name);\n }\n}\n\n",
"const process = (data) => {\n for (let i = 0; i < data.length; i++) {\n console.log(data[i]);\n }\n};\n\n",
"function helper() { return 42; }\n"
)).unwrap();
let metrics = analyzer.analyze_file_complexity(&test_file).await.unwrap();
assert!(metrics.function_count >= 2);
assert!(metrics.avg_complexity >= 1.0);
}
#[tokio::test]
async fn test_analyze_file_complexity_ts_file() {
let analyzer = SimpleDeepContext;
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("app.ts");
fs::write(&test_file, "function greet(name: string): void {\n console.log('Hello ' + name);\n}\n\nconst add = (a: number, b: number): number => {\n return a + b;\n};\n").unwrap();
let metrics = analyzer.analyze_file_complexity(&test_file).await.unwrap();
assert!(metrics.function_count >= 1);
}
#[tokio::test]
async fn test_analyze_file_complexity_ruby_file() {
let analyzer = SimpleDeepContext;
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("app.rb");
fs::write(&test_file, "def hello\n puts 'hello'\nend\n\ndef process(data)\n data.each do |item|\n puts item\n end\nend\n").unwrap();
let metrics = analyzer.analyze_file_complexity(&test_file).await.unwrap();
assert!(metrics.function_count >= 2);
assert!(metrics.avg_complexity >= 1.0);
}
#[tokio::test]
async fn test_analyze_file_complexity_go_file() {
let analyzer = SimpleDeepContext;
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("main.go");
fs::write(&test_file, "package main\n\nfunc main() {\n fmt.Println(\"hello\")\n}\n\nfunc helper(x int) int {\n if x > 0 {\n return x * 2\n }\n return 0\n}\n").unwrap();
let metrics = analyzer.analyze_file_complexity(&test_file).await.unwrap();
assert!(metrics.function_count >= 2);
assert!(metrics.avg_complexity >= 1.0);
}
#[tokio::test]
async fn test_analyze_file_complexity_cs_file() {
let analyzer = SimpleDeepContext;
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("Program.cs");
fs::write(&test_file, "public class Program {\n public static void Main(string[] args) {\n Console.WriteLine(\"Hello\");\n }\n\n public static int Process(int x) {\n if (x > 0) { return x * 2; }\n return 0;\n }\n}\n").unwrap();
let metrics = analyzer.analyze_file_complexity(&test_file).await.unwrap();
assert!(metrics.function_count >= 2);
}
#[tokio::test]
async fn test_analyze_file_complexity_kt_file() {
let analyzer = SimpleDeepContext;
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("Main.kt");
fs::write(&test_file, "fun main() {\n println(\"Hello\")\n}\n\nfun process(x: Int): Int {\n return if (x > 0) x * 2 else 0\n}\n").unwrap();
let metrics = analyzer.analyze_file_complexity(&test_file).await.unwrap();
assert!(metrics.function_count >= 2);
}
#[tokio::test]
async fn test_analyze_file_complexity_nonexistent_file() {
let analyzer = SimpleDeepContext;
let path = std::path::Path::new("/tmp/definitely_does_not_exist_abc123.rs");
let metrics = analyzer.analyze_file_complexity(path).await.unwrap();
assert_eq!(metrics.function_count, 0);
assert_eq!(metrics.avg_complexity, 0.0);
}
#[tokio::test]
async fn test_analyze_file_complexity_no_extension() {
let analyzer = SimpleDeepContext;
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("Makefile");
fs::write(&test_file, "all:\n\techo hello\n").unwrap();
let metrics = analyzer.analyze_file_complexity(&test_file).await.unwrap();
assert!(metrics.avg_complexity >= 0.0);
}
#[tokio::test]
async fn test_analyze_file_complexity_java_file() {
let analyzer = SimpleDeepContext;
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("Main.java");
fs::write(&test_file, "public class Main {\n public static void main(String[] args) {\n System.out.println(\"Hello\");\n }\n\n public int process(int x) {\n if (x > 0) { return x * 2; }\n return 0;\n }\n}\n").unwrap();
let metrics = analyzer.analyze_file_complexity(&test_file).await.unwrap();
assert!(metrics.function_count >= 2);
}
#[tokio::test]
async fn test_analyze_file_complexity_c_file() {
let analyzer = SimpleDeepContext;
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("main.c");
fs::write(&test_file, "int main() {\n printf(\"hello\\n\");\n return 0;\n}\n\nint helper(int x) {\n if (x > 0) { return x * 2; }\n return 0;\n}\n").unwrap();
let metrics = analyzer.analyze_file_complexity(&test_file).await.unwrap();
assert!(metrics.function_count >= 2);
assert!(metrics.avg_complexity >= 1.0);
}
#[tokio::test]
async fn test_analyze_file_complexity_cpp_file() {
let analyzer = SimpleDeepContext;
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("main.cpp");
fs::write(&test_file, "#include <iostream>\n\nint main() {\n std::cout << \"Hello\" << std::endl;\n return 0;\n}\n\nvoid process(int x) {\n if (x > 0) {\n for (int i = 0; i < x; i++) { std::cout << i; }\n }\n}\n").unwrap();
let metrics = analyzer.analyze_file_complexity(&test_file).await.unwrap();
assert!(metrics.function_count >= 2);
}
#[tokio::test]
async fn test_analyze_file_complexity_wat_file() {
let analyzer = SimpleDeepContext;
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("module.wat");
fs::write(&test_file, "(module\n (func $add (param $a i32) (param $b i32) (result i32)\n local.get $a\n local.get $b\n i32.add))\n").unwrap();
let metrics = analyzer.analyze_file_complexity(&test_file).await.unwrap();
assert!(metrics.avg_complexity >= 0.0);
}
#[tokio::test]
async fn test_analyze_file_complexity_wasm_binary() {
let analyzer = SimpleDeepContext;
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("module.wasm");
fs::write(&test_file, [0x00, 0x61, 0x73, 0x6D]).unwrap();
let metrics = analyzer.analyze_file_complexity(&test_file).await.unwrap();
assert!(metrics.avg_complexity >= 0.0);
}
#[tokio::test]
async fn test_analyze_file_complexity_empty_rust_file() {
let analyzer = SimpleDeepContext;
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("empty.rs");
fs::write(&test_file, "").unwrap();
let metrics = analyzer.analyze_file_complexity(&test_file).await.unwrap();
assert_eq!(metrics.function_count, 0);
assert_eq!(metrics.high_complexity_functions, 0);
assert_eq!(metrics.avg_complexity, 0.0);
}
#[tokio::test]
async fn test_analyze_file_complexity_ruchy_file() {
let analyzer = SimpleDeepContext;
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("app.ruchy");
fs::write(&test_file, "def hello\n puts 'hello'\nend\n\ndef process(data)\n data.each do |item|\n puts item\n end\nend\n").unwrap();
let metrics = analyzer.analyze_file_complexity(&test_file).await.unwrap();
assert!(metrics.function_count >= 2);
}
#[tokio::test]
async fn test_analyze_multi_language_project() {
let temp_dir = TempDir::new().unwrap();
let src_dir = temp_dir.path().join("src");
fs::create_dir_all(&src_dir).unwrap();
fs::write(
src_dir.join("lib.rs"),
"fn compute(x: i32) -> i32 {\n if x > 0 { x * 2 } else { -x }\n}\n",
)
.unwrap();
fs::write(
src_dir.join("utils.py"),
"def calculate(x):\n if x > 0:\n return x * 2\n return -x\n",
)
.unwrap();
let analyzer = SimpleDeepContext::new();
let config = SimpleAnalysisConfig {
project_path: temp_dir.path().to_path_buf(),
include_features: vec![],
include_patterns: vec![],
exclude_patterns: vec![],
enable_verbose: false,
};
let report = analyzer.analyze(config).await.unwrap();
assert_eq!(report.file_count, 2);
assert!(report.complexity_metrics.total_functions >= 2);
assert!(report.file_complexity_details.len() == 2);
}
}
mod property_tests {
use proptest::prelude::*;
use std::fs;
use tempfile::TempDir;
proptest! {
#[test]
fn prop_complexity_never_returns_fixed_one(
num_functions in 1..10usize,
has_conditions in any::<bool>(),
) {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let temp_dir = TempDir::new().unwrap();
let src_dir = temp_dir.path().join("src");
fs::create_dir_all(&src_dir).unwrap();
let mut code = String::new();
for i in 0..num_functions {
if has_conditions && i % 2 == 0 {
code.push_str(&format!(r#"
fn func_{i}() {{
if true {{
println!("complex");
}}
}}
"#));
} else {
code.push_str(&format!(r#"
fn func_{i}() {{
println!("simple");
}}
"#));
}
}
let test_file = src_dir.join("test.rs");
fs::write(&test_file, code).unwrap();
let analyzer = crate::services::simple_deep_context::SimpleDeepContext::new();
let config = crate::services::simple_deep_context::SimpleAnalysisConfig {
project_path: temp_dir.path().to_path_buf(),
include_features: vec![],
include_patterns: vec![],
exclude_patterns: vec![],
enable_verbose: false,
};
let report = analyzer.analyze(config).await.unwrap();
if has_conditions && num_functions > 1 {
prop_assert!(report.complexity_metrics.avg_complexity > 1.0);
}
prop_assert_eq!(report.complexity_metrics.total_functions, num_functions);
Ok(())
})?;
}
}
}