From 59aa7214f0c76f95f192f5b4641342ebd740f567 Mon Sep 17 00:00:00 2001 From: Andres Suarez Date: Tue, 30 Jul 2019 13:14:17 -0400 Subject: Add tests covering custom deserializers --- test-suite/tests/de-errors.rs | 296 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 296 insertions(+) create mode 100644 test-suite/tests/de-errors.rs (limited to 'test-suite/tests') diff --git a/test-suite/tests/de-errors.rs b/test-suite/tests/de-errors.rs new file mode 100644 index 0000000..d2e8f64 --- /dev/null +++ b/test-suite/tests/de-errors.rs @@ -0,0 +1,296 @@ +extern crate serde; +extern crate toml; + +use serde::{de, Deserialize}; +use std::fmt; + +macro_rules! bad { + ($toml:expr, $ty:ty, $msg:expr) => { + match toml::from_str::<$ty>($toml) { + Ok(s) => panic!("parsed to: {:#?}", s), + Err(e) => assert_eq!(e.to_string(), $msg), + } + }; +} + +#[derive(Debug, Deserialize, PartialEq)] +struct Parent { + p_a: T, + p_b: Vec>, +} + +#[derive(Debug, Deserialize, PartialEq)] +#[serde(deny_unknown_fields)] +struct Child { + c_a: T, + c_b: T, +} + +#[derive(Debug, PartialEq)] +enum CasedString { + Lowercase(String), + Uppercase(String), +} + +impl<'de> de::Deserialize<'de> for CasedString { + fn deserialize(deserializer: D) -> Result + where + D: de::Deserializer<'de>, + { + struct CasedStringVisitor; + + impl<'de> de::Visitor<'de> for CasedStringVisitor { + type Value = CasedString; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a string") + } + + fn visit_str(self, s: &str) -> Result + where + E: de::Error, + { + if s.is_empty() { + Err(de::Error::invalid_length(0, &"a non-empty string")) + } else if s.chars().all(|x| x.is_ascii_lowercase()) { + Ok(CasedString::Lowercase(s.to_string())) + } else if s.chars().all(|x| x.is_ascii_uppercase()) { + Ok(CasedString::Uppercase(s.to_string())) + } else { + Err(de::Error::invalid_value( + de::Unexpected::Str(s), + &"all lowercase or all uppercase", + )) + } + } + } + + deserializer.deserialize_any(CasedStringVisitor) + } +} + +#[test] +fn custom_errors() { + toml::from_str::>( + " + p_a = 'a' + p_b = [{c_a = 'a', c_b = 'c'}] + ", + ) + .unwrap(); + + // Custom error at p_b value. + bad!( + " + p_a = '' + ", + Parent, + "invalid length 0, expected a non-empty string for key `p_a`" + ); + + // Missing field in table. + bad!( + " + p_a = 'a' + ", + Parent, + "missing field `p_b`" + ); + + // Invalid type in p_b. + bad!( + " + p_a = 'a' + p_b = 1 + ", + Parent, + "invalid type: integer `1`, expected a sequence for key `p_b`" + ); + + // Sub-table in Vec is missing a field. + bad!( + " + p_a = 'a' + p_b = [ + {c_a = 'a'} + ] + ", + Parent, + "missing field `c_b` for key `p_b`" + ); + + // Sub-table in Vec has a field with a bad value. + bad!( + " + p_a = 'a' + p_b = [ + {c_a = 'a', c_b = '*'} + ] + ", + Parent, + "invalid value: string \"*\", expected all lowercase or all uppercase for key `p_b`" + ); + + // Sub-table in Vec is missing a field. + bad!( + " + p_a = 'a' + p_b = [ + {c_a = 'a', c_b = 'b'}, + {c_a = 'aa'} + ] + ", + Parent, + "missing field `c_b` for key `p_b`" + ); + + // Sub-table in the middle of a Vec is missing a field. + bad!( + " + p_a = 'a' + p_b = [ + {c_a = 'a', c_b = 'b'}, + {c_a = 'aa'}, + {c_a = 'aaa', c_b = 'bbb'}, + ] + ", + Parent, + "missing field `c_b` for key `p_b`" + ); + + // Sub-table in the middle of a Vec has a field with a bad value. + bad!( + " + p_a = 'a' + p_b = [ + {c_a = 'a', c_b = 'b'}, + {c_a = 'aa', c_b = 1}, + {c_a = 'aaa', c_b = 'bbb'}, + ] + ", + Parent, + "invalid type: integer `1`, expected a string for key `p_b`" + ); + + // Sub-table in the middle of a Vec has an extra field. + bad!( + " + p_a = 'a' + p_b = [ + {c_a = 'a', c_b = 'b'}, + {c_a = 'aa', c_b = 'bb', c_d = 'd'}, + {c_a = 'aaa', c_b = 'bbb'}, + ] + ", + Parent, + "unknown field `c_d`, expected `c_a` or `c_b` for key `p_b`" // FIX ME + ); + + // Sub-table in the middle of a Vec is missing a field. + bad!( + " + p_a = 'a' + [[p_b]] + c_a = 'a' + c_b = 'b' + [[p_b]] + c_a = 'aa' + [[p_b]] + c_a = 'aaa' + c_b = 'bbb' + ", + Parent, + "missing field `c_b` for key `p_b`" + ); + + // Sub-table in the middle of a Vec has a field with a bad value. + bad!( + " + p_a = 'a' + [[p_b]] + c_a = 'a' + c_b = 'b' + [[p_b]] + c_a = 'aa' + c_b = '*' + [[p_b]] + c_a = 'aaa' + c_b = 'bbb' + ", + Parent, + "invalid value: string \"*\", expected all lowercase or all uppercase for key `p_b.c_b`" + ); + + // Sub-table in the middle of a Vec has an extra field. + bad!( + " + p_a = 'a' + [[p_b]] + c_a = 'a' + c_b = 'b' + [[p_b]] + c_a = 'aa' + c_d = 'dd' # unknown field + [[p_b]] + c_a = 'aaa' + c_b = 'bbb' + ", + Parent, + "unknown field `c_d`, expected `c_a` or `c_b` for key `p_b`" + ); +} + +#[test] +fn serde_derive_deserialize_errors() { + bad!( + " + p_a = '' + ", + Parent, + "missing field `p_b`" + ); + + bad!( + " + p_a = '' + p_b = [ + {c_a = ''} + ] + ", + Parent, + "missing field `c_b` for key `p_b`" + ); + + bad!( + " + p_a = '' + p_b = [ + {c_a = '', c_b = 1} + ] + ", + Parent, + "invalid type: integer `1`, expected a string for key `p_b`" + ); + + bad!( + " + p_a = '' + p_b = [ + {c_a = '', c_b = '', c_d = ''}, + ] + ", + Parent, + "unknown field `c_d`, expected `c_a` or `c_b` for key `p_b`" // FIX ME + ); + + bad!( + " + p_a = 'a' + p_b = [ + {c_a = '', c_b = 1, c_d = ''}, + ] + ", + Parent, + "invalid type: integer `1`, expected a string for key `p_b`" + ); +} -- cgit v1.2.3 From 144e1d0f90f3e83e7e3cf0764869c2bbef687397 Mon Sep 17 00:00:00 2001 From: Andres Suarez Date: Tue, 30 Jul 2019 17:20:18 +0000 Subject: Add line and column to all Errors --- test-suite/tests/datetime.rs | 56 +++++++++++++++++++++++------------ test-suite/tests/de-errors.rs | 63 +++++++++++++++++++++++++++++----------- test-suite/tests/invalid-misc.rs | 7 +++-- test-suite/tests/invalid.rs | 10 +++---- test-suite/tests/parser.rs | 37 ++++++++++++++--------- test-suite/tests/serde.rs | 4 +-- 6 files changed, 118 insertions(+), 59 deletions(-) (limited to 'test-suite/tests') diff --git a/test-suite/tests/datetime.rs b/test-suite/tests/datetime.rs index 4855f54..74b5939 100644 --- a/test-suite/tests/datetime.rs +++ b/test-suite/tests/datetime.rs @@ -42,75 +42,93 @@ fn times() { #[test] fn bad_times() { - bad!("foo = 199-09-09", "failed to parse datetime for key `foo`"); - bad!("foo = 199709-09", "failed to parse datetime for key `foo`"); - bad!("foo = 1997-9-09", "failed to parse datetime for key `foo`"); - bad!("foo = 1997-09-9", "failed to parse datetime for key `foo`"); + bad!( + "foo = 199-09-09", + "failed to parse datetime for key `foo` at line 1 column 7" + ); + bad!( + "foo = 199709-09", + "failed to parse datetime for key `foo` at line 1 column 7" + ); + bad!( + "foo = 1997-9-09", + "failed to parse datetime for key `foo` at line 1 column 7" + ); + bad!( + "foo = 1997-09-9", + "failed to parse datetime for key `foo` at line 1 column 7" + ); bad!( "foo = 1997-09-0909:09:09", - "failed to parse datetime for key `foo`" + "failed to parse datetime for key `foo` at line 1 column 7" ); bad!( "foo = 1997-09-09T09:09:09.", "invalid date at line 1 column 7" ); - bad!("foo = T", "failed to parse datetime for key `foo`"); + bad!( + "foo = T", + "failed to parse datetime for key `foo` at line 1 column 7" + ); bad!( "foo = T.", "expected newline, found a period at line 1 column 8" ); - bad!("foo = TZ", "failed to parse datetime for key `foo`"); + bad!( + "foo = TZ", + "failed to parse datetime for key `foo` at line 1 column 7" + ); bad!( "foo = 1997-09-09T09:09:09.09+", "invalid date at line 1 column 7" ); bad!( "foo = 1997-09-09T09:09:09.09+09", - "failed to parse datetime for key `foo`" + "failed to parse datetime for key `foo` at line 1 column 7" ); bad!( "foo = 1997-09-09T09:09:09.09+09:9", - "failed to parse datetime for key `foo`" + "failed to parse datetime for key `foo` at line 1 column 7" ); bad!( "foo = 1997-09-09T09:09:09.09+0909", - "failed to parse datetime for key `foo`" + "failed to parse datetime for key `foo` at line 1 column 7" ); bad!( "foo = 1997-09-09T09:09:09.09-", - "failed to parse datetime for key `foo`" + "failed to parse datetime for key `foo` at line 1 column 7" ); bad!( "foo = 1997-09-09T09:09:09.09-09", - "failed to parse datetime for key `foo`" + "failed to parse datetime for key `foo` at line 1 column 7" ); bad!( "foo = 1997-09-09T09:09:09.09-09:9", - "failed to parse datetime for key `foo`" + "failed to parse datetime for key `foo` at line 1 column 7" ); bad!( "foo = 1997-09-09T09:09:09.09-0909", - "failed to parse datetime for key `foo`" + "failed to parse datetime for key `foo` at line 1 column 7" ); bad!( "foo = 1997-00-09T09:09:09.09Z", - "failed to parse datetime for key `foo`" + "failed to parse datetime for key `foo` at line 1 column 7" ); bad!( "foo = 1997-09-00T09:09:09.09Z", - "failed to parse datetime for key `foo`" + "failed to parse datetime for key `foo` at line 1 column 7" ); bad!( "foo = 1997-09-09T30:09:09.09Z", - "failed to parse datetime for key `foo`" + "failed to parse datetime for key `foo` at line 1 column 7" ); bad!( "foo = 1997-09-09T12:69:09.09Z", - "failed to parse datetime for key `foo`" + "failed to parse datetime for key `foo` at line 1 column 7" ); bad!( "foo = 1997-09-09T12:09:69.09Z", - "failed to parse datetime for key `foo`" + "failed to parse datetime for key `foo` at line 1 column 7" ); } diff --git a/test-suite/tests/de-errors.rs b/test-suite/tests/de-errors.rs index d2e8f64..7cceb7b 100644 --- a/test-suite/tests/de-errors.rs +++ b/test-suite/tests/de-errors.rs @@ -83,18 +83,20 @@ fn custom_errors() { bad!( " p_a = '' + # ^ ", Parent, - "invalid length 0, expected a non-empty string for key `p_a`" + "invalid length 0, expected a non-empty string for key `p_a` at line 2 column 19" ); // Missing field in table. bad!( " p_a = 'a' + # ^ ", Parent, - "missing field `p_b`" + "missing field `p_b` at line 1 column 1" ); // Invalid type in p_b. @@ -102,9 +104,10 @@ fn custom_errors() { " p_a = 'a' p_b = 1 + # ^ ", Parent, - "invalid type: integer `1`, expected a sequence for key `p_b`" + "invalid type: integer `1`, expected a sequence for key `p_b` at line 3 column 19" ); // Sub-table in Vec is missing a field. @@ -113,10 +116,11 @@ fn custom_errors() { p_a = 'a' p_b = [ {c_a = 'a'} + # ^ ] ", Parent, - "missing field `c_b` for key `p_b`" + "missing field `c_b` for key `p_b` at line 4 column 17" ); // Sub-table in Vec has a field with a bad value. @@ -125,10 +129,11 @@ fn custom_errors() { p_a = 'a' p_b = [ {c_a = 'a', c_b = '*'} + # ^ ] ", Parent, - "invalid value: string \"*\", expected all lowercase or all uppercase for key `p_b`" + "invalid value: string \"*\", expected all lowercase or all uppercase for key `p_b` at line 4 column 35" ); // Sub-table in Vec is missing a field. @@ -138,10 +143,11 @@ fn custom_errors() { p_b = [ {c_a = 'a', c_b = 'b'}, {c_a = 'aa'} + # ^ ] ", Parent, - "missing field `c_b` for key `p_b`" + "missing field `c_b` for key `p_b` at line 5 column 17" ); // Sub-table in the middle of a Vec is missing a field. @@ -151,11 +157,12 @@ fn custom_errors() { p_b = [ {c_a = 'a', c_b = 'b'}, {c_a = 'aa'}, + # ^ {c_a = 'aaa', c_b = 'bbb'}, ] ", Parent, - "missing field `c_b` for key `p_b`" + "missing field `c_b` for key `p_b` at line 5 column 17" ); // Sub-table in the middle of a Vec has a field with a bad value. @@ -165,28 +172,33 @@ fn custom_errors() { p_b = [ {c_a = 'a', c_b = 'b'}, {c_a = 'aa', c_b = 1}, + # ^ {c_a = 'aaa', c_b = 'bbb'}, ] ", Parent, - "invalid type: integer `1`, expected a string for key `p_b`" + "invalid type: integer `1`, expected a string for key `p_b` at line 5 column 36" ); // Sub-table in the middle of a Vec has an extra field. + // FIXME: This location could be better. bad!( " p_a = 'a' p_b = [ {c_a = 'a', c_b = 'b'}, {c_a = 'aa', c_b = 'bb', c_d = 'd'}, + # ^ {c_a = 'aaa', c_b = 'bbb'}, + {c_a = 'aaaa', c_b = 'bbbb'}, ] ", Parent, - "unknown field `c_d`, expected `c_a` or `c_b` for key `p_b`" // FIX ME + "unknown field `c_d`, expected `c_a` or `c_b` for key `p_b` at line 5 column 17" ); // Sub-table in the middle of a Vec is missing a field. + // FIXME: This location is pretty off. bad!( " p_a = 'a' @@ -195,12 +207,17 @@ fn custom_errors() { c_b = 'b' [[p_b]] c_a = 'aa' + # c_b = 'bb' # <- missing field [[p_b]] c_a = 'aaa' c_b = 'bbb' + [[p_b]] + # ^ + c_a = 'aaaa' + c_b = 'bbbb' ", Parent, - "missing field `c_b` for key `p_b`" + "missing field `c_b` for key `p_b` at line 12 column 13" ); // Sub-table in the middle of a Vec has a field with a bad value. @@ -213,15 +230,17 @@ fn custom_errors() { [[p_b]] c_a = 'aa' c_b = '*' + # ^ [[p_b]] c_a = 'aaa' c_b = 'bbb' ", Parent, - "invalid value: string \"*\", expected all lowercase or all uppercase for key `p_b.c_b`" + "invalid value: string \"*\", expected all lowercase or all uppercase for key `p_b.c_b` at line 8 column 19" ); // Sub-table in the middle of a Vec has an extra field. + // FIXME: This location is pretty off. bad!( " p_a = 'a' @@ -234,9 +253,13 @@ fn custom_errors() { [[p_b]] c_a = 'aaa' c_b = 'bbb' + [[p_b]] + # ^ + c_a = 'aaaa' + c_b = 'bbbb' ", Parent, - "unknown field `c_d`, expected `c_a` or `c_b` for key `p_b`" + "unknown field `c_d`, expected `c_a` or `c_b` for key `p_b` at line 12 column 13" ); } @@ -245,9 +268,10 @@ fn serde_derive_deserialize_errors() { bad!( " p_a = '' + # ^ ", Parent, - "missing field `p_b`" + "missing field `p_b` at line 1 column 1" ); bad!( @@ -255,10 +279,11 @@ fn serde_derive_deserialize_errors() { p_a = '' p_b = [ {c_a = ''} + # ^ ] ", Parent, - "missing field `c_b` for key `p_b`" + "missing field `c_b` for key `p_b` at line 4 column 17" ); bad!( @@ -266,21 +291,24 @@ fn serde_derive_deserialize_errors() { p_a = '' p_b = [ {c_a = '', c_b = 1} + # ^ ] ", Parent, - "invalid type: integer `1`, expected a string for key `p_b`" + "invalid type: integer `1`, expected a string for key `p_b` at line 4 column 34" ); + // FIXME: This location could be better. bad!( " p_a = '' p_b = [ {c_a = '', c_b = '', c_d = ''}, + # ^ ] ", Parent, - "unknown field `c_d`, expected `c_a` or `c_b` for key `p_b`" // FIX ME + "unknown field `c_d`, expected `c_a` or `c_b` for key `p_b` at line 4 column 17" ); bad!( @@ -288,9 +316,10 @@ fn serde_derive_deserialize_errors() { p_a = 'a' p_b = [ {c_a = '', c_b = 1, c_d = ''}, + # ^ ] ", Parent, - "invalid type: integer `1`, expected a string for key `p_b`" + "invalid type: integer `1`, expected a string for key `p_b` at line 4 column 34" ); } diff --git a/test-suite/tests/invalid-misc.rs b/test-suite/tests/invalid-misc.rs index f18012a..cea0801 100644 --- a/test-suite/tests/invalid-misc.rs +++ b/test-suite/tests/invalid-misc.rs @@ -27,14 +27,17 @@ fn bad() { ); bad!("a = -0x1", "invalid number at line 1 column 5"); - bad!("a = 0x-1", "failed to parse datetime for key `a`"); + bad!( + "a = 0x-1", + "failed to parse datetime for key `a` at line 1 column 5" + ); // Dotted keys. bad!( "a.b.c = 1 a.b = 2 ", - "duplicate key: `b` for key `a`" + "duplicate key: `b` for key `a` at line 1 column 9" ); bad!( "a = 1 diff --git a/test-suite/tests/invalid.rs b/test-suite/tests/invalid.rs index bfde2d4..3312629 100644 --- a/test-suite/tests/invalid.rs +++ b/test-suite/tests/invalid.rs @@ -32,7 +32,7 @@ test!( test!( datetime_malformed_no_leads, include_str!("invalid/datetime-malformed-no-leads.toml"), - "failed to parse datetime for key `no-leads`" + "failed to parse datetime for key `no-leads` at line 1 column 12" ); test!( datetime_malformed_no_secs, @@ -42,22 +42,22 @@ test!( test!( datetime_malformed_no_t, include_str!("invalid/datetime-malformed-no-t.toml"), - "failed to parse datetime for key `no-t`" + "failed to parse datetime for key `no-t` at line 1 column 8" ); test!( datetime_malformed_with_milli, include_str!("invalid/datetime-malformed-with-milli.toml"), - "failed to parse datetime for key `with-milli`" + "failed to parse datetime for key `with-milli` at line 1 column 14" ); test!( duplicate_key_table, include_str!("invalid/duplicate-key-table.toml"), - "duplicate key: `type` for key `fruit`" + "duplicate key: `type` for key `fruit` at line 4 column 1" ); test!( duplicate_keys, include_str!("invalid/duplicate-keys.toml"), - "duplicate key: `dupe`" + "duplicate key: `dupe` at line 1 column 1" ); test!( duplicate_table, diff --git a/test-suite/tests/parser.rs b/test-suite/tests/parser.rs index 1a684c5..012bd65 100644 --- a/test-suite/tests/parser.rs +++ b/test-suite/tests/parser.rs @@ -453,7 +453,10 @@ fn inline_tables() { "a = {,}", "expected a table key, found a comma at line 1 column 6" ); - bad!("a = {a=1,a=1}", "duplicate key: `a` for key `a`"); + bad!( + "a = {a=1,a=1}", + "duplicate key: `a` for key `a` at line 1 column 5" + ); bad!( "a = {\n}", "expected a table key, found a newline at line 1 column 6" @@ -533,9 +536,15 @@ fn booleans() { let table = "foo = false".parse::().unwrap(); assert_eq!(table["foo"].as_bool(), Some(false)); - bad!("foo = true2", "failed to parse datetime for key `foo`"); + bad!( + "foo = true2", + "failed to parse datetime for key `foo` at line 1 column 7" + ); bad!("foo = false2", "invalid number at line 1 column 7"); - bad!("foo = t1", "failed to parse datetime for key `foo`"); + bad!( + "foo = t1", + "failed to parse datetime for key `foo` at line 1 column 7" + ); bad!("foo = f2", "invalid number at line 1 column 7"); } @@ -547,28 +556,28 @@ fn bad_nesting() { [[a]] b = 5 ", - "duplicate key: `a`" + "duplicate key: `a` at line 3 column 9" ); bad!( " a = 1 [a.b] ", - "duplicate key: `a`" + "duplicate key: `a` at line 3 column 9" ); bad!( " a = [] [a.b] ", - "duplicate key: `a`" + "duplicate key: `a` at line 3 column 9" ); bad!( " a = [] [[a.b]] ", - "duplicate key: `a`" + "duplicate key: `a` at line 3 column 9" ); bad!( " @@ -577,7 +586,7 @@ fn bad_nesting() { [a.b] c = 2 ", - "duplicate key: `b` for key `a`" + "duplicate key: `b` for key `a` at line 4 column 9" ); } @@ -608,7 +617,7 @@ fn bad_table_redefine() { b = {} [a.b] ", - "duplicate key: `b` for key `a`" + "duplicate key: `b` for key `a` at line 4 column 9" ); bad!( @@ -637,23 +646,23 @@ fn datetimes() { t!("2016-09-09T09:09:09.123456789-02:00"); bad!( "foo = 2016-09-09T09:09:09.Z", - "failed to parse datetime for key `foo`" + "failed to parse datetime for key `foo` at line 1 column 7" ); bad!( "foo = 2016-9-09T09:09:09Z", - "failed to parse datetime for key `foo`" + "failed to parse datetime for key `foo` at line 1 column 7" ); bad!( "foo = 2016-09-09T09:09:09+2:00", - "failed to parse datetime for key `foo`" + "failed to parse datetime for key `foo` at line 1 column 7" ); bad!( "foo = 2016-09-09T09:09:09-2:00", - "failed to parse datetime for key `foo`" + "failed to parse datetime for key `foo` at line 1 column 7" ); bad!( "foo = 2016-09-09T09:09:09Z-2:00", - "failed to parse datetime for key `foo`" + "failed to parse datetime for key `foo` at line 1 column 7" ); } diff --git a/test-suite/tests/serde.rs b/test-suite/tests/serde.rs index 95b8bc4..56172bd 100644 --- a/test-suite/tests/serde.rs +++ b/test-suite/tests/serde.rs @@ -273,7 +273,7 @@ fn type_errors() { Table(map! { bar: Value::String("a".to_string()) }), - "invalid type: string \"a\", expected isize for key `bar`", + "invalid type: string \"a\", expected isize for key `bar` at line 1 column 7", "invalid type: string \"a\", expected isize for key `bar`" } @@ -290,7 +290,7 @@ fn type_errors() { bar: Value::String("a".to_string()) }) }), - "invalid type: string \"a\", expected isize for key `foo.bar`", + "invalid type: string \"a\", expected isize for key `foo.bar` at line 2 column 7", "invalid type: string \"a\", expected isize for key `foo.bar`" } } -- cgit v1.2.3