Skip to content

Commit 58f85a3

Browse files
Demo quietness test
1 parent e494418 commit 58f85a3

File tree

3 files changed

+176
-2
lines changed

3 files changed

+176
-2
lines changed

Cargo.toml

+4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ edition = "2021"
1414
name = "rustfmt"
1515
path = "src/bin/main.rs"
1616

17+
[[bin]]
18+
name = "quiet-test"
19+
path = "src/bin/quiet.rs"
20+
1721
[[bin]]
1822
name = "cargo-fmt"
1923
path = "src/cargo-fmt/main.rs"

src/bin/quiet.rs

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
use std::collections::HashMap;
2+
use std::fs;
3+
use std::io::{BufRead, BufReader, Write};
4+
use std::path::{Path, PathBuf};
5+
6+
use rustfmt_nightly::{load_config, CliOptions, Config, Input, Session};
7+
8+
fn main() {
9+
let mut args = std::env::args();
10+
let Some(_arg0) = args.next() else {
11+
std::process::exit(1);
12+
};
13+
let Some(filename) = args.next() else {
14+
std::process::exit(1);
15+
};
16+
let filename: PathBuf = filename.into();
17+
let opt_config = args.next().map(PathBuf::from);
18+
19+
let config = if let Some(ref config_file_path) = opt_config {
20+
load_config(Some(config_file_path), None::<NullOptions>)
21+
.expect("`rustfmt.toml` not found")
22+
.0
23+
} else {
24+
read_config(&filename)
25+
};
26+
27+
let input = Input::File(filename);
28+
let mut session = Session::<Blackhole>::new(config, None);
29+
let _ = session.format(input).unwrap();
30+
}
31+
32+
struct Blackhole;
33+
impl Write for Blackhole {
34+
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
35+
Ok(buf.len())
36+
}
37+
38+
fn flush(&mut self) -> std::io::Result<()> {
39+
Ok(())
40+
}
41+
}
42+
43+
struct NullOptions;
44+
45+
impl CliOptions for NullOptions {
46+
fn apply_to(self, _: &mut Config) {
47+
unreachable!();
48+
}
49+
fn config_path(&self) -> Option<&Path> {
50+
unreachable!();
51+
}
52+
}
53+
54+
fn read_config(filename: &Path) -> Config {
55+
let sig_comments = read_significant_comments(filename);
56+
// Look for a config file. If there is a 'config' property in the significant comments, use
57+
// that. Otherwise, if there are no significant comments at all, look for a config file with
58+
// the same name as the test file.
59+
let mut config = if !sig_comments.is_empty() {
60+
load_config(
61+
sig_comments.get("config").map(Path::new),
62+
None::<NullOptions>,
63+
)
64+
.map(|(config, _)| config)
65+
.unwrap_or_default()
66+
} else {
67+
load_config(
68+
filename.with_extension("toml").file_name().map(Path::new),
69+
None::<NullOptions>,
70+
)
71+
.map(|(config, _)| config)
72+
.unwrap_or_default()
73+
};
74+
75+
for (key, val) in &sig_comments {
76+
if key != "target" && key != "config" && key != "unstable" {
77+
config.override_value(key, val);
78+
}
79+
}
80+
81+
config
82+
}
83+
84+
// Reads significant comments of the form: `// rustfmt-key: value` into a hash map.
85+
fn read_significant_comments(file_name: &Path) -> HashMap<String, String> {
86+
let file = fs::File::open(file_name)
87+
.unwrap_or_else(|_| panic!("couldn't read file {}", file_name.display()));
88+
let reader = BufReader::new(file);
89+
let pattern = r"^\s*//\s*rustfmt-([^:]+):\s*(\S+)";
90+
let regex = regex::Regex::new(pattern).expect("failed creating pattern 1");
91+
92+
// Matches lines containing significant comments or whitespace.
93+
let line_regex = regex::Regex::new(r"(^\s*$)|(^\s*//\s*rustfmt-[^:]+:\s*\S+)")
94+
.expect("failed creating pattern 2");
95+
96+
reader
97+
.lines()
98+
.map(|line| line.expect("failed getting line"))
99+
.filter(|line| line_regex.is_match(line))
100+
.filter_map(|line| {
101+
regex.captures_iter(&line).next().map(|capture| {
102+
(
103+
capture
104+
.get(1)
105+
.expect("couldn't unwrap capture")
106+
.as_str()
107+
.to_owned(),
108+
capture
109+
.get(2)
110+
.expect("couldn't unwrap capture")
111+
.as_str()
112+
.to_owned(),
113+
)
114+
})
115+
})
116+
.collect()
117+
}

src/test/mod.rs

+55-2
Original file line numberDiff line numberDiff line change
@@ -661,28 +661,81 @@ fn check_files(files: Vec<PathBuf>, opt_config: &Option<PathBuf>) -> (Vec<Format
661661
continue;
662662
}
663663

664+
count += 1;
665+
664666
debug!("Testing '{}'...", file_name.display());
665667

666668
match idempotent_check(&file_name, opt_config) {
667669
Ok(ref report) if report.has_warnings() => {
668670
print!("{}", FormatReportFormatterBuilder::new(report).build());
669671
fails += 1;
672+
continue;
670673
}
671674
Ok(report) => reports.push(report),
672675
Err(err) => {
673676
if let IdempotentCheckError::Mismatch(msg) = err {
674677
print_mismatches_default_message(msg);
675678
}
676679
fails += 1;
680+
continue;
677681
}
678682
}
679683

680-
count += 1;
684+
match quiet_test(&file_name, opt_config.as_deref()) {
685+
Ok(()) => {}
686+
Err(QuietError::Fail { output }) => {
687+
println!(
688+
"Unexpected error from rustfmt when formatting `{file_name}`:\n{output}\n",
689+
file_name = file_name.display(),
690+
);
691+
fails += 1;
692+
continue;
693+
}
694+
Err(QuietError::Erroneous { output }) => {
695+
println!(
696+
"Erroneous output from rustfmt when formatting `{file_name}`:\n{output}\n",
697+
file_name = file_name.display(),
698+
);
699+
fails += 1;
700+
continue;
701+
}
702+
}
681703
}
682704

683705
(reports, count, fails)
684706
}
685707

708+
enum QuietError {
709+
Erroneous { output: String },
710+
Fail { output: String },
711+
}
712+
713+
fn quiet_test(file_name: &Path, opt_config: Option<&Path>) -> Result<(), QuietError> {
714+
let cargo = PathBuf::from(std::env::var_os("CARGO").unwrap_or_else(|| "cargo".into()));
715+
let cmd = Command::new(cargo)
716+
.args(["run", "--bin", "quiet-test"])
717+
.arg(file_name)
718+
.args(opt_config)
719+
.output();
720+
match cmd {
721+
Ok(cmd) => {
722+
let output = String::from_utf8_lossy(&cmd.stdout).into_owned();
723+
if !cmd.status.success() {
724+
Err(QuietError::Fail {
725+
output: format!("non-success error code"),
726+
})
727+
} else if !output.is_empty() {
728+
Err(QuietError::Erroneous { output })
729+
} else {
730+
Ok(())
731+
}
732+
}
733+
Err(e) => Err(QuietError::Fail {
734+
output: e.to_string(),
735+
}),
736+
}
737+
}
738+
686739
fn print_mismatches_default_message(result: HashMap<PathBuf, Vec<Mismatch>>) {
687740
for (file_name, diff) in result {
688741
let mismatch_msg_formatter =
@@ -708,7 +761,7 @@ fn print_mismatches<T: Fn(u32) -> String>(
708761
}
709762
}
710763

711-
fn read_config(filename: &Path) -> Config {
764+
pub fn read_config(filename: &Path) -> Config {
712765
let sig_comments = read_significant_comments(filename);
713766
// Look for a config file. If there is a 'config' property in the significant comments, use
714767
// that. Otherwise, if there are no significant comments at all, look for a config file with

0 commit comments

Comments
 (0)