aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/google/protobuf/util/internal/json_stream_parser_test.cc
diff options
context:
space:
mode:
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.cc194
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}";