From 3ad6e71f53a87215fb5286bcf87de15524699561 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 12 Nov 2017 17:26:09 -0800 Subject: Move tests into their own crate --- test-suite/tests/parser.rs | 495 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 495 insertions(+) create mode 100644 test-suite/tests/parser.rs (limited to 'test-suite/tests/parser.rs') diff --git a/test-suite/tests/parser.rs b/test-suite/tests/parser.rs new file mode 100644 index 0000000..2282416 --- /dev/null +++ b/test-suite/tests/parser.rs @@ -0,0 +1,495 @@ +extern crate toml; + +use toml::Value; + +macro_rules! bad { + ($s:expr, $msg:expr) => ({ + match $s.parse::() { + Ok(s) => panic!("successfully parsed as {}", s), + Err(e) => { + let e = e.to_string(); + assert!(e.contains($msg), "error: {}", e); + } + } + }) +} + +#[test] +fn crlf() { + "\ +[project]\r\n\ +\r\n\ +name = \"splay\"\r\n\ +version = \"0.1.0\"\r\n\ +authors = [\"alex@crichton.co\"]\r\n\ +\r\n\ +[[lib]]\r\n\ +\r\n\ +path = \"lib.rs\"\r\n\ +name = \"splay\"\r\n\ +description = \"\"\"\ +A Rust implementation of a TAR file reader and writer. This library does not\r\n\ +currently handle compression, but it is abstract over all I/O readers and\r\n\ +writers. Additionally, great lengths are taken to ensure that the entire\r\n\ +contents are never required to be entirely resident in memory all at once.\r\n\ +\"\"\"\ +".parse::().unwrap(); +} + +#[test] +fn fun_with_strings() { + let table = r#" +bar = "\U00000000" +key1 = "One\nTwo" +key2 = """One\nTwo""" +key3 = """ +One +Two""" + +key4 = "The quick brown fox jumps over the lazy dog." +key5 = """ +The quick brown \ + + +fox jumps over \ +the lazy dog.""" +key6 = """\ + The quick brown \ + fox jumps over \ + the lazy dog.\ + """ +# What you see is what you get. +winpath = 'C:\Users\nodejs\templates' +winpath2 = '\\ServerX\admin$\system32\' +quoted = 'Tom "Dubs" Preston-Werner' +regex = '<\i\c*\s*>' + +regex2 = '''I [dw]on't need \d{2} apples''' +lines = ''' +The first newline is +trimmed in raw strings. +All other whitespace +is preserved. +''' +"#.parse::().unwrap(); + assert_eq!(table["bar"].as_str(), Some("\0")); + assert_eq!(table["key1"].as_str(), Some("One\nTwo")); + assert_eq!(table["key2"].as_str(), Some("One\nTwo")); + assert_eq!(table["key3"].as_str(), Some("One\nTwo")); + + let msg = "The quick brown fox jumps over the lazy dog."; + assert_eq!(table["key4"].as_str(), Some(msg)); + assert_eq!(table["key5"].as_str(), Some(msg)); + assert_eq!(table["key6"].as_str(), Some(msg)); + + assert_eq!(table["winpath"].as_str(), Some(r"C:\Users\nodejs\templates")); + assert_eq!(table["winpath2"].as_str(), Some(r"\\ServerX\admin$\system32\")); + assert_eq!(table["quoted"].as_str(), Some(r#"Tom "Dubs" Preston-Werner"#)); + assert_eq!(table["regex"].as_str(), Some(r"<\i\c*\s*>")); + assert_eq!(table["regex2"].as_str(), Some(r"I [dw]on't need \d{2} apples")); + assert_eq!(table["lines"].as_str(), + Some("The first newline is\n\ + trimmed in raw strings.\n\ + All other whitespace\n\ + is preserved.\n")); +} + +#[test] +fn tables_in_arrays() { + let table = r#" +[[foo]] +#… +[foo.bar] +#… + +[[foo]] # ... +#… +[foo.bar] +#... +"#.parse::().unwrap(); + table["foo"][0]["bar"].as_table().unwrap(); + table["foo"][1]["bar"].as_table().unwrap(); +} + +#[test] +fn empty_table() { + let table = r#" +[foo]"#.parse::().unwrap(); + table["foo"].as_table().unwrap(); +} + +#[test] +fn fruit() { + let table = r#" +[[fruit]] +name = "apple" + +[fruit.physical] +color = "red" +shape = "round" + +[[fruit.variety]] +name = "red delicious" + +[[fruit.variety]] +name = "granny smith" + +[[fruit]] +name = "banana" + +[[fruit.variety]] +name = "plantain" +"#.parse::().unwrap(); + assert_eq!(table["fruit"][0]["name"].as_str(), Some("apple")); + assert_eq!(table["fruit"][0]["physical"]["color"].as_str(), Some("red")); + assert_eq!(table["fruit"][0]["physical"]["shape"].as_str(), Some("round")); + assert_eq!(table["fruit"][0]["variety"][0]["name"].as_str(), Some("red delicious")); + assert_eq!(table["fruit"][0]["variety"][1]["name"].as_str(), Some("granny smith")); + assert_eq!(table["fruit"][1]["name"].as_str(), Some("banana")); + assert_eq!(table["fruit"][1]["variety"][0]["name"].as_str(), Some("plantain")); +} + +#[test] +fn stray_cr() { + "\r".parse::().unwrap_err(); + "a = [ \r ]".parse::().unwrap_err(); + "a = \"\"\"\r\"\"\"".parse::().unwrap_err(); + "a = \"\"\"\\ \r \"\"\"".parse::().unwrap_err(); + "a = '''\r'''".parse::().unwrap_err(); + "a = '\r'".parse::().unwrap_err(); + "a = \"\r\"".parse::().unwrap_err(); +} + +#[test] +fn blank_literal_string() { + let table = "foo = ''".parse::().unwrap(); + assert_eq!(table["foo"].as_str(), Some("")); +} + +#[test] +fn many_blank() { + let table = "foo = \"\"\"\n\n\n\"\"\"".parse::().unwrap(); + assert_eq!(table["foo"].as_str(), Some("\n\n")); +} + +#[test] +fn literal_eats_crlf() { + let table = " + foo = \"\"\"\\\r\n\"\"\" + bar = \"\"\"\\\r\n \r\n \r\n a\"\"\" + ".parse::().unwrap(); + assert_eq!(table["foo"].as_str(), Some("")); + assert_eq!(table["bar"].as_str(), Some("a")); +} + +#[test] +fn string_no_newline() { + "a = \"\n\"".parse::().unwrap_err(); + "a = '\n'".parse::().unwrap_err(); +} + +#[test] +fn bad_leading_zeros() { + "a = 00".parse::().unwrap_err(); + "a = -00".parse::().unwrap_err(); + "a = +00".parse::().unwrap_err(); + "a = 00.0".parse::().unwrap_err(); + "a = -00.0".parse::().unwrap_err(); + "a = +00.0".parse::().unwrap_err(); + "a = 9223372036854775808".parse::().unwrap_err(); + "a = -9223372036854775809".parse::().unwrap_err(); +} + +#[test] +fn bad_floats() { + "a = 0.".parse::().unwrap_err(); + "a = 0.e".parse::().unwrap_err(); + "a = 0.E".parse::().unwrap_err(); + "a = 0.0E".parse::().unwrap_err(); + "a = 0.0e".parse::().unwrap_err(); + "a = 0.0e-".parse::().unwrap_err(); + "a = 0.0e+".parse::().unwrap_err(); + "a = 0.0e+00".parse::().unwrap_err(); +} + +#[test] +fn floats() { + macro_rules! t { + ($actual:expr, $expected:expr) => ({ + let f = format!("foo = {}", $actual); + println!("{}", f); + let a = f.parse::().unwrap(); + assert_eq!(a["foo"].as_float().unwrap(), $expected); + }) + } + + t!("1.0", 1.0); + t!("1.0e0", 1.0); + t!("1.0e+0", 1.0); + t!("1.0e-0", 1.0); + t!("1.001e-0", 1.001); + t!("2e10", 2e10); + t!("2e+10", 2e10); + t!("2e-10", 2e-10); + t!("2_0.0", 20.0); + t!("2_0.0_0e1_0", 20.0e10); + t!("2_0.1_0e1_0", 20.1e10); +} + +#[test] +fn bare_key_names() { + let a = " + foo = 3 + foo_3 = 3 + foo_-2--3--r23f--4-f2-4 = 3 + _ = 3 + - = 3 + 8 = 8 + \"a\" = 3 + \"!\" = 3 + \"a^b\" = 3 + \"\\\"\" = 3 + \"character encoding\" = \"value\" + 'ʎǝʞ' = \"value\" + ".parse::().unwrap(); + &a["foo"]; + &a["-"]; + &a["_"]; + &a["8"]; + &a["foo_3"]; + &a["foo_-2--3--r23f--4-f2-4"]; + &a["a"]; + &a["!"]; + &a["\""]; + &a["character encoding"]; + &a["ʎǝʞ"]; +} + +#[test] +fn bad_keys() { + "key\n=3".parse::().unwrap_err(); + "key=\n3".parse::().unwrap_err(); + "key|=3".parse::().unwrap_err(); + "\"\"=3".parse::().unwrap_err(); + "=3".parse::().unwrap_err(); + "\"\"|=3".parse::().unwrap_err(); + "\"\n\"|=3".parse::().unwrap_err(); + "\"\r\"|=3".parse::().unwrap_err(); +} + +#[test] +fn bad_table_names() { + "[]".parse::().unwrap_err(); + "[.]".parse::().unwrap_err(); + "[\"\".\"\"]".parse::().unwrap_err(); + "[a.]".parse::().unwrap_err(); + "[\"\"]".parse::().unwrap_err(); + "[!]".parse::().unwrap_err(); + "[\"\n\"]".parse::().unwrap_err(); + "[a.b]\n[a.\"b\"]".parse::().unwrap_err(); + "[']".parse::().unwrap_err(); + "[''']".parse::().unwrap_err(); + "['''''']".parse::().unwrap_err(); + "['\n']".parse::().unwrap_err(); + "['\r\n']".parse::().unwrap_err(); +} + +#[test] +fn table_names() { + let a = " + [a.\"b\"] + [\"f f\"] + [\"f.f\"] + [\"\\\"\"] + ['a.a'] + ['\"\"'] + ".parse::().unwrap(); + println!("{:?}", a); + &a["a"]["b"]; + &a["f f"]; + &a["f.f"]; + &a["\""]; + &a["\"\""]; +} + +#[test] +fn invalid_bare_numeral() { + "4".parse::().unwrap_err(); +} + +#[test] +fn inline_tables() { + "a = {}".parse::().unwrap(); + "a = {b=1}".parse::().unwrap(); + "a = { b = 1 }".parse::().unwrap(); + "a = {a=1,b=2}".parse::().unwrap(); + "a = {a=1,b=2,c={}}".parse::().unwrap(); + "a = {a=1,}".parse::().unwrap_err(); + "a = {,}".parse::().unwrap_err(); + "a = {a=1,a=1}".parse::().unwrap_err(); + "a = {\n}".parse::().unwrap_err(); + "a = {".parse::().unwrap_err(); + "a = {a=[\n]}".parse::().unwrap(); + "a = {\"a\"=[\n]}".parse::().unwrap(); + "a = [\n{},\n{},\n]".parse::().unwrap(); +} + +#[test] +fn number_underscores() { + macro_rules! t { + ($actual:expr, $expected:expr) => ({ + let f = format!("foo = {}", $actual); + let table = f.parse::().unwrap(); + assert_eq!(table["foo"].as_integer().unwrap(), $expected); + }) + } + + t!("1_0", 10); + t!("1_0_0", 100); + t!("1_000", 1000); + t!("+1_000", 1000); + t!("-1_000", -1000); +} + +#[test] +fn bad_underscores() { + bad!("foo = 0_", "invalid number"); + bad!("foo = 0__0", "invalid number"); + bad!("foo = __0", "invalid number"); + bad!("foo = 1_0_", "invalid number"); +} + +#[test] +fn bad_unicode_codepoint() { + bad!("foo = \"\\uD800\"", "invalid escape value"); +} + +#[test] +fn bad_strings() { + bad!("foo = \"\\uxx\"", "invalid hex escape"); + bad!("foo = \"\\u\"", "invalid hex escape"); + bad!("foo = \"\\", "unterminated"); + bad!("foo = '", "unterminated"); +} + +#[test] +fn empty_string() { + assert_eq!("foo = \"\"".parse::() + .unwrap()["foo"] + .as_str() + .unwrap(), + ""); +} + +#[test] +fn booleans() { + let table = "foo = true".parse::().unwrap(); + assert_eq!(table["foo"].as_bool(), Some(true)); + + let table = "foo = false".parse::().unwrap(); + assert_eq!(table["foo"].as_bool(), Some(false)); + + assert!("foo = true2".parse::().is_err()); + assert!("foo = false2".parse::().is_err()); + assert!("foo = t1".parse::().is_err()); + assert!("foo = f2".parse::().is_err()); +} + +#[test] +fn bad_nesting() { + bad!(" + a = [2] + [[a]] + b = 5 + ", "duplicate key: `a`"); + bad!(" + a = 1 + [a.b] + ", "duplicate key: `a`"); + bad!(" + a = [] + [a.b] + ", "duplicate key: `a`"); + bad!(" + a = [] + [[a.b]] + ", "duplicate key: `a`"); + bad!(" + [a] + b = { c = 2, d = {} } + [a.b] + c = 2 + ", "duplicate key: `b`"); +} + +#[test] +fn bad_table_redefine() { + bad!(" + [a] + foo=\"bar\" + [a.b] + foo=\"bar\" + [a] + ", "redefinition of table `a`"); + bad!(" + [a] + foo=\"bar\" + b = { foo = \"bar\" } + [a] + ", "redefinition of table `a`"); + bad!(" + [a] + b = {} + [a.b] + ", "duplicate key: `b`"); + + bad!(" + [a] + b = {} + [a] + ", "redefinition of table `a`"); +} + +#[test] +fn datetimes() { + macro_rules! t { + ($actual:expr) => ({ + let f = format!("foo = {}", $actual); + let toml = f.parse::().expect(&format!("failed: {}", f)); + assert_eq!(toml["foo"].as_datetime().unwrap().to_string(), $actual); + }) + } + + t!("2016-09-09T09:09:09Z"); + t!("2016-09-09T09:09:09.1Z"); + t!("2016-09-09T09:09:09.2+10:00"); + t!("2016-09-09T09:09:09.123456789-02:00"); + bad!("foo = 2016-09-09T09:09:09.Z", "failed to parse date"); + bad!("foo = 2016-9-09T09:09:09Z", "failed to parse date"); + bad!("foo = 2016-09-09T09:09:09+2:00", "failed to parse date"); + bad!("foo = 2016-09-09T09:09:09-2:00", "failed to parse date"); + bad!("foo = 2016-09-09T09:09:09Z-2:00", "failed to parse date"); +} + +#[test] +fn require_newline_after_value() { + bad!("0=0r=false", "invalid number at line 1"); + bad!(r#" +0=""o=""m=""r=""00="0"q="""0"""e="""0""" +"#, "expected newline"); + bad!(r#" +[[0000l0]] +0="0"[[0000l0]] +0="0"[[0000l0]] +0="0"l="0" +"#, "expected newline"); + bad!(r#" +0=[0]00=[0,0,0]t=["0","0","0"]s=[1000-00-00T00:00:00Z,2000-00-00T00:00:00Z] +"#, "expected newline"); + bad!(r#" +0=0r0=0r=false +"#, "invalid number at line 2"); + bad!(r#" +0=0r0=0r=falsefal=false +"#, "invalid number at line 2"); +} -- cgit v1.2.3