Skip to content

Commit

Permalink
Demo quietness test
Browse files Browse the repository at this point in the history
  • Loading branch information
compiler-errors committed Jun 24, 2024
1 parent e494418 commit b70aa6b
Show file tree
Hide file tree
Showing 3 changed files with 176 additions and 2 deletions.
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ edition = "2021"
name = "rustfmt"
path = "src/bin/main.rs"

[[bin]]
name = "quiet-test"
path = "src/bin/quiet.rs"

[[bin]]
name = "cargo-fmt"
path = "src/cargo-fmt/main.rs"
Expand Down
117 changes: 117 additions & 0 deletions src/bin/quiet.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
use std::collections::HashMap;
use std::fs;
use std::io::{BufRead, BufReader, Write};
use std::path::{Path, PathBuf};

use rustfmt_nightly::{load_config, CliOptions, Config, Input, Session};

fn main() {
let mut args = std::env::args();
let Some(_arg0) = args.next() else {
std::process::exit(1);
};
let Some(filename) = args.next() else {
std::process::exit(1);
};
let filename: PathBuf = filename.into();
let opt_config = args.next().map(PathBuf::from);

let config = if let Some(ref config_file_path) = opt_config {
load_config(Some(config_file_path), None::<NullOptions>)
.expect("`rustfmt.toml` not found")
.0
} else {
read_config(&filename)
};

let input = Input::File(filename);
let mut session = Session::<Blackhole>::new(config, None);
let _ = session.format(input).unwrap();
}

struct Blackhole;
impl Write for Blackhole {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
Ok(buf.len())
}

fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}

struct NullOptions;

impl CliOptions for NullOptions {
fn apply_to(self, _: &mut Config) {
unreachable!();
}
fn config_path(&self) -> Option<&Path> {
unreachable!();
}
}

fn read_config(filename: &Path) -> Config {
let sig_comments = read_significant_comments(filename);
// Look for a config file. If there is a 'config' property in the significant comments, use
// that. Otherwise, if there are no significant comments at all, look for a config file with
// the same name as the test file.
let mut config = if !sig_comments.is_empty() {
load_config(
sig_comments.get("config").map(Path::new),
None::<NullOptions>,
)
.map(|(config, _)| config)
.unwrap_or_default()
} else {
load_config(
filename.with_extension("toml").file_name().map(Path::new),
None::<NullOptions>,
)
.map(|(config, _)| config)
.unwrap_or_default()
};

for (key, val) in &sig_comments {
if key != "target" && key != "config" && key != "unstable" {
config.override_value(key, val);
}
}

config
}

// Reads significant comments of the form: `// rustfmt-key: value` into a hash map.
fn read_significant_comments(file_name: &Path) -> HashMap<String, String> {
let file = fs::File::open(file_name)
.unwrap_or_else(|_| panic!("couldn't read file {}", file_name.display()));
let reader = BufReader::new(file);
let pattern = r"^\s*//\s*rustfmt-([^:]+):\s*(\S+)";
let regex = regex::Regex::new(pattern).expect("failed creating pattern 1");

// Matches lines containing significant comments or whitespace.
let line_regex = regex::Regex::new(r"(^\s*$)|(^\s*//\s*rustfmt-[^:]+:\s*\S+)")
.expect("failed creating pattern 2");

reader
.lines()
.map(|line| line.expect("failed getting line"))
.filter(|line| line_regex.is_match(line))
.filter_map(|line| {
regex.captures_iter(&line).next().map(|capture| {
(
capture
.get(1)
.expect("couldn't unwrap capture")
.as_str()
.to_owned(),
capture
.get(2)
.expect("couldn't unwrap capture")
.as_str()
.to_owned(),
)
})
})
.collect()
}
57 changes: 55 additions & 2 deletions src/test/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -661,28 +661,81 @@ fn check_files(files: Vec<PathBuf>, opt_config: &Option<PathBuf>) -> (Vec<Format
continue;
}

count += 1;

debug!("Testing '{}'...", file_name.display());

match idempotent_check(&file_name, opt_config) {
Ok(ref report) if report.has_warnings() => {
print!("{}", FormatReportFormatterBuilder::new(report).build());
fails += 1;
continue;
}
Ok(report) => reports.push(report),
Err(err) => {
if let IdempotentCheckError::Mismatch(msg) = err {
print_mismatches_default_message(msg);
}
fails += 1;
continue;
}
}

count += 1;
match quiet_test(&file_name, opt_config.as_deref()) {
Ok(()) => {}
Err(QuietError::Fail { output }) => {
println!(
"Unexpected error from rustfmt when formatting `{file_name}`:\n{output}\n",
file_name = file_name.display(),
);
fails += 1;
continue;
}
Err(QuietError::Erroneous(output)) => {
println!(
"Erroneous output from rustfmt when formatting `{file_name}`:\n{output}\n",
file_name = file_name.display(),
);
fails += 1;
continue;
}
}
}

(reports, count, fails)
}

enum QuietError {
Erroneous { output: String },
Fail { output: String },
}

fn quiet_test(file_name: &Path, opt_config: Option<&Path>) -> Result<(), QuietError> {
let cargo = PathBuf::from(std::env::var_os("CARGO").unwrap_or_else(|| "cargo".into()));
let cmd = Command::new(cargo)
.args(["run", "--bin", "quiet-test"])
.arg(file_name)
.args(opt_config)
.output();
match cmd {
Ok(cmd) => {
let output = String::from_utf8_lossy(&cmd.stdout).into_owned();
if !cmd.status.success() {
Err(QuietError::Fail {
output: format!("non-success error code"),
})
} else if !output.is_empty() {
Err(QuietError::Erroneous { output })
} else {
Ok(())
}
}
Err(e) => Err(QuietError::Fail {
output: e.to_string(),
}),
}
}

fn print_mismatches_default_message(result: HashMap<PathBuf, Vec<Mismatch>>) {
for (file_name, diff) in result {
let mismatch_msg_formatter =
Expand All @@ -708,7 +761,7 @@ fn print_mismatches<T: Fn(u32) -> String>(
}
}

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

0 comments on commit b70aa6b

Please sign in to comment.