diff options
Diffstat (limited to 'src/google/protobuf/util/internal/json_stream_parser_test.cc')
-rw-r--r-- | src/google/protobuf/util/internal/json_stream_parser_test.cc | 194 |
1 files changed, 161 insertions, 33 deletions
diff --git a/src/google/protobuf/util/internal/json_stream_parser_test.cc b/src/google/protobuf/util/internal/json_stream_parser_test.cc index 3414826e..a11e9be0 100644 --- a/src/google/protobuf/util/internal/json_stream_parser_test.cc +++ b/src/google/protobuf/util/internal/json_stream_parser_test.cc @@ -81,12 +81,16 @@ using util::Status; // For each test we split the input string on every possible character to ensure // the parser is able to handle arbitrarily split input for all cases. We also // do a final test of the entire test case one character at a time. +// +// It is verified that expected calls to the mocked objects are in sequence. class JsonStreamParserTest : public ::testing::Test { protected: JsonStreamParserTest() : mock_(), ow_(&mock_) {} virtual ~JsonStreamParserTest() {} - util::Status RunTest(StringPiece json, int split, bool coerce_utf8 = false) { + util::Status RunTest(StringPiece json, int split, bool coerce_utf8 = false, + bool allow_empty_null = false, + bool loose_float_number_conversion = false) { JsonStreamParser parser(&mock_); // Special case for split == length, test parsing one character at a time. @@ -116,22 +120,33 @@ class JsonStreamParserTest : public ::testing::Test { return result; } - void DoTest(StringPiece json, int split, bool coerce_utf8 = false) { - util::Status result = RunTest(json, split, coerce_utf8); + void DoTest(StringPiece json, int split, bool coerce_utf8 = false, + bool allow_empty_null = false, + bool loose_float_number_conversion = false) { + util::Status result = RunTest(json, split, coerce_utf8, allow_empty_null, + loose_float_number_conversion); if (!result.ok()) { GOOGLE_LOG(WARNING) << result; } EXPECT_OK(result); } - void DoErrorTest(StringPiece json, int split, StringPiece error_prefix) { - util::Status result = RunTest(json, split); + void DoErrorTest(StringPiece json, int split, StringPiece error_prefix, + bool coerce_utf8 = false, bool allow_empty_null = false) { + util::Status result = + RunTest(json, split, coerce_utf8, allow_empty_null); EXPECT_EQ(util::error::INVALID_ARGUMENT, result.error_code()); StringPiece error_message(result.error_message()); EXPECT_EQ(error_prefix, error_message.substr(0, error_prefix.size())); } +#ifndef _MSC_VER + // TODO(xiaofeng): We have to disable InSequence check for MSVC because it + // causes stack overflow due to its use of a linked list that is desctructed + // recursively. + ::testing::InSequence in_sequence_; +#endif // !_MSC_VER MockObjectWriter mock_; ExpectingObjectWriter ow_; }; @@ -230,6 +245,32 @@ TEST_F(JsonStreamParserTest, SimpleUnsignedInt) { } } +TEST_F(JsonStreamParserTest, OctalNumberIsInvalid) { + StringPiece str = "01234"; + for (int i = 0; i <= str.length(); ++i) { + DoErrorTest(str, i, "Octal/hex numbers are not valid JSON values."); + } + str = "-01234"; + for (int i = 0; i <= str.length(); ++i) { + DoErrorTest(str, i, "Octal/hex numbers are not valid JSON values."); + } +} + +TEST_F(JsonStreamParserTest, HexNumberIsInvalid) { + StringPiece str = "0x1234"; + for (int i = 0; i <= str.length(); ++i) { + DoErrorTest(str, i, "Octal/hex numbers are not valid JSON values."); + } + str = "-0x1234"; + for (int i = 0; i <= str.length(); ++i) { + DoErrorTest(str, i, "Octal/hex numbers are not valid JSON values."); + } + str = "12x34"; + for (int i = 0; i <= str.length(); ++i) { + DoErrorTest(str, i, "Unable to parse number."); + } +} + // - single and double quoted strings TEST_F(JsonStreamParserTest, EmptyDoubleQuotedString) { StringPiece str = "\"\""; @@ -281,18 +322,27 @@ TEST_F(JsonStreamParserTest, ObjectKeyTypes) { } } -// - array containing array, object, values (true, false, null, num, string) -TEST_F(JsonStreamParserTest, ArrayValues) { - StringPiece str = - "[true, false, null, 'a string', \"another string\", [22, -127, 45.3, " - "-1056.4, 11779497823553162765], {'key': true}]"; +// - array containing primitive values (true, false, null, num, string) +TEST_F(JsonStreamParserTest, ArrayPrimitiveValues) { + StringPiece str = "[true, false, null, 'one', \"two\"]"; for (int i = 0; i <= str.length(); ++i) { ow_.StartList("") ->RenderBool("", true) ->RenderBool("", false) ->RenderNull("") - ->RenderString("", "a string") - ->RenderString("", "another string") + ->RenderString("", "one") + ->RenderString("", "two") + ->EndList(); + DoTest(str, i); + } +} + +// - array containing array, object +TEST_F(JsonStreamParserTest, ArrayComplexValues) { + StringPiece str = + "[[22, -127, 45.3, -1056.4, 11779497823553162765], {'key': true}]"; + for (int i = 0; i <= str.length(); ++i) { + ow_.StartList("") ->StartList("") ->RenderUint64("", 22) ->RenderInt64("", -127) @@ -308,6 +358,7 @@ TEST_F(JsonStreamParserTest, ArrayValues) { } } + // - object containing array, object, value (true, false, null, num, string) TEST_F(JsonStreamParserTest, ObjectValues) { StringPiece str = @@ -351,19 +402,62 @@ TEST_F(JsonStreamParserTest, RejectNonUtf8WhenNotCoerced) { DoErrorTest("\xFF{}", 0, "Encountered non UTF-8 code points."); } -#ifndef _MSC_VER // - unicode handling in strings TEST_F(JsonStreamParserTest, UnicodeEscaping) { StringPiece str = "[\"\\u0639\\u0631\\u0628\\u0649\"]"; for (int i = 0; i <= str.length(); ++i) { - // TODO(xiaofeng): Figure out what default encoding to use for JSON strings. - // In protobuf we use UTF-8 for strings, but for JSON we probably should - // allow different encodings? - ow_.StartList("")->RenderString("", "\u0639\u0631\u0628\u0649")->EndList(); + ow_.StartList("") + ->RenderString("", "\xD8\xB9\xD8\xB1\xD8\xA8\xD9\x89") + ->EndList(); DoTest(str, i); } } -#endif + +// - unicode UTF-16 surrogate pair handling in strings +TEST_F(JsonStreamParserTest, UnicodeSurrogatePairEscaping) { + StringPiece str = + "[\"\\u0bee\\ud800\\uddf1\\uD80C\\uDDA4\\uD83d\\udC1D\\uD83C\\uDF6F\"]"; + for (int i = 0; i <= str.length(); ++i) { + ow_.StartList("") + ->RenderString("", + "\xE0\xAF\xAE\xF0\x90\x87\xB1\xF0\x93\x86\xA4\xF0" + "\x9F\x90\x9D\xF0\x9F\x8D\xAF") + ->EndList(); + DoTest(str, i); + } +} + + +TEST_F(JsonStreamParserTest, UnicodeEscapingInvalidCodePointWhenNotCoerced) { + // A low surrogate alone. + StringPiece str = "[\"\\ude36\"]"; + for (int i = 0; i <= str.length(); ++i) { + DoErrorTest(str, i, "Invalid unicode code point."); + } +} + +TEST_F(JsonStreamParserTest, UnicodeEscapingMissingLowSurrogateWhenNotCoerced) { + // A high surrogate alone. + StringPiece str = "[\"\\ud83d\"]"; + for (int i = 0; i <= str.length(); ++i) { + DoErrorTest(str, i, "Missing low surrogate."); + } + // A high surrogate with some trailing characters. + str = "[\"\\ud83d|ude36\"]"; + for (int i = 0; i <= str.length(); ++i) { + DoErrorTest(str, i, "Missing low surrogate."); + } + // A high surrogate with half a low surrogate. + str = "[\"\\ud83d\\ude--\"]"; + for (int i = 0; i <= str.length(); ++i) { + DoErrorTest(str, i, "Invalid escape sequence."); + } + // Two high surrogates. + str = "[\"\\ud83d\\ud83d\"]"; + for (int i = 0; i <= str.length(); ++i) { + DoErrorTest(str, i, "Invalid low surrogate."); + } +} // - ascii escaping (\b, \f, \n, \r, \t, \v) TEST_F(JsonStreamParserTest, AsciiEscaping) { @@ -600,34 +694,51 @@ TEST_F(JsonStreamParserTest, ExtraCharactersAfterObject) { } } -// numbers too large -TEST_F(JsonStreamParserTest, PositiveNumberTooBig) { - StringPiece str = "[18446744073709551616]"; // 2^64 +TEST_F(JsonStreamParserTest, PositiveNumberTooBigIsDouble) { + StringPiece str = "18446744073709551616"; // 2^64 for (int i = 0; i <= str.length(); ++i) { - ow_.StartList(""); - DoErrorTest(str, i, "Unable to parse number."); + ow_.RenderDouble("", 18446744073709552000.0); + DoTest(str, i); } } -TEST_F(JsonStreamParserTest, NegativeNumberTooBig) { - StringPiece str = "[-18446744073709551616]"; +TEST_F(JsonStreamParserTest, NegativeNumberTooBigIsDouble) { + StringPiece str = "-18446744073709551616"; for (int i = 0; i <= str.length(); ++i) { - ow_.StartList(""); - DoErrorTest(str, i, "Unable to parse number."); + ow_.RenderDouble("", -18446744073709551616.0); + DoTest(str, i); } } -/* -TODO(sven): Fail parsing when parsing a double that is too large. - TEST_F(JsonStreamParserTest, DoubleTooBig) { - StringPiece str = "[184464073709551232321616.45]"; + StringPiece str = "[1.89769e+308]"; for (int i = 0; i <= str.length(); ++i) { ow_.StartList(""); - DoErrorTest(str, i, "Unable to parse number"); + DoErrorTest(str, i, "Number exceeds the range of double."); + } + str = "[-1.89769e+308]"; + for (int i = 0; i <= str.length(); ++i) { + ow_.StartList(""); + DoErrorTest(str, i, "Number exceeds the range of double."); + } +} + + +// invalid bare backslash. +TEST_F(JsonStreamParserTest, UnfinishedEscape) { + StringPiece str = "\"\\"; + for (int i = 0; i <= str.length(); ++i) { + DoErrorTest(str, i, "Closing quote expected in string."); + } +} + +// invalid bare backslash u. +TEST_F(JsonStreamParserTest, UnfinishedUnicodeEscape) { + StringPiece str = "\"\\u"; + for (int i = 0; i <= str.length(); ++i) { + DoErrorTest(str, i, "Illegal hex string."); } } -*/ // invalid unicode sequence. TEST_F(JsonStreamParserTest, UnicodeEscapeCutOff) { @@ -637,6 +748,15 @@ TEST_F(JsonStreamParserTest, UnicodeEscapeCutOff) { } } +// invalid unicode sequence (valid in modern EcmaScript but not in JSON). +TEST_F(JsonStreamParserTest, BracketedUnicodeEscape) { + StringPiece str = "\"\\u{1f36f}\""; + for (int i = 0; i <= str.length(); ++i) { + DoErrorTest(str, i, "Invalid escape sequence."); + } +} + + TEST_F(JsonStreamParserTest, UnicodeEscapeInvalidCharacters) { StringPiece str = "\"\\u12$4hello"; for (int i = 0; i <= str.length(); ++i) { @@ -644,6 +764,14 @@ TEST_F(JsonStreamParserTest, UnicodeEscapeInvalidCharacters) { } } +// invalid unicode sequence in low half surrogate: g is not a hex digit. +TEST_F(JsonStreamParserTest, UnicodeEscapeLowHalfSurrogateInvalidCharacters) { + StringPiece str = "\"\\ud800\\udcfg\""; + for (int i = 0; i <= str.length(); ++i) { + DoErrorTest(str, i, "Invalid escape sequence."); + } +} + // Extra commas with an object or array. TEST_F(JsonStreamParserTest, ExtraCommaInObject) { StringPiece str = "{'k1': true,,'k2': false}"; |