Revert "feat(cli): support snapshot testing with --update flag"

This reverts commit 1b033fdfa4.
This commit is contained in:
Ika 2019-09-06 10:42:37 +08:00
parent 1b033fdfa4
commit 807fdf3ef0
6 changed files with 105 additions and 274 deletions

View file

@ -9,7 +9,6 @@ pub mod test;
pub mod util;
pub mod wasm;
pub mod web_ui;
pub mod print;
#[cfg(test)]
mod tests;

View file

@ -83,8 +83,7 @@ fn run() -> error::Result<()> {
.takes_value(true),
)
.arg(Arg::with_name("debug").long("debug").short("d"))
.arg(Arg::with_name("debug-graph").long("debug-graph").short("D"))
.arg(Arg::with_name("update").long("update").short("u")),
.arg(Arg::with_name("debug-graph").long("debug-graph").short("D")),
)
.subcommand(
SubCommand::with_name("highlight")
@ -151,10 +150,9 @@ fn run() -> error::Result<()> {
let debug = matches.is_present("debug");
let debug_graph = matches.is_present("debug-graph");
let filter = matches.value_of("filter");
let update = matches.is_present("update");
let corpus_path = current_dir.join("corpus");
if let Some(language) = loader.languages_at_path(&current_dir)?.first() {
test::run_tests_at_path(*language, &corpus_path, debug, debug_graph, filter, update)?;
test::run_tests_at_path(*language, &corpus_path, debug, debug_graph, filter)?;
} else {
eprintln!("No language found");
}

View file

@ -1,5 +1,4 @@
use super::error::{Error, Result};
use super::print::print_tree;
use super::util;
use std::io::{self, Write};
use std::path::Path;
@ -82,7 +81,57 @@ pub fn parse_file_at_path(
let mut cursor = tree.walk();
if !quiet {
print_tree(&mut stdout, &mut cursor, true)?;
let mut needs_newline = false;
let mut indent_level = 0;
let mut did_visit_children = false;
loop {
let node = cursor.node();
let is_named = node.is_named();
if did_visit_children {
if is_named {
stdout.write(b")")?;
needs_newline = true;
}
if cursor.goto_next_sibling() {
did_visit_children = false;
} else if cursor.goto_parent() {
did_visit_children = true;
indent_level -= 1;
} else {
break;
}
} else {
if is_named {
if needs_newline {
stdout.write(b"\n")?;
}
for _ in 0..indent_level {
stdout.write(b" ")?;
}
let start = node.start_position();
let end = node.end_position();
if let Some(field_name) = cursor.field_name() {
write!(&mut stdout, "{}: ", field_name)?;
}
write!(
&mut stdout,
"({} [{}, {}] - [{}, {}]",
node.kind(),
start.row,
start.column,
end.row,
end.column
)?;
needs_newline = true;
}
if cursor.goto_first_child() {
did_visit_children = false;
indent_level += 1;
} else {
did_visit_children = true;
}
}
}
cursor.reset(tree.root_node());
println!("");
}

View file

@ -1,60 +0,0 @@
use super::error::{Result};
use std::io::{Write};
use tree_sitter::{TreeCursor};
pub fn print_tree(output: &mut Write, cursor: &mut TreeCursor, prints_position: bool) -> Result<()> {
let mut needs_newline = false;
let mut indent_level = 0;
let mut did_visit_children = false;
loop {
let node = cursor.node();
let is_named = node.is_named();
if did_visit_children {
if is_named {
output.write(b")")?;
needs_newline = true;
}
if cursor.goto_next_sibling() {
did_visit_children = false;
} else if cursor.goto_parent() {
did_visit_children = true;
indent_level -= 1;
} else {
break;
}
} else {
if is_named {
if needs_newline {
output.write(b"\n")?;
}
for _ in 0..indent_level {
output.write(b" ")?;
}
if let Some(field_name) = cursor.field_name() {
write!(output, "{}: ", field_name)?;
}
write!(output, "({}", node.kind())?;
if prints_position {
let start = node.start_position();
let end = node.end_position();
write!(
output,
" [{}, {}] - [{}, {}]",
start.row,
start.column,
end.row,
end.column
)?;
}
needs_newline = true;
}
if cursor.goto_first_child() {
did_visit_children = false;
indent_level += 1;
} else {
did_visit_children = true;
}
}
}
return Ok(());
}

View file

@ -1,5 +1,4 @@
use super::error::{Error, Result};
use super::print::print_tree;
use super::util;
use ansi_term::Colour;
use difference::{Changeset, Difference};
@ -9,7 +8,7 @@ use regex::Regex;
use std::char;
use std::fs;
use std::io::{self, Write};
use std::path::{Path, PathBuf};
use std::path::Path;
use std::str;
use tree_sitter::{Language, LogType, Parser};
@ -31,7 +30,6 @@ pub enum TestEntry {
Group {
name: String,
children: Vec<TestEntry>,
file_path: Option<PathBuf>,
},
Example {
name: String,
@ -46,7 +44,6 @@ impl Default for TestEntry {
TestEntry::Group {
name: String::new(),
children: Vec::new(),
file_path: None,
}
}
}
@ -57,52 +54,43 @@ pub fn run_tests_at_path(
debug: bool,
debug_graph: bool,
filter: Option<&str>,
update: bool,
) -> Result<()> {
let test_entry = parse_tests(path, false)?;
let test_entry = parse_tests(path)?;
let mut _log_session = None;
let mut parser = Parser::new();
parser.set_language(language).map_err(|e| e.to_string())?;
if !update {
if debug_graph {
_log_session = Some(util::log_graphs(&mut parser, "log.html")?);
} else if debug {
parser.set_logger(Some(Box::new(|log_type, message| {
if log_type == LogType::Lex {
io::stderr().write(b" ").unwrap();
}
write!(&mut io::stderr(), "{}\n", message).unwrap();
})));
if debug_graph {
_log_session = Some(util::log_graphs(&mut parser, "log.html")?);
} else if debug {
parser.set_logger(Some(Box::new(|log_type, message| {
if log_type == LogType::Lex {
io::stderr().write(b" ").unwrap();
}
write!(&mut io::stderr(), "{}\n", message).unwrap();
})));
}
let mut failures = Vec::new();
if let TestEntry::Group { children, .. } = test_entry {
for child in children {
run_tests(&mut parser, child, filter, 0, &mut failures)?;
}
}
let mut diffs = Vec::new();
let mut update_entries = Vec::new();
run_tests(&mut parser, test_entry, filter, update, &mut update_entries, -1, &mut diffs)?;
if diffs.len() > 0 {
if failures.len() > 0 {
println!("");
let diff_name = if update { "update" } else { "failure" };
if diffs.len() == 1 {
println!("1 {}:", diff_name)
if failures.len() == 1 {
println!("1 failure:")
} else {
println!("{} {}s:", diffs.len(), diff_name)
println!("{} failures:", failures.len())
}
if update {
print_update_diff_key();
} else {
print_diff_key();
}
for (i, (name, parsed, provided)) in diffs.iter().enumerate() {
print_diff_key();
for (i, (name, actual, expected)) in failures.iter().enumerate() {
println!("\n {}. {}:", i + 1, name);
if update {
print_update_diff(provided, parsed);
} else {
print_diff(parsed, provided);
}
print_diff(actual, expected);
}
Error::err(String::new())
} else {
@ -111,40 +99,14 @@ pub fn run_tests_at_path(
}
pub fn print_diff_key() {
print_diff_key_with_colors("actual", "expected", Colour::Red, Colour::Green);
}
fn print_update_diff_key() {
print_diff_key_with_colors("original", "updated", Colour::Yellow, Colour::Green);
}
fn print_diff_key_with_colors(
actual_name: &str,
expected_name: &str,
actual_color: Colour,
expected_color: Colour,
) {
println!(
"\n{} / {}",
expected_color.paint(expected_name),
actual_color.paint(actual_name)
Colour::Green.paint("expected"),
Colour::Red.paint("actual")
);
}
pub fn print_diff(actual: &String, expected: &String) {
print_diff_with_colors(actual, expected, Colour::Red, Colour::Green);
}
fn print_update_diff(actual: &String, expected: &String) {
print_diff_with_colors(actual, expected, Colour::Yellow, Colour::Green);
}
fn print_diff_with_colors(
actual: &String,
expected: &String,
actual_color: Colour,
expected_color: Colour,
) {
let changeset = Changeset::new(actual, expected, " ");
print!(" ");
for diff in &changeset.diffs {
@ -153,10 +115,10 @@ fn print_diff_with_colors(
print!("{}{}", part, changeset.split);
}
Difference::Add(part) => {
print!("{}{}", expected_color.paint(part), changeset.split);
print!("{}{}", Colour::Green.paint(part), changeset.split);
}
Difference::Rem(part) => {
print!("{}{}", actual_color.paint(part), changeset.split);
print!("{}{}", Colour::Red.paint(part), changeset.split);
}
}
}
@ -167,10 +129,8 @@ fn run_tests(
parser: &mut Parser,
test_entry: TestEntry,
filter: Option<&str>,
update: bool,
update_entries: &mut Vec<(String, String, String)>,
mut indent_level: i32,
diffs: &mut Vec<(String, String, String)>,
failures: &mut Vec<(String, String, String)>,
) -> Result<()> {
match test_entry {
TestEntry::Example {
@ -181,97 +141,39 @@ fn run_tests(
} => {
if let Some(filter) = filter {
if !name.contains(filter) {
if update {
let input = String::from_utf8(input).unwrap();
update_entries.push((name, input, output));
}
return Ok(());
}
}
let tree = parser.parse(&input, None).unwrap();
let mut parsed = tree.root_node().to_sexp();
let mut actual = tree.root_node().to_sexp();
if !has_fields {
parsed = strip_sexp_fields(parsed);
actual = strip_sexp_fields(actual);
}
for _ in 0..indent_level {
print!(" ");
}
let provided = normalize_sexp(&output);
if parsed == provided {
if actual == output {
println!("{}", Colour::Green.paint(&name));
if update {
let input = String::from_utf8(input).unwrap();
update_entries.push((name, input, output));
}
} else {
if update {
let input = String::from_utf8(input).unwrap();
let mut fixed_output = Vec::new();
let mut cursor = tree.walk();
print_tree(&mut fixed_output, &mut cursor, false)?;
let fixed_output = String::from_utf8(fixed_output).unwrap();
update_entries.push((name.clone(), input, fixed_output));
println!("{}", Colour::Yellow.paint(&name));
} else {
println!("{}", Colour::Red.paint(&name));
}
diffs.push((name, parsed, provided));
println!("{}", Colour::Red.paint(&name));
failures.push((name, actual, output));
}
}
TestEntry::Group { name, children, file_path } => {
if indent_level >= 0 {
for _ in 0..indent_level {
print!(" ");
}
println!("{}:", name);
TestEntry::Group { name, children } => {
for _ in 0..indent_level {
print!(" ");
}
let diff_count = diffs.len();
println!("{}:", name);
indent_level += 1;
for child in children {
run_tests(parser, child, filter, update, update_entries, indent_level, diffs)?;
}
if let Some(file_path) = file_path {
if update && diffs.len() - diff_count > 0 {
write_tests(&file_path, &update_entries)?;
}
update_entries.clear();
run_tests(parser, child, filter, indent_level, failures)?;
}
}
}
Ok(())
}
fn write_tests(file_path: &Path, update_entries: &Vec<(String, String, String)>) -> Result<()> {
let mut buffer = fs::File::create(file_path)?;
write_tests_to_buffer(&mut buffer, update_entries)
}
fn write_tests_to_buffer(
buffer: &mut Write,
update_entries: &Vec<(String, String, String)>,
) -> Result<()> {
for (i, (name, input, output)) in update_entries.iter().enumerate() {
if i > 0 {
write!(buffer, "\n")?;
}
write!(
buffer,
"{}\n{}\n{}\n{}\n{}\n\n{}\n",
"=".repeat(80),
name,
"=".repeat(80),
input,
"-".repeat(80),
output.trim()
)?;
}
Ok(())
}
pub fn parse_tests(path: &Path, norm_sexp: bool) -> io::Result<TestEntry> {
pub fn parse_tests(path: &Path) -> io::Result<TestEntry> {
let name = path
.file_stem()
.and_then(|s| s.to_str())
@ -287,13 +189,13 @@ pub fn parse_tests(path: &Path, norm_sexp: bool) -> io::Result<TestEntry> {
.unwrap_or("")
.starts_with(".");
if !hidden {
children.push(parse_tests(&entry.path(), norm_sexp)?);
children.push(parse_tests(&entry.path())?);
}
}
Ok(TestEntry::Group { name, children, file_path: None })
Ok(TestEntry::Group { name, children })
} else {
let content = fs::read_to_string(path)?;
Ok(parse_test_content(name, content, Some(path.to_path_buf()), norm_sexp))
Ok(parse_test_content(name, content))
}
}
@ -301,12 +203,7 @@ pub fn strip_sexp_fields(sexp: String) -> String {
SEXP_FIELD_REGEX.replace_all(&sexp, " (").to_string()
}
fn parse_test_content(
name: String,
content: String,
file_path: Option<PathBuf>,
norm_sexp: bool,
) -> TestEntry {
fn parse_test_content(name: String, content: String) -> TestEntry {
let mut children = Vec::new();
let bytes = content.as_bytes();
let mut previous_name = String::new();
@ -327,11 +224,8 @@ fn parse_test_content(
);
if let Ok(output) = str::from_utf8(&bytes[divider_end..header_start]) {
let input = bytes[previous_header_end..divider_start].to_vec();
let output = if norm_sexp {
normalize_sexp(output)
} else {
output.to_owned()
};
let output = WHITESPACE_REGEX.replace_all(output.trim(), " ").to_string();
let output = output.replace(" )", ")");
let has_fields = SEXP_FIELD_REGEX.is_match(&output);
children.push(TestEntry::Example {
name: previous_name,
@ -347,13 +241,7 @@ fn parse_test_content(
.to_string();
previous_header_end = header_end;
}
TestEntry::Group { name, children, file_path }
}
fn normalize_sexp(sexp: &str) -> String {
let sexp = WHITESPACE_REGEX.replace_all(sexp.trim(), " ").to_string();
let sexp = sexp.replace(" )", ")");
return sexp;
TestEntry::Group { name, children }
}
#[cfg(test)]
@ -385,8 +273,6 @@ d
"#
.trim()
.to_string(),
None,
true,
);
assert_eq!(
@ -406,49 +292,8 @@ d
output: "(d)".to_string(),
has_fields: false,
},
],
file_path: None
]
}
);
}
#[test]
fn test_write_tests_to_buffer() {
let mut buffer = Vec::new();
let update_entries = vec![
(
"title 1".to_string(),
"input 1".to_string(),
"output 1".to_string(),
),
(
"title 2".to_string(),
"input 2".to_string(),
"output 2".to_string(),
),
];
write_tests_to_buffer(&mut buffer, &update_entries).unwrap();
assert_eq!(
String::from_utf8(buffer).unwrap(),
r#"
================================================================================
title 1
================================================================================
input 1
--------------------------------------------------------------------------------
output 1
================================================================================
title 2
================================================================================
input 2
--------------------------------------------------------------------------------
output 2
"#
.trim_start()
.to_string()
);
}
}

View file

@ -59,8 +59,8 @@ fn test_real_language_corpus_files() {
let language = get_language(language_name);
let corpus_dir = grammars_dir.join(language_name).join("corpus");
let error_corpus_file = error_corpus_dir.join(&format!("{}_errors.txt", language_name));
let main_tests = parse_tests(&corpus_dir, true).unwrap();
let error_tests = parse_tests(&error_corpus_file, true).unwrap_or(TestEntry::default());
let main_tests = parse_tests(&corpus_dir).unwrap();
let error_tests = parse_tests(&error_corpus_file).unwrap_or(TestEntry::default());
let mut tests = flatten_tests(main_tests);
tests.extend(flatten_tests(error_tests));
@ -243,7 +243,7 @@ fn test_feature_corpus_files() {
let corpus_path = test_path.join("corpus.txt");
let c_code = generate_result.unwrap().1;
let language = get_test_language(language_name, &c_code, Some(&test_path));
let test = parse_tests(&corpus_path, true).unwrap();
let test = parse_tests(&corpus_path).unwrap();
let tests = flatten_tests(test);
if !tests.is_empty() {
@ -381,7 +381,7 @@ fn flatten_tests(test: TestEntry) -> Vec<(String, Vec<u8>, String, bool)> {
}
result.push((name, input, output, has_fields));
}
TestEntry::Group { mut name, children, .. } => {
TestEntry::Group { mut name, children } => {
if !prefix.is_empty() {
name.insert_str(0, " - ");
name.insert_str(0, prefix);