Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Demo quietness test #6216

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading