From 913635b1d21173a2d48c0d7530581ada3d899900 Mon Sep 17 00:00:00 2001 From: Denis Lisov Date: Mon, 17 Jan 2022 01:46:49 +0300 Subject: [PATCH 1/7] testing: add an integration test for yaml-test-suite The official YAML test suite (https://github.com/yaml/yaml-test-suite). Requires the submodule to be checked out. --- .gitmodules | 3 + tests/yaml-test-suite | 1 + tests/yaml-test-suite.rs | 277 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 281 insertions(+) create mode 100644 .gitmodules create mode 160000 tests/yaml-test-suite create mode 100644 tests/yaml-test-suite.rs diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..cbc1e88b --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "tests/yaml-test-suite"] + path = tests/yaml-test-suite + url = https://github.com/yaml/yaml-test-suite/ diff --git a/tests/yaml-test-suite b/tests/yaml-test-suite new file mode 160000 index 00000000..534eb754 --- /dev/null +++ b/tests/yaml-test-suite @@ -0,0 +1 @@ +Subproject commit 534eb75451fada039442460a79b79989fc87f9c3 diff --git a/tests/yaml-test-suite.rs b/tests/yaml-test-suite.rs new file mode 100644 index 00000000..04b1fbaf --- /dev/null +++ b/tests/yaml-test-suite.rs @@ -0,0 +1,277 @@ +use std::{ffi::OsStr, fs, path::Path}; + +use yaml_rust::{ + parser::{Event, EventReceiver, Parser}, + scanner::{TokenType, TScalarStyle}, + ScanError, + Yaml, + YamlLoader, + yaml, +}; + +#[test] +fn yaml_test_suite() -> Result<(), Box> { + let mut error_count = 0; + for entry in std::fs::read_dir("tests/yaml-test-suite/src")? { + let entry = entry?; + error_count += run_tests_from_file(&entry.path(), &entry.file_name())?; + } + println!("Expected errors: {}", EXPECTED_FAILURES.len()); + if error_count > 0 { + panic!("Unexpected errors in testsuite: {}", error_count); + } + Ok(()) +} + +fn run_tests_from_file(path: impl AsRef, file_name: &OsStr) -> Result> { + let test_name = path.as_ref() + .file_name().ok_or("")? + .to_string_lossy().strip_suffix(".yaml").ok_or("unexpected filename")?.to_owned(); + let data = fs::read_to_string(path.as_ref())?; + let tests = YamlLoader::load_from_str(&data)?; + let tests = tests[0].as_vec().unwrap(); + let mut error_count = 0; + + let mut test = yaml::Hash::new(); + for (idx, test_data) in tests.iter().enumerate() { + let desc = format!("{}-{}", test_name, idx); + let is_xfail = EXPECTED_FAILURES.contains(&desc.as_str()); + + // Test fields except `fail` are "inherited" + let test_data = test_data.as_hash().unwrap(); + test.remove(&Yaml::String("fail".into())); + for (key, value) in test_data.clone() { + test.insert(key, value); + } + + if let Some(error) = run_single_test(&test) { + if !is_xfail { + eprintln!("[{}] {}", desc, error); + error_count += 1; + } + } else { + if is_xfail { + eprintln!("[{}] UNEXPECTED PASS", desc); + error_count += 1; + } + } + } + Ok(error_count) +} + +fn run_single_test(test: &yaml::Hash) -> Option { + if test.get(&Yaml::String("skip".into())).is_some() { + return None; + } + let source = test[&Yaml::String("yaml".into())].as_str().unwrap(); + let should_fail = test.get(&Yaml::String("fail".into())) == Some(&Yaml::Boolean(true)); + let actual_events = parse_to_events(&yaml_to_raw(source)); + if should_fail { + if actual_events.is_ok() { + return Some(format!("no error while expected")); + } + } else { + let expected_events = yaml_to_raw(test[&Yaml::String("tree".into())].as_str().unwrap()); + match actual_events { + Ok(events) => { + if let Some(diff) = events_differ(events, &expected_events) { + //dbg!(source, yaml_to_raw(source)); + return Some(format!("events differ: {}", diff)); + } + } + Err(error) => { + //dbg!(source, yaml_to_raw(source)); + return Some(format!("unexpected error {:?}", error)); + } + } + } + None +} + +fn parse_to_events(source: &str) -> Result, ScanError> { + let mut reporter = EventReporter::new(); + Parser::new(source.chars()) + .load(&mut reporter, true)?; + Ok(reporter.events) +} + +struct EventReporter { + events: Vec, +} + +impl EventReporter { + fn new() -> Self { + Self { + events: vec![], + } + } +} + +impl EventReceiver for EventReporter { + fn on_event(&mut self, ev: Event) { + let line: String = match ev { + Event::StreamStart => "+STR".into(), + Event::StreamEnd => "-STR".into(), + + Event::DocumentStart => "+DOC".into(), + Event::DocumentEnd => "-DOC".into(), + + Event::SequenceStart(idx) => format!("+SEQ{}", format_index(idx)), + Event::SequenceEnd => "-SEQ".into(), + + Event::MappingStart(idx) => format!("+MAP{}", format_index(idx)), + Event::MappingEnd => "-MAP".into(), + + Event::Scalar(ref text, style, idx, ref tag) => { + let kind = match style { + TScalarStyle::Plain => ":", + TScalarStyle::SingleQuoted => "'", + TScalarStyle::DoubleQuoted => r#"""#, + TScalarStyle::Literal => "|", + TScalarStyle::Foled => ">", + TScalarStyle::Any => unreachable!(), + }; + format!("=VAL{}{} {}{}", + format_index(idx), format_tag(tag), kind, escape_text(text)) + } + Event::Alias(idx) => format!("=ALI *{}", idx), + Event::Nothing => return, + }; + self.events.push(line); + } +} + +fn format_index(idx: usize) -> String { + if idx > 0 { + format!(" &{}", idx) + } else { + "".into() + } +} + +fn escape_text(text: &str) -> String { + let mut text = text.to_owned(); + for (ch, replacement) in [ + ('\\', r#"\\"#), + ('\n', "\\n"), + ('\r', "\\r"), + ('\x08', "\\b"), + ('\t', "\\t"), + ] { + text = text.replace(ch, replacement); + } + text +} + +fn format_tag(tag: &Option) -> String { + if let Some(TokenType::Tag(ns, tag)) = tag { + let ns = match ns.as_str() { + "!!" => "tag:yaml.org,2002:", // Wrong if this ns is overridden + other => other, + }; + format!(" <{}{}>", ns, tag) + } else { + "".into() + } +} + +fn events_differ(actual: Vec, expected: &str) -> Option { + let actual = actual.iter().map(Some).chain(std::iter::repeat(None)); + let expected = expected_events(expected); + let expected = expected.iter().map(Some).chain(std::iter::repeat(None)); + for (idx, (act, exp)) in actual.zip(expected).enumerate() { + return match (act, exp) { + (Some(act), Some(exp)) => { + if act == exp { + continue; + } else { + Some(format!("line {} differs: expected `{}`, found `{}`", idx, exp, act)) + } + } + (Some(a), None) => Some(format!("extra actual line: {:?}", a)), + (None, Some(e)) => Some(format!("extra expected line: {:?}", e)), + (None, None) => None, + } + } + unreachable!() +} + +/// Replace the unprintable characters used in the YAML examples with normal +fn yaml_to_raw(yaml: &str) -> String { + let mut yaml = yaml.to_owned(); + for (pat, replacement) in [ + ("␣", " "), + ("»", "\t"), + ("—", ""), // Tab line continuation ——» + ("←", "\r"), + ("⇔", "\u{FEFF}"), + ("↵", ""), // Trailing newline marker + ("∎\n", ""), + ] { + yaml = yaml.replace(pat, replacement); + } + yaml +} + +/// Adapt the expectations to the yaml-rust reasonable limitations +/// +/// Drop information on node styles (flow/block) and anchor names. +/// Both are things that can be omitted according to spec. +fn expected_events(expected_tree: &str) -> Vec { + let mut anchors = vec![]; + expected_tree.split("\n") + .map(|s| s.trim_start().to_owned()) + .filter(|s| !s.is_empty()) + .map(|mut s| { + // Anchor name-to-number conversion + if let Some(start) = s.find("&") { + if s[..start].find(":").is_none() { + let len = s[start..].find(" ").unwrap_or(s[start..].len()); + anchors.push(s[start+1..start + len].to_owned()); + s = s.replace(&s[start..start + len], &format!("&{}", anchors.len())); + } + } + // Alias nodes name-to-number + if s.starts_with("=ALI") { + let start = s.find("*").unwrap(); + let name = &s[start + 1 ..]; + let idx = anchors.iter().enumerate().filter(|(_, v)| v == &name).last().unwrap().0; + s = s.replace(&s[start..], &format!("*{}", idx + 1)); + } + // Dropping style information + match &*s { + "+DOC ---" => "+DOC".into(), + "-DOC ..." => "-DOC".into(), + s if s.starts_with("+SEQ []") => s.replacen("+SEQ []", "+SEQ", 1), + s if s.starts_with("+MAP {}") => s.replacen("+MAP {}", "+MAP", 1), + "=VAL :" => "=VAL :~".into(), // FIXME: known bug + s => s.into(), + } + }) + .collect() +} + +static EXPECTED_FAILURES: &[&str] = &[ + // These seem to be API limited (not enough information on the event stream level) + // No tag available for SEQ and MAP + "2XXW-0", + "35KP-0", + "57H4-0", + "6JWB-0", + "735Y-0", + "9KAX-0", + "BU8L-0", + "C4HZ-0", + "EHF6-0", + "J7PZ-0", + "UGM3-0", + // Cannot resolve tag namespaces + "5TYM-0", + "6CK3-0", + "6WLZ-0", + "9WXW-0", + "CC74-0", + "U3C3-0", + "Z9M4-0", + "P76L-0", // overriding the `!!` namespace! +]; From 54f7a6eb437e6101c4a130fda15bd1522dc99aed Mon Sep 17 00:00:00 2001 From: Denis Lisov Date: Tue, 18 Jan 2022 02:44:29 +0300 Subject: [PATCH 2/7] yaml-test-suite: move to libtest-mimic --- Cargo.toml | 5 ++ tests/yaml-test-suite.rs | 185 +++++++++++++++++++++------------------ 2 files changed, 106 insertions(+), 84 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 91d7da42..3b07108c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,4 +14,9 @@ edition = "2018" linked-hash-map = "0.5.3" [dev-dependencies] +libtest-mimic = "0.3.0" quickcheck = "0.9" + +[[test]] +name = "yaml-test-suite" +harness = false diff --git a/tests/yaml-test-suite.rs b/tests/yaml-test-suite.rs index 04b1fbaf..06433bd6 100644 --- a/tests/yaml-test-suite.rs +++ b/tests/yaml-test-suite.rs @@ -1,4 +1,6 @@ -use std::{ffi::OsStr, fs, path::Path}; +use std::fs::{self, DirEntry}; + +use libtest_mimic::{Arguments, Test, Outcome, run_tests}; use yaml_rust::{ parser::{Event, EventReceiver, Parser}, @@ -9,83 +11,98 @@ use yaml_rust::{ yaml, }; -#[test] -fn yaml_test_suite() -> Result<(), Box> { - let mut error_count = 0; - for entry in std::fs::read_dir("tests/yaml-test-suite/src")? { - let entry = entry?; - error_count += run_tests_from_file(&entry.path(), &entry.file_name())?; +type Result> = std::result::Result; + +struct YamlTest { + yaml: String, + expected_events: String, + expected_error: bool, + is_xfail: bool, +} + +fn main() -> Result<()> { + let mut arguments = Arguments::from_args(); + if arguments.num_threads.is_none() { + arguments.num_threads = Some(1); } - println!("Expected errors: {}", EXPECTED_FAILURES.len()); - if error_count > 0 { - panic!("Unexpected errors in testsuite: {}", error_count); + let tests: Vec> = std::fs::read_dir("tests/yaml-test-suite/src")? + .map(|entry| -> Result<_> { + let entry = entry?; + let tests = load_tests_from_file(&entry)?; + Ok(tests) + }) + .collect::>()?; + let mut tests: Vec<_> = tests.into_iter().flatten().collect(); + tests.sort_by_key(|t| t.name.clone()); + run_tests( + &arguments, + tests, + run_yaml_test, + ).exit(); +} + +fn run_yaml_test(test: &Test) -> Outcome { + let desc = &test.data; + let actual_events = parse_to_events(&desc.yaml); + let events_diff = actual_events.map(|events| events_differ(events, &desc.expected_events)); + let error_text = match (events_diff, desc.expected_error) { + (Ok(_), true) => Some("no error when expected".into()), + (Err(_), true) => None, + (Err(e), false) => Some(format!("unexpected error {:?}", e)), + (Ok(Some(diff)), false) => Some(format!("events differ: {}", diff)), + (Ok(None), false) => None, + }; + match (error_text, desc.is_xfail) { + (None, false) => Outcome::Passed, + (Some(text), false) => Outcome::Failed { msg: Some(text) }, + (Some(_), true) => Outcome::Ignored, + (None, true) => Outcome::Failed { msg: Some("expected to fail but passes".into()) }, } - Ok(()) } -fn run_tests_from_file(path: impl AsRef, file_name: &OsStr) -> Result> { - let test_name = path.as_ref() - .file_name().ok_or("")? - .to_string_lossy().strip_suffix(".yaml").ok_or("unexpected filename")?.to_owned(); - let data = fs::read_to_string(path.as_ref())?; - let tests = YamlLoader::load_from_str(&data)?; - let tests = tests[0].as_vec().unwrap(); - let mut error_count = 0; +fn load_tests_from_file(entry: &DirEntry) -> Result>> { + let file_name = entry.file_name().to_string_lossy().to_string(); + let test_name = file_name.strip_suffix(".yaml").ok_or("unexpected filename")?; + let tests = YamlLoader::load_from_str(&fs::read_to_string(&entry.path())?)?; + let tests = tests[0].as_vec().ok_or("no test list found in file")?; - let mut test = yaml::Hash::new(); + let mut result = vec![]; + let mut current_test = yaml::Hash::new(); for (idx, test_data) in tests.iter().enumerate() { - let desc = format!("{}-{}", test_name, idx); - let is_xfail = EXPECTED_FAILURES.contains(&desc.as_str()); + let name = if tests.len() > 1 { + format!("{}-{:02}", test_name, idx) + } else { + test_name.to_string() + }; + let is_xfail = EXPECTED_FAILURES.contains(&name.as_str()); // Test fields except `fail` are "inherited" let test_data = test_data.as_hash().unwrap(); - test.remove(&Yaml::String("fail".into())); + current_test.remove(&Yaml::String("fail".into())); for (key, value) in test_data.clone() { - test.insert(key, value); + current_test.insert(key, value); } - if let Some(error) = run_single_test(&test) { - if !is_xfail { - eprintln!("[{}] {}", desc, error); - error_count += 1; - } - } else { - if is_xfail { - eprintln!("[{}] UNEXPECTED PASS", desc); - error_count += 1; - } - } - } - Ok(error_count) -} + let current_test = Yaml::Hash(current_test.clone()); // Much better indexing -fn run_single_test(test: &yaml::Hash) -> Option { - if test.get(&Yaml::String("skip".into())).is_some() { - return None; - } - let source = test[&Yaml::String("yaml".into())].as_str().unwrap(); - let should_fail = test.get(&Yaml::String("fail".into())) == Some(&Yaml::Boolean(true)); - let actual_events = parse_to_events(&yaml_to_raw(source)); - if should_fail { - if actual_events.is_ok() { - return Some(format!("no error while expected")); - } - } else { - let expected_events = yaml_to_raw(test[&Yaml::String("tree".into())].as_str().unwrap()); - match actual_events { - Ok(events) => { - if let Some(diff) = events_differ(events, &expected_events) { - //dbg!(source, yaml_to_raw(source)); - return Some(format!("events differ: {}", diff)); - } - } - Err(error) => { - //dbg!(source, yaml_to_raw(source)); - return Some(format!("unexpected error {:?}", error)); - } + if current_test["skip"] != Yaml::BadValue { + continue; } + + result.push(Test { + name, + kind: String::new(), + is_ignored: false, + is_bench: false, + data: YamlTest { + yaml: visual_to_raw(current_test["yaml"].as_str().unwrap()), + expected_events: visual_to_raw(current_test["tree"].as_str().unwrap()), + expected_error: current_test["fail"].as_bool() == Some(true), + is_xfail, + }, + }); } - None + Ok(result) } fn parse_to_events(source: &str) -> Result, ScanError> { @@ -196,8 +213,8 @@ fn events_differ(actual: Vec, expected: &str) -> Option { unreachable!() } -/// Replace the unprintable characters used in the YAML examples with normal -fn yaml_to_raw(yaml: &str) -> String { +/// Convert the snippets from "visual" to "actual" representation +fn visual_to_raw(yaml: &str) -> String { let mut yaml = yaml.to_owned(); for (pat, replacement) in [ ("␣", " "), @@ -254,24 +271,24 @@ fn expected_events(expected_tree: &str) -> Vec { static EXPECTED_FAILURES: &[&str] = &[ // These seem to be API limited (not enough information on the event stream level) // No tag available for SEQ and MAP - "2XXW-0", - "35KP-0", - "57H4-0", - "6JWB-0", - "735Y-0", - "9KAX-0", - "BU8L-0", - "C4HZ-0", - "EHF6-0", - "J7PZ-0", - "UGM3-0", + "2XXW", + "35KP", + "57H4", + "6JWB", + "735Y", + "9KAX", + "BU8L", + "C4HZ", + "EHF6", + "J7PZ", + "UGM3", // Cannot resolve tag namespaces - "5TYM-0", - "6CK3-0", - "6WLZ-0", - "9WXW-0", - "CC74-0", - "U3C3-0", - "Z9M4-0", - "P76L-0", // overriding the `!!` namespace! + "5TYM", + "6CK3", + "6WLZ", + "9WXW", + "CC74", + "U3C3", + "Z9M4", + "P76L", // overriding the `!!` namespace! ]; From 84bc5602e81c8b647ac6f1e6fe2ae2f4e8e7ca0f Mon Sep 17 00:00:00 2001 From: Denis Lisov Date: Tue, 18 Jan 2022 03:57:40 +0300 Subject: [PATCH 3/7] yaml-test-suite: add ignores and classify failures --- tests/yaml-test-suite.rs | 79 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/tests/yaml-test-suite.rs b/tests/yaml-test-suite.rs index 06433bd6..8316e7ac 100644 --- a/tests/yaml-test-suite.rs +++ b/tests/yaml-test-suite.rs @@ -291,4 +291,83 @@ static EXPECTED_FAILURES: &[&str] = &[ "U3C3", "Z9M4", "P76L", // overriding the `!!` namespace! + + // These seem to be plain bugs + // Leading TAB in literal scalars + "96NN-00", + "96NN-01", + "R4YG", + "Y79Y-01", + // TAB as start of plain scalar instead of whitespace + "6BCT", + "6CA3", + "A2M4", + "DK95-00", + "Q5MG", + "Y79Y-06", + "4EJS", // unexpected pass + "Y79Y-03", // unexpected pass + "Y79Y-04", // unexpected pass + "Y79Y-05", // unexpected pass + // TABs in whitespace-only lines + "DK95-03", + "DK95-04", + // Other TABs + "DK95-01", // in double-quoted scalar + // Empty key in flow mappings + "CFD4", + // Document with no nodes and document end + "HWV9", + "QT73", + // Unusual characters in anchors/aliases + "2SXE", // : + "8XYN", // emoji!! + "W5VH", // :@*!$": + "Y2GN", // : in the middle + // Flow mapping colon on next line / multiline key in flow mapping + "4MUZ-00", + "4MUZ-01", + "5MUD", + "9SA2", + "C4WK", + "K3WX", + "NJ66", + "UT92", + "VJP3-01", + // Bare document after end marker + "7Z25", + "M7A3", + // Scalar marker on document start line + "DK3J", + "FP8R", + // Comments on nonempty lines need leading space + "9JBA", + "CVW2", + "MUS6-00", + "SU5Z", + "X4QW", + // Directives (various) + "9HCY", // Directive after content + "EB22", // Directive after content + "MUS6-01", // no document end marker? + "QLJ7", // TAG directives should not be inherited between documents + "RHX7", // no document end marker + "SF5V", // duplicate directive + "W4TN", // scalar confused as directive + // Losing trailing newline + "JEF9-02", + "L24T-01", + // Dashes in flow sequence (should be forbidden) + "G5U8", + "YJV2", + // Misc + "9MMW", // Mapping key in implicit mapping in flow sequence(!) + "G9HC", // Anchor indent problem(?) + "H7J7", // Anchor indent / linebreak problem? + "3UYS", // Escaped / + "HRE5", // Escaped ' in double-quoted (should not work) + "QB6E", // Indent for multiline double-quoted scalar + "S98Z", // Block scalar and indent problems? + "U99R", // Comma is not allowed in tags + "WZ62", // Empty content ]; From feba0e36221ce6e24558b097aea96fd80b132fde Mon Sep 17 00:00:00 2001 From: Denis Lisov Date: Tue, 18 Jan 2022 03:59:58 +0300 Subject: [PATCH 4/7] yaml-test-suite: ensure all XFAILs do exist as tests --- tests/yaml-test-suite.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/yaml-test-suite.rs b/tests/yaml-test-suite.rs index 8316e7ac..0972e262 100644 --- a/tests/yaml-test-suite.rs +++ b/tests/yaml-test-suite.rs @@ -34,6 +34,11 @@ fn main() -> Result<()> { .collect::>()?; let mut tests: Vec<_> = tests.into_iter().flatten().collect(); tests.sort_by_key(|t| t.name.clone()); + + for &test in EXPECTED_FAILURES { + assert!(tests.iter().find(|t| t.name == test).is_some()); + } + run_tests( &arguments, tests, From 65d27d01d1a75ae115d91cfb0a3efe187327e1fe Mon Sep 17 00:00:00 2001 From: Denis Lisov Date: Tue, 18 Jan 2022 13:47:16 +0300 Subject: [PATCH 5/7] yaml-test-suite: print the names of missing XFAILs --- tests/yaml-test-suite.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/yaml-test-suite.rs b/tests/yaml-test-suite.rs index 0972e262..49f74d74 100644 --- a/tests/yaml-test-suite.rs +++ b/tests/yaml-test-suite.rs @@ -35,8 +35,11 @@ fn main() -> Result<()> { let mut tests: Vec<_> = tests.into_iter().flatten().collect(); tests.sort_by_key(|t| t.name.clone()); - for &test in EXPECTED_FAILURES { - assert!(tests.iter().find(|t| t.name == test).is_some()); + let missing_xfails: Vec<_> = EXPECTED_FAILURES.iter() + .filter(|&&test| !tests.iter().any(|t| t.name == test)) + .collect(); + if !missing_xfails.is_empty() { + panic!("The following EXPECTED_FAILURES not found during discovery: {:?}", missing_xfails); } run_tests( From aa96655e6351c2cc46045d6b303bd43aa15afd0b Mon Sep 17 00:00:00 2001 From: Denis Lisov Date: Tue, 18 Jan 2022 13:48:00 +0300 Subject: [PATCH 6/7] yaml-test-suite: update to YTS v2022-01-17 --- tests/yaml-test-suite | 2 +- tests/yaml-test-suite.rs | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/yaml-test-suite b/tests/yaml-test-suite index 534eb754..45db50ae 160000 --- a/tests/yaml-test-suite +++ b/tests/yaml-test-suite @@ -1 +1 @@ -Subproject commit 534eb75451fada039442460a79b79989fc87f9c3 +Subproject commit 45db50aecf9b1520f8258938c88f396e96f30831 diff --git a/tests/yaml-test-suite.rs b/tests/yaml-test-suite.rs index 49f74d74..9048ebe8 100644 --- a/tests/yaml-test-suite.rs +++ b/tests/yaml-test-suite.rs @@ -317,9 +317,14 @@ static EXPECTED_FAILURES: &[&str] = &[ "Y79Y-03", // unexpected pass "Y79Y-04", // unexpected pass "Y79Y-05", // unexpected pass + "Y79Y-10", // TABs in whitespace-only lines "DK95-03", "DK95-04", + // TABs after marker ? or : (space required?) + "Y79Y-07", + "Y79Y-08", + "Y79Y-09", // Other TABs "DK95-01", // in double-quoted scalar // Empty key in flow mappings @@ -335,9 +340,9 @@ static EXPECTED_FAILURES: &[&str] = &[ // Flow mapping colon on next line / multiline key in flow mapping "4MUZ-00", "4MUZ-01", + "4MUZ-02", "5MUD", "9SA2", - "C4WK", "K3WX", "NJ66", "UT92", From 2c39ece186eea024aba6953478d67bd41f0ade52 Mon Sep 17 00:00:00 2001 From: Denis Lisov Date: Wed, 19 Jan 2022 03:03:53 +0300 Subject: [PATCH 7/7] yaml-test-suite: print the YAML text on failure --- tests/yaml-test-suite.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/yaml-test-suite.rs b/tests/yaml-test-suite.rs index 9048ebe8..61f54b63 100644 --- a/tests/yaml-test-suite.rs +++ b/tests/yaml-test-suite.rs @@ -14,6 +14,7 @@ use yaml_rust::{ type Result> = std::result::Result; struct YamlTest { + yaml_visual: String, yaml: String, expected_events: String, expected_error: bool, @@ -53,13 +54,17 @@ fn run_yaml_test(test: &Test) -> Outcome { let desc = &test.data; let actual_events = parse_to_events(&desc.yaml); let events_diff = actual_events.map(|events| events_differ(events, &desc.expected_events)); - let error_text = match (events_diff, desc.expected_error) { + let mut error_text = match (events_diff, desc.expected_error) { (Ok(_), true) => Some("no error when expected".into()), (Err(_), true) => None, (Err(e), false) => Some(format!("unexpected error {:?}", e)), (Ok(Some(diff)), false) => Some(format!("events differ: {}", diff)), (Ok(None), false) => None, }; + if let Some(text) = &mut error_text { + use std::fmt::Write; + let _ = write!(text, "\n### Input:\n{}\n### End", desc.yaml_visual); + } match (error_text, desc.is_xfail) { (None, false) => Outcome::Passed, (Some(text), false) => Outcome::Failed { msg: Some(text) }, @@ -103,6 +108,7 @@ fn load_tests_from_file(entry: &DirEntry) -> Result>> { is_ignored: false, is_bench: false, data: YamlTest { + yaml_visual: current_test["yaml"].as_str().unwrap().to_string(), yaml: visual_to_raw(current_test["yaml"].as_str().unwrap()), expected_events: visual_to_raw(current_test["tree"].as_str().unwrap()), expected_error: current_test["fail"].as_bool() == Some(true),