fn extract_contract_metadata_from_context(
full_content: &str,
start_line: usize,
) -> (Option<String>, Option<String>) {
let lines: Vec<&str> = full_content.lines().collect();
let scan_start = start_line.saturating_sub(5).max(1);
for line_num in scan_start..start_line {
if line_num == 0 || line_num > lines.len() {
continue;
}
let trimmed = lines[line_num - 1].trim(); if trimmed.contains("contract(") && trimmed.contains("equation") {
if let Some(eq_start) = trimmed.find("equation") {
let after_eq = &trimmed[eq_start..];
if let Some(q1) = after_eq.find('"') {
let after_q1 = &after_eq[q1 + 1..];
if let Some(q2) = after_q1.find('"') {
let equation = after_q1[..q2].to_string();
return (Some("L2".to_string()), Some(equation));
}
}
}
return (Some("L2".to_string()), None);
}
}
(None, None)
}
pub(super) fn is_ignored_dir(path: &Path) -> bool {
let name = path.file_name().and_then(|n| n.to_str()).unwrap_or("");
matches!(
name,
"target"
| "node_modules"
| ".git"
| ".pmat"
| "__pycache__"
| "venv"
| ".venv"
| "dist"
| "build"
| ".next"
| ".cache"
| "vendor"
| "third_party"
| "third-party"
| "external"
| "deps"
| "book"
| "theme"
| "fixtures"
| ".cargo"
)
}
pub(super) fn detect_language(path: &Path) -> Option<Language> {
let ext = path.extension()?.to_str()?;
match ext {
"rs" => Some(Language::Rust),
"ts" | "tsx" | "js" | "jsx" => Some(Language::TypeScript),
"py" => Some(Language::Python),
"c" => Some(Language::C),
"h" => Some(Language::C), "cpp" | "cc" | "cxx" | "hpp" | "cu" | "cuh" => Some(Language::Cpp),
"go" => Some(Language::Go),
"lua" => Some(Language::Lua),
"ptx" => Some(Language::Ptx),
_ => None,
}
}
pub(super) fn classify_header_language(content: &str) -> Language {
const CPP_INDICATORS: &[&str] = &[
"extern \"C\"",
"class ",
"namespace ",
"template<",
"template <",
"virtual ",
"constexpr ",
"nullptr",
"std::",
"public:",
"private:",
"protected:",
];
if CPP_INDICATORS.iter().any(|kw| content.contains(kw)) {
return Language::Cpp;
}
Language::C
}
#[allow(clippy::cast_possible_truncation)]
pub(super) fn extract_quality_metrics(chunk: &CodeChunk, _full_content: &str) -> QualityMetrics {
let loc = chunk.content.lines().count() as u32;
let mut complexity = count_complexity(&chunk.content);
let lang = chunk.language.as_str();
if lang == "cpp" || lang == "c" || lang == "cuda" {
complexity += cpp_complexity_penalty(&chunk.content);
}
let satd_count = count_satd_markers(&chunk.content);
let big_o = estimate_big_o(&chunk.content);
use crate::services::semantic::ChunkType;
let effective_loc = match chunk.chunk_type {
ChunkType::Enum | ChunkType::Struct | ChunkType::Trait | ChunkType::TypeAlias => 0,
_ => loc,
};
let tdg_score = calculate_simple_tdg(complexity, satd_count, effective_loc);
let tdg_grade = score_to_grade(tdg_score);
let (contract_level, contract_equation) = extract_contract_metadata_from_context(
_full_content, chunk.start_line
);
QualityMetrics {
tdg_score,
tdg_grade,
complexity,
cognitive_complexity: complexity, big_o,
satd_count,
loc,
commit_count: 0, churn_score: 0.0, contract_level,
contract_equation,
}
}
pub(super) fn count_complexity(source: &str) -> u32 {
let mut complexity = 1u32;
for line in source.lines() {
let trimmed = line.trim();
if trimmed.starts_with("if ")
|| trimmed.starts_with("if(")
|| trimmed.starts_with("else if ")
|| trimmed.starts_with("} else if ")
|| trimmed.contains(" if ")
|| trimmed.starts_with("match ")
|| trimmed.starts_with("switch ")
|| trimmed.starts_with("switch(")
|| trimmed.starts_with("while ")
|| trimmed.starts_with("while(")
|| trimmed.starts_with("for ")
|| trimmed.starts_with("for(")
|| trimmed.starts_with("loop ")
|| trimmed.starts_with("do {")
|| trimmed.starts_with("do{")
|| trimmed.starts_with("catch ")
|| trimmed.starts_with("catch(")
|| trimmed.contains("&&")
|| trimmed.contains("||")
|| trimmed.contains("? ")
{
complexity += 1;
}
if trimmed.starts_with("case ") && trimmed.contains(':') && !trimmed.starts_with("//") {
complexity += 1;
}
if trimmed.contains("=>") && !trimmed.starts_with("//") {
complexity += 1;
}
}
complexity
}
#[allow(clippy::cast_possible_truncation)]
pub(super) fn cpp_complexity_penalty(source: &str) -> u32 {
let mut penalty = 0u32;
let mut ifdef_depth = 0u32;
let mut macro_call_count = 0u32;
for line in source.lines() {
let trimmed = line.trim();
if trimmed.starts_with("#if") || trimmed.starts_with("#ifdef") || trimmed.starts_with("#ifndef") {
ifdef_depth += 1;
penalty += ifdef_depth; } else if trimmed.starts_with("#endif") {
ifdef_depth = ifdef_depth.saturating_sub(1);
}
if trimmed.contains("GGML_") || trimmed.contains("TORCH_") || trimmed.contains("AT_")
|| trimmed.contains("CUDA_") || trimmed.contains("CHECK_") {
macro_call_count += 1;
}
}
if macro_call_count > 5 {
penalty += 3;
}
if source.contains("enable_if") || source.contains("requires ") || source.contains("SFINAE") {
penalty += 3;
}
let template_depth = source.matches("template<").count() + source.matches("template <").count();
if template_depth > 1 {
penalty += (template_depth as u32 - 1) * 2;
}
if source.contains("const_cast<") || source.contains("reinterpret_cast<") {
penalty += 2;
}
if source.contains("__shared__") {
penalty += 2;
}
if source.contains("__syncthreads()") {
penalty += 3;
}
if source.contains("__shfl_") || source.contains("__ballot_") || source.contains("__any_sync")
|| source.contains("__all_sync") {
penalty += 2;
}
if source.contains("__global__") && (source.contains("if (") || source.contains("if(")) {
penalty += 2;
}
penalty
}
#[allow(clippy::cast_possible_truncation)]
pub(super) fn count_satd_markers(source: &str) -> u32 {
let mut count = 0u32;
let mut in_block_comment = false;
let mut in_raw_string = false;
for line in source.lines() {
let trimmed = line.trim();
if update_raw_string_state(trimmed, &mut in_raw_string) {
continue;
}
if in_block_comment {
count += count_markers_in_line(trimmed);
if trimmed.contains("*/") {
in_block_comment = false;
}
continue;
}
if trimmed.starts_with("/*") {
in_block_comment = true;
count += count_markers_in_line(trimmed);
if trimmed.contains("*/") {
in_block_comment = false;
}
continue;
}
if trimmed.starts_with("///") || trimmed.starts_with("//!") {
continue;
}
count += count_markers_in_comment(trimmed);
}
count
}
fn count_markers_in_line(line: &str) -> u32 {
let upper = line.to_uppercase();
let mut count = 0u32;
for marker in ["TODO", "FIXME", "HACK", "OPTIMIZE"] {
count += upper.matches(marker).count() as u32;
}
count
}
fn count_markers_in_comment(trimmed: &str) -> u32 {
let Some(comment_start) = trimmed.find("//") else {
return 0;
};
let before = &trimmed[..comment_start];
if before.chars().filter(|&c| c == '"').count() % 2 != 0 {
return 0;
}
count_markers_in_line(&trimmed[comment_start..])
}
fn update_raw_string_state(trimmed: &str, in_raw_string: &mut bool) -> bool {
if *in_raw_string {
if trimmed.contains("\"#") || trimmed.ends_with('"') {
*in_raw_string = false;
}
return true;
}
if let Some(pos) = trimmed.find("r#\"") {
let after_open = &trimmed[pos + 3..];
if !after_open.contains("\"#") {
*in_raw_string = true;
}
return true;
}
false
}
pub(super) fn estimate_big_o(source: &str) -> String {
let mut current_nesting = 0;
let mut max_nesting = 0;
for line in source.lines() {
let trimmed = line.trim();
if trimmed.starts_with("for ")
|| trimmed.starts_with("while ")
|| trimmed.starts_with("loop ")
{
current_nesting += 1;
max_nesting = max_nesting.max(current_nesting);
}
if trimmed == "}" && current_nesting > 0 {
current_nesting -= 1;
}
}
match max_nesting {
0 => "O(1)".to_string(),
1 => "O(n)".to_string(),
2 => "O(n^2)".to_string(),
3 => "O(n^3)".to_string(),
n => format!("O(n^{n})"),
}
}
#[allow(clippy::cast_possible_truncation)]
pub(super) fn calculate_simple_tdg(complexity: u32, satd_count: u32, loc: u32) -> f32 {
let mut score = 0.0f32;
score += (complexity as f32 / 25.0).min(4.0);
score += (satd_count.saturating_sub(2) as f32 * 0.5).min(2.0);
if loc > 200 {
score += ((loc - 200) as f32 / 200.0).min(2.0);
}
if complexity <= 1 {
score = score.min(1.99);
}
score.min(10.0)
}
pub(super) fn score_to_grade(score: f32) -> String {
match score {
s if s < 2.0 => "A".to_string(),
s if s < 4.0 => "B".to_string(),
s if s < 6.0 => "C".to_string(),
s if s < 8.0 => "D".to_string(),
_ => "F".to_string(),
}
}
enum DocLineKind<'a> {
DocComment(&'a str),
BlockCommentStart,
BlockCommentBody(&'a str),
SkipLine, Other,
}
fn classify_doc_line(line: &str) -> DocLineKind<'_> {
if line.starts_with("///") || line.starts_with("//!") {
DocLineKind::DocComment(
line.trim_start_matches("///")
.trim_start_matches("//!")
.trim(),
)
} else if line.starts_with("/**") || line.starts_with("/*") {
DocLineKind::BlockCommentStart
} else if line.starts_with('*') {
DocLineKind::BlockCommentBody(line.trim_start_matches('*').trim())
} else if line.is_empty() || line.starts_with("#[") || line.starts_with('@') {
DocLineKind::SkipLine
} else {
DocLineKind::Other
}
}
pub(super) fn extract_doc_comment(content: &str, start_line: usize) -> Option<String> {
if start_line <= 1 {
return None;
}
let bytes = content.as_bytes();
let mut line_num = 1usize;
let mut def_line_start = 0usize;
for (i, &b) in bytes.iter().enumerate() {
if line_num >= start_line {
def_line_start = i;
break;
}
if b == b'\n' {
line_num += 1;
if line_num >= start_line {
def_line_start = i + 1;
break;
}
}
}
if line_num < start_line {
return None;
}
let mut doc_lines = Vec::new();
let mut end = def_line_start; if end > 0 && bytes[end.saturating_sub(1)] == b'\n' {
end = end.saturating_sub(1);
}
let mut pos = end;
loop {
let line_start = if pos == 0 {
0
} else {
match content[..pos].rfind('\n') {
Some(nl) => nl + 1,
None => 0,
}
};
let line = content.get(line_start..pos).unwrap_or("").trim();
match classify_doc_line(line) {
DocLineKind::DocComment(text) => doc_lines.push(text),
DocLineKind::BlockCommentBody(text) => doc_lines.push(text),
DocLineKind::BlockCommentStart | DocLineKind::Other => break,
DocLineKind::SkipLine => {
if line_start == 0 {
break;
}
pos = line_start.saturating_sub(1);
continue;
}
}
if line_start == 0 {
break;
}
pos = line_start.saturating_sub(1);
}
if doc_lines.is_empty() {
return None;
}
doc_lines.reverse();
Some(doc_lines.join(" "))
}
#[cfg(test)]
mod quality_metrics_tests {
use super::*;
use std::path::PathBuf;
#[test]
fn test_is_ignored_dir_target() {
assert!(is_ignored_dir(&PathBuf::from("/foo/target")));
}
#[test]
fn test_is_ignored_dir_node_modules() {
assert!(is_ignored_dir(&PathBuf::from("node_modules")));
}
#[test]
fn test_is_ignored_dir_python_cache() {
assert!(is_ignored_dir(&PathBuf::from("__pycache__")));
assert!(is_ignored_dir(&PathBuf::from(".venv")));
assert!(is_ignored_dir(&PathBuf::from("venv")));
}
#[test]
fn test_is_ignored_dir_build_artifacts() {
assert!(is_ignored_dir(&PathBuf::from("dist")));
assert!(is_ignored_dir(&PathBuf::from("build")));
assert!(is_ignored_dir(&PathBuf::from(".next")));
assert!(is_ignored_dir(&PathBuf::from(".cache")));
}
#[test]
fn test_is_ignored_dir_vendor_third_party() {
assert!(is_ignored_dir(&PathBuf::from("vendor")));
assert!(is_ignored_dir(&PathBuf::from("third_party")));
assert!(is_ignored_dir(&PathBuf::from("third-party")));
assert!(is_ignored_dir(&PathBuf::from("external")));
}
#[test]
fn test_is_ignored_dir_normal_dirs() {
assert!(!is_ignored_dir(&PathBuf::from("src")));
assert!(!is_ignored_dir(&PathBuf::from("docs")));
assert!(!is_ignored_dir(&PathBuf::from("lib")));
}
#[test]
fn test_detect_language_rust() {
assert!(matches!(
detect_language(&PathBuf::from("foo.rs")),
Some(Language::Rust)
));
}
#[test]
fn test_detect_language_typescript_variants() {
for ext in &["ts", "tsx", "js", "jsx"] {
let p = PathBuf::from(format!("foo.{ext}"));
assert!(matches!(detect_language(&p), Some(Language::TypeScript)));
}
}
#[test]
fn test_detect_language_python() {
assert!(matches!(
detect_language(&PathBuf::from("foo.py")),
Some(Language::Python)
));
}
#[test]
fn test_detect_language_c_and_h_default_c() {
assert!(matches!(
detect_language(&PathBuf::from("foo.c")),
Some(Language::C)
));
assert!(matches!(
detect_language(&PathBuf::from("foo.h")),
Some(Language::C)
));
}
#[test]
fn test_detect_language_cpp_variants() {
for ext in &["cpp", "cc", "cxx", "hpp", "cu", "cuh"] {
let p = PathBuf::from(format!("foo.{ext}"));
assert!(matches!(detect_language(&p), Some(Language::Cpp)));
}
}
#[test]
fn test_detect_language_other() {
assert!(matches!(
detect_language(&PathBuf::from("foo.go")),
Some(Language::Go)
));
assert!(matches!(
detect_language(&PathBuf::from("foo.lua")),
Some(Language::Lua)
));
assert!(matches!(
detect_language(&PathBuf::from("foo.ptx")),
Some(Language::Ptx)
));
}
#[test]
fn test_detect_language_unknown_extension_returns_none() {
assert!(detect_language(&PathBuf::from("foo.txt")).is_none());
assert!(detect_language(&PathBuf::from("Cargo.toml")).is_none());
assert!(detect_language(&PathBuf::from("Makefile")).is_none());
}
#[test]
fn test_classify_header_language_no_indicators_is_c() {
assert!(matches!(classify_header_language(""), Language::C));
assert!(matches!(
classify_header_language("int foo(int x);"),
Language::C
));
}
#[test]
fn test_classify_header_language_extern_c_is_cpp() {
assert!(matches!(
classify_header_language("extern \"C\" {}"),
Language::Cpp
));
}
#[test]
fn test_classify_header_language_class_keyword_is_cpp() {
assert!(matches!(
classify_header_language("class Foo { };"),
Language::Cpp
));
}
#[test]
fn test_classify_header_language_namespace_is_cpp() {
assert!(matches!(
classify_header_language("namespace foo { }"),
Language::Cpp
));
}
#[test]
fn test_classify_header_language_template_is_cpp() {
assert!(matches!(
classify_header_language("template<typename T>"),
Language::Cpp
));
assert!(matches!(
classify_header_language("template <class T>"),
Language::Cpp
));
}
#[test]
fn test_classify_header_language_visibility_modifiers_are_cpp() {
assert!(matches!(
classify_header_language("public:"),
Language::Cpp
));
assert!(matches!(
classify_header_language("private:"),
Language::Cpp
));
assert!(matches!(
classify_header_language("protected:"),
Language::Cpp
));
}
#[test]
fn test_count_complexity_empty_source_base_one() {
assert_eq!(count_complexity(""), 1);
}
#[test]
fn test_count_complexity_single_if() {
let src = "if x { y }";
assert_eq!(count_complexity(src), 2);
}
#[test]
fn test_count_complexity_match_arms() {
let src = "match x {\n 1 => a,\n 2 => b,\n _ => c,\n}";
assert_eq!(count_complexity(src), 5);
}
#[test]
fn test_count_complexity_loops_count() {
let src = "for i in v { }\nwhile cond { }\nloop { break; }";
assert_eq!(count_complexity(src), 4);
}
#[test]
fn test_count_complexity_short_circuit_operators_per_line() {
let src = "if cond_a {\n a && b\n a || b\n}";
assert_eq!(count_complexity(src), 4);
}
#[test]
fn test_count_complexity_single_line_with_multiple_ops_only_counts_once() {
let src = "if a && b || c { }";
assert_eq!(count_complexity(src), 2);
}
#[test]
fn test_count_complexity_c_switch_case() {
let src = "switch (x) {\ncase 1: foo;\ncase 2: bar;\n}";
assert_eq!(count_complexity(src), 4);
}
#[test]
fn test_count_complexity_skips_comment_arrows_and_cases() {
let src = "// case 1:\n// =>\nlet x = 1;";
assert_eq!(count_complexity(src), 1);
}
#[test]
fn test_cpp_complexity_penalty_no_patterns_is_zero() {
assert_eq!(cpp_complexity_penalty(""), 0);
assert_eq!(cpp_complexity_penalty("int x = 1;"), 0);
}
#[test]
fn test_cpp_complexity_penalty_ifdef_nesting() {
let src = "#ifdef A\n#ifdef B\nfoo\n#endif\n#endif";
assert_eq!(cpp_complexity_penalty(src), 3);
}
#[test]
fn test_cpp_complexity_penalty_macro_heavy_above_5_adds_3() {
let src = "GGML_X();\nGGML_Y();\nGGML_Z();\nGGML_W();\nGGML_V();\nGGML_U();";
assert_eq!(cpp_complexity_penalty(src), 3);
}
#[test]
fn test_cpp_complexity_penalty_macros_on_single_line_only_counts_once() {
let src = "GGML_X(); GGML_Y(); GGML_Z(); GGML_W(); GGML_V(); GGML_U();";
assert_eq!(cpp_complexity_penalty(src), 0);
}
#[test]
fn test_cpp_complexity_penalty_sfinae_adds_3() {
assert_eq!(cpp_complexity_penalty("std::enable_if<>"), 3);
assert_eq!(cpp_complexity_penalty("requires (T x)"), 3);
assert_eq!(cpp_complexity_penalty("// SFINAE pattern"), 3);
}
#[test]
fn test_cpp_complexity_penalty_template_nesting() {
let src = "template<class A>\ntemplate<class B>\ntemplate<class C>";
assert_eq!(cpp_complexity_penalty(src), 4);
}
#[test]
fn test_cpp_complexity_penalty_unsafe_casts_add_2() {
assert_eq!(cpp_complexity_penalty("const_cast<int>(x);"), 2);
assert_eq!(cpp_complexity_penalty("reinterpret_cast<u8*>(p);"), 2);
}
#[test]
fn test_cpp_complexity_penalty_cuda_shared_memory_adds_2() {
assert_eq!(cpp_complexity_penalty("__shared__ int x[64];"), 2);
}
#[test]
fn test_cpp_complexity_penalty_cuda_syncthreads_adds_3() {
assert_eq!(cpp_complexity_penalty("__syncthreads();"), 3);
}
#[test]
fn test_cpp_complexity_penalty_cuda_warp_primitives_add_2() {
assert_eq!(cpp_complexity_penalty("__shfl_sync(...)"), 2);
assert_eq!(cpp_complexity_penalty("__ballot_sync(...)"), 2);
assert_eq!(cpp_complexity_penalty("__any_sync(...)"), 2);
assert_eq!(cpp_complexity_penalty("__all_sync(...)"), 2);
}
#[test]
fn test_cpp_complexity_penalty_global_kernel_with_branch_adds_2() {
let src = "__global__ void k() { if (x) { } }";
assert_eq!(cpp_complexity_penalty(src), 2);
}
#[test]
fn test_count_satd_markers_no_markers() {
assert_eq!(count_satd_markers(""), 0);
assert_eq!(count_satd_markers("// regular comment"), 0);
}
#[test]
fn test_count_satd_markers_inline_todo() {
assert_eq!(count_satd_markers("let x = 1; // TODO: rename"), 1);
}
#[test]
fn test_count_satd_markers_multiple_in_one_line() {
assert_eq!(count_satd_markers("// TODO: x and FIXME: y"), 2);
}
#[test]
fn test_count_satd_markers_skips_doc_comment() {
assert_eq!(count_satd_markers("/// TODO is in doc"), 0);
assert_eq!(count_satd_markers("//! FIXME doc"), 0);
}
#[test]
fn test_count_satd_markers_block_comment() {
let src = "/* TODO: something */";
assert_eq!(count_satd_markers(src), 1);
}
#[test]
fn test_count_satd_markers_in_string_literal_skipped() {
let src = "let s = \"hello // FIXME ignored\";";
assert_eq!(count_satd_markers(src), 0);
}
#[test]
fn test_count_satd_markers_all_four_marker_types() {
let src = "// TODO and FIXME and HACK and OPTIMIZE";
assert_eq!(count_satd_markers(src), 4);
}
#[test]
fn test_count_markers_in_line_case_insensitive_match() {
assert_eq!(count_markers_in_line("// todo lowercase"), 1);
assert_eq!(count_markers_in_line("// Fixme MixedCase"), 1);
assert_eq!(count_markers_in_line("// hack/optimize"), 2);
}
#[test]
fn test_count_markers_in_comment_no_double_slash_returns_zero() {
assert_eq!(count_markers_in_comment("just text TODO here"), 0);
}
#[test]
fn test_update_raw_string_state_open_and_close_same_line() {
let mut in_raw = false;
let skipped = update_raw_string_state("let s = r#\"foo\"#;", &mut in_raw);
assert!(skipped);
assert!(!in_raw);
}
#[test]
fn test_update_raw_string_state_open_only_sets_state() {
let mut in_raw = false;
let skipped = update_raw_string_state("let s = r#\"unclosed", &mut in_raw);
assert!(skipped);
assert!(in_raw);
}
#[test]
fn test_update_raw_string_state_already_in_raw_continues() {
let mut in_raw = true;
let skipped = update_raw_string_state("middle of string", &mut in_raw);
assert!(skipped);
assert!(in_raw); }
#[test]
fn test_update_raw_string_state_already_in_raw_with_close() {
let mut in_raw = true;
let skipped = update_raw_string_state("end\"#", &mut in_raw);
assert!(skipped);
assert!(!in_raw); }
#[test]
fn test_update_raw_string_state_no_raw_string_returns_false() {
let mut in_raw = false;
let skipped = update_raw_string_state("normal code line", &mut in_raw);
assert!(!skipped);
assert!(!in_raw);
}
#[test]
fn test_estimate_big_o_no_loops_is_o1() {
assert_eq!(estimate_big_o("let x = 1;"), "O(1)");
assert_eq!(estimate_big_o(""), "O(1)");
}
#[test]
fn test_estimate_big_o_single_loop_is_on() {
assert_eq!(estimate_big_o("for i in v { print(i); }\n}"), "O(n)");
}
#[test]
fn test_estimate_big_o_nested_loops_is_n2() {
let src = "for i in v {\nfor j in v {\nfoo();\n}\n}\n";
assert_eq!(estimate_big_o(src), "O(n^2)");
}
#[test]
fn test_estimate_big_o_triple_nested_is_n3() {
let src = "for a in v {\nfor b in v {\nfor c in v {\nfoo();\n}\n}\n}\n";
assert_eq!(estimate_big_o(src), "O(n^3)");
}
#[test]
fn test_estimate_big_o_while_and_loop_count_too() {
assert_eq!(estimate_big_o("while x {}\n}"), "O(n)");
assert_eq!(estimate_big_o("loop {}\n}"), "O(n)");
}
#[test]
fn test_calculate_simple_tdg_complexity_under_25_low_score() {
let s = calculate_simple_tdg(1, 0, 50);
assert!(s <= 1.99 + 1e-3);
}
#[test]
fn test_calculate_simple_tdg_high_complexity_increases_score() {
let s = calculate_simple_tdg(50, 0, 0);
assert!((s - 2.0).abs() < 1e-3);
}
#[test]
fn test_calculate_simple_tdg_complexity_capped_at_4() {
let s = calculate_simple_tdg(200, 0, 0);
assert!(s >= 4.0);
assert!(s <= 4.0 + 1e-3);
}
#[test]
fn test_calculate_simple_tdg_satd_penalty_starts_at_3() {
let s_no_satd = calculate_simple_tdg(2, 0, 0);
let s_with_satd = calculate_simple_tdg(2, 3, 0);
assert!(s_with_satd > s_no_satd);
}
#[test]
fn test_calculate_simple_tdg_loc_penalty_above_200() {
let s_low = calculate_simple_tdg(2, 0, 200);
let s_high = calculate_simple_tdg(2, 0, 400);
assert!(s_high > s_low);
}
#[test]
fn test_calculate_simple_tdg_cc_one_capped_at_b_threshold() {
let s = calculate_simple_tdg(1, 100, 1000);
assert!(s < 2.0);
}
#[test]
fn test_score_to_grade_a() {
assert_eq!(score_to_grade(0.0), "A");
assert_eq!(score_to_grade(1.99), "A");
}
#[test]
fn test_score_to_grade_b() {
assert_eq!(score_to_grade(2.0), "B");
assert_eq!(score_to_grade(3.99), "B");
}
#[test]
fn test_score_to_grade_c() {
assert_eq!(score_to_grade(4.0), "C");
assert_eq!(score_to_grade(5.99), "C");
}
#[test]
fn test_score_to_grade_d() {
assert_eq!(score_to_grade(6.0), "D");
assert_eq!(score_to_grade(7.99), "D");
}
#[test]
fn test_score_to_grade_f() {
assert_eq!(score_to_grade(8.0), "F");
assert_eq!(score_to_grade(10.0), "F");
}
#[test]
fn test_extract_contract_metadata_finds_l2_with_equation() {
let src = "use foo;\n#[provable_contracts_macros::contract(\"yaml\", equation = \"my_eq\")]\npub fn target() {}";
let (level, eq) = extract_contract_metadata_from_context(src, 3);
assert_eq!(level, Some("L2".to_string()));
assert_eq!(eq, Some("my_eq".to_string()));
}
#[test]
fn test_extract_contract_metadata_no_contract_returns_none() {
let src = "use foo;\nfn target() {}";
let (level, eq) = extract_contract_metadata_from_context(src, 2);
assert!(level.is_none());
assert!(eq.is_none());
}
#[test]
fn test_extract_contract_metadata_contract_without_equation() {
let src = "#[contract(\"y\")]\nfn t() {}";
let (level, eq) = extract_contract_metadata_from_context(src, 2);
assert!(level.is_none());
assert!(eq.is_none());
}
#[test]
fn test_extract_contract_metadata_only_scans_5_lines_back() {
let mut src = String::new();
for _ in 0..10 {
src.push_str("// fill\n");
}
src.push_str("#[contract(\"y\", equation = \"e\")]\n");
for _ in 0..6 {
src.push_str("// more fill\n");
}
src.push_str("fn t() {}\n");
let (level, _) = extract_contract_metadata_from_context(&src, 18);
assert!(level.is_none());
}
#[test]
fn test_classify_doc_line_triple_slash() {
match classify_doc_line("/// hello docs") {
DocLineKind::DocComment(text) => assert_eq!(text, "hello docs"),
_ => panic!("expected DocComment"),
}
}
#[test]
fn test_classify_doc_line_inner_doc() {
match classify_doc_line("//! module docs") {
DocLineKind::DocComment(text) => assert_eq!(text, "module docs"),
_ => panic!("expected DocComment"),
}
}
#[test]
fn test_classify_doc_line_block_comment_start() {
assert!(matches!(
classify_doc_line("/* block */"),
DocLineKind::BlockCommentStart
));
assert!(matches!(
classify_doc_line("/** doc block */"),
DocLineKind::BlockCommentStart
));
}
#[test]
fn test_classify_doc_line_block_body() {
match classify_doc_line("* body of block") {
DocLineKind::BlockCommentBody(text) => assert_eq!(text, "body of block"),
_ => panic!("expected BlockCommentBody"),
}
}
#[test]
fn test_classify_doc_line_skip_kinds() {
assert!(matches!(classify_doc_line(""), DocLineKind::SkipLine));
assert!(matches!(
classify_doc_line("#[derive(Debug)]"),
DocLineKind::SkipLine
));
assert!(matches!(
classify_doc_line("@override"),
DocLineKind::SkipLine
));
}
#[test]
fn test_classify_doc_line_other() {
assert!(matches!(classify_doc_line("fn foo()"), DocLineKind::Other));
assert!(matches!(
classify_doc_line("let x = 1;"),
DocLineKind::Other
));
}
#[test]
fn test_extract_doc_comment_returns_doc_above_function() {
let src = "/// This is the doc.\nfn target() {}\n";
let doc = extract_doc_comment(src, 2);
assert_eq!(doc, Some("This is the doc.".to_string()));
}
#[test]
fn test_extract_doc_comment_multiline_doc() {
let src = "/// Line one.\n/// Line two.\nfn t() {}\n";
let doc = extract_doc_comment(src, 3);
assert_eq!(doc, Some("Line one. Line two.".to_string()));
}
#[test]
fn test_extract_doc_comment_no_doc_returns_none() {
let src = "fn t() {}\n";
let doc = extract_doc_comment(src, 1);
assert!(doc.is_none());
}
#[test]
fn test_extract_doc_comment_attribute_skipped() {
let src = "/// Doc.\n#[derive(Debug)]\nfn t() {}\n";
let doc = extract_doc_comment(src, 3);
assert_eq!(doc, Some("Doc.".to_string()));
}
#[test]
fn test_extract_doc_comment_start_line_one_returns_none() {
let doc = extract_doc_comment("fn t() {}", 1);
assert!(doc.is_none());
}
}