use crate::tdg::analyzer_simple::TdgAnalyzer;
use std::path::{Path, PathBuf};
fn twelve_file_tree(dir: &Path) {
let files: [(&str, &str); 12] = [
("a.rs", "pub fn a() -> i32 { 1 }\n"),
("a.py", "def a():\n return 1\n"),
("a.go", "package main\n\nfunc a() int { return 1 }\n"),
("a.ts", "export const a = (): number => 1;\n"),
("a.js", "export const a = () => 1;\n"),
("a.c", "int a(void) { return 1; }\n"),
("a.sh", "#!/bin/sh\na() { echo 1; }\n"),
("a.php", "<?php function a() { return 1; }\n"),
("a.md", "# Title\n\nprose\n"),
("a.lua", "function a() return 1 end\n"),
("a.cs", "class A { int a() { return 1; } }\n"),
("a.zig", "pub fn a() i32 { return 1; }\n"),
];
for (name, body) in files {
std::fs::write(dir.join(name), body).expect("write fixture");
}
}
fn scan(dir: &Path) -> (crate::tdg::ProjectScore, Vec<(PathBuf, String)>) {
TdgAnalyzer::new()
.expect("analyzer")
.analyze_project_reporting_ungraded(dir)
.expect("scan")
}
fn names(ungraded: &[(PathBuf, String)]) -> Vec<String> {
ungraded
.iter()
.map(|(p, _)| {
p.file_name()
.and_then(|n| n.to_str())
.unwrap_or_default()
.to_string()
})
.collect()
}
#[test]
fn source_files_this_build_cannot_grade_are_named_not_dropped() {
let dir = tempfile::tempdir().expect("tempdir");
twelve_file_tree(dir.path());
let (_project, ungraded) = scan(dir.path());
let listed = names(&ungraded);
for expected in ["a.sh", "a.php", "a.cs", "a.zig"] {
assert!(
listed.iter().any(|n| n == expected),
"{expected} is source code this build cannot grade, and it was dropped \
from the score without appearing anywhere: {listed:?}"
);
}
}
#[test]
fn every_ungraded_file_states_why() {
let dir = tempfile::tempdir().expect("tempdir");
twelve_file_tree(dir.path());
let (_project, ungraded) = scan(dir.path());
assert!(!ungraded.is_empty(), "the fixture has ungradable source");
for (path, reason) in &ungraded {
assert!(
!reason.trim().is_empty(),
"{} was refused with no reason",
path.display()
);
}
}
#[test]
fn graded_plus_disclosed_accounts_for_every_source_file() {
let dir = tempfile::tempdir().expect("tempdir");
twelve_file_tree(dir.path());
let (project, ungraded) = scan(dir.path());
let accounted = project.total_files + ungraded.len();
assert_eq!(
accounted,
11,
"11 source files in, {} graded + {} disclosed = {accounted} accounted for",
project.total_files,
ungraded.len()
);
}
#[test]
fn a_language_with_an_analyzer_is_actually_graded() {
let dir = tempfile::tempdir().expect("tempdir");
twelve_file_tree(dir.path());
let (project, ungraded) = scan(dir.path());
let graded: Vec<String> = project
.files
.iter()
.filter_map(|s| s.file_path.as_ref())
.filter_map(|p| p.file_name().and_then(|n| n.to_str()))
.map(str::to_string)
.collect();
assert!(
graded.iter().any(|n| n == "a.lua"),
"TDG has a Lua analyzer; the file must be scored, not skipped. \
graded={graded:?} ungraded={:?}",
names(&ungraded)
);
}
#[test]
fn the_project_score_itself_carries_the_skip_list() {
let dir = tempfile::tempdir().expect("tempdir");
twelve_file_tree(dir.path());
let (project, ungraded) = scan(dir.path());
assert!(
!project.ungraded_files.is_empty(),
"the fixture has ungradable source, so the score must carry it"
);
assert_eq!(
project.ungraded_files.len(),
ungraded.len(),
"the score and the side channel must agree"
);
assert!(project
.ungraded_files
.iter()
.all(|u| !u.reason.trim().is_empty()));
}
#[test]
fn a_fully_graded_tree_reports_nothing_ungraded() {
let dir = tempfile::tempdir().expect("tempdir");
std::fs::write(dir.path().join("a.rs"), "pub fn a() -> i32 { 1 }\n").expect("write");
std::fs::write(dir.path().join("b.rs"), "pub fn b() -> i32 { 2 }\n").expect("write");
let (project, ungraded) = scan(dir.path());
assert_eq!(project.total_files, 2);
assert!(ungraded.is_empty(), "{:?}", names(&ungraded));
assert!(project.ungraded_files.is_empty());
}