-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcoverage_report.rs
More file actions
127 lines (116 loc) · 3.98 KB
/
Copy pathcoverage_report.rs
File metadata and controls
127 lines (116 loc) · 3.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
//! Honest coverage report — runs the entity dispatcher against every
//! DWG file in a directory and prints per-file + aggregate
//! decoded/unhandled/errored counts. No rationalization, no rounding
//! up, no excuses.
//!
//! ```bash
//! cargo run --release --example coverage_report -- path/to/corpus/
//! ```
//!
//! This example intentionally does NOT dump per-entity field values.
//! Its audience is CI (the coverage-smoke job calls it) and humans
//! wanting a quick corpus-wide summary; printing every decoded
//! value would bury the summary in noise. For per-entity field
//! inspection, see the sibling example
//! [`dump_decoded_entities`](../examples/dump_decoded_entities.rs).
use dwg::{DwgFile, entities::DispatchSummary};
use std::collections::BTreeMap;
use std::env;
use std::path::PathBuf;
use std::process::ExitCode;
fn main() -> ExitCode {
let Some(dir_arg) = env::args().nth(1) else {
eprintln!("usage: coverage_report <directory-of-dwg-files>");
return ExitCode::FAILURE;
};
let dir = PathBuf::from(dir_arg);
let Ok(read) = std::fs::read_dir(&dir) else {
eprintln!("cannot read directory {}", dir.display());
return ExitCode::FAILURE;
};
let mut files: Vec<PathBuf> = read
.flatten()
.map(|e| e.path())
.filter(|p| p.extension().and_then(|s| s.to_str()) == Some("dwg"))
.collect();
files.sort();
if files.is_empty() {
eprintln!("no .dwg files under {}", dir.display());
return ExitCode::FAILURE;
}
let mut totals = DispatchSummary::default();
let mut type_histo: BTreeMap<u16, (usize, usize, usize)> = BTreeMap::new();
println!(
"{:<32} {:<12} {:>6} {:>6} {:>6} {:>7}",
"file", "version", "deco", "skip", "err", "ratio%"
);
println!("{}", "-".repeat(80));
for path in &files {
let filename = path
.file_name()
.and_then(|s| s.to_str())
.unwrap_or("(unnamed)");
let file = match DwgFile::open(path) {
Ok(f) => f,
Err(e) => {
println!("{:<32} open-failed: {e}", filename);
continue;
}
};
let version = file.version();
let (_entities, summary) = match file.decoded_entities() {
Some(Ok((e, s))) => (e, s),
Some(Err(e)) => {
println!("{:<32} decoded_entities-failed: {e}", filename);
continue;
}
None => {
println!(
"{:<32} {:<12} n/a n/a n/a (no-handle-map)",
filename,
format!("{version}")
);
continue;
}
};
println!(
"{:<32} {:<12} {:>6} {:>6} {:>6} {:>7.1}",
filename,
format!("{version}"),
summary.decoded,
summary.unhandled,
summary.errored,
summary.decoded_ratio() * 100.0
);
// Accumulate per-type histogram via error dedup.
for (tc, _msg) in &summary.errors {
type_histo.entry(*tc).or_default().2 += 1;
}
totals.decoded += summary.decoded;
totals.unhandled += summary.unhandled;
totals.errored += summary.errored;
}
println!("{}", "-".repeat(80));
println!(
"{:<32} {:<12} {:>6} {:>6} {:>6} {:>7.1}",
"TOTAL",
"",
totals.decoded,
totals.unhandled,
totals.errored,
totals.decoded_ratio() * 100.0
);
println!();
if !type_histo.is_empty() {
println!("Error histogram by type code (top 10):");
let mut err_counts: Vec<(u16, usize)> = type_histo
.iter()
.map(|(tc, (_, _, err))| (*tc, *err))
.collect();
err_counts.sort_by_key(|(_, cnt)| std::cmp::Reverse(*cnt));
for (tc, cnt) in err_counts.iter().take(10) {
println!(" type_code {tc:<5} → {cnt} errors");
}
}
ExitCode::SUCCESS
}