From 30820671e14ec6b4923da9c4a45bfced81c73677 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 21 Dec 2022 14:12:10 -0600 Subject: [PATCH] test(parser): Move parser tests next to there definitions --- crates/toml_edit/src/parser/array.rs | 55 +++ crates/toml_edit/src/parser/datetime.rs | 45 ++ crates/toml_edit/src/parser/document.rs | 111 +++++ crates/toml_edit/src/parser/inline_table.rs | 26 + crates/toml_edit/src/parser/key.rs | 25 + crates/toml_edit/src/parser/macros.rs | 65 +++ crates/toml_edit/src/parser/mod.rs | 518 -------------------- crates/toml_edit/src/parser/numbers.rs | 60 +++ crates/toml_edit/src/parser/strings.rs | 111 +++++ crates/toml_edit/src/parser/trivia.rs | 40 ++ crates/toml_edit/src/parser/value.rs | 27 + 11 files changed, 565 insertions(+), 518 deletions(-) diff --git a/crates/toml_edit/src/parser/array.rs b/crates/toml_edit/src/parser/array.rs index 030d2bd1..95604ecc 100644 --- a/crates/toml_edit/src/parser/array.rs +++ b/crates/toml_edit/src/parser/array.rs @@ -56,3 +56,58 @@ parse!(array_value() -> Value, { Ok(v) }) }); + +#[cfg(test)] +mod test { + use super::*; + + use combine::stream::position::Stream; + + #[test] + fn arrays() { + let inputs = [ + r#"[]"#, + r#"[ ]"#, + r#"[ + 1, 2, 3 +]"#, + r#"[ + 1, + 2, # this is ok +]"#, + r#"[# comment +# comment2 + + + ]"#, + r#"[# comment +# comment2 + 1 + +#sd +, +# comment3 + + ]"#, + r#"[1]"#, + r#"[1,]"#, + r#"[ "all", 'strings', """are the same""", '''type''']"#, + r#"[ 100, -2,]"#, + r#"[1, 2, 3]"#, + r#"[1.1, 2.1, 3.1]"#, + r#"["a", "b", "c"]"#, + r#"[ [ 1, 2 ], [3, 4, 5] ]"#, + r#"[ [ 1, 2 ], ["a", "b", "c"] ]"#, + r#"[ { x = 1, a = "2" }, {a = "a",b = "b", c = "c"} ]"#, + ]; + for input in inputs { + parsed_value_eq!(input); + } + + let invalid_inputs = [r#"["#, r#"[,]"#, r#"[,2]"#, r#"[1e165,,]"#]; + for input in invalid_inputs { + let parsed = array().easy_parse(Stream::new(input.as_bytes())); + assert!(parsed.is_err()); + } + } +} diff --git a/crates/toml_edit/src/parser/datetime.rs b/crates/toml_edit/src/parser/datetime.rs index b3ee8e9e..b3360c04 100644 --- a/crates/toml_edit/src/parser/datetime.rs +++ b/crates/toml_edit/src/parser/datetime.rs @@ -208,3 +208,48 @@ parse!(unsigned_digits(count: usize) -> u32, { s.parse::() }) }); + +#[cfg(test)] +mod test { + #[test] + fn offset_date_time() { + let inputs = [ + "1979-05-27T07:32:00Z", + "1979-05-27T00:32:00-07:00", + "1979-05-27T00:32:00.999999-07:00", + ]; + for input in inputs { + parsed_date_time_eq!(input, is_datetime); + } + } + + #[test] + fn local_date_time() { + let inputs = ["1979-05-27T07:32:00", "1979-05-27T00:32:00.999999"]; + for input in inputs { + parsed_date_time_eq!(input, is_datetime); + } + } + + #[test] + fn local_date() { + let inputs = ["1979-05-27", "2017-07-20"]; + for input in inputs { + parsed_date_time_eq!(input, is_datetime); + } + } + + #[test] + fn local_time() { + let inputs = ["07:32:00", "00:32:00.999999"]; + for input in inputs { + parsed_date_time_eq!(input, is_datetime); + } + } + + #[test] + fn time_fraction_truncated() { + let input = "1987-07-05T17:45:00.123456789012345Z"; + parsed_date_time_eq!(input, is_datetime); + } +} diff --git a/crates/toml_edit/src/parser/document.rs b/crates/toml_edit/src/parser/document.rs index d528255c..16032ffe 100644 --- a/crates/toml_edit/src/parser/document.rs +++ b/crates/toml_edit/src/parser/document.rs @@ -121,3 +121,114 @@ parser! { }) } } + +#[cfg(test)] +mod test { + use super::*; + + use snapbox::assert_eq; + + #[test] + fn documents() { + let documents = [ + r#" +# This is a TOML document. + +title = "TOML Example" + + [owner] + name = "Tom Preston-Werner" + dob = 1979-05-27T07:32:00-08:00 # First class dates + + [database] + server = "192.168.1.1" + ports = [ 8001, 8001, 8002 ] + connection_max = 5000 + enabled = true + + [servers] + + # Indentation (tabs and/or spaces) is allowed but not required +[servers.alpha] + ip = "10.0.0.1" + dc = "eqdc10" + + [servers.beta] + ip = "10.0.0.2" + dc = "eqdc10" + + [clients] + data = [ ["gamma", "delta"], [1, 2] ] + + # Line breaks are OK when inside arrays +hosts = [ + "alpha", + "omega" +] + + 'some.wierd .stuff' = """ + like + that + # """ # this broke my sintax highlighting + " also. like " = ''' +that +''' + double = 2e39 # this number looks familiar +# trailing comment"#, + r#""#, + r#" "#, + r#" hello = 'darkness' # my old friend +"#, + r#"[parent . child] +key = "value" +"#, + r#"hello.world = "a" +"#, + r#"foo = 1979-05-27 # Comment +"#, + ]; + for input in documents { + let doc = document(input.as_bytes()); + let doc = match doc { + Ok(doc) => doc, + Err(err) => { + panic!( + "Parse error: {}\nFailed to parse:\n```\n{}\n```", + err, input + ) + } + }; + + dbg!(doc.to_string()); + dbg!(input); + assert_eq(input, doc.to_string()); + } + + let parse_only = ["\u{FEFF} +[package] +name = \"foo\" +version = \"0.0.1\" +authors = [] +"]; + for input in parse_only { + let doc = document(input.as_bytes()); + match doc { + Ok(_) => (), + Err(err) => { + panic!( + "Parse error: {}\nFailed to parse:\n```\n{}\n```", + err, input + ) + } + } + } + + let invalid_inputs = [r#" hello = 'darkness' # my old friend +$"#]; + for input in invalid_inputs { + let doc = document(input.as_bytes()); + + assert!(doc.is_err()); + } + } +} diff --git a/crates/toml_edit/src/parser/inline_table.rs b/crates/toml_edit/src/parser/inline_table.rs index 438ca07e..442e9832 100644 --- a/crates/toml_edit/src/parser/inline_table.rs +++ b/crates/toml_edit/src/parser/inline_table.rs @@ -110,3 +110,29 @@ parse!(keyval() -> (Vec, TableKeyValue), { ) }) }); + +#[cfg(test)] +mod test { + use super::*; + + use combine::stream::position::Stream; + + #[test] + fn inline_tables() { + let inputs = [ + r#"{}"#, + r#"{ }"#, + r#"{a = 1e165}"#, + r#"{ hello = "world", a = 1}"#, + r#"{ hello.world = "a" }"#, + ]; + for input in inputs { + parsed_value_eq!(input); + } + let invalid_inputs = [r#"{a = 1e165"#, r#"{ hello = "world", a = 2, hello = 1}"#]; + for input in invalid_inputs { + let parsed = inline_table().easy_parse(Stream::new(input.as_bytes())); + assert!(parsed.is_err()); + } + } +} diff --git a/crates/toml_edit/src/parser/key.rs b/crates/toml_edit/src/parser/key.rs index ad578ccd..ceb1873f 100644 --- a/crates/toml_edit/src/parser/key.rs +++ b/crates/toml_edit/src/parser/key.rs @@ -55,3 +55,28 @@ pub(crate) fn is_unquoted_char(c: u8) -> bool { // dot-sep = ws %x2E ws ; . Period const DOT_SEP: u8 = b'.'; + +#[cfg(test)] +mod test { + use super::*; + + use combine::stream::position::Stream; + use snapbox::assert_eq; + + #[test] + fn keys() { + let cases = [ + ("a", "a"), + (r#""hello\n ""#, "hello\n "), + (r#"'hello\n '"#, "hello\\n "), + ]; + + for (input, expected) in cases { + let parsed = simple_key().easy_parse(Stream::new(input.as_bytes())); + assert!(parsed.is_ok()); + let ((.., k), rest) = parsed.unwrap(); + assert_eq(k.as_str(), expected); + assert_eq!(rest.input.len(), 0); + } + } +} diff --git a/crates/toml_edit/src/parser/macros.rs b/crates/toml_edit/src/parser/macros.rs index 58d05986..8f27f577 100644 --- a/crates/toml_edit/src/parser/macros.rs +++ b/crates/toml_edit/src/parser/macros.rs @@ -45,3 +45,68 @@ macro_rules! toml_parser ( } ); ); + +#[cfg(test)] +macro_rules! parsed_eq { + ($parsed:ident, $expected:expr) => {{ + assert!($parsed.is_ok(), "{:?}", $parsed.err().unwrap()); + let (v, rest) = $parsed.unwrap(); + assert_eq!(v, $expected); + assert!(rest.input.is_empty()); + }}; +} + +#[cfg(test)] +macro_rules! parsed_float_eq { + ($input:ident, $expected:expr) => {{ + let parsed = crate::parser::numbers::float().easy_parse(Stream::new($input.as_bytes())); + let (v, rest) = match parsed { + Ok(parsed) => parsed, + Err(err) => { + panic!("Unexpected error for {:?}: {:?}", $input, err); + } + }; + if $expected.is_nan() { + assert!(v.is_nan()); + } else if $expected.is_infinite() { + assert!(v.is_infinite()); + assert_eq!($expected.is_sign_positive(), v.is_sign_positive()); + } else { + dbg!($expected); + dbg!(v); + assert!(($expected - v).abs() < std::f64::EPSILON); + } + assert!(rest.input.is_empty()); + }}; +} + +#[cfg(test)] +macro_rules! parsed_value_eq { + ($input:expr) => { + use combine::EasyParser; + let parsed = crate::parser::value::value() + .easy_parse(combine::stream::position::Stream::new($input.as_bytes())); + let (v, rest) = match parsed { + Ok(parsed) => parsed, + Err(err) => { + panic!("Unexpected error for {:?}: {:?}", $input, err); + } + }; + snapbox::assert_eq(v.to_string(), $input); + assert!(rest.input.is_empty()); + }; +} + +#[cfg(test)] +macro_rules! parsed_date_time_eq { + ($input:expr, $is:ident) => {{ + use combine::EasyParser; + let parsed = crate::parser::value::value() + .easy_parse(combine::stream::position::Stream::new($input.as_bytes())); + assert!(parsed.is_ok()); + let (v, rest) = parsed.unwrap(); + snapbox::assert_eq(v.to_string(), $input); + assert!(rest.input.is_empty()); + assert!(v.$is()); + }}; +} diff --git a/crates/toml_edit/src/parser/mod.rs b/crates/toml_edit/src/parser/mod.rs index e1342c29..03c9ed34 100644 --- a/crates/toml_edit/src/parser/mod.rs +++ b/crates/toml_edit/src/parser/mod.rs @@ -24,521 +24,3 @@ pub(crate) use self::key::simple_key; pub(crate) use self::value::value as value_parser; use self::state::ParseState; - -#[cfg(test)] -mod tests { - use crate::parser::*; - use combine::stream::position::Stream; - use combine::*; - use snapbox::assert_eq; - use std::fmt; - // Copied from https://github.com/colin-kiegel/rust-pretty-assertions/issues/24 - /// Wrapper around string slice that makes debug output `{:?}` to print string same way as `{}`. - /// Used in different `assert*!` macros in combination with `pretty_assertions` crate to make - /// test failures to show nice diffs. - #[derive(PartialEq, Eq)] - struct PrettyString<'a>(pub(crate) &'a str); - /// Make diff to display string as multi-line string - impl<'a> fmt::Debug for PrettyString<'a> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(self.0) - } - } - - macro_rules! parsed_eq { - ($parsed:ident, $expected:expr) => {{ - assert!($parsed.is_ok(), "{:?}", $parsed.err().unwrap()); - let (v, rest) = $parsed.unwrap(); - assert_eq!(v, $expected); - assert!(rest.input.is_empty()); - }}; - } - - macro_rules! parsed_float_eq { - ($input:ident, $expected:expr) => {{ - let parsed = numbers::float().easy_parse(Stream::new($input.as_bytes())); - let (v, rest) = match parsed { - Ok(parsed) => parsed, - Err(err) => { - panic!("Unexpected error for {:?}: {:?}", $input, err); - } - }; - if $expected.is_nan() { - assert!(v.is_nan()); - } else if $expected.is_infinite() { - assert!(v.is_infinite()); - assert_eq!($expected.is_sign_positive(), v.is_sign_positive()); - } else { - dbg!($expected); - dbg!(v); - assert!(($expected - v).abs() < std::f64::EPSILON); - } - assert!(rest.input.is_empty()); - }}; - } - - macro_rules! parsed_value_eq { - ($input:expr) => { - let parsed = value::value().easy_parse(Stream::new($input.as_bytes())); - let (v, rest) = match parsed { - Ok(parsed) => parsed, - Err(err) => { - panic!("Unexpected error for {:?}: {:?}", $input, err); - } - }; - assert_eq(v.to_string(), $input); - assert!(rest.input.is_empty()); - }; - } - - macro_rules! parsed_date_time_eq { - ($input:expr, $is:ident) => {{ - let parsed = value::value().easy_parse(Stream::new($input.as_bytes())); - assert!(parsed.is_ok()); - let (v, rest) = parsed.unwrap(); - assert_eq(v.to_string(), $input); - assert!(rest.input.is_empty()); - assert!(v.$is()); - }}; - } - - #[test] - fn integers() { - let cases = [ - ("+99", 99), - ("42", 42), - ("0", 0), - ("-17", -17), - ("1_000", 1_000), - ("5_349_221", 5_349_221), - ("1_2_3_4_5", 1_2_3_4_5), - ("0xF", 15), - ("0o0_755", 493), - ("0b1_0_1", 5), - (&std::i64::MIN.to_string()[..], std::i64::MIN), - (&std::i64::MAX.to_string()[..], std::i64::MAX), - ]; - for &(input, expected) in &cases { - let parsed = numbers::integer().easy_parse(Stream::new(input.as_bytes())); - parsed_eq!(parsed, expected); - } - - let overflow = "1000000000000000000000000000000000"; - let parsed = numbers::integer().easy_parse(Stream::new(overflow.as_bytes())); - assert!(parsed.is_err()); - } - - #[test] - fn floats() { - let cases = [ - ("+1.0", 1.0), - ("3.1419", 3.1419), - ("-0.01", -0.01), - ("5e+22", 5e+22), - ("1e6", 1e6), - ("-2E-2", -2E-2), - ("6.626e-34", 6.626e-34), - ("9_224_617.445_991_228_313", 9_224_617.445_991_227), - ("-1.7976931348623157e+308", std::f64::MIN), - ("1.7976931348623157e+308", std::f64::MAX), - ("nan", f64::NAN), - ("+nan", f64::NAN), - ("-nan", f64::NAN), - ("inf", f64::INFINITY), - ("+inf", f64::INFINITY), - ("-inf", f64::NEG_INFINITY), - // ("1e+400", std::f64::INFINITY), - ]; - for &(input, expected) in &cases { - parsed_float_eq!(input, expected); - } - } - - #[test] - fn basic_string() { - let input = - r#""I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF. \U0002070E""#; - let parsed = strings::string().easy_parse(Stream::new(input.as_bytes())); - parsed_eq!( - parsed, - "I\'m a string. \"You can quote me\". Name\tJosé\nLocation\tSF. \u{2070E}" - ); - } - - #[test] - fn ml_basic_string() { - let cases = [ - ( - r#"""" -Roses are red -Violets are blue""""#, - r#"Roses are red -Violets are blue"#, - ), - (r#"""" \""" """"#, " \"\"\" "), - (r#"""" \\""""#, " \\"), - ]; - - for &(input, expected) in &cases { - dbg!(input); - let parsed = strings::string().easy_parse(Stream::new(input.as_bytes())); - parsed_eq!(parsed, expected); - } - - let invalid_cases = [r#"""" """#, r#"""" \""""#]; - - for input in &invalid_cases { - let parsed = strings::ml_basic_string().easy_parse(Stream::new(input.as_bytes())); - assert!(parsed.is_err()); - } - } - - #[test] - fn ml_basic_string_escape_ws() { - let inputs = [ - r#"""" -The quick brown \ - - - fox jumps over \ - the lazy dog.""""#, - r#""""\ - The quick brown \ - fox jumps over \ - the lazy dog.\ - """"#, - ]; - for input in &inputs { - dbg!(input); - let parsed = strings::string().easy_parse(Stream::new(input.as_bytes())); - parsed_eq!(parsed, "The quick brown fox jumps over the lazy dog."); - } - let empties = [ - r#""""\ - """"#, - r#"""" -\ - \ -""""#, - ]; - for empty in &empties { - let parsed = strings::string().easy_parse(Stream::new(empty.as_bytes())); - parsed_eq!(parsed, ""); - } - } - - #[test] - fn literal_string() { - let inputs = [ - r#"'C:\Users\nodejs\templates'"#, - r#"'\\ServerX\admin$\system32\'"#, - r#"'Tom "Dubs" Preston-Werner'"#, - r#"'<\i\c*\s*>'"#, - ]; - - for input in &inputs { - let parsed = strings::string().easy_parse(Stream::new(input.as_bytes())); - parsed_eq!(parsed, &input[1..input.len() - 1]); - } - } - - #[test] - fn ml_literal_string() { - let input = r#"'''I [dw]on't need \d{2} apples'''"#; - let parsed = strings::string().easy_parse(Stream::new(input.as_bytes())); - parsed_eq!(parsed, &input[3..input.len() - 3]); - let input = r#"''' -The first newline is -trimmed in raw strings. - All other whitespace - is preserved. -'''"#; - let parsed = strings::string().easy_parse(Stream::new(input.as_bytes())); - parsed_eq!(parsed, &input[4..input.len() - 3]); - } - - #[test] - fn offset_date_time() { - let inputs = [ - "1979-05-27T07:32:00Z", - "1979-05-27T00:32:00-07:00", - "1979-05-27T00:32:00.999999-07:00", - ]; - for input in inputs { - parsed_date_time_eq!(input, is_datetime); - } - } - - #[test] - fn local_date_time() { - let inputs = ["1979-05-27T07:32:00", "1979-05-27T00:32:00.999999"]; - for input in inputs { - parsed_date_time_eq!(input, is_datetime); - } - } - - #[test] - fn local_date() { - let inputs = ["1979-05-27", "2017-07-20"]; - for input in inputs { - parsed_date_time_eq!(input, is_datetime); - } - } - - #[test] - fn local_time() { - let inputs = ["07:32:00", "00:32:00.999999"]; - for input in inputs { - parsed_date_time_eq!(input, is_datetime); - } - } - - #[test] - fn time_fraction_truncated() { - let input = "1987-07-05T17:45:00.123456789012345Z"; - parsed_date_time_eq!(input, is_datetime); - } - - #[test] - fn trivia() { - let inputs = [ - "", - r#" "#, - r#" -"#, - r#" -# comment - -# comment2 - - -"#, - r#" - "#, - r#"# comment -# comment2 - - - "#, - ]; - for input in inputs { - let parsed = trivia::ws_comment_newline().easy_parse(Stream::new(input.as_bytes())); - assert!(parsed.is_ok()); - let (t, rest) = parsed.unwrap(); - assert!(rest.input.is_empty()); - assert_eq(t, input.as_bytes()); - } - } - - #[test] - fn arrays() { - let inputs = [ - r#"[]"#, - r#"[ ]"#, - r#"[ - 1, 2, 3 -]"#, - r#"[ - 1, - 2, # this is ok -]"#, - r#"[# comment -# comment2 - - - ]"#, - r#"[# comment -# comment2 - 1 - -#sd -, -# comment3 - - ]"#, - r#"[1]"#, - r#"[1,]"#, - r#"[ "all", 'strings', """are the same""", '''type''']"#, - r#"[ 100, -2,]"#, - r#"[1, 2, 3]"#, - r#"[1.1, 2.1, 3.1]"#, - r#"["a", "b", "c"]"#, - r#"[ [ 1, 2 ], [3, 4, 5] ]"#, - r#"[ [ 1, 2 ], ["a", "b", "c"] ]"#, - r#"[ { x = 1, a = "2" }, {a = "a",b = "b", c = "c"} ]"#, - ]; - for input in inputs { - parsed_value_eq!(input); - } - - let invalid_inputs = [r#"["#, r#"[,]"#, r#"[,2]"#, r#"[1e165,,]"#]; - for input in invalid_inputs { - let parsed = array::array().easy_parse(Stream::new(input.as_bytes())); - assert!(parsed.is_err()); - } - } - - #[test] - fn inline_tables() { - let inputs = [ - r#"{}"#, - r#"{ }"#, - r#"{a = 1e165}"#, - r#"{ hello = "world", a = 1}"#, - r#"{ hello.world = "a" }"#, - ]; - for input in inputs { - parsed_value_eq!(input); - } - let invalid_inputs = [r#"{a = 1e165"#, r#"{ hello = "world", a = 2, hello = 1}"#]; - for input in invalid_inputs { - let parsed = inline_table::inline_table().easy_parse(Stream::new(input.as_bytes())); - assert!(parsed.is_err()); - } - } - - #[test] - fn keys() { - let cases = [ - ("a", "a"), - (r#""hello\n ""#, "hello\n "), - (r#"'hello\n '"#, "hello\\n "), - ]; - - for (input, expected) in cases { - let parsed = key::simple_key().easy_parse(Stream::new(input.as_bytes())); - assert!(parsed.is_ok()); - let ((.., k), rest) = parsed.unwrap(); - assert_eq(k.as_str(), expected); - assert_eq!(rest.input.len(), 0); - } - } - - #[test] - fn values() { - let inputs = [ - "1979-05-27T00:32:00.999999", - "-239", - "1e200", - "9_224_617.445_991_228_313", - r#"'''I [dw]on't need \d{2} apples'''"#, - r#"''' -The first newline is -trimmed in raw strings. - All other whitespace - is preserved. -'''"#, - r#""Jos\u00E9\n""#, - r#""\\\"\b/\f\n\r\t\u00E9\U000A0000""#, - r#"{ hello = "world", a = 1}"#, - r#"[ { x = 1, a = "2" }, {a = "a",b = "b", c = "c"} ]"#, - ]; - for input in inputs { - parsed_value_eq!(input); - } - } - - #[test] - fn documents() { - let documents = [ - r#" -# This is a TOML document. - -title = "TOML Example" - - [owner] - name = "Tom Preston-Werner" - dob = 1979-05-27T07:32:00-08:00 # First class dates - - [database] - server = "192.168.1.1" - ports = [ 8001, 8001, 8002 ] - connection_max = 5000 - enabled = true - - [servers] - - # Indentation (tabs and/or spaces) is allowed but not required -[servers.alpha] - ip = "10.0.0.1" - dc = "eqdc10" - - [servers.beta] - ip = "10.0.0.2" - dc = "eqdc10" - - [clients] - data = [ ["gamma", "delta"], [1, 2] ] - - # Line breaks are OK when inside arrays -hosts = [ - "alpha", - "omega" -] - - 'some.wierd .stuff' = """ - like - that - # """ # this broke my sintax highlighting - " also. like " = ''' -that -''' - double = 2e39 # this number looks familiar -# trailing comment"#, - r#""#, - r#" "#, - r#" hello = 'darkness' # my old friend -"#, - r#"[parent . child] -key = "value" -"#, - r#"hello.world = "a" -"#, - r#"foo = 1979-05-27 # Comment -"#, - ]; - for input in documents { - let doc = document(input.as_bytes()); - let doc = match doc { - Ok(doc) => doc, - Err(err) => { - panic!( - "Parse error: {}\nFailed to parse:\n```\n{}\n```", - err, input - ) - } - }; - - dbg!(doc.to_string()); - dbg!(input); - assert_eq(input, doc.to_string()); - } - - let parse_only = ["\u{FEFF} -[package] -name = \"foo\" -version = \"0.0.1\" -authors = [] -"]; - for input in parse_only { - let doc = document(input.as_bytes()); - match doc { - Ok(_) => (), - Err(err) => { - panic!( - "Parse error: {}\nFailed to parse:\n```\n{}\n```", - err, input - ) - } - } - } - - let invalid_inputs = [r#" hello = 'darkness' # my old friend -$"#]; - for input in invalid_inputs { - let doc = document(input.as_bytes()); - - assert!(doc.is_err()); - } - } -} diff --git a/crates/toml_edit/src/parser/numbers.rs b/crates/toml_edit/src/parser/numbers.rs index a93abb01..58a2c4a3 100644 --- a/crates/toml_edit/src/parser/numbers.rs +++ b/crates/toml_edit/src/parser/numbers.rs @@ -194,3 +194,63 @@ pub(crate) const NAN: &[u8] = b"nan"; parse!(nan() -> f64, { bytes(NAN).map(|_| f64::NAN) }); + +#[cfg(test)] +mod test { + use super::*; + + use crate::parser::*; + use combine::stream::position::Stream; + + #[test] + fn integers() { + let cases = [ + ("+99", 99), + ("42", 42), + ("0", 0), + ("-17", -17), + ("1_000", 1_000), + ("5_349_221", 5_349_221), + ("1_2_3_4_5", 1_2_3_4_5), + ("0xF", 15), + ("0o0_755", 493), + ("0b1_0_1", 5), + (&std::i64::MIN.to_string()[..], std::i64::MIN), + (&std::i64::MAX.to_string()[..], std::i64::MAX), + ]; + for &(input, expected) in &cases { + let parsed = numbers::integer().easy_parse(Stream::new(input.as_bytes())); + parsed_eq!(parsed, expected); + } + + let overflow = "1000000000000000000000000000000000"; + let parsed = numbers::integer().easy_parse(Stream::new(overflow.as_bytes())); + assert!(parsed.is_err()); + } + + #[test] + fn floats() { + let cases = [ + ("+1.0", 1.0), + ("3.1419", 3.1419), + ("-0.01", -0.01), + ("5e+22", 5e+22), + ("1e6", 1e6), + ("-2E-2", -2E-2), + ("6.626e-34", 6.626e-34), + ("9_224_617.445_991_228_313", 9_224_617.445_991_227), + ("-1.7976931348623157e+308", std::f64::MIN), + ("1.7976931348623157e+308", std::f64::MAX), + ("nan", f64::NAN), + ("+nan", f64::NAN), + ("-nan", f64::NAN), + ("inf", f64::INFINITY), + ("+inf", f64::INFINITY), + ("-inf", f64::NEG_INFINITY), + // ("1e+400", std::f64::INFINITY), + ]; + for &(input, expected) in &cases { + parsed_float_eq!(input, expected); + } + } +} diff --git a/crates/toml_edit/src/parser/strings.rs b/crates/toml_edit/src/parser/strings.rs index cfa69887..3b078976 100644 --- a/crates/toml_edit/src/parser/strings.rs +++ b/crates/toml_edit/src/parser/strings.rs @@ -294,3 +294,114 @@ parse!(mll_quotes() -> &'a str, { unsafe { from_utf8_unchecked(b, "`bytes` out npn-ASCII") } }) }); + +#[cfg(test)] +mod test { + use super::*; + + use crate::parser::*; + use combine::stream::position::Stream; + + #[test] + fn basic_string() { + let input = + r#""I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF. \U0002070E""#; + let parsed = strings::string().easy_parse(Stream::new(input.as_bytes())); + parsed_eq!( + parsed, + "I\'m a string. \"You can quote me\". Name\tJosé\nLocation\tSF. \u{2070E}" + ); + } + + #[test] + fn ml_basic_string() { + let cases = [ + ( + r#"""" +Roses are red +Violets are blue""""#, + r#"Roses are red +Violets are blue"#, + ), + (r#"""" \""" """"#, " \"\"\" "), + (r#"""" \\""""#, " \\"), + ]; + + for &(input, expected) in &cases { + dbg!(input); + let parsed = strings::string().easy_parse(Stream::new(input.as_bytes())); + parsed_eq!(parsed, expected); + } + + let invalid_cases = [r#"""" """#, r#"""" \""""#]; + + for input in &invalid_cases { + let parsed = strings::ml_basic_string().easy_parse(Stream::new(input.as_bytes())); + assert!(parsed.is_err()); + } + } + + #[test] + fn ml_basic_string_escape_ws() { + let inputs = [ + r#"""" +The quick brown \ + + + fox jumps over \ + the lazy dog.""""#, + r#""""\ + The quick brown \ + fox jumps over \ + the lazy dog.\ + """"#, + ]; + for input in &inputs { + dbg!(input); + let parsed = strings::string().easy_parse(Stream::new(input.as_bytes())); + parsed_eq!(parsed, "The quick brown fox jumps over the lazy dog."); + } + let empties = [ + r#""""\ + """"#, + r#"""" +\ + \ +""""#, + ]; + for empty in &empties { + let parsed = strings::string().easy_parse(Stream::new(empty.as_bytes())); + parsed_eq!(parsed, ""); + } + } + + #[test] + fn literal_string() { + let inputs = [ + r#"'C:\Users\nodejs\templates'"#, + r#"'\\ServerX\admin$\system32\'"#, + r#"'Tom "Dubs" Preston-Werner'"#, + r#"'<\i\c*\s*>'"#, + ]; + + for input in &inputs { + let parsed = strings::string().easy_parse(Stream::new(input.as_bytes())); + parsed_eq!(parsed, &input[1..input.len() - 1]); + } + } + + #[test] + fn ml_literal_string() { + let input = r#"'''I [dw]on't need \d{2} apples'''"#; + let parsed = strings::string().easy_parse(Stream::new(input.as_bytes())); + parsed_eq!(parsed, &input[3..input.len() - 3]); + let input = r#"''' +The first newline is +trimmed in raw strings. + All other whitespace + is preserved. +'''"#; + let parsed = strings::string().easy_parse(Stream::new(input.as_bytes())); + parsed_eq!(parsed, &input[4..input.len() - 3]); + } +} diff --git a/crates/toml_edit/src/parser/trivia.rs b/crates/toml_edit/src/parser/trivia.rs index f50df7c1..93b001db 100644 --- a/crates/toml_edit/src/parser/trivia.rs +++ b/crates/toml_edit/src/parser/trivia.rs @@ -122,3 +122,43 @@ parse!(line_trailing() -> &'a [u8], { optional(comment()), )).skip(line_ending()) }); + +#[cfg(test)] +mod test { + use super::*; + + use crate::parser::*; + use combine::stream::position::Stream; + use snapbox::assert_eq; + + #[test] + fn trivia() { + let inputs = [ + "", + r#" "#, + r#" +"#, + r#" +# comment + +# comment2 + + +"#, + r#" + "#, + r#"# comment +# comment2 + + + "#, + ]; + for input in inputs { + let parsed = trivia::ws_comment_newline().easy_parse(Stream::new(input.as_bytes())); + assert!(parsed.is_ok()); + let (t, rest) = parsed.unwrap(); + assert!(rest.input.is_empty()); + assert_eq(t, input.as_bytes()); + } + } +} diff --git a/crates/toml_edit/src/parser/value.rs b/crates/toml_edit/src/parser/value.rs index 9dadbf5e..d55713dd 100644 --- a/crates/toml_edit/src/parser/value.rs +++ b/crates/toml_edit/src/parser/value.rs @@ -85,3 +85,30 @@ fn apply_raw(mut val: Value, raw: &[u8]) -> Result { val.decorate("", ""); Ok(val) } + +#[cfg(test)] +mod test { + #[test] + fn values() { + let inputs = [ + "1979-05-27T00:32:00.999999", + "-239", + "1e200", + "9_224_617.445_991_228_313", + r#"'''I [dw]on't need \d{2} apples'''"#, + r#"''' +The first newline is +trimmed in raw strings. + All other whitespace + is preserved. +'''"#, + r#""Jos\u00E9\n""#, + r#""\\\"\b/\f\n\r\t\u00E9\U000A0000""#, + r#"{ hello = "world", a = 1}"#, + r#"[ { x = 1, a = "2" }, {a = "a",b = "b", c = "c"} ]"#, + ]; + for input in inputs { + parsed_value_eq!(input); + } + } +}