readertest.cpp (rapidjson-1.0.2) | : | readertest.cpp (rapidjson-1.1.0) | ||
---|---|---|---|---|
skipping to change at line 22 | skipping to change at line 22 | |||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the | // CONDITIONS OF ANY KIND, either express or implied. See the License for the | |||
// specific language governing permissions and limitations under the License. | // specific language governing permissions and limitations under the License. | |||
#include "unittest.h" | #include "unittest.h" | |||
#include "rapidjson/reader.h" | #include "rapidjson/reader.h" | |||
#include "rapidjson/internal/dtoa.h" | #include "rapidjson/internal/dtoa.h" | |||
#include "rapidjson/internal/itoa.h" | #include "rapidjson/internal/itoa.h" | |||
#include "rapidjson/memorystream.h" | #include "rapidjson/memorystream.h" | |||
#include <limits> | ||||
using namespace rapidjson; | using namespace rapidjson; | |||
#ifdef __GNUC__ | ||||
RAPIDJSON_DIAG_PUSH | RAPIDJSON_DIAG_PUSH | |||
#ifdef __GNUC__ | ||||
RAPIDJSON_DIAG_OFF(effc++) | RAPIDJSON_DIAG_OFF(effc++) | |||
RAPIDJSON_DIAG_OFF(float-equal) | RAPIDJSON_DIAG_OFF(float-equal) | |||
RAPIDJSON_DIAG_OFF(missing-noreturn) | ||||
#if __GNUC__ >= 7 | ||||
RAPIDJSON_DIAG_OFF(dangling-else) | ||||
#endif | ||||
#endif // __GNUC__ | ||||
#ifdef __clang__ | ||||
RAPIDJSON_DIAG_OFF(variadic-macros) | ||||
RAPIDJSON_DIAG_OFF(c++98-compat-pedantic) | ||||
#endif | #endif | |||
template<bool expect> | template<bool expect> | |||
struct ParseBoolHandler : BaseReaderHandler<UTF8<>, ParseBoolHandler<expect> > { | struct ParseBoolHandler : BaseReaderHandler<UTF8<>, ParseBoolHandler<expect> > { | |||
ParseBoolHandler() : step_(0) {} | ParseBoolHandler() : step_(0) {} | |||
bool Default() { ADD_FAILURE(); return false; } | bool Default() { ADD_FAILURE(); return false; } | |||
// gcc 4.8.x generates warning in EXPECT_EQ(bool, bool) on this gtest versio n. | // gcc 4.8.x generates warning in EXPECT_EQ(bool, bool) on this gtest versio n. | |||
// Workaround with EXPECT_TRUE(). | // Workaround with EXPECT_TRUE(). | |||
bool Bool(bool b) { /*EXPECT_EQ(expect, b); */EXPECT_TRUE(expect == b); ++s tep_; return true; } | bool Bool(bool b) { /*EXPECT_EQ(expect, b); */EXPECT_TRUE(expect == b); ++s tep_; return true; } | |||
skipping to change at line 162 | skipping to change at line 173 | |||
uint64_t u; | uint64_t u; | |||
int64_t i; | int64_t i; | |||
}u; | }u; | |||
Random r; | Random r; | |||
for (unsigned i = 0; i < 100000; i++) { | for (unsigned i = 0; i < 100000; i++) { | |||
u.u = uint64_t(r()) << 32; | u.u = uint64_t(r()) << 32; | |||
u.u |= r(); | u.u |= r(); | |||
char buffer[32]; | char buffer[32]; | |||
if (u.u >= 4294967296ULL) { | if (u.u > uint64_t(4294967295u)) { | |||
*internal::u64toa(u.u, buffer) = '\0'; | *internal::u64toa(u.u, buffer) = '\0'; | |||
TEST_INTEGER(ParseUint64Handler, buffer, u.u); | TEST_INTEGER(ParseUint64Handler, buffer, u.u); | |||
} | } | |||
if (u.i <= -2147483649LL) { | if (u.i < -int64_t(2147483648u)) { | |||
*internal::i64toa(u.i, buffer) = '\0'; | *internal::i64toa(u.i, buffer) = '\0'; | |||
TEST_INTEGER(ParseInt64Handler, buffer, u.i); | TEST_INTEGER(ParseInt64Handler, buffer, u.i); | |||
} | } | |||
} | } | |||
} | } | |||
#undef TEST_INTEGER | #undef TEST_INTEGER | |||
} | } | |||
template<bool fullPrecision> | template<bool fullPrecision> | |||
static void TestParseDouble() { | static void TestParseDouble() { | |||
skipping to change at line 237 | skipping to change at line 248 | |||
TEST_DOUBLE(fullPrecision, "45913141877270640000.0", 45913141877270640000.0) ; | TEST_DOUBLE(fullPrecision, "45913141877270640000.0", 45913141877270640000.0) ; | |||
TEST_DOUBLE(fullPrecision, "2.2250738585072011e-308", 2.2250738585072011e-30 8); // http://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072 011e-308/ | TEST_DOUBLE(fullPrecision, "2.2250738585072011e-308", 2.2250738585072011e-30 8); // http://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072 011e-308/ | |||
TEST_DOUBLE(fullPrecision, "1e-00011111111111", 0.0); // Issue #313 | TEST_DOUBLE(fullPrecision, "1e-00011111111111", 0.0); // Issue #313 | |||
TEST_DOUBLE(fullPrecision, "-1e-00011111111111", -0.0); | TEST_DOUBLE(fullPrecision, "-1e-00011111111111", -0.0); | |||
TEST_DOUBLE(fullPrecision, "1e-214748363", 0.0); // Maximum supported negative exponent | TEST_DOUBLE(fullPrecision, "1e-214748363", 0.0); // Maximum supported negative exponent | |||
TEST_DOUBLE(fullPrecision, "1e-214748364", 0.0); | TEST_DOUBLE(fullPrecision, "1e-214748364", 0.0); | |||
TEST_DOUBLE(fullPrecision, "1e-21474836311", 0.0); | TEST_DOUBLE(fullPrecision, "1e-21474836311", 0.0); | |||
TEST_DOUBLE(fullPrecision, "0.017976931348623157e+310", 1.7976931348623157e+ 308); // Max double in another form | TEST_DOUBLE(fullPrecision, "0.017976931348623157e+310", 1.7976931348623157e+ 308); // Max double in another form | |||
// Since | // Since | |||
// abs((2^-1022 - 2^-1074) - 2.2250738585072012e-308) = 3.109754131239141401 | // abs((2^-1022 - 2^-1074) - 2.2250738585072012e-308) = 3.109754131239141401 | |||
123495768877590405345064751974375599... ¡Á 10^-324 | 123495768877590405345064751974375599... �� 10^-324 | |||
// abs((2^-1022) - 2.2250738585072012e-308) = 1.8309023271733240406421921598 | // abs((2^-1022) - 2.2250738585072012e-308) = 1.8309023271733240406421921598 | |||
04623318305533274168872044... ¡Á 10 ^ -324 | 04623318305533274168872044... �� 10 ^ -324 | |||
// So 2.2250738585072012e-308 should round to 2^-1022 = 2.2250738585072014e- 308 | // So 2.2250738585072012e-308 should round to 2^-1022 = 2.2250738585072014e- 308 | |||
TEST_DOUBLE(fullPrecision, "2.2250738585072012e-308", 2.2250738585072014e-30 8); // http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072 012e-308/ | TEST_DOUBLE(fullPrecision, "2.2250738585072012e-308", 2.2250738585072014e-30 8); // http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072 012e-308/ | |||
// More closer to normal/subnormal boundary | // More closer to normal/subnormal boundary | |||
// boundary = 2^-1022 - 2^-1075 = 2.2250738585072011360574097967091319759348 19546351645648... ¡Á 10^-308 | // boundary = 2^-1022 - 2^-1075 = 2.2250738585072011360574097967091319759348 19546351645648... �� 10^-308 | |||
TEST_DOUBLE(fullPrecision, "2.2250738585072011360574097967091319759348195463 5164564e-308", 2.2250738585072009e-308); | TEST_DOUBLE(fullPrecision, "2.2250738585072011360574097967091319759348195463 5164564e-308", 2.2250738585072009e-308); | |||
TEST_DOUBLE(fullPrecision, "2.2250738585072011360574097967091319759348195463 5164565e-308", 2.2250738585072014e-308); | TEST_DOUBLE(fullPrecision, "2.2250738585072011360574097967091319759348195463 5164565e-308", 2.2250738585072014e-308); | |||
// 1.0 is in (1.0 - 2^-54, 1.0 + 2^-53) | // 1.0 is in (1.0 - 2^-54, 1.0 + 2^-53) | |||
// 1.0 - 2^-54 = 0.999999999999999944488848768742172978818416595458984375 | // 1.0 - 2^-54 = 0.999999999999999944488848768742172978818416595458984375 | |||
TEST_DOUBLE(fullPrecision, "0.9999999999999999444888487687421729788184165954 58984375", 1.0); // round to even | TEST_DOUBLE(fullPrecision, "0.9999999999999999444888487687421729788184165954 58984375", 1.0); // round to even | |||
TEST_DOUBLE(fullPrecision, "0.9999999999999999444888487687421729788184165954 58984374", 0.99999999999999989); // previous double | TEST_DOUBLE(fullPrecision, "0.9999999999999999444888487687421729788184165954 58984374", 0.99999999999999989); // previous double | |||
TEST_DOUBLE(fullPrecision, "0.9999999999999999444888487687421729788184165954 58984376", 1.0); // next double | TEST_DOUBLE(fullPrecision, "0.9999999999999999444888487687421729788184165954 58984376", 1.0); // next double | |||
// 1.0 + 2^-53 = 1.00000000000000011102230246251565404236316680908203125 | // 1.0 + 2^-53 = 1.00000000000000011102230246251565404236316680908203125 | |||
TEST_DOUBLE(fullPrecision, "1.0000000000000001110223024625156540423631668090 8203125", 1.0); // round to even | TEST_DOUBLE(fullPrecision, "1.0000000000000001110223024625156540423631668090 8203125", 1.0); // round to even | |||
skipping to change at line 405 | skipping to change at line 416 | |||
StringStream s(buffer); | StringStream s(buffer); | |||
ParseDoubleHandler h; | ParseDoubleHandler h; | |||
Reader reader; | Reader reader; | |||
ASSERT_EQ(kParseErrorNone, reader.Parse(s, h).Code()); | ASSERT_EQ(kParseErrorNone, reader.Parse(s, h).Code()); | |||
EXPECT_EQ(1u, h.step_); | EXPECT_EQ(1u, h.step_); | |||
a = h.actual_; | a = h.actual_; | |||
uint64_t bias1 = e.ToBias(); | uint64_t bias1 = e.ToBias(); | |||
uint64_t bias2 = a.ToBias(); | uint64_t bias2 = a.ToBias(); | |||
double ulp = bias1 >= bias2 ? bias1 - bias2 : bias2 - bias1; | double ulp = static_cast<double>(bias1 >= bias2 ? bias1 - bias2 : bias2 - bias1); | |||
ulpMax = std::max(ulpMax, ulp); | ulpMax = std::max(ulpMax, ulp); | |||
ulpSum += ulp; | ulpSum += ulp; | |||
} | } | |||
printf("ULP Average = %g, Max = %g \n", ulpSum / count, ulpMax); | printf("ULP Average = %g, Max = %g \n", ulpSum / count, ulpMax); | |||
} | } | |||
TEST(Reader, ParseNumber_Error) { | TEST(Reader, ParseNumber_Error) { | |||
#define TEST_NUMBER_ERROR(errorCode, str) \ | #define TEST_NUMBER_ERROR(errorCode, str, errorOffset, streamPos) \ | |||
{ \ | { \ | |||
char buffer[1001]; \ | char buffer[1001]; \ | |||
sprintf(buffer, "%s", str); \ | sprintf(buffer, "%s", str); \ | |||
InsituStringStream s(buffer); \ | InsituStringStream s(buffer); \ | |||
BaseReaderHandler<> h; \ | BaseReaderHandler<> h; \ | |||
Reader reader; \ | Reader reader; \ | |||
EXPECT_FALSE(reader.Parse(s, h)); \ | EXPECT_FALSE(reader.Parse(s, h)); \ | |||
EXPECT_EQ(errorCode, reader.GetParseErrorCode());\ | EXPECT_EQ(errorCode, reader.GetParseErrorCode());\ | |||
EXPECT_EQ(errorOffset, reader.GetErrorOffset());\ | ||||
EXPECT_EQ(streamPos, s.Tell());\ | ||||
} | } | |||
// Number too big to be stored in double. | // Number too big to be stored in double. | |||
{ | { | |||
char n1e309[311]; // '1' followed by 309 '0' | char n1e309[311]; // '1' followed by 309 '0' | |||
n1e309[0] = '1'; | n1e309[0] = '1'; | |||
for (int i = 1; i < 310; i++) | for (int i = 1; i < 310; i++) | |||
n1e309[i] = '0'; | n1e309[i] = '0'; | |||
n1e309[310] = '\0'; | n1e309[310] = '\0'; | |||
TEST_NUMBER_ERROR(kParseErrorNumberTooBig, n1e309); | TEST_NUMBER_ERROR(kParseErrorNumberTooBig, n1e309, 0, 309); | |||
} | } | |||
TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "1e309"); | TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "1e309", 0, 5); | |||
// Miss fraction part in number. | // Miss fraction part in number. | |||
TEST_NUMBER_ERROR(kParseErrorNumberMissFraction, "1."); | TEST_NUMBER_ERROR(kParseErrorNumberMissFraction, "1.", 2, 2); | |||
TEST_NUMBER_ERROR(kParseErrorNumberMissFraction, "1.a"); | TEST_NUMBER_ERROR(kParseErrorNumberMissFraction, "1.a", 2, 2); | |||
// Miss exponent in number. | // Miss exponent in number. | |||
TEST_NUMBER_ERROR(kParseErrorNumberMissExponent, "1e"); | TEST_NUMBER_ERROR(kParseErrorNumberMissExponent, "1e", 2, 2); | |||
TEST_NUMBER_ERROR(kParseErrorNumberMissExponent, "1e_"); | TEST_NUMBER_ERROR(kParseErrorNumberMissExponent, "1e_", 2, 2); | |||
#undef TEST_NUMBER_ERROR | #undef TEST_NUMBER_ERROR | |||
} | } | |||
template <typename Encoding> | template <typename Encoding> | |||
struct ParseStringHandler : BaseReaderHandler<Encoding, ParseStringHandler<Encod ing> > { | struct ParseStringHandler : BaseReaderHandler<Encoding, ParseStringHandler<Encod ing> > { | |||
ParseStringHandler() : str_(0), length_(0), copy_() {} | ParseStringHandler() : str_(0), length_(0), copy_() {} | |||
~ParseStringHandler() { EXPECT_TRUE(str_ != 0); if (copy_) free(const_cast<t ypename Encoding::Ch*>(str_)); } | ~ParseStringHandler() { EXPECT_TRUE(str_ != 0); if (copy_) free(const_cast<t ypename Encoding::Ch*>(str_)); } | |||
ParseStringHandler(const ParseStringHandler&); | ParseStringHandler(const ParseStringHandler&); | |||
ParseStringHandler& operator=(const ParseStringHandler&); | ParseStringHandler& operator=(const ParseStringHandler&); | |||
bool Default() { ADD_FAILURE(); return false; } | bool Default() { ADD_FAILURE(); return false; } | |||
bool String(const typename Encoding::Ch* str, size_t length, bool copy) { | bool String(const typename Encoding::Ch* str, size_t length, bool copy) { | |||
EXPECT_EQ(0, str_); | EXPECT_EQ(0, str_); | |||
if (copy) { | if (copy) { | |||
str_ = (typename Encoding::Ch*)malloc((length + 1) * sizeof(typename Encoding::Ch)); | str_ = static_cast<typename Encoding::Ch*>(malloc((length + 1) * siz eof(typename Encoding::Ch))); | |||
memcpy(const_cast<typename Encoding::Ch*>(str_), str, (length + 1) * sizeof(typename Encoding::Ch)); | memcpy(const_cast<typename Encoding::Ch*>(str_), str, (length + 1) * sizeof(typename Encoding::Ch)); | |||
} | } | |||
else | else | |||
str_ = str; | str_ = str; | |||
length_ = length; | length_ = length; | |||
copy_ = copy; | copy_ = copy; | |||
return true; | return true; | |||
} | } | |||
const typename Encoding::Ch* str_; | const typename Encoding::Ch* str_; | |||
skipping to change at line 599 | skipping to change at line 612 | |||
template <typename Encoding> | template <typename Encoding> | |||
ParseErrorCode TestString(const typename Encoding::Ch* str) { | ParseErrorCode TestString(const typename Encoding::Ch* str) { | |||
GenericStringStream<Encoding> s(str); | GenericStringStream<Encoding> s(str); | |||
BaseReaderHandler<Encoding> h; | BaseReaderHandler<Encoding> h; | |||
GenericReader<Encoding, Encoding> reader; | GenericReader<Encoding, Encoding> reader; | |||
reader.template Parse<kParseValidateEncodingFlag>(s, h); | reader.template Parse<kParseValidateEncodingFlag>(s, h); | |||
return reader.GetParseErrorCode(); | return reader.GetParseErrorCode(); | |||
} | } | |||
TEST(Reader, ParseString_Error) { | TEST(Reader, ParseString_Error) { | |||
#define TEST_STRING_ERROR(errorCode, str)\ | #define TEST_STRING_ERROR(errorCode, str, errorOffset, streamPos)\ | |||
EXPECT_EQ(errorCode, TestString<UTF8<> >(str)) | {\ | |||
GenericStringStream<UTF8<> > s(str);\ | ||||
BaseReaderHandler<UTF8<> > h;\ | ||||
GenericReader<UTF8<> , UTF8<> > reader;\ | ||||
reader.Parse<kParseValidateEncodingFlag>(s, h);\ | ||||
EXPECT_EQ(errorCode, reader.GetParseErrorCode());\ | ||||
EXPECT_EQ(errorOffset, reader.GetErrorOffset());\ | ||||
EXPECT_EQ(streamPos, s.Tell());\ | ||||
} | ||||
#define ARRAY(...) { __VA_ARGS__ } | #define ARRAY(...) { __VA_ARGS__ } | |||
#define TEST_STRINGENCODING_ERROR(Encoding, TargetEncoding, utype, array) \ | #define TEST_STRINGENCODING_ERROR(Encoding, TargetEncoding, utype, array) \ | |||
{ \ | { \ | |||
static const utype ue[] = array; \ | static const utype ue[] = array; \ | |||
static const Encoding::Ch* e = reinterpret_cast<const Encoding::Ch *>(&u e[0]); \ | static const Encoding::Ch* e = reinterpret_cast<const Encoding::Ch *>(&u e[0]); \ | |||
EXPECT_EQ(kParseErrorStringInvalidEncoding, TestString<Encoding>(e));\ | EXPECT_EQ(kParseErrorStringInvalidEncoding, TestString<Encoding>(e));\ | |||
/* decode error */\ | /* decode error */\ | |||
GenericStringStream<Encoding> s(e);\ | GenericStringStream<Encoding> s(e);\ | |||
BaseReaderHandler<TargetEncoding> h;\ | BaseReaderHandler<TargetEncoding> h;\ | |||
GenericReader<Encoding, TargetEncoding> reader;\ | GenericReader<Encoding, TargetEncoding> reader;\ | |||
reader.Parse(s, h);\ | reader.Parse(s, h);\ | |||
EXPECT_EQ(kParseErrorStringInvalidEncoding, reader.GetParseErrorCode()); \ | EXPECT_EQ(kParseErrorStringInvalidEncoding, reader.GetParseErrorCode()); \ | |||
} | } | |||
// Invalid escape character in string. | // Invalid escape character in string. | |||
TEST_STRING_ERROR(kParseErrorStringEscapeInvalid, "[\"\\a\"]"); | TEST_STRING_ERROR(kParseErrorStringEscapeInvalid, "[\"\\a\"]", 2, 3); | |||
// Incorrect hex digit after \\u escape in string. | // Incorrect hex digit after \\u escape in string. | |||
TEST_STRING_ERROR(kParseErrorStringUnicodeEscapeInvalidHex, "[\"\\uABCG\"]") ; | TEST_STRING_ERROR(kParseErrorStringUnicodeEscapeInvalidHex, "[\"\\uABCG\"]", 2, 7); | |||
// Quotation in \\u escape in string (Issue #288) | // Quotation in \\u escape in string (Issue #288) | |||
TEST_STRING_ERROR(kParseErrorStringUnicodeEscapeInvalidHex, "[\"\\uaaa\"]"); | TEST_STRING_ERROR(kParseErrorStringUnicodeEscapeInvalidHex, "[\"\\uaaa\"]", | |||
TEST_STRING_ERROR(kParseErrorStringUnicodeEscapeInvalidHex, "[\"\\uD800\\uFF | 2, 7); | |||
F\"]"); | TEST_STRING_ERROR(kParseErrorStringUnicodeEscapeInvalidHex, "[\"\\uD800\\uFF | |||
F\"]", 2, 13); | ||||
// The surrogate pair in string is invalid. | // The surrogate pair in string is invalid. | |||
TEST_STRING_ERROR(kParseErrorStringUnicodeSurrogateInvalid, "[\"\\uD800X\"]" | TEST_STRING_ERROR(kParseErrorStringUnicodeSurrogateInvalid, "[\"\\uD800X\"]" | |||
); | , 2, 8); | |||
TEST_STRING_ERROR(kParseErrorStringUnicodeSurrogateInvalid, "[\"\\uD800\\uFF | TEST_STRING_ERROR(kParseErrorStringUnicodeSurrogateInvalid, "[\"\\uD800\\uFF | |||
FF\"]"); | FF\"]", 2, 14); | |||
// Missing a closing quotation mark in string. | // Missing a closing quotation mark in string. | |||
TEST_STRING_ERROR(kParseErrorStringMissQuotationMark, "[\"Test]"); | TEST_STRING_ERROR(kParseErrorStringMissQuotationMark, "[\"Test]", 7, 7); | |||
// http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt | // http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt | |||
// 3 Malformed sequences | // 3 Malformed sequences | |||
// 3.1 Unexpected continuation bytes | // 3.1 Unexpected continuation bytes | |||
{ | { | |||
char e[] = { '[', '\"', 0, '\"', ']', '\0' }; | char e[] = { '[', '\"', 0, '\"', ']', '\0' }; | |||
for (unsigned char c = 0x80u; c <= 0xBFu; c++) { | for (unsigned char c = 0x80u; c <= 0xBFu; c++) { | |||
e[2] = c; | e[2] = static_cast<char>(c); | |||
ParseErrorCode error = TestString<UTF8<> >(e); | ParseErrorCode error = TestString<UTF8<> >(e); | |||
EXPECT_EQ(kParseErrorStringInvalidEncoding, error); | EXPECT_EQ(kParseErrorStringInvalidEncoding, error); | |||
if (error != kParseErrorStringInvalidEncoding) | if (error != kParseErrorStringInvalidEncoding) | |||
std::cout << (unsigned)(unsigned char)c << std::endl; | std::cout << static_cast<unsigned>(c) << std::endl; | |||
} | } | |||
} | } | |||
// 3.2 Lonely start characters, 3.5 Impossible bytes | // 3.2 Lonely start characters, 3.5 Impossible bytes | |||
{ | { | |||
char e[] = { '[', '\"', 0, ' ', '\"', ']', '\0' }; | char e[] = { '[', '\"', 0, ' ', '\"', ']', '\0' }; | |||
for (unsigned c = 0xC0u; c <= 0xFFu; c++) { | for (unsigned c = 0xC0u; c <= 0xFFu; c++) { | |||
e[2] = (char)c; | e[2] = static_cast<char>(c); | |||
TEST_STRING_ERROR(kParseErrorStringInvalidEncoding, e); | int streamPos; | |||
if (c <= 0xC1u) | ||||
streamPos = 3; // 0xC0 - 0xC1 | ||||
else if (c <= 0xDFu) | ||||
streamPos = 4; // 0xC2 - 0xDF | ||||
else if (c <= 0xEFu) | ||||
streamPos = 5; // 0xE0 - 0xEF | ||||
else if (c <= 0xF4u) | ||||
streamPos = 6; // 0xF0 - 0xF4 | ||||
else | ||||
streamPos = 3; // 0xF5 - 0xFF | ||||
TEST_STRING_ERROR(kParseErrorStringInvalidEncoding, e, 2, streamPos) | ||||
; | ||||
} | } | |||
} | } | |||
// 4 Overlong sequences | // 4 Overlong sequences | |||
// 4.1 Examples of an overlong ASCII character | // 4.1 Examples of an overlong ASCII character | |||
TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0 xC0u, 0xAFu, '\"', ']', '\0')); | TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0 xC0u, 0xAFu, '\"', ']', '\0')); | |||
TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0 xE0u, 0x80u, 0xAFu, '\"', ']', '\0')); | TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0 xE0u, 0x80u, 0xAFu, '\"', ']', '\0')); | |||
TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0 xF0u, 0x80u, 0x80u, 0xAFu, '\"', ']', '\0')); | TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0 xF0u, 0x80u, 0x80u, 0xAFu, '\"', ']', '\0')); | |||
skipping to change at line 694 | skipping to change at line 726 | |||
TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0 xEDu, 0xBFu, 0xBFu, '\"', ']', '\0')); | TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0 xEDu, 0xBFu, 0xBFu, '\"', ']', '\0')); | |||
// Malform UTF-16 sequences | // Malform UTF-16 sequences | |||
TEST_STRINGENCODING_ERROR(UTF16<>, UTF8<>, wchar_t, ARRAY('[', '\"', 0xDC00, 0xDC00, '\"', ']', '\0')); | TEST_STRINGENCODING_ERROR(UTF16<>, UTF8<>, wchar_t, ARRAY('[', '\"', 0xDC00, 0xDC00, '\"', ']', '\0')); | |||
TEST_STRINGENCODING_ERROR(UTF16<>, UTF8<>, wchar_t, ARRAY('[', '\"', 0xD800, 0xD800, '\"', ']', '\0')); | TEST_STRINGENCODING_ERROR(UTF16<>, UTF8<>, wchar_t, ARRAY('[', '\"', 0xD800, 0xD800, '\"', ']', '\0')); | |||
// Malform UTF-32 sequence | // Malform UTF-32 sequence | |||
TEST_STRINGENCODING_ERROR(UTF32<>, UTF8<>, unsigned, ARRAY('[', '\"', 0x1100 00, '\"', ']', '\0')); | TEST_STRINGENCODING_ERROR(UTF32<>, UTF8<>, unsigned, ARRAY('[', '\"', 0x1100 00, '\"', ']', '\0')); | |||
// Malform ASCII sequence | // Malform ASCII sequence | |||
TEST_STRINGENCODING_ERROR(ASCII<>, UTF8<>, char, ARRAY('[', '\"', char(0x80) , '\"', ']', '\0')); | TEST_STRINGENCODING_ERROR(ASCII<>, UTF8<>, char, ARRAY('[', '\"', char(0x80u ), '\"', ']', '\0')); | |||
#undef ARRAY | #undef ARRAY | |||
#undef TEST_STRINGARRAY_ERROR | #undef TEST_STRINGARRAY_ERROR | |||
} | } | |||
template <unsigned count> | template <unsigned count> | |||
struct ParseArrayHandler : BaseReaderHandler<UTF8<>, ParseArrayHandler<count> > { | struct ParseArrayHandler : BaseReaderHandler<UTF8<>, ParseArrayHandler<count> > { | |||
ParseArrayHandler() : step_(0) {} | ParseArrayHandler() : step_(0) {} | |||
bool Default() { ADD_FAILURE(); return false; } | bool Default() { ADD_FAILURE(); return false; } | |||
skipping to change at line 733 | skipping to change at line 765 | |||
char *json = StrDup("[1, 2, 3, 4]"); | char *json = StrDup("[1, 2, 3, 4]"); | |||
InsituStringStream s(json); | InsituStringStream s(json); | |||
ParseArrayHandler<4> h; | ParseArrayHandler<4> h; | |||
Reader reader; | Reader reader; | |||
reader.Parse(s, h); | reader.Parse(s, h); | |||
EXPECT_EQ(6u, h.step_); | EXPECT_EQ(6u, h.step_); | |||
free(json); | free(json); | |||
} | } | |||
TEST(Reader, ParseArray_Error) { | TEST(Reader, ParseArray_Error) { | |||
#define TEST_ARRAY_ERROR(errorCode, str) \ | #define TEST_ARRAY_ERROR(errorCode, str, errorOffset) \ | |||
{ \ | { \ | |||
int streamPos = errorOffset; \ | ||||
char buffer[1001]; \ | char buffer[1001]; \ | |||
strncpy(buffer, str, 1000); \ | strncpy(buffer, str, 1000); \ | |||
InsituStringStream s(buffer); \ | InsituStringStream s(buffer); \ | |||
BaseReaderHandler<> h; \ | BaseReaderHandler<> h; \ | |||
GenericReader<UTF8<>, UTF8<>, CrtAllocator> reader; \ | GenericReader<UTF8<>, UTF8<>, CrtAllocator> reader; \ | |||
EXPECT_FALSE(reader.Parse(s, h)); \ | EXPECT_FALSE(reader.Parse(s, h)); \ | |||
EXPECT_EQ(errorCode, reader.GetParseErrorCode());\ | EXPECT_EQ(errorCode, reader.GetParseErrorCode());\ | |||
EXPECT_EQ(errorOffset, reader.GetErrorOffset());\ | ||||
EXPECT_EQ(streamPos, s.Tell());\ | ||||
} | } | |||
// Missing a comma or ']' after an array element. | // Missing a comma or ']' after an array element. | |||
TEST_ARRAY_ERROR(kParseErrorArrayMissCommaOrSquareBracket, "[1"); | TEST_ARRAY_ERROR(kParseErrorArrayMissCommaOrSquareBracket, "[1", 2); | |||
TEST_ARRAY_ERROR(kParseErrorArrayMissCommaOrSquareBracket, "[1}"); | TEST_ARRAY_ERROR(kParseErrorArrayMissCommaOrSquareBracket, "[1}", 2); | |||
TEST_ARRAY_ERROR(kParseErrorArrayMissCommaOrSquareBracket, "[1 2]"); | TEST_ARRAY_ERROR(kParseErrorArrayMissCommaOrSquareBracket, "[1 2]", 3); | |||
// Array cannot have a trailing comma (without kParseTrailingCommasFlag); | ||||
// a value must follow a comma | ||||
TEST_ARRAY_ERROR(kParseErrorValueInvalid, "[1,]", 3); | ||||
#undef TEST_ARRAY_ERROR | #undef TEST_ARRAY_ERROR | |||
} | } | |||
struct ParseObjectHandler : BaseReaderHandler<UTF8<>, ParseObjectHandler> { | struct ParseObjectHandler : BaseReaderHandler<UTF8<>, ParseObjectHandler> { | |||
ParseObjectHandler() : step_(0) {} | ParseObjectHandler() : step_(0) {} | |||
bool Default() { ADD_FAILURE(); return false; } | bool Default() { ADD_FAILURE(); return false; } | |||
bool Null() { EXPECT_EQ(8u, step_); step_++; return true; } | bool Null() { EXPECT_EQ(8u, step_); step_++; return true; } | |||
bool Bool(bool b) { | bool Bool(bool b) { | |||
skipping to change at line 773 | skipping to change at line 812 | |||
} | } | |||
bool Int(int i) { | bool Int(int i) { | |||
switch(step_) { | switch(step_) { | |||
case 10: EXPECT_EQ(123, i); step_++; return true; | case 10: EXPECT_EQ(123, i); step_++; return true; | |||
case 15: EXPECT_EQ(1, i); step_++; return true; | case 15: EXPECT_EQ(1, i); step_++; return true; | |||
case 16: EXPECT_EQ(2, i); step_++; return true; | case 16: EXPECT_EQ(2, i); step_++; return true; | |||
case 17: EXPECT_EQ(3, i); step_++; return true; | case 17: EXPECT_EQ(3, i); step_++; return true; | |||
default: ADD_FAILURE(); return false; | default: ADD_FAILURE(); return false; | |||
} | } | |||
} | } | |||
bool Uint(unsigned i) { return Int(i); } | bool Uint(unsigned i) { return Int(static_cast<int>(i)); } | |||
bool Double(double d) { EXPECT_EQ(12u, step_); EXPECT_DOUBLE_EQ(3.1416, d); step_++; return true; } | bool Double(double d) { EXPECT_EQ(12u, step_); EXPECT_DOUBLE_EQ(3.1416, d); step_++; return true; } | |||
bool String(const char* str, size_t, bool) { | bool String(const char* str, size_t, bool) { | |||
switch(step_) { | switch(step_) { | |||
case 1: EXPECT_STREQ("hello", str); step_++; return true; | case 1: EXPECT_STREQ("hello", str); step_++; return true; | |||
case 2: EXPECT_STREQ("world", str); step_++; return true; | case 2: EXPECT_STREQ("world", str); step_++; return true; | |||
case 3: EXPECT_STREQ("t", str); step_++; return true; | case 3: EXPECT_STREQ("t", str); step_++; return true; | |||
case 5: EXPECT_STREQ("f", str); step_++; return true; | case 5: EXPECT_STREQ("f", str); step_++; return true; | |||
case 7: EXPECT_STREQ("n", str); step_++; return true; | case 7: EXPECT_STREQ("n", str); step_++; return true; | |||
case 9: EXPECT_STREQ("i", str); step_++; return true; | case 9: EXPECT_STREQ("i", str); step_++; return true; | |||
case 11: EXPECT_STREQ("pi", str); step_++; return true; | case 11: EXPECT_STREQ("pi", str); step_++; return true; | |||
skipping to change at line 894 | skipping to change at line 933 | |||
} | } | |||
TEST(Reader, ParseInsitu_MultipleRoot) { | TEST(Reader, ParseInsitu_MultipleRoot) { | |||
TestInsituMultipleRoot<kParseStopWhenDoneFlag>(); | TestInsituMultipleRoot<kParseStopWhenDoneFlag>(); | |||
} | } | |||
TEST(Reader, ParseInsituIterative_MultipleRoot) { | TEST(Reader, ParseInsituIterative_MultipleRoot) { | |||
TestInsituMultipleRoot<kParseIterativeFlag | kParseStopWhenDoneFlag>(); | TestInsituMultipleRoot<kParseIterativeFlag | kParseStopWhenDoneFlag>(); | |||
} | } | |||
#define TEST_ERROR(errorCode, str) \ | #define TEST_ERROR(errorCode, str, errorOffset) \ | |||
{ \ | { \ | |||
int streamPos = errorOffset; \ | ||||
char buffer[1001]; \ | char buffer[1001]; \ | |||
strncpy(buffer, str, 1000); \ | strncpy(buffer, str, 1000); \ | |||
InsituStringStream s(buffer); \ | InsituStringStream s(buffer); \ | |||
BaseReaderHandler<> h; \ | BaseReaderHandler<> h; \ | |||
Reader reader; \ | Reader reader; \ | |||
EXPECT_FALSE(reader.Parse(s, h)); \ | EXPECT_FALSE(reader.Parse(s, h)); \ | |||
EXPECT_EQ(errorCode, reader.GetParseErrorCode());\ | EXPECT_EQ(errorCode, reader.GetParseErrorCode());\ | |||
EXPECT_EQ(errorOffset, reader.GetErrorOffset());\ | ||||
EXPECT_EQ(streamPos, s.Tell());\ | ||||
} | } | |||
TEST(Reader, ParseDocument_Error) { | TEST(Reader, ParseDocument_Error) { | |||
// The document is empty. | // The document is empty. | |||
TEST_ERROR(kParseErrorDocumentEmpty, ""); | TEST_ERROR(kParseErrorDocumentEmpty, "", 0); | |||
TEST_ERROR(kParseErrorDocumentEmpty, " "); | TEST_ERROR(kParseErrorDocumentEmpty, " ", 1); | |||
TEST_ERROR(kParseErrorDocumentEmpty, " \n"); | TEST_ERROR(kParseErrorDocumentEmpty, " \n", 2); | |||
// The document root must not follow by other values. | // The document root must not follow by other values. | |||
TEST_ERROR(kParseErrorDocumentRootNotSingular, "[] 0"); | TEST_ERROR(kParseErrorDocumentRootNotSingular, "[] 0", 3); | |||
TEST_ERROR(kParseErrorDocumentRootNotSingular, "{} 0"); | TEST_ERROR(kParseErrorDocumentRootNotSingular, "{} 0", 3); | |||
TEST_ERROR(kParseErrorDocumentRootNotSingular, "null []"); | TEST_ERROR(kParseErrorDocumentRootNotSingular, "null []", 5); | |||
TEST_ERROR(kParseErrorDocumentRootNotSingular, "0 {}"); | TEST_ERROR(kParseErrorDocumentRootNotSingular, "0 {}", 2); | |||
} | } | |||
TEST(Reader, ParseValue_Error) { | TEST(Reader, ParseValue_Error) { | |||
// Invalid value. | // Invalid value. | |||
TEST_ERROR(kParseErrorValueInvalid, "nulL"); | TEST_ERROR(kParseErrorValueInvalid, "nulL", 3); | |||
TEST_ERROR(kParseErrorValueInvalid, "truE"); | TEST_ERROR(kParseErrorValueInvalid, "truE", 3); | |||
TEST_ERROR(kParseErrorValueInvalid, "falsE"); | TEST_ERROR(kParseErrorValueInvalid, "falsE", 4); | |||
TEST_ERROR(kParseErrorValueInvalid, "a]"); | TEST_ERROR(kParseErrorValueInvalid, "a]", 0); | |||
TEST_ERROR(kParseErrorValueInvalid, ".1"); | TEST_ERROR(kParseErrorValueInvalid, ".1", 0); | |||
} | } | |||
TEST(Reader, ParseObject_Error) { | TEST(Reader, ParseObject_Error) { | |||
// Missing a name for object member. | // Missing a name for object member. | |||
TEST_ERROR(kParseErrorObjectMissName, "{1}"); | TEST_ERROR(kParseErrorObjectMissName, "{1}", 1); | |||
TEST_ERROR(kParseErrorObjectMissName, "{:1}"); | TEST_ERROR(kParseErrorObjectMissName, "{:1}", 1); | |||
TEST_ERROR(kParseErrorObjectMissName, "{null:1}"); | TEST_ERROR(kParseErrorObjectMissName, "{null:1}", 1); | |||
TEST_ERROR(kParseErrorObjectMissName, "{true:1}"); | TEST_ERROR(kParseErrorObjectMissName, "{true:1}", 1); | |||
TEST_ERROR(kParseErrorObjectMissName, "{false:1}"); | TEST_ERROR(kParseErrorObjectMissName, "{false:1}", 1); | |||
TEST_ERROR(kParseErrorObjectMissName, "{1:1}"); | TEST_ERROR(kParseErrorObjectMissName, "{1:1}", 1); | |||
TEST_ERROR(kParseErrorObjectMissName, "{[]:1}"); | TEST_ERROR(kParseErrorObjectMissName, "{[]:1}", 1); | |||
TEST_ERROR(kParseErrorObjectMissName, "{{}:1}"); | TEST_ERROR(kParseErrorObjectMissName, "{{}:1}", 1); | |||
TEST_ERROR(kParseErrorObjectMissName, "{xyz:1}"); | TEST_ERROR(kParseErrorObjectMissName, "{xyz:1}", 1); | |||
// Missing a colon after a name of object member. | // Missing a colon after a name of object member. | |||
TEST_ERROR(kParseErrorObjectMissColon, "{\"a\" 1}"); | TEST_ERROR(kParseErrorObjectMissColon, "{\"a\" 1}", 5); | |||
TEST_ERROR(kParseErrorObjectMissColon, "{\"a\",1}"); | TEST_ERROR(kParseErrorObjectMissColon, "{\"a\",1}", 4); | |||
// Must be a comma or '}' after an object member | // Must be a comma or '}' after an object member | |||
TEST_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, "{\"a\":1]"); | TEST_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, "{\"a\":1]", 6); | |||
// Object cannot have a trailing comma (without kParseTrailingCommasFlag); | ||||
// an object member name must follow a comma | ||||
TEST_ERROR(kParseErrorObjectMissName, "{\"a\":1,}", 7); | ||||
// This tests that MemoryStream is checking the length in Peek(). | // This tests that MemoryStream is checking the length in Peek(). | |||
{ | { | |||
MemoryStream ms("{\"a\"", 1); | MemoryStream ms("{\"a\"", 1); | |||
BaseReaderHandler<> h; | BaseReaderHandler<> h; | |||
Reader reader; | Reader reader; | |||
EXPECT_FALSE(reader.Parse<kParseStopWhenDoneFlag>(ms, h)); | EXPECT_FALSE(reader.Parse<kParseStopWhenDoneFlag>(ms, h)); | |||
EXPECT_EQ(kParseErrorObjectMissName, reader.GetParseErrorCode()); | EXPECT_EQ(kParseErrorObjectMissName, reader.GetParseErrorCode()); | |||
} | } | |||
} | } | |||
skipping to change at line 1026 | skipping to change at line 1072 | |||
#include <sstream> | #include <sstream> | |||
class IStreamWrapper { | class IStreamWrapper { | |||
public: | public: | |||
typedef char Ch; | typedef char Ch; | |||
IStreamWrapper(std::istream& is) : is_(is) {} | IStreamWrapper(std::istream& is) : is_(is) {} | |||
Ch Peek() const { | Ch Peek() const { | |||
int c = is_.peek(); | int c = is_.peek(); | |||
return c == std::char_traits<char>::eof() ? '\0' : (Ch)c; | return c == std::char_traits<char>::eof() ? '\0' : static_cast<Ch>(c); | |||
} | } | |||
Ch Take() { | Ch Take() { | |||
int c = is_.get(); | int c = is_.get(); | |||
return c == std::char_traits<char>::eof() ? '\0' : (Ch)c; | return c == std::char_traits<char>::eof() ? '\0' : static_cast<Ch>(c); | |||
} | } | |||
size_t Tell() const { return (size_t)is_.tellg(); } | size_t Tell() const { return static_cast<size_t>(is_.tellg()); } | |||
Ch* PutBegin() { assert(false); return 0; } | Ch* PutBegin() { assert(false); return 0; } | |||
void Put(Ch) { assert(false); } | void Put(Ch) { assert(false); } | |||
void Flush() { assert(false); } | void Flush() { assert(false); } | |||
size_t PutEnd(Ch*) { assert(false); return 0; } | size_t PutEnd(Ch*) { assert(false); return 0; } | |||
private: | private: | |||
IStreamWrapper(const IStreamWrapper&); | IStreamWrapper(const IStreamWrapper&); | |||
IStreamWrapper& operator=(const IStreamWrapper&); | IStreamWrapper& operator=(const IStreamWrapper&); | |||
skipping to change at line 1064 | skipping to change at line 1110 | |||
Reader reader; | Reader reader; | |||
ParseArrayHandler<4> h; | ParseArrayHandler<4> h; | |||
reader.Parse(is, h); | reader.Parse(is, h); | |||
EXPECT_FALSE(reader.HasParseError()); | EXPECT_FALSE(reader.HasParseError()); | |||
} | } | |||
// Test iterative parsing. | // Test iterative parsing. | |||
#define TESTERRORHANDLING(text, errorCode, offset)\ | #define TESTERRORHANDLING(text, errorCode, offset)\ | |||
{\ | {\ | |||
int streamPos = offset; \ | ||||
StringStream json(text); \ | StringStream json(text); \ | |||
BaseReaderHandler<> handler; \ | BaseReaderHandler<> handler; \ | |||
Reader reader; \ | Reader reader; \ | |||
reader.Parse<kParseIterativeFlag>(json, handler); \ | reader.Parse<kParseIterativeFlag>(json, handler); \ | |||
EXPECT_TRUE(reader.HasParseError()); \ | EXPECT_TRUE(reader.HasParseError()); \ | |||
EXPECT_EQ(errorCode, reader.GetParseErrorCode()); \ | EXPECT_EQ(errorCode, reader.GetParseErrorCode()); \ | |||
EXPECT_EQ(offset, reader.GetErrorOffset()); \ | EXPECT_EQ(offset, reader.GetErrorOffset()); \ | |||
EXPECT_EQ(streamPos, json.Tell()); \ | ||||
} | } | |||
TEST(Reader, IterativeParsing_ErrorHandling) { | TEST(Reader, IterativeParsing_ErrorHandling) { | |||
TESTERRORHANDLING("{\"a\": a}", kParseErrorValueInvalid, 6u); | TESTERRORHANDLING("{\"a\": a}", kParseErrorValueInvalid, 6u); | |||
TESTERRORHANDLING("", kParseErrorDocumentEmpty, 0u); | TESTERRORHANDLING("", kParseErrorDocumentEmpty, 0u); | |||
TESTERRORHANDLING("{}{}", kParseErrorDocumentRootNotSingular, 2u); | TESTERRORHANDLING("{}{}", kParseErrorDocumentRootNotSingular, 2u); | |||
TESTERRORHANDLING("{1}", kParseErrorObjectMissName, 1u); | TESTERRORHANDLING("{1}", kParseErrorObjectMissName, 1u); | |||
TESTERRORHANDLING("{\"a\", 1}", kParseErrorObjectMissColon, 4u); | TESTERRORHANDLING("{\"a\", 1}", kParseErrorObjectMissColon, 4u); | |||
TESTERRORHANDLING("{\"a\"}", kParseErrorObjectMissColon, 4u); | TESTERRORHANDLING("{\"a\"}", kParseErrorObjectMissColon, 4u); | |||
TESTERRORHANDLING("{\"a\": 1", kParseErrorObjectMissCommaOrCurlyBracket, 7u) ; | TESTERRORHANDLING("{\"a\": 1", kParseErrorObjectMissCommaOrCurlyBracket, 7u) ; | |||
TESTERRORHANDLING("[1 2 3]", kParseErrorArrayMissCommaOrSquareBracket, 3u); | TESTERRORHANDLING("[1 2 3]", kParseErrorArrayMissCommaOrSquareBracket, 3u); | |||
TESTERRORHANDLING("{\"a: 1", kParseErrorStringMissQuotationMark, 5u); | TESTERRORHANDLING("{\"a: 1", kParseErrorStringMissQuotationMark, 6u); | |||
TESTERRORHANDLING("{\"a\":}", kParseErrorValueInvalid, 5u); | ||||
TESTERRORHANDLING("{\"a\":]", kParseErrorValueInvalid, 5u); | ||||
TESTERRORHANDLING("[1,2,}", kParseErrorValueInvalid, 5u); | ||||
TESTERRORHANDLING("[}]", kParseErrorValueInvalid, 1u); | ||||
TESTERRORHANDLING("[,]", kParseErrorValueInvalid, 1u); | ||||
TESTERRORHANDLING("[1,,]", kParseErrorValueInvalid, 3u); | ||||
// Trailing commas are not allowed without kParseTrailingCommasFlag | ||||
TESTERRORHANDLING("{\"a\": 1,}", kParseErrorObjectMissName, 8u); | ||||
TESTERRORHANDLING("[1,2,3,]", kParseErrorValueInvalid, 7u); | ||||
// Any JSON value can be a valid root element in RFC7159. | // Any JSON value can be a valid root element in RFC7159. | |||
TESTERRORHANDLING("\"ab", kParseErrorStringMissQuotationMark, 2u); | TESTERRORHANDLING("\"ab", kParseErrorStringMissQuotationMark, 3u); | |||
TESTERRORHANDLING("truE", kParseErrorValueInvalid, 3u); | TESTERRORHANDLING("truE", kParseErrorValueInvalid, 3u); | |||
TESTERRORHANDLING("False", kParseErrorValueInvalid, 0u); | TESTERRORHANDLING("False", kParseErrorValueInvalid, 0u); | |||
TESTERRORHANDLING("true, false", kParseErrorDocumentRootNotSingular, 4u); | TESTERRORHANDLING("true, false", kParseErrorDocumentRootNotSingular, 4u); | |||
TESTERRORHANDLING("false, false", kParseErrorDocumentRootNotSingular, 5u); | TESTERRORHANDLING("false, false", kParseErrorDocumentRootNotSingular, 5u); | |||
TESTERRORHANDLING("nulL", kParseErrorValueInvalid, 3u); | TESTERRORHANDLING("nulL", kParseErrorValueInvalid, 3u); | |||
TESTERRORHANDLING("null , null", kParseErrorDocumentRootNotSingular, 5u); | TESTERRORHANDLING("null , null", kParseErrorDocumentRootNotSingular, 5u); | |||
TESTERRORHANDLING("1a", kParseErrorDocumentRootNotSingular, 1u); | TESTERRORHANDLING("1a", kParseErrorDocumentRootNotSingular, 1u); | |||
} | } | |||
template<typename Encoding = UTF8<> > | template<typename Encoding = UTF8<> > | |||
skipping to change at line 1136 | skipping to change at line 1194 | |||
bool Int(int) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_INT; return true; } | bool Int(int) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_INT; return true; } | |||
bool Uint(unsigned) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCoun t++] = LOG_INT; return true; } | bool Uint(unsigned) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCoun t++] = LOG_INT; return true; } | |||
bool Int64(int64_t) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCoun t++] = LOG_INT64; return true; } | bool Int64(int64_t) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCoun t++] = LOG_INT64; return true; } | |||
bool Uint64(uint64_t) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCo unt++] = LOG_UINT64; return true; } | bool Uint64(uint64_t) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCo unt++] = LOG_UINT64; return true; } | |||
bool Double(double) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCoun t++] = LOG_DOUBLE; return true; } | bool Double(double) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCoun t++] = LOG_DOUBLE; return true; } | |||
bool RawNumber(const Ch*, SizeType, bool) { RAPIDJSON_ASSERT(LogCount < LogC | ||||
apacity); Logs[LogCount++] = LOG_STRING; return true; } | ||||
bool String(const Ch*, SizeType, bool) { RAPIDJSON_ASSERT(LogCount < LogCapa city); Logs[LogCount++] = LOG_STRING; return true; } | bool String(const Ch*, SizeType, bool) { RAPIDJSON_ASSERT(LogCount < LogCapa city); Logs[LogCount++] = LOG_STRING; return true; } | |||
bool StartObject() { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount ++] = LOG_STARTOBJECT; return true; } | bool StartObject() { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount ++] = LOG_STARTOBJECT; return true; } | |||
bool Key (const Ch*, SizeType, bool) { RAPIDJSON_ASSERT(LogCount < LogCapaci ty); Logs[LogCount++] = LOG_KEY; return true; } | bool Key (const Ch*, SizeType, bool) { RAPIDJSON_ASSERT(LogCount < LogCapaci ty); Logs[LogCount++] = LOG_KEY; return true; } | |||
bool EndObject(SizeType c) { | bool EndObject(SizeType c) { | |||
RAPIDJSON_ASSERT(LogCount < LogCapacity); | RAPIDJSON_ASSERT(LogCount < LogCapacity); | |||
Logs[LogCount++] = LOG_ENDOBJECT; | Logs[LogCount++] = LOG_ENDOBJECT; | |||
Logs[LogCount++] = (int)c; | Logs[LogCount++] = static_cast<int>(c); | |||
return true; | return true; | |||
} | } | |||
bool StartArray() { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount+ +] = LOG_STARTARRAY; return true; } | bool StartArray() { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount+ +] = LOG_STARTARRAY; return true; } | |||
bool EndArray(SizeType c) { | bool EndArray(SizeType c) { | |||
RAPIDJSON_ASSERT(LogCount < LogCapacity); | RAPIDJSON_ASSERT(LogCount < LogCapacity); | |||
Logs[LogCount++] = LOG_ENDARRAY; | Logs[LogCount++] = LOG_ENDARRAY; | |||
Logs[LogCount++] = (int)c; | Logs[LogCount++] = static_cast<int>(c); | |||
return true; | return true; | |||
} | } | |||
}; | }; | |||
TEST(Reader, IterativeParsing_General) { | TEST(Reader, IterativeParsing_General) { | |||
{ | { | |||
StringStream is("[1, {\"k\": [1, 2]}, null, false, true, \"string\", 1.2 ]"); | StringStream is("[1, {\"k\": [1, 2]}, null, false, true, \"string\", 1.2 ]"); | |||
Reader reader; | Reader reader; | |||
IterativeParsingReaderHandler<> handler; | IterativeParsingReaderHandler<> handler; | |||
skipping to change at line 1315 | skipping to change at line 1375 | |||
template <int e> | template <int e> | |||
struct TerminateHandler { | struct TerminateHandler { | |||
bool Null() { return e != 0; } | bool Null() { return e != 0; } | |||
bool Bool(bool) { return e != 1; } | bool Bool(bool) { return e != 1; } | |||
bool Int(int) { return e != 2; } | bool Int(int) { return e != 2; } | |||
bool Uint(unsigned) { return e != 3; } | bool Uint(unsigned) { return e != 3; } | |||
bool Int64(int64_t) { return e != 4; } | bool Int64(int64_t) { return e != 4; } | |||
bool Uint64(uint64_t) { return e != 5; } | bool Uint64(uint64_t) { return e != 5; } | |||
bool Double(double) { return e != 6; } | bool Double(double) { return e != 6; } | |||
bool String(const char*, SizeType, bool) { return e != 7; } | bool RawNumber(const char*, SizeType, bool) { return e != 7; } | |||
bool StartObject() { return e != 8; } | bool String(const char*, SizeType, bool) { return e != 8; } | |||
bool Key(const char*, SizeType, bool) { return e != 9; } | bool StartObject() { return e != 9; } | |||
bool EndObject(SizeType) { return e != 10; } | bool Key(const char*, SizeType, bool) { return e != 10; } | |||
bool StartArray() { return e != 11; } | bool EndObject(SizeType) { return e != 11; } | |||
bool EndArray(SizeType) { return e != 12; } | bool StartArray() { return e != 12; } | |||
bool EndArray(SizeType) { return e != 13; } | ||||
}; | }; | |||
#define TEST_TERMINATION(e, json)\ | #define TEST_TERMINATION(e, json)\ | |||
{\ | {\ | |||
Reader reader;\ | Reader reader;\ | |||
TerminateHandler<e> h;\ | TerminateHandler<e> h;\ | |||
StringStream is(json);\ | StringStream is(json);\ | |||
EXPECT_FALSE(reader.Parse(is, h));\ | EXPECT_FALSE(reader.Parse(is, h));\ | |||
EXPECT_EQ(kParseErrorTermination, reader.GetParseErrorCode());\ | EXPECT_EQ(kParseErrorTermination, reader.GetParseErrorCode());\ | |||
} | } | |||
TEST(Reader, ParseTerminationByHandler) { | TEST(Reader, ParseTerminationByHandler) { | |||
TEST_TERMINATION(0, "[null"); | TEST_TERMINATION(0, "[null"); | |||
TEST_TERMINATION(1, "[true"); | TEST_TERMINATION(1, "[true"); | |||
TEST_TERMINATION(1, "[false"); | TEST_TERMINATION(1, "[false"); | |||
TEST_TERMINATION(2, "[-1"); | TEST_TERMINATION(2, "[-1"); | |||
TEST_TERMINATION(3, "[1"); | TEST_TERMINATION(3, "[1"); | |||
TEST_TERMINATION(4, "[-1234567890123456789"); | TEST_TERMINATION(4, "[-1234567890123456789"); | |||
TEST_TERMINATION(5, "[1234567890123456789"); | TEST_TERMINATION(5, "[1234567890123456789"); | |||
TEST_TERMINATION(6, "[0.5]"); | TEST_TERMINATION(6, "[0.5]"); | |||
TEST_TERMINATION(7, "[\"a\""); | // RawNumber() is never called | |||
TEST_TERMINATION(8, "[{"); | TEST_TERMINATION(8, "[\"a\""); | |||
TEST_TERMINATION(9, "[{\"a\""); | TEST_TERMINATION(9, "[{"); | |||
TEST_TERMINATION(10, "[{}"); | TEST_TERMINATION(10, "[{\"a\""); | |||
TEST_TERMINATION(10, "[{\"a\":1}"); // non-empty object | TEST_TERMINATION(11, "[{}"); | |||
TEST_TERMINATION(11, "{\"a\":["); | TEST_TERMINATION(11, "[{\"a\":1}"); // non-empty object | |||
TEST_TERMINATION(12, "{\"a\":[]"); | TEST_TERMINATION(12, "{\"a\":["); | |||
TEST_TERMINATION(12, "{\"a\":[1]"); // non-empty array | TEST_TERMINATION(13, "{\"a\":[]"); | |||
TEST_TERMINATION(13, "{\"a\":[1]"); // non-empty array | ||||
} | ||||
TEST(Reader, ParseComments) { | ||||
const char* json = | ||||
"// Here is a one-line comment.\n" | ||||
"{// And here's another one\n" | ||||
" /*And here's an in-line one.*/\"hello\" : \"world\"," | ||||
" \"t\" :/* And one with '*' symbol*/true ," | ||||
"/* A multiline comment\n" | ||||
" goes here*/" | ||||
" \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3]" | ||||
"}/*And the last one to be sure */"; | ||||
StringStream s(json); | ||||
ParseObjectHandler h; | ||||
Reader reader; | ||||
EXPECT_TRUE(reader.Parse<kParseCommentsFlag>(s, h)); | ||||
EXPECT_EQ(20u, h.step_); | ||||
} | ||||
TEST(Reader, ParseEmptyInlineComment) { | ||||
const char* json = "{/**/\"hello\" : \"world\", \"t\" : true, \"f\" : false, | ||||
\"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3] }"; | ||||
StringStream s(json); | ||||
ParseObjectHandler h; | ||||
Reader reader; | ||||
EXPECT_TRUE(reader.Parse<kParseCommentsFlag>(s, h)); | ||||
EXPECT_EQ(20u, h.step_); | ||||
} | ||||
TEST(Reader, ParseEmptyOnelineComment) { | ||||
const char* json = "{//\n\"hello\" : \"world\", \"t\" : true, \"f\" : false, | ||||
\"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3] }"; | ||||
StringStream s(json); | ||||
ParseObjectHandler h; | ||||
Reader reader; | ||||
EXPECT_TRUE(reader.Parse<kParseCommentsFlag>(s, h)); | ||||
EXPECT_EQ(20u, h.step_); | ||||
} | ||||
TEST(Reader, ParseMultipleCommentsInARow) { | ||||
const char* json = | ||||
"{/* first comment *//* second */\n" | ||||
"/* third */ /*fourth*/// last one\n" | ||||
"\"hello\" : \"world\", \"t\" : true, \"f\" : false, \"n\": null, \"i\":123, | ||||
\"pi\": 3.1416, \"a\":[1, 2, 3] }"; | ||||
StringStream s(json); | ||||
ParseObjectHandler h; | ||||
Reader reader; | ||||
EXPECT_TRUE(reader.Parse<kParseCommentsFlag>(s, h)); | ||||
EXPECT_EQ(20u, h.step_); | ||||
} | ||||
TEST(Reader, InlineCommentsAreDisabledByDefault) { | ||||
{ | ||||
const char* json = "{/* Inline comment. */\"hello\" : \"world\", \"t\" : | ||||
true, \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3] }" | ||||
; | ||||
StringStream s(json); | ||||
ParseObjectHandler h; | ||||
Reader reader; | ||||
EXPECT_FALSE(reader.Parse<kParseDefaultFlags>(s, h)); | ||||
} | ||||
{ | ||||
const char* json = | ||||
"{\"hello\" : /* Multiline comment starts here\n" | ||||
" continues here\n" | ||||
" and ends here */\"world\", \"t\" :true , \"f\" : false, \"n\": null, \ | ||||
"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3] }"; | ||||
StringStream s(json); | ||||
ParseObjectHandler h; | ||||
Reader reader; | ||||
EXPECT_FALSE(reader.Parse<kParseDefaultFlags>(s, h)); | ||||
} | ||||
} | ||||
TEST(Reader, OnelineCommentsAreDisabledByDefault) { | ||||
const char* json = "{// One-line comment\n\"hello\" : \"world\", \"t\" : tru | ||||
e , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3] }"; | ||||
StringStream s(json); | ||||
ParseObjectHandler h; | ||||
Reader reader; | ||||
EXPECT_FALSE(reader.Parse<kParseDefaultFlags>(s, h)); | ||||
} | ||||
TEST(Reader, EofAfterOneLineComment) { | ||||
const char* json = "{\"hello\" : \"world\" // EOF is here -->\0 \n}"; | ||||
StringStream s(json); | ||||
ParseObjectHandler h; | ||||
Reader reader; | ||||
EXPECT_FALSE(reader.Parse<kParseCommentsFlag>(s, h)); | ||||
EXPECT_EQ(kParseErrorObjectMissCommaOrCurlyBracket, reader.GetParseErrorCode | ||||
()); | ||||
} | ||||
TEST(Reader, IncompleteMultilineComment) { | ||||
const char* json = "{\"hello\" : \"world\" /* EOF is here -->\0 */}"; | ||||
StringStream s(json); | ||||
ParseObjectHandler h; | ||||
Reader reader; | ||||
EXPECT_FALSE(reader.Parse<kParseCommentsFlag>(s, h)); | ||||
EXPECT_EQ(kParseErrorUnspecificSyntaxError, reader.GetParseErrorCode()); | ||||
} | ||||
TEST(Reader, IncompleteMultilineComment2) { | ||||
const char* json = "{\"hello\" : \"world\" /* *\0 */}"; | ||||
StringStream s(json); | ||||
ParseObjectHandler h; | ||||
Reader reader; | ||||
EXPECT_FALSE(reader.Parse<kParseCommentsFlag>(s, h)); | ||||
EXPECT_EQ(kParseErrorUnspecificSyntaxError, reader.GetParseErrorCode()); | ||||
} | ||||
TEST(Reader, UnrecognizedComment) { | ||||
const char* json = "{\"hello\" : \"world\" /! }"; | ||||
StringStream s(json); | ||||
ParseObjectHandler h; | ||||
Reader reader; | ||||
EXPECT_FALSE(reader.Parse<kParseCommentsFlag>(s, h)); | ||||
EXPECT_EQ(kParseErrorUnspecificSyntaxError, reader.GetParseErrorCode()); | ||||
} | ||||
struct NumbersAsStringsHandler { | ||||
bool Null() { return true; } | ||||
bool Bool(bool) { return true; } | ||||
bool Int(int) { return true; } | ||||
bool Uint(unsigned) { return true; } | ||||
bool Int64(int64_t) { return true; } | ||||
bool Uint64(uint64_t) { return true; } | ||||
bool Double(double) { return true; } | ||||
// 'str' is not null-terminated | ||||
bool RawNumber(const char* str, SizeType length, bool) { | ||||
EXPECT_TRUE(str != 0); | ||||
EXPECT_TRUE(expected_len_ == length); | ||||
EXPECT_TRUE(strncmp(str, expected_, length) == 0); | ||||
return true; | ||||
} | ||||
bool String(const char*, SizeType, bool) { return true; } | ||||
bool StartObject() { return true; } | ||||
bool Key(const char*, SizeType, bool) { return true; } | ||||
bool EndObject(SizeType) { return true; } | ||||
bool StartArray() { return true; } | ||||
bool EndArray(SizeType) { return true; } | ||||
NumbersAsStringsHandler(const char* expected) | ||||
: expected_(expected) | ||||
, expected_len_(strlen(expected)) {} | ||||
const char* expected_; | ||||
size_t expected_len_; | ||||
}; | ||||
TEST(Reader, NumbersAsStrings) { | ||||
{ | ||||
const char* json = "{ \"pi\": 3.1416 } "; | ||||
StringStream s(json); | ||||
NumbersAsStringsHandler h("3.1416"); | ||||
Reader reader; | ||||
EXPECT_TRUE(reader.Parse<kParseNumbersAsStringsFlag>(s, h)); | ||||
} | ||||
{ | ||||
char* json = StrDup("{ \"pi\": 3.1416 } "); | ||||
InsituStringStream s(json); | ||||
NumbersAsStringsHandler h("3.1416"); | ||||
Reader reader; | ||||
EXPECT_TRUE(reader.Parse<kParseInsituFlag|kParseNumbersAsStringsFlag>(s, | ||||
h)); | ||||
free(json); | ||||
} | ||||
{ | ||||
const char* json = "{ \"gigabyte\": 1.0e9 } "; | ||||
StringStream s(json); | ||||
NumbersAsStringsHandler h("1.0e9"); | ||||
Reader reader; | ||||
EXPECT_TRUE(reader.Parse<kParseNumbersAsStringsFlag>(s, h)); | ||||
} | ||||
{ | ||||
char* json = StrDup("{ \"gigabyte\": 1.0e9 } "); | ||||
InsituStringStream s(json); | ||||
NumbersAsStringsHandler h("1.0e9"); | ||||
Reader reader; | ||||
EXPECT_TRUE(reader.Parse<kParseInsituFlag|kParseNumbersAsStringsFlag>(s, | ||||
h)); | ||||
free(json); | ||||
} | ||||
{ | ||||
const char* json = "{ \"pi\": 314.159e-2 } "; | ||||
StringStream s(json); | ||||
NumbersAsStringsHandler h("314.159e-2"); | ||||
Reader reader; | ||||
EXPECT_TRUE(reader.Parse<kParseNumbersAsStringsFlag>(s, h)); | ||||
} | ||||
{ | ||||
char* json = StrDup("{ \"gigabyte\": 314.159e-2 } "); | ||||
InsituStringStream s(json); | ||||
NumbersAsStringsHandler h("314.159e-2"); | ||||
Reader reader; | ||||
EXPECT_TRUE(reader.Parse<kParseInsituFlag|kParseNumbersAsStringsFlag>(s, | ||||
h)); | ||||
free(json); | ||||
} | ||||
{ | ||||
const char* json = "{ \"negative\": -1.54321 } "; | ||||
StringStream s(json); | ||||
NumbersAsStringsHandler h("-1.54321"); | ||||
Reader reader; | ||||
EXPECT_TRUE(reader.Parse<kParseNumbersAsStringsFlag>(s, h)); | ||||
} | ||||
{ | ||||
char* json = StrDup("{ \"negative\": -1.54321 } "); | ||||
InsituStringStream s(json); | ||||
NumbersAsStringsHandler h("-1.54321"); | ||||
Reader reader; | ||||
EXPECT_TRUE(reader.Parse<kParseInsituFlag|kParseNumbersAsStringsFlag>(s, | ||||
h)); | ||||
free(json); | ||||
} | ||||
{ | ||||
const char* json = "{ \"pi\": 314.159e-2 } "; | ||||
std::stringstream ss(json); | ||||
IStreamWrapper s(ss); | ||||
NumbersAsStringsHandler h("314.159e-2"); | ||||
Reader reader; | ||||
EXPECT_TRUE(reader.Parse<kParseNumbersAsStringsFlag>(s, h)); | ||||
} | ||||
} | ||||
template <unsigned extraFlags> | ||||
void TestTrailingCommas() { | ||||
{ | ||||
StringStream s("[1,2,3,]"); | ||||
ParseArrayHandler<3> h; | ||||
Reader reader; | ||||
EXPECT_TRUE(reader.Parse<extraFlags|kParseTrailingCommasFlag>(s, h)); | ||||
EXPECT_EQ(5u, h.step_); | ||||
} | ||||
{ | ||||
const char* json = "{ \"hello\" : \"world\", \"t\" : true , \"f\" : fals | ||||
e," | ||||
"\"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3],}"; | ||||
StringStream s(json); | ||||
ParseObjectHandler h; | ||||
Reader reader; | ||||
EXPECT_TRUE(reader.Parse<extraFlags|kParseTrailingCommasFlag>(s, h)); | ||||
EXPECT_EQ(20u, h.step_); | ||||
} | ||||
{ | ||||
// whitespace around trailing commas | ||||
const char* json = "{ \"hello\" : \"world\", \"t\" : true , \"f\" : fals | ||||
e," | ||||
"\"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3\n,\n]\n, | ||||
\n} "; | ||||
StringStream s(json); | ||||
ParseObjectHandler h; | ||||
Reader reader; | ||||
EXPECT_TRUE(reader.Parse<extraFlags|kParseTrailingCommasFlag>(s, h)); | ||||
EXPECT_EQ(20u, h.step_); | ||||
} | ||||
{ | ||||
// comments around trailing commas | ||||
const char* json = "{ \"hello\" : \"world\", \"t\" : true , \"f\" : fals | ||||
e, \"n\": null," | ||||
"\"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3/*test*/,/*test*/]/*te | ||||
st*/,/*test*/}"; | ||||
StringStream s(json); | ||||
ParseObjectHandler h; | ||||
Reader reader; | ||||
EXPECT_TRUE(reader.Parse<extraFlags|kParseTrailingCommasFlag|kParseComme | ||||
ntsFlag>(s, h)); | ||||
EXPECT_EQ(20u, h.step_); | ||||
} | ||||
} | ||||
TEST(Reader, TrailingCommas) { | ||||
TestTrailingCommas<kParseNoFlags>(); | ||||
} | ||||
TEST(Reader, TrailingCommasIterative) { | ||||
TestTrailingCommas<kParseIterativeFlag>(); | ||||
} | ||||
template <unsigned extraFlags> | ||||
void TestMultipleTrailingCommaErrors() { | ||||
// only a single trailing comma is allowed. | ||||
{ | ||||
StringStream s("[1,2,3,,]"); | ||||
ParseArrayHandler<3> h; | ||||
Reader reader; | ||||
ParseResult r = reader.Parse<extraFlags|kParseTrailingCommasFlag>(s, h); | ||||
EXPECT_TRUE(reader.HasParseError()); | ||||
EXPECT_EQ(kParseErrorValueInvalid, r.Code()); | ||||
EXPECT_EQ(7u, r.Offset()); | ||||
} | ||||
{ | ||||
const char* json = "{ \"hello\" : \"world\", \"t\" : true , \"f\" : fals | ||||
e," | ||||
"\"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3,],,}"; | ||||
StringStream s(json); | ||||
ParseObjectHandler h; | ||||
Reader reader; | ||||
ParseResult r = reader.Parse<extraFlags|kParseTrailingCommasFlag>(s, h); | ||||
EXPECT_TRUE(reader.HasParseError()); | ||||
EXPECT_EQ(kParseErrorObjectMissName, r.Code()); | ||||
EXPECT_EQ(95u, r.Offset()); | ||||
} | ||||
} | ||||
TEST(Reader, MultipleTrailingCommaErrors) { | ||||
TestMultipleTrailingCommaErrors<kParseNoFlags>(); | ||||
} | ||||
TEST(Reader, MultipleTrailingCommaErrorsIterative) { | ||||
TestMultipleTrailingCommaErrors<kParseIterativeFlag>(); | ||||
} | ||||
template <unsigned extraFlags> | ||||
void TestEmptyExceptForCommaErrors() { | ||||
// not allowed even with trailing commas enabled; the | ||||
// trailing comma must follow a value. | ||||
{ | ||||
StringStream s("[,]"); | ||||
ParseArrayHandler<3> h; | ||||
Reader reader; | ||||
ParseResult r = reader.Parse<extraFlags|kParseTrailingCommasFlag>(s, h); | ||||
EXPECT_TRUE(reader.HasParseError()); | ||||
EXPECT_EQ(kParseErrorValueInvalid, r.Code()); | ||||
EXPECT_EQ(1u, r.Offset()); | ||||
} | ||||
{ | ||||
StringStream s("{,}"); | ||||
ParseObjectHandler h; | ||||
Reader reader; | ||||
ParseResult r = reader.Parse<extraFlags|kParseTrailingCommasFlag>(s, h); | ||||
EXPECT_TRUE(reader.HasParseError()); | ||||
EXPECT_EQ(kParseErrorObjectMissName, r.Code()); | ||||
EXPECT_EQ(1u, r.Offset()); | ||||
} | ||||
} | ||||
TEST(Reader, EmptyExceptForCommaErrors) { | ||||
TestEmptyExceptForCommaErrors<kParseNoFlags>(); | ||||
} | ||||
TEST(Reader, EmptyExceptForCommaErrorsIterative) { | ||||
TestEmptyExceptForCommaErrors<kParseIterativeFlag>(); | ||||
} | ||||
template <unsigned extraFlags> | ||||
void TestTrailingCommaHandlerTermination() { | ||||
{ | ||||
HandlerTerminateAtEndArray h; | ||||
Reader reader; | ||||
StringStream s("[1,2,3,]"); | ||||
ParseResult r = reader.Parse<extraFlags|kParseTrailingCommasFlag>(s, h); | ||||
EXPECT_TRUE(reader.HasParseError()); | ||||
EXPECT_EQ(kParseErrorTermination, r.Code()); | ||||
EXPECT_EQ(7u, r.Offset()); | ||||
} | ||||
{ | ||||
HandlerTerminateAtEndObject h; | ||||
Reader reader; | ||||
StringStream s("{\"t\": true, \"f\": false,}"); | ||||
ParseResult r = reader.Parse<extraFlags|kParseTrailingCommasFlag>(s, h); | ||||
EXPECT_TRUE(reader.HasParseError()); | ||||
EXPECT_EQ(kParseErrorTermination, r.Code()); | ||||
EXPECT_EQ(23u, r.Offset()); | ||||
} | ||||
} | ||||
TEST(Reader, TrailingCommaHandlerTermination) { | ||||
TestTrailingCommaHandlerTermination<kParseNoFlags>(); | ||||
} | ||||
TEST(Reader, TrailingCommaHandlerTerminationIterative) { | ||||
TestTrailingCommaHandlerTermination<kParseIterativeFlag>(); | ||||
} | ||||
TEST(Reader, ParseNanAndInfinity) { | ||||
#define TEST_NAN_INF(str, x) \ | ||||
{ \ | ||||
{ \ | ||||
StringStream s(str); \ | ||||
ParseDoubleHandler h; \ | ||||
Reader reader; \ | ||||
ASSERT_EQ(kParseErrorNone, reader.Parse<kParseNanAndInfFlag>(s, h).C | ||||
ode()); \ | ||||
EXPECT_EQ(1u, h.step_); \ | ||||
internal::Double e(x), a(h.actual_); \ | ||||
EXPECT_EQ(e.IsNan(), a.IsNan()); \ | ||||
EXPECT_EQ(e.IsInf(), a.IsInf()); \ | ||||
if (!e.IsNan()) \ | ||||
EXPECT_EQ(e.Sign(), a.Sign()); \ | ||||
} \ | ||||
{ \ | ||||
const char* json = "{ \"naninfdouble\": " str " } "; \ | ||||
StringStream s(json); \ | ||||
NumbersAsStringsHandler h(str); \ | ||||
Reader reader; \ | ||||
EXPECT_TRUE(reader.Parse<kParseNumbersAsStringsFlag|kParseNanAndInfF | ||||
lag>(s, h)); \ | ||||
} \ | ||||
{ \ | ||||
char* json = StrDup("{ \"naninfdouble\": " str " } "); \ | ||||
InsituStringStream s(json); \ | ||||
NumbersAsStringsHandler h(str); \ | ||||
Reader reader; \ | ||||
EXPECT_TRUE(reader.Parse<kParseInsituFlag|kParseNumbersAsStringsFlag | ||||
|kParseNanAndInfFlag>(s, h)); \ | ||||
free(json); \ | ||||
} \ | ||||
} | ||||
#define TEST_NAN_INF_ERROR(errorCode, str, errorOffset) \ | ||||
{ \ | ||||
int streamPos = errorOffset; \ | ||||
char buffer[1001]; \ | ||||
strncpy(buffer, str, 1000); \ | ||||
InsituStringStream s(buffer); \ | ||||
BaseReaderHandler<> h; \ | ||||
Reader reader; \ | ||||
EXPECT_FALSE(reader.Parse<kParseNanAndInfFlag>(s, h)); \ | ||||
EXPECT_EQ(errorCode, reader.GetParseErrorCode());\ | ||||
EXPECT_EQ(errorOffset, reader.GetErrorOffset());\ | ||||
EXPECT_EQ(streamPos, s.Tell());\ | ||||
} | ||||
double nan = std::numeric_limits<double>::quiet_NaN(); | ||||
double inf = std::numeric_limits<double>::infinity(); | ||||
TEST_NAN_INF("NaN", nan); | ||||
TEST_NAN_INF("-NaN", nan); | ||||
TEST_NAN_INF("Inf", inf); | ||||
TEST_NAN_INF("Infinity", inf); | ||||
TEST_NAN_INF("-Inf", -inf); | ||||
TEST_NAN_INF("-Infinity", -inf); | ||||
TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "nan", 1); | ||||
TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "-nan", 1); | ||||
TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "NAN", 1); | ||||
TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "-Infinty", 6); | ||||
#undef TEST_NAN_INF_ERROR | ||||
#undef TEST_NAN_INF | ||||
} | } | |||
#ifdef __GNUC__ | ||||
RAPIDJSON_DIAG_POP | RAPIDJSON_DIAG_POP | |||
#endif | ||||
End of changes. 54 change blocks. | ||||
86 lines changed or deleted | 602 lines changed or added |