diff --git a/.travis.yml b/.travis.yml index c64d62b..8c29fe5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,7 +21,7 @@ before_install: - sudo apt-get install -qq cmake valgrind - sudo apt-get --no-install-recommends install doxygen # Don't install LaTeX stuffs - if [ "$ARCH" = "x86" ]; then sudo apt-get install -qq g++-multilib libc6-dbg:i386; fi - - if [ "$CC" = "gcc" ] && [ "$CONF" = "debug" ]; then sudo pip install cpp-coveralls; export GCOV_FLAGS='--coverage'; fi + - if [ "$CC" = "gcc" ] && [ "CONF" = "debug" ]; then sudo pip install cpp-coveralls; export GCOV_FLAGS='--coverage'; fi install: true @@ -48,4 +48,4 @@ script: - make travis_doc after_success: - - coveralls -r .. --gcov-options '\-lp' -e thirdparty -e example -e test -e build/CMakeFiles -e include/rapidjson/msinttypes + - coveralls -r .. --gcov-options '\-lp' -e thirdparty -e example -e test -e build/CMakeFiles -e include/rapidjson/msinttypes -e include/rapidjson/internal/meta.h -e include/rapidjson/error/en.h diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 204e3bd..9ef0d91 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -1459,17 +1459,14 @@ public: case kStringType: return handler.String(GetString(), GetStringLength(), (flags_ & kCopyFlag) != 0); - case kNumberType: + default: + RAPIDJSON_ASSERT(GetType() == kNumberType); if (IsInt()) return handler.Int(data_.n.i.i); else if (IsUint()) return handler.Uint(data_.n.u.u); else if (IsInt64()) return handler.Int64(data_.n.i64); else if (IsUint64()) return handler.Uint64(data_.n.u64); else return handler.Double(data_.n.d); - - default: - RAPIDJSON_ASSERT(false); } - return false; } private: @@ -1753,7 +1750,7 @@ public: */ template GenericDocument& ParseStream(InputStream& is) { - return ParseStream(is); + return ParseStream(is); } //! Parse JSON text from an input stream (with \ref kParseDefaultFlags) diff --git a/include/rapidjson/encodedstream.h b/include/rapidjson/encodedstream.h index f2f3ea3..9a93b38 100644 --- a/include/rapidjson/encodedstream.h +++ b/include/rapidjson/encodedstream.h @@ -109,6 +109,7 @@ public: \param type UTF encoding type if it is not detected from the stream. */ AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) { + RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE); DetectType(); static const TakeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Take) }; takeFunc_ = f[type_]; @@ -177,21 +178,8 @@ private: } // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. - switch (type_) { - case kUTF8: - // Do nothing - break; - case kUTF16LE: - case kUTF16BE: - RAPIDJSON_ASSERT(sizeof(Ch) >= 2); - break; - case kUTF32LE: - case kUTF32BE: - RAPIDJSON_ASSERT(sizeof(Ch) >= 4); - break; - default: - RAPIDJSON_ASSERT(false); // Invalid type - } + if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2); + if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4); } typedef Ch (*TakeFunc)(InputByteStream& is); @@ -220,22 +208,11 @@ public: \param putBOM Whether to write BOM at the beginning of the stream. */ AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) { - // RUntime check whether the size of character type is sufficient. It only perform checks with assertion. - switch (type_) { - case kUTF16LE: - case kUTF16BE: - RAPIDJSON_ASSERT(sizeof(Ch) >= 2); - break; - case kUTF32LE: - case kUTF32BE: - RAPIDJSON_ASSERT(sizeof(Ch) >= 4); - break; - case kUTF8: - // Do nothing - break; - default: - RAPIDJSON_ASSERT(false); // Invalid UTFType - } + RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE); + + // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. + if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2); + if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4); static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) }; putFunc_ = f[type_]; diff --git a/include/rapidjson/internal/biginteger.h b/include/rapidjson/internal/biginteger.h index e4703d6..5baba9b 100755 --- a/include/rapidjson/internal/biginteger.h +++ b/include/rapidjson/internal/biginteger.h @@ -172,13 +172,10 @@ public: } // Compute absolute difference of this and rhs. - // Return false if this < rhs + // Assume this != rhs bool Difference(const BigInteger& rhs, BigInteger* out) const { int cmp = Compare(rhs); - if (cmp == 0) { - *out = BigInteger(0); - return false; - } + RAPIDJSON_ASSERT(cmp != 0); const BigInteger *a, *b; // Makes a > b bool ret; if (cmp < 0) { a = &rhs; b = this; ret = true; } @@ -269,12 +266,6 @@ private: #endif } - static Type FullAdd(Type a, Type b, bool inCarry, bool* outCarry) { - Type c = a + b + (inCarry ? 1 : 0); - *outCarry = c < a; - return c; - } - static const size_t kBitCount = 3328; // 64bit * 54 > 10^1000 static const size_t kCapacity = kBitCount / sizeof(Type); static const size_t kTypeBit = sizeof(Type) * 8; diff --git a/include/rapidjson/internal/diyfp.h b/include/rapidjson/internal/diyfp.h index 0b098af..de3d1f0 100644 --- a/include/rapidjson/internal/diyfp.h +++ b/include/rapidjson/internal/diyfp.h @@ -135,25 +135,9 @@ struct DiyFp { double d; uint64_t u64; }u; - uint64_t significand = f; - int exponent = e; - while (significand > kDpHiddenBit + kDpSignificandMask) { - significand >>= 1; - exponent++; - } - while (exponent > kDpDenormalExponent && (significand & kDpHiddenBit) == 0) { - significand <<= 1; - exponent--; - } - if (exponent >= kDpMaxExponent) { - u.u64 = kDpExponentMask; // Infinity - return u.d; - } - else if (exponent < kDpDenormalExponent) - return 0.0; - const uint64_t be = (exponent == kDpDenormalExponent && (significand & kDpHiddenBit) == 0) ? 0 : - static_cast(exponent + kDpExponentBias); - u.u64 = (significand & kDpSignificandMask) | (be << kDpSignificandSize); + const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 : + static_cast(e + kDpExponentBias); + u.u64 = (f & kDpSignificandMask) | (be << kDpSignificandSize); return u.d; } diff --git a/include/rapidjson/internal/dtoa.h b/include/rapidjson/internal/dtoa.h index aec6bcd..2d8d2e4 100644 --- a/include/rapidjson/internal/dtoa.h +++ b/include/rapidjson/internal/dtoa.h @@ -50,8 +50,10 @@ inline unsigned CountDecimalDigit32(uint32_t n) { if (n < 1000000) return 6; if (n < 10000000) return 7; if (n < 100000000) return 8; - if (n < 1000000000) return 9; - return 10; + // Will not reach 10 digits in DigitGen() + //if (n < 1000000000) return 9; + //return 10; + return 9; } inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) { @@ -60,13 +62,12 @@ inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buff const DiyFp wp_w = Mp - W; uint32_t p1 = static_cast(Mp.f >> -one.e); uint64_t p2 = Mp.f & (one.f - 1); - int kappa = CountDecimalDigit32(p1); + int kappa = CountDecimalDigit32(p1); // kappa in [0, 9] *len = 0; while (kappa > 0) { - uint32_t d; + uint32_t d = 0; switch (kappa) { - case 10: d = p1 / 1000000000; p1 %= 1000000000; break; case 9: d = p1 / 100000000; p1 %= 100000000; break; case 8: d = p1 / 10000000; p1 %= 10000000; break; case 7: d = p1 / 1000000; p1 %= 1000000; break; @@ -76,14 +77,7 @@ inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buff case 3: d = p1 / 100; p1 %= 100; break; case 2: d = p1 / 10; p1 %= 10; break; case 1: d = p1; p1 = 0; break; - default: -#if defined(_MSC_VER) - __assume(0); -#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) - __builtin_unreachable(); -#else - d = 0; -#endif + default:; } if (d || *len) buffer[(*len)++] = static_cast('0' + static_cast(d)); diff --git a/include/rapidjson/internal/ieee754.h b/include/rapidjson/internal/ieee754.h index 152be8f..9a82880 100644 --- a/include/rapidjson/internal/ieee754.h +++ b/include/rapidjson/internal/ieee754.h @@ -34,14 +34,6 @@ public: return Double(u + 1).Value(); } - double PreviousPositiveDouble() const { - RAPIDJSON_ASSERT(!Sign()); - if (IsZero()) - return 0.0; - else - return Double(u - 1).Value(); - } - bool Sign() const { return (u & kSignMask) != 0; } uint64_t Significand() const { return u & kSignificandMask; } int Exponent() const { return ((u & kExponentMask) >> kSignificandSize) - kExponentBias; } diff --git a/include/rapidjson/internal/itoa.h b/include/rapidjson/internal/itoa.h index 9ecf630..01a4e7e 100644 --- a/include/rapidjson/internal/itoa.h +++ b/include/rapidjson/internal/itoa.h @@ -15,6 +15,8 @@ #ifndef RAPIDJSON_ITOA_ #define RAPIDJSON_ITOA_ +#include "../rapidjson.h" + RAPIDJSON_NAMESPACE_BEGIN namespace internal { diff --git a/include/rapidjson/internal/strtod.h b/include/rapidjson/internal/strtod.h index 805c752..fa85286 100644 --- a/include/rapidjson/internal/strtod.h +++ b/include/rapidjson/internal/strtod.h @@ -52,7 +52,7 @@ inline T Min3(T a, T b, T c) { return m; } -inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp, bool* adjustToNegative) { +inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp) { const Double db(b); const uint64_t bInt = db.IntegerSignificand(); const int bExp = db.IntegerExponent(); @@ -104,19 +104,9 @@ inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp, bool* adj hS.MultiplyPow5(hS_Exp5) <<= hS_Exp2; BigInteger delta(0); - *adjustToNegative = dS.Difference(bS, &delta); + dS.Difference(bS, &delta); - int cmp = delta.Compare(hS); - // If delta is within 1/2 ULP, check for special case when significand is power of two. - // In this case, need to compare with 1/2h in the lower bound. - if (cmp < 0 && *adjustToNegative && // within and dS < bS - db.IsNormal() && (bInt & (bInt - 1)) == 0 && // Power of 2 - db.Uint64Value() != RAPIDJSON_UINT64_C2(0x00100000, 0x00000000)) // minimum normal number must not do this - { - delta <<= 1; - return delta.Compare(hS); - } - return cmp; + return delta.Compare(hS); } inline bool StrtodFast(double d, int p, double* result) { @@ -213,24 +203,18 @@ inline double StrtodBigInteger(double approx, const char* decimals, size_t lengt const BigInteger dInt(decimals, length); const int dExp = (int)decimalPosition - (int)length + exp; Double a(approx); - for (int i = 0; i < 10; i++) { - bool adjustToNegative; - int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp, &adjustToNegative); - if (cmp < 0) - return a.Value(); // within half ULP - else if (cmp == 0) { - // Round towards even - if (a.Significand() & 1) - return adjustToNegative ? a.PreviousPositiveDouble() : a.NextPositiveDouble(); - else - return a.Value(); - } - else // adjustment - a = adjustToNegative ? a.PreviousPositiveDouble() : a.NextPositiveDouble(); + int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp); + if (cmp < 0) + return a.Value(); // within half ULP + else if (cmp == 0) { + // Round towards even + if (a.Significand() & 1) + return a.NextPositiveDouble(); + else + return a.Value(); } - - // This should not happen, but in case there is really a bug, break the infinite-loop - return a.Value(); + else // adjustment + return a.NextPositiveDouble(); } inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t length, size_t decimalPosition, int exp) { @@ -258,7 +242,9 @@ inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t // Trim right-most digits const int kMaxDecimalDigit = 780; if ((int)length > kMaxDecimalDigit) { - exp += (int(length) - kMaxDecimalDigit); + int delta = (int(length) - kMaxDecimalDigit); + exp += delta; + decimalPosition -= delta; length = kMaxDecimalDigit; } diff --git a/include/rapidjson/prettywriter.h b/include/rapidjson/prettywriter.h index 90d1983..fff8886 100644 --- a/include/rapidjson/prettywriter.h +++ b/include/rapidjson/prettywriter.h @@ -78,7 +78,7 @@ public: #if RAPIDJSON_HAS_STDSTRING bool String(const std::basic_string& str) { - return String(str.data(), SizeType(str.size())); + return String(str.data(), SizeType(str.size())); } #endif @@ -100,8 +100,9 @@ public: Base::os_->Put('\n'); WriteIndent(); } - if (!Base::WriteEndObject()) - return false; + bool ret = Base::WriteEndObject(); + (void)ret; + RAPIDJSON_ASSERT(ret == true); if (Base::level_stack_.Empty()) // end of json text Base::os_->Flush(); return true; @@ -123,8 +124,9 @@ public: Base::os_->Put('\n'); WriteIndent(); } - if (!Base::WriteEndArray()) - return false; + bool ret = Base::WriteEndArray(); + (void)ret; + RAPIDJSON_ASSERT(ret == true); if (Base::level_stack_.Empty()) // end of json text Base::os_->Flush(); return true; diff --git a/include/rapidjson/rapidjson.h b/include/rapidjson/rapidjson.h index b847ac7..602dabd 100644 --- a/include/rapidjson/rapidjson.h +++ b/include/rapidjson/rapidjson.h @@ -122,9 +122,9 @@ #ifndef RAPIDJSON_FORCEINLINE //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN -#ifdef _MSC_VER +#if defined(_MSC_VER) && !defined(NDEBUG) #define RAPIDJSON_FORCEINLINE __forceinline -#elif defined(__GNUC__) && __GNUC__ >= 4 +#elif defined(__GNUC__) && __GNUC__ >= 4 && !defined(NDEBUG) #define RAPIDJSON_FORCEINLINE __attribute__((always_inline)) #else #define RAPIDJSON_FORCEINLINE diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index d9da869..31a145f 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -1227,14 +1227,9 @@ private: // May return a new state on state pop. template RAPIDJSON_FORCEINLINE IterativeParsingState Transit(IterativeParsingState src, Token token, IterativeParsingState dst, InputStream& is, Handler& handler) { + (void)token; + switch (dst) { - case IterativeParsingStartState: - RAPIDJSON_ASSERT(false); - return IterativeParsingErrorState; - - case IterativeParsingFinishState: - return dst; - case IterativeParsingErrorState: return dst; @@ -1273,12 +1268,9 @@ private: return dst; case IterativeParsingKeyValueDelimiterState: - if (token == ColonToken) { - is.Take(); - return dst; - } - else - return IterativeParsingErrorState; + RAPIDJSON_ASSERT(token == ColonToken); + is.Take(); + return dst; case IterativeParsingMemberValueState: // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. @@ -1353,17 +1345,25 @@ private: } } - case IterativeParsingValueState: + default: + // This branch is for IterativeParsingValueState actually. + // Use `default:` rather than + // `case IterativeParsingValueState:` is for code coverage. + + // The IterativeParsingStartState is not enumerated in this switch-case. + // It is impossible for that case. And it can be caught by following assertion. + + // The IterativeParsingFinishState is not enumerated in this switch-case either. + // It is a "derivative" state which cannot triggered from Predict() directly. + // Therefore it cannot happen here. And it can be caught by following assertion. + RAPIDJSON_ASSERT(dst == IterativeParsingValueState); + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. ParseValue(is, handler); if (HasParseError()) { return IterativeParsingErrorState; } return IterativeParsingFinishState; - - default: - RAPIDJSON_ASSERT(false); - return IterativeParsingErrorState; } } diff --git a/include/rapidjson/writer.h b/include/rapidjson/writer.h index 6030fe0..40cdb35 100644 --- a/include/rapidjson/writer.h +++ b/include/rapidjson/writer.h @@ -127,7 +127,7 @@ public: #if RAPIDJSON_HAS_STDSTRING bool String(const std::basic_string& str) { - return String(str.data(), SizeType(str.size())); + return String(str.data(), SizeType(str.size())); } #endif @@ -272,7 +272,8 @@ protected: os_->Put(hexDigits[(codepoint >> 4) & 15]); os_->Put(hexDigits[(codepoint ) & 15]); } - else if (codepoint >= 0x010000 && codepoint <= 0x10FFFF) { + else { + RAPIDJSON_ASSERT(codepoint >= 0x010000 && codepoint <= 0x10FFFF); // Surrogate pair unsigned s = codepoint - 0x010000; unsigned lead = (s >> 10) + 0xD800; @@ -288,8 +289,6 @@ protected: os_->Put(hexDigits[(trail >> 4) & 15]); os_->Put(hexDigits[(trail ) & 15]); } - else - return false; // invalid code point } else if ((sizeof(Ch) == 1 || (unsigned)c < 256) && escape[(unsigned char)c]) { is.Take(); @@ -303,7 +302,8 @@ protected: } } else - Transcoder::Transcode(is, *os_); + if (!Transcoder::Transcode(is, *os_)) + return false; } os_->Put('\"'); return true; diff --git a/test/unittest/CMakeLists.txt b/test/unittest/CMakeLists.txt index 326e6de..bcc16d9 100644 --- a/test/unittest/CMakeLists.txt +++ b/test/unittest/CMakeLists.txt @@ -1,12 +1,16 @@ set(UNITTEST_SOURCES + allocatorstest.cpp bigintegertest.cpp documenttest.cpp encodedstreamtest.cpp encodingstest.cpp filestreamtest.cpp + itoatest.cpp jsoncheckertest.cpp namespacetest.cpp + prettywritertest.cpp readertest.cpp + simdtest.cpp stringbuffertest.cpp strtodtest.cpp unittest.cpp @@ -33,8 +37,9 @@ add_test(NAME unittest WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/bin) if(NOT MSVC) + # Not running SIMD.* unit test cases for Valgrind add_test(NAME valgrind_unittest - COMMAND valgrind --leak-check=full --error-exitcode=1 ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unittest + COMMAND valgrind --leak-check=full --error-exitcode=1 ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unittest --gtest_filter=-SIMD.* WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/bin) if(CMAKE_BUILD_TYPE STREQUAL "Debug") diff --git a/test/unittest/allocatorstest.cpp b/test/unittest/allocatorstest.cpp new file mode 100644 index 0000000..2cf9a2e --- /dev/null +++ b/test/unittest/allocatorstest.cpp @@ -0,0 +1,64 @@ +// Copyright (C) 2011 Milo Yip +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include "unittest.h" + +#include "rapidjson/allocators.h" + +using namespace rapidjson; + +template +void TestAllocator(Allocator& a) { + uint8_t* p = (uint8_t*)a.Malloc(100); + EXPECT_TRUE(p != 0); + for (size_t i = 0; i < 100; i++) + p[i] = (uint8_t)i; + + // Expand + uint8_t* q = (uint8_t*)a.Realloc(p, 100, 200); + EXPECT_TRUE(q != 0); + for (size_t i = 0; i < 100; i++) + EXPECT_EQ(i, q[i]); + for (size_t i = 100; i < 200; i++) + q[i] = (uint8_t)i; + + // Shrink + uint8_t *r = (uint8_t*)a.Realloc(q, 200, 150); + EXPECT_TRUE(r != 0); + for (size_t i = 0; i < 150; i++) + EXPECT_EQ(i, r[i]); + + Allocator::Free(r); +} + +TEST(Allocator, CrtAllocator) { + CrtAllocator a; + TestAllocator(a); +} + +TEST(Allocator, MemoryPoolAllocator) { + MemoryPoolAllocator<> a; + TestAllocator(a); + + for (int i = 0; i < 1000; i++) { + EXPECT_TRUE(a.Malloc(i) != 0); + EXPECT_LE(a.Size(), a.Capacity()); + } +} diff --git a/test/unittest/documenttest.cpp b/test/unittest/documenttest.cpp index 12ea751..dd4fb13 100644 --- a/test/unittest/documenttest.cpp +++ b/test/unittest/documenttest.cpp @@ -28,13 +28,11 @@ using namespace rapidjson; -template -void ParseTest() { - typedef GenericDocument, Allocator, StackAllocator> DocumentType; +template +void ParseCheck(DocumentType& doc) { typedef typename DocumentType::ValueType ValueType; - DocumentType doc; - doc.Parse(" { \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3, 4] } "); + EXPECT_FALSE(doc.HasParseError()); EXPECT_TRUE(doc.IsObject()); @@ -73,6 +71,28 @@ void ParseTest() { EXPECT_EQ(i + 1, a[i].GetUint()); } +template +void ParseTest() { + typedef GenericDocument, Allocator, StackAllocator> DocumentType; + DocumentType doc; + + const char* json = " { \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3, 4] } "; + + doc.Parse(json); + ParseCheck(doc); + + doc.SetNull(); + StringStream s(json); + doc.template ParseStream<0>(s); + ParseCheck(doc); + + doc.SetNull(); + char *buffer = strdup(json); + doc.ParseInsitu(buffer); + ParseCheck(doc); + free(buffer); +} + TEST(Document, Parse) { ParseTest, CrtAllocator>(); ParseTest, MemoryPoolAllocator<> >(); @@ -81,14 +101,21 @@ TEST(Document, Parse) { } static FILE* OpenEncodedFile(const char* filename) { + const char *paths[] = { + "encodings/%s", + "bin/encodings/%s", + "../bin/encodings/%s", + "../../bin/encodings/%s", + "../../../bin/encodings/%s" + }; char buffer[1024]; - sprintf(buffer, "encodings/%s", filename); - FILE *fp = fopen(buffer, "rb"); - if (!fp) { - sprintf(buffer, "../../bin/encodings/%s", filename); - fp = fopen(buffer, "rb"); + for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) { + sprintf(buffer, paths[i], filename); + FILE *fp = fopen(buffer, "rb"); + if (fp) + return fp; } - return fp; + return 0; } TEST(Document, ParseStream_EncodedInputStream) { @@ -223,6 +250,10 @@ TEST(Document, UserBuffer) { EXPECT_FALSE(doc.HasParseError()); EXPECT_LE(valueAllocator.Size(), sizeof(valueBuffer)); EXPECT_LE(parseAllocator.Size(), sizeof(parseBuffer)); + + // Cover MemoryPoolAllocator::Capacity() + EXPECT_LE(valueAllocator.Size(), valueAllocator.Capacity()); + EXPECT_LE(parseAllocator.Size(), parseAllocator.Capacity()); } // Issue 226: Value of string type should not point to NULL diff --git a/test/unittest/encodedstreamtest.cpp b/test/unittest/encodedstreamtest.cpp index 3580608..5affb5d 100644 --- a/test/unittest/encodedstreamtest.cpp +++ b/test/unittest/encodedstreamtest.cpp @@ -47,14 +47,21 @@ private: protected: static FILE* Open(const char* filename) { + const char *paths[] = { + "encodings/%s", + "bin/encodings/%s", + "../bin/encodings/%s", + "../../bin/encodings/%s", + "../../../bin/encodings/%s" + }; char buffer[1024]; - sprintf(buffer, "encodings/%s", filename); - FILE *fp = fopen(buffer, "rb"); - if (!fp) { - sprintf(buffer, "../../bin/encodings/%s", filename); - fp = fopen(buffer, "rb"); + for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) { + sprintf(buffer, paths[i], filename); + FILE *fp = fopen(buffer, "rb"); + if (fp) + return fp; } - return fp; + return 0; } static char *ReadFile(const char* filename, bool appendPath, size_t* outLength) { @@ -112,10 +119,11 @@ protected: } EXPECT_EQ('\0', s.Peek()); free(data); + EXPECT_EQ(size, eis.Tell()); } } - void TestAutoUTFInputStream(const char *filename) { + void TestAutoUTFInputStream(const char *filename, bool expectHasBOM) { // Test FileReadStream { char buffer[16]; @@ -123,6 +131,7 @@ protected: ASSERT_TRUE(fp != 0); FileReadStream fs(fp, buffer, sizeof(buffer)); AutoUTFInputStream eis(fs); + EXPECT_EQ(expectHasBOM, eis.HasBOM()); StringStream s(json_); while (eis.Peek() != '\0') { unsigned expected, actual; @@ -140,6 +149,7 @@ protected: char* data = ReadFile(filename, true, &size); MemoryStream ms(data, size); AutoUTFInputStream eis(ms); + EXPECT_EQ(expectHasBOM, eis.HasBOM()); StringStream s(json_); while (eis.Peek() != '\0') { @@ -150,6 +160,7 @@ protected: } EXPECT_EQ('\0', s.Peek()); free(data); + EXPECT_EQ(size, eis.Tell()); } } @@ -257,16 +268,25 @@ TEST_F(EncodedStreamTest, EncodedInputStream) { } TEST_F(EncodedStreamTest, AutoUTFInputStream) { - TestAutoUTFInputStream("utf8.json"); - TestAutoUTFInputStream("utf8bom.json"); - TestAutoUTFInputStream("utf16le.json"); - TestAutoUTFInputStream("utf16lebom.json"); - TestAutoUTFInputStream("utf16be.json"); - TestAutoUTFInputStream("utf16bebom.json"); - TestAutoUTFInputStream("utf32le.json"); - TestAutoUTFInputStream("utf32lebom.json"); - TestAutoUTFInputStream("utf32be.json"); - TestAutoUTFInputStream("utf32bebom.json"); + TestAutoUTFInputStream("utf8.json", false); + TestAutoUTFInputStream("utf8bom.json", true); + TestAutoUTFInputStream("utf16le.json", false); + TestAutoUTFInputStream("utf16lebom.json",true); + TestAutoUTFInputStream("utf16be.json", false); + TestAutoUTFInputStream("utf16bebom.json",true); + TestAutoUTFInputStream("utf32le.json", false); + TestAutoUTFInputStream("utf32lebom.json",true); + TestAutoUTFInputStream("utf32be.json", false); + TestAutoUTFInputStream("utf32bebom.json", true); + + { + // Auto detection fail, use user defined UTF type + const char json[] = "{ }"; + MemoryStream ms(json, sizeof(json)); + AutoUTFInputStream eis(ms, kUTF8); + EXPECT_FALSE(eis.HasBOM()); + EXPECT_EQ(kUTF8, eis.GetType()); + } } TEST_F(EncodedStreamTest, EncodedOutputStream) { diff --git a/test/unittest/filestreamtest.cpp b/test/unittest/filestreamtest.cpp index 253a9d8..1fd5d19 100644 --- a/test/unittest/filestreamtest.cpp +++ b/test/unittest/filestreamtest.cpp @@ -30,9 +30,21 @@ public: FileStreamTest() : filename_(), json_(), length_() {} virtual void SetUp() { - FILE *fp = fopen(filename_ = "data/sample.json", "rb"); - if (!fp) - fp = fopen(filename_ = "../../bin/data/sample.json", "rb"); + const char *paths[] = { + "data/sample.json", + "bin/data/sample.json", + "../bin/data/sample.json", + "../../bin/data/sample.json", + "../../../bin/data/sample.json" + }; + FILE* fp = 0; + for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) { + fp = fopen(paths[i], "rb"); + if (fp) { + filename_ = paths[i]; + break; + } + } ASSERT_TRUE(fp != 0); fseek(fp, 0, SEEK_END); diff --git a/test/unittest/itoatest.cpp b/test/unittest/itoatest.cpp new file mode 100644 index 0000000..31a008c --- /dev/null +++ b/test/unittest/itoatest.cpp @@ -0,0 +1,164 @@ +// Copyright (C) 2011 Milo Yip +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include "unittest.h" +#include "rapidjson/internal/itoa.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(type-limits) +#endif + +using namespace rapidjson::internal; + +template +struct Traits { +}; + +template <> +struct Traits { + enum { kBufferSize = 11 }; + enum { kMaxDigit = 10 }; + static uint32_t Negate(uint32_t x) { return x; }; +}; + +template <> +struct Traits { + enum { kBufferSize = 12 }; + enum { kMaxDigit = 10 }; + static int32_t Negate(int32_t x) { return -x; }; +}; + +template <> +struct Traits { + enum { kBufferSize = 21 }; + enum { kMaxDigit = 20 }; + static uint64_t Negate(uint64_t x) { return x; }; +}; + +template <> +struct Traits { + enum { kBufferSize = 22 }; + enum { kMaxDigit = 20 }; + static int64_t Negate(int64_t x) { return -x; }; +}; + +template +static void VerifyValue(T value, void(*f)(T, char*), char* (*g)(T, char*)) { + char buffer1[Traits::kBufferSize]; + char buffer2[Traits::kBufferSize]; + + f(value, buffer1); + *g(value, buffer2) = '\0'; + + + EXPECT_STREQ(buffer1, buffer2); +} + +template +static void Verify(void(*f)(T, char*), char* (*g)(T, char*)) { + // Boundary cases + VerifyValue(0, f, g); + VerifyValue(std::numeric_limits::min(), f, g); + VerifyValue(std::numeric_limits::max(), f, g); + + // 2^n - 1, 2^n, 10^n - 1, 10^n until overflow + for (uint32_t power = 2; power <= 10; power += 8) { + T i = 1, last; + do { + VerifyValue(i - 1, f, g); + VerifyValue(i, f, g); + if (std::numeric_limits::min() < 0) { + VerifyValue(Traits::Negate(i), f, g); + VerifyValue(Traits::Negate(i + 1), f, g); + } + last = i; + i *= power; + } while (last < i); + } +} + +static void u32toa_naive(uint32_t value, char* buffer) { + char temp[10]; + char *p = temp; + do { + *p++ = char(value % 10) + '0'; + value /= 10; + } while (value > 0); + + do { + *buffer++ = *--p; + } while (p != temp); + + *buffer = '\0'; +} + +static void i32toa_naive(int32_t value, char* buffer) { + uint32_t u = static_cast(value); + if (value < 0) { + *buffer++ = '-'; + u = ~u + 1; + } + u32toa_naive(u, buffer); +} + +static void u64toa_naive(uint64_t value, char* buffer) { + char temp[20]; + char *p = temp; + do { + *p++ = char(value % 10) + '0'; + value /= 10; + } while (value > 0); + + do { + *buffer++ = *--p; + } while (p != temp); + + *buffer = '\0'; +} + +static void i64toa_naive(int64_t value, char* buffer) { + uint64_t u = static_cast(value); + if (value < 0) { + *buffer++ = '-'; + u = ~u + 1; + } + u64toa_naive(u, buffer); +} + +TEST(itoa, u32toa) { + Verify(u32toa_naive, u32toa); +} + +TEST(itoa, i32toa) { + Verify(i32toa_naive, i32toa); +} + +TEST(itoa, u64toa) { + Verify(u64toa_naive, u64toa); +} + +TEST(itoa, i64toa) { + Verify(i64toa_naive, i64toa); +} + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif diff --git a/test/unittest/jsoncheckertest.cpp b/test/unittest/jsoncheckertest.cpp index caa1b8a..a89b8d2 100644 --- a/test/unittest/jsoncheckertest.cpp +++ b/test/unittest/jsoncheckertest.cpp @@ -25,9 +25,22 @@ using namespace rapidjson; static char* ReadFile(const char* filename, size_t& length) { - FILE *fp = fopen(filename, "rb"); - if (!fp) - fp = fopen(filename, "rb"); + const char *paths[] = { + "jsonchecker/%s", + "bin/jsonchecker/%s", + "../bin/jsonchecker/%s", + "../../bin/jsonchecker/%s", + "../../../bin/jsonchecker/%s" + }; + char buffer[1024]; + FILE *fp = 0; + for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) { + sprintf(buffer, paths[i], filename); + fp = fopen(buffer, "rb"); + if (fp) + break; + } + if (!fp) return 0; @@ -51,17 +64,13 @@ TEST(JsonChecker, Reader) { if (i == 18) // fail18.json is valid in rapidjson, which has no limitation on depth of nesting. continue; - sprintf(filename, "jsonchecker/fail%d.json", i); + sprintf(filename, "fail%d.json", i); size_t length; char* json = ReadFile(filename, length); if (!json) { - sprintf(filename, "../../bin/jsonchecker/fail%d.json", i); - json = ReadFile(filename, length); - if (!json) { - printf("jsonchecker file %s not found", filename); - ADD_FAILURE(); - continue; - } + printf("jsonchecker file %s not found", filename); + ADD_FAILURE(); + continue; } GenericDocument, CrtAllocator> document; // Use Crt allocator to check exception-safety (no memory leak) @@ -76,16 +85,12 @@ TEST(JsonChecker, Reader) { // passX.json for (int i = 1; i <= 3; i++) { - sprintf(filename, "jsonchecker/pass%d.json", i); + sprintf(filename, "pass%d.json", i); size_t length; char* json = ReadFile(filename, length); if (!json) { - sprintf(filename, "../../bin/jsonchecker/pass%d.json", i); - json = ReadFile(filename, length); - if (!json) { - printf("jsonchecker file %s not found", filename); - continue; - } + printf("jsonchecker file %s not found", filename); + continue; } GenericDocument, CrtAllocator> document; // Use Crt allocator to check exception-safety (no memory leak) diff --git a/test/unittest/prettywritertest.cpp b/test/unittest/prettywritertest.cpp new file mode 100644 index 0000000..6ae14b9 --- /dev/null +++ b/test/unittest/prettywritertest.cpp @@ -0,0 +1,167 @@ +// Copyright (C) 2011 Milo Yip +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include "unittest.h" +#include "rapidjson/reader.h" +#include "rapidjson/prettywriter.h" +#include "rapidjson/stringbuffer.h" +#include "rapidjson/filewritestream.h" + +using namespace rapidjson; + +static const char kJson[] = "{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3,-1],\"u64\":1234567890123456789,\"i64\":-1234567890123456789}"; +static const char kPrettyJson[] = +"{\n" +" \"hello\": \"world\",\n" +" \"t\": true,\n" +" \"f\": false,\n" +" \"n\": null,\n" +" \"i\": 123,\n" +" \"pi\": 3.1416,\n" +" \"a\": [\n" +" 1,\n" +" 2,\n" +" 3,\n" +" -1\n" +" ],\n" +" \"u64\": 1234567890123456789,\n" +" \"i64\": -1234567890123456789\n" +"}"; + +TEST(PrettyWriter, Basic) { + StringBuffer buffer; + PrettyWriter writer(buffer); + Reader reader; + StringStream s(kJson); + reader.Parse(s, writer); + EXPECT_STREQ(kPrettyJson, buffer.GetString()); +} + +TEST(PrettyWriter, SetIndent) { + StringBuffer buffer; + PrettyWriter writer(buffer); + writer.SetIndent('\t', 1); + Reader reader; + StringStream s(kJson); + reader.Parse(s, writer); + EXPECT_STREQ( + "{\n" + "\t\"hello\": \"world\",\n" + "\t\"t\": true,\n" + "\t\"f\": false,\n" + "\t\"n\": null,\n" + "\t\"i\": 123,\n" + "\t\"pi\": 3.1416,\n" + "\t\"a\": [\n" + "\t\t1,\n" + "\t\t2,\n" + "\t\t3,\n" + "\t\t-1\n" + "\t],\n" + "\t\"u64\": 1234567890123456789,\n" + "\t\"i64\": -1234567890123456789\n" + "}", + buffer.GetString()); +} + +TEST(PrettyWriter, String) { + StringBuffer buffer; + PrettyWriter writer(buffer); + EXPECT_TRUE(writer.StartArray()); + EXPECT_TRUE(writer.String("Hello\n")); + EXPECT_TRUE(writer.EndArray()); + EXPECT_STREQ("[\n \"Hello\\n\"\n]", buffer.GetString()); +} + +#if RAPIDJSON_HAS_STDSTRING +TEST(PrettyWriter, String_STDSTRING) { + StringBuffer buffer; + PrettyWriter writer(buffer); + EXPECT_TRUE(writer.StartArray()); + EXPECT_TRUE(writer.String(std::string("Hello\n"))); + EXPECT_TRUE(writer.EndArray()); + EXPECT_STREQ("[\n \"Hello\\n\"\n]", buffer.GetString()); +} +#endif + +#include + +class OStreamWrapper { +public: + typedef char Ch; + + OStreamWrapper(std::ostream& os) : os_(os) {} + + Ch Peek() const { assert(false); return '\0'; } + Ch Take() { assert(false); return '\0'; } + size_t Tell() const { return 0; } + + Ch* PutBegin() { assert(false); return 0; } + void Put(Ch c) { os_.put(c); } + void Flush() { os_.flush(); } + size_t PutEnd(Ch*) { assert(false); return 0; } + +private: + OStreamWrapper(const OStreamWrapper&); + OStreamWrapper& operator=(const OStreamWrapper&); + + std::ostream& os_; +}; + +// For covering PutN() generic version +TEST(PrettyWriter, OStreamWrapper) { + StringStream s(kJson); + + std::stringstream ss; + OStreamWrapper os(ss); + + PrettyWriter writer(os); + + Reader reader; + reader.Parse(s, writer); + + std::string actual = ss.str(); + EXPECT_STREQ(kPrettyJson, actual.c_str()); +} + +// For covering FileWriteStream::PutN() +TEST(PrettyWriter, FileWriteStream) { + char filename[L_tmpnam]; + FILE* fp = TempFile(filename); + char buffer[16]; + FileWriteStream os(fp, buffer, sizeof(buffer)); + PrettyWriter writer(os); + Reader reader; + StringStream s(kJson); + reader.Parse(s, writer); + fclose(fp); + + fp = fopen(filename, "rb"); + fseek(fp, 0, SEEK_END); + size_t size = (size_t)ftell(fp); + fseek(fp, 0, SEEK_SET); + char* json = (char*)malloc(size + 1); + size_t readLength = fread(json, 1, size, fp); + json[readLength] = '\0'; + fclose(fp); + remove(filename); + EXPECT_STREQ(kPrettyJson, json); + free(json); +} \ No newline at end of file diff --git a/test/unittest/readertest.cpp b/test/unittest/readertest.cpp index 8c8c63c..0b4196f 100644 --- a/test/unittest/readertest.cpp +++ b/test/unittest/readertest.cpp @@ -254,6 +254,32 @@ static void TestParseDouble() { TEST_DOUBLE(fullPrecision, "1.00000000000000011102230246251565404236316680908203124", 1.0); // previous double TEST_DOUBLE(fullPrecision, "1.00000000000000011102230246251565404236316680908203126", 1.00000000000000022); // next double + // Numbers from https://github.com/floitsch/double-conversion/blob/master/test/cctest/test-strtod.cc + + TEST_DOUBLE(fullPrecision, "72057594037927928.0", 72057594037927928.0); + TEST_DOUBLE(fullPrecision, "72057594037927936.0", 72057594037927936.0); + TEST_DOUBLE(fullPrecision, "72057594037927932.0", 72057594037927936.0); + TEST_DOUBLE(fullPrecision, "7205759403792793199999e-5", 72057594037927928.0); + TEST_DOUBLE(fullPrecision, "7205759403792793200001e-5", 72057594037927936.0); + + TEST_DOUBLE(fullPrecision, "9223372036854774784.0", 9223372036854774784.0); + TEST_DOUBLE(fullPrecision, "9223372036854775808.0", 9223372036854775808.0); + TEST_DOUBLE(fullPrecision, "9223372036854775296.0", 9223372036854775808.0); + TEST_DOUBLE(fullPrecision, "922337203685477529599999e-5", 9223372036854774784.0); + TEST_DOUBLE(fullPrecision, "922337203685477529600001e-5", 9223372036854775808.0); + + TEST_DOUBLE(fullPrecision, "10141204801825834086073718800384", 10141204801825834086073718800384.0); + TEST_DOUBLE(fullPrecision, "10141204801825835211973625643008", 10141204801825835211973625643008.0); + TEST_DOUBLE(fullPrecision, "10141204801825834649023672221696", 10141204801825835211973625643008.0); + TEST_DOUBLE(fullPrecision, "1014120480182583464902367222169599999e-5", 10141204801825834086073718800384.0); + TEST_DOUBLE(fullPrecision, "1014120480182583464902367222169600001e-5", 10141204801825835211973625643008.0); + + TEST_DOUBLE(fullPrecision, "5708990770823838890407843763683279797179383808", 5708990770823838890407843763683279797179383808.0); + TEST_DOUBLE(fullPrecision, "5708990770823839524233143877797980545530986496", 5708990770823839524233143877797980545530986496.0); + TEST_DOUBLE(fullPrecision, "5708990770823839207320493820740630171355185152", 5708990770823839524233143877797980545530986496.0); + TEST_DOUBLE(fullPrecision, "5708990770823839207320493820740630171355185151999e-3", 5708990770823838890407843763683279797179383808.0); + TEST_DOUBLE(fullPrecision, "5708990770823839207320493820740630171355185152001e-3", 5708990770823839524233143877797980545530986496.0); + { char n1e308[310]; // '1' followed by 308 '0' n1e308[0] = '1'; @@ -263,28 +289,53 @@ static void TestParseDouble() { TEST_DOUBLE(fullPrecision, n1e308, 1E308); } -#if 0 // Very slow - static const unsigned count = 10000000; - // Random test for double + // Cover trimming + TEST_DOUBLE(fullPrecision, +"2.22507385850720113605740979670913197593481954635164564802342610972482222202107694551652952390813508" +"7914149158913039621106870086438694594645527657207407820621743379988141063267329253552286881372149012" +"9811224514518898490572223072852551331557550159143974763979834118019993239625482890171070818506906306" +"6665599493827577257201576306269066333264756530000924588831643303777979186961204949739037782970490505" +"1080609940730262937128958950003583799967207254304360284078895771796150945516748243471030702609144621" +"5722898802581825451803257070188608721131280795122334262883686223215037756666225039825343359745688844" +"2390026549819838548794829220689472168983109969836584681402285424333066033985088644580400103493397042" +"7567186443383770486037861622771738545623065874679014086723327636718751234567890123456789012345678901" +"e-308", + 2.2250738585072014e-308); + { + static const unsigned count = 100; // Tested with 1000000 locally Random r; + Reader reader; // Reusing reader to prevent heap allocation - for (unsigned i = 0; i < count; i++) { - internal::Double d; - do { + // Exhaustively test different exponents with random significant + for (uint64_t exp = 0; exp < 2047; exp++) { + ; + for (unsigned i = 0; i < count; i++) { // Need to call r() in two statements for cross-platform coherent sequence. - uint64_t u = uint64_t(r()) << 32; + uint64_t u = (exp << 52) | uint64_t(r() & 0x000FFFFF) << 32; u |= uint64_t(r()); - d = internal::Double(u); - } while (d.IsNan() || d.IsInf()/* || !d.IsNormal()*/); // Also work for subnormal now + internal::Double d = internal::Double(u); - char buffer[32]; - *internal::dtoa(d.Value(), buffer) = '\0'; - TEST_DOUBLE(fullPrecision, buffer, d.Value()); + char buffer[32]; + *internal::dtoa(d.Value(), buffer) = '\0'; + + StringStream s(buffer); + ParseDoubleHandler h; + ASSERT_EQ(kParseErrorNone, reader.Parse(s, h).Code()); + EXPECT_EQ(1u, h.step_); + internal::Double a(h.actual_); + if (fullPrecision) { + EXPECT_EQ(d.Uint64Value(), a.Uint64Value()); + if (d.Uint64Value() != a.Uint64Value()) + printf(" String: %sn Actual: %.17gnExpected: %.17gn", buffer, h.actual_, d.Value()); + } + else { + EXPECT_EQ(d.Sign(), a.Sign()); /* for 0.0 != -0.0 */ + EXPECT_DOUBLE_EQ(d.Value(), h.actual_); + } + } } } -#endif - #undef TEST_DOUBLE } @@ -487,6 +538,17 @@ TEST(Reader, ParseString_Transcoding) { EXPECT_EQ(StrLen(e), h.length_); } +TEST(Reader, ParseString_TranscodingWithValidation) { + const char* x = "\"Hello\""; + const wchar_t* e = L"Hello"; + GenericStringStream > is(x); + GenericReader, UTF16<> > reader; + ParseStringHandler > h; + reader.Parse(is, h); + EXPECT_EQ(0, StrCmp::Ch>(e, h.str_)); + EXPECT_EQ(StrLen(e), h.length_); +} + TEST(Reader, ParseString_NonDestructive) { StringStream s("\"Hello\\nWorld\""); ParseStringHandler > h; @@ -496,24 +558,31 @@ TEST(Reader, ParseString_NonDestructive) { EXPECT_EQ(11u, h.length_); } -ParseErrorCode TestString(const char* str) { - StringStream s(str); - BaseReaderHandler<> h; - Reader reader; - reader.Parse(s, h); +template +ParseErrorCode TestString(const typename Encoding::Ch* str) { + GenericStringStream s(str); + BaseReaderHandler h; + GenericReader reader; + reader.template Parse(s, h); return reader.GetParseErrorCode(); } TEST(Reader, ParseString_Error) { #define TEST_STRING_ERROR(errorCode, str)\ - EXPECT_EQ(errorCode, TestString(str)) + EXPECT_EQ(errorCode, TestString >(str)) #define ARRAY(...) { __VA_ARGS__ } -#define TEST_STRINGENCODING_ERROR(Encoding, utype, array) \ +#define TEST_STRINGENCODING_ERROR(Encoding, TargetEncoding, utype, array) \ { \ static const utype ue[] = array; \ static const Encoding::Ch* e = reinterpret_cast(&ue[0]); \ - EXPECT_EQ(kParseErrorStringInvalidEncoding, TestString(e));\ + EXPECT_EQ(kParseErrorStringInvalidEncoding, TestString(e));\ + /* decode error */\ + GenericStringStream s(e);\ + BaseReaderHandler h;\ + GenericReader reader;\ + reader.Parse(s, h);\ + EXPECT_EQ(kParseErrorStringInvalidEncoding, reader.GetParseErrorCode());\ } // Invalid escape character in string. @@ -542,7 +611,7 @@ TEST(Reader, ParseString_Error) { char e[] = { '[', '\"', 0, '\"', ']', '\0' }; for (unsigned char c = 0x80u; c <= 0xBFu; c++) { e[2] = c; - ParseErrorCode error = TestString(e); + ParseErrorCode error = TestString >(e); EXPECT_EQ(kParseErrorStringInvalidEncoding, error); if (error != kParseErrorStringInvalidEncoding) std::cout << (unsigned)(unsigned char)c << std::endl; @@ -561,30 +630,40 @@ TEST(Reader, ParseString_Error) { // 4 Overlong sequences // 4.1 Examples of an overlong ASCII character - TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xC0u, 0xAFu, '\"', ']', '\0')); - TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xE0u, 0x80u, 0xAFu, '\"', ']', '\0')); - TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xF0u, 0x80u, 0x80u, 0xAFu, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xC0u, 0xAFu, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xE0u, 0x80u, 0xAFu, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xF0u, 0x80u, 0x80u, 0xAFu, '\"', ']', '\0')); // 4.2 Maximum overlong sequences - TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xC1u, 0xBFu, '\"', ']', '\0')); - TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xE0u, 0x9Fu, 0xBFu, '\"', ']', '\0')); - TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xF0u, 0x8Fu, 0xBFu, 0xBFu, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xC1u, 0xBFu, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xE0u, 0x9Fu, 0xBFu, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xF0u, 0x8Fu, 0xBFu, 0xBFu, '\"', ']', '\0')); // 4.3 Overlong representation of the NUL character - TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xC0u, 0x80u, '\"', ']', '\0')); - TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xE0u, 0x80u, 0x80u, '\"', ']', '\0')); - TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xF0u, 0x80u, 0x80u, 0x80u, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xC0u, 0x80u, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xE0u, 0x80u, 0x80u, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xF0u, 0x80u, 0x80u, 0x80u, '\"', ']', '\0')); // 5 Illegal code positions // 5.1 Single UTF-16 surrogates - TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xA0u, 0x80u, '\"', ']', '\0')); - TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xADu, 0xBFu, '\"', ']', '\0')); - TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xAEu, 0x80u, '\"', ']', '\0')); - TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xAFu, 0xBFu, '\"', ']', '\0')); - TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xB0u, 0x80u, '\"', ']', '\0')); - TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xBEu, 0x80u, '\"', ']', '\0')); - TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xBFu, 0xBFu, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xA0u, 0x80u, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xADu, 0xBFu, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xAEu, 0x80u, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xAFu, 0xBFu, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xB0u, 0x80u, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xBEu, 0x80u, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xBFu, 0xBFu, '\"', ']', '\0')); + + // Malform UTF-16 sequences + TEST_STRINGENCODING_ERROR(UTF16<>, UTF8<>, wchar_t, ARRAY('[', '\"', 0xDC00, 0xDC00, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF16<>, UTF8<>, wchar_t, ARRAY('[', '\"', 0xD800, 0xD800, '\"', ']', '\0')); + + // Malform UTF-32 sequence + TEST_STRINGENCODING_ERROR(UTF32<>, UTF8<>, unsigned, ARRAY('[', '\"', 0x110000, '\"', ']', '\0')); + + // Malform ASCII sequence + TEST_STRINGENCODING_ERROR(ASCII<>, UTF8<>, char, ARRAY('[', '\"', char(0x80), '\"', ']', '\0')); #undef ARRAY #undef TEST_STRINGARRAY_ERROR @@ -974,6 +1053,17 @@ TEST(Reader, IterativeParsing_ErrorHandling) { TESTERRORHANDLING("{\"a\"}", kParseErrorObjectMissColon, 4u); TESTERRORHANDLING("{\"a\": 1", kParseErrorObjectMissCommaOrCurlyBracket, 7u); TESTERRORHANDLING("[1 2 3]", kParseErrorArrayMissCommaOrSquareBracket, 3u); + TESTERRORHANDLING("{\"a: 1", kParseErrorStringMissQuotationMark, 5u); + + // Any JSON value can be a valid root element in RFC7159. + TESTERRORHANDLING("\"ab", kParseErrorStringMissQuotationMark, 2u); + TESTERRORHANDLING("truE", kParseErrorValueInvalid, 3u); + TESTERRORHANDLING("False", kParseErrorValueInvalid, 0u); + TESTERRORHANDLING("true, false", kParseErrorDocumentRootNotSingular, 4u); + TESTERRORHANDLING("false, false", kParseErrorDocumentRootNotSingular, 5u); + TESTERRORHANDLING("nulL", kParseErrorValueInvalid, 3u); + TESTERRORHANDLING("null , null", kParseErrorDocumentRootNotSingular, 5u); + TESTERRORHANDLING("1a", kParseErrorDocumentRootNotSingular, 1u); } template > @@ -1177,6 +1267,59 @@ TEST(Reader, IterativeParsing_ShortCircuit) { } } +// For covering BaseReaderHandler default functions +TEST(Reader, BaseReaderHandler_Default) { + BaseReaderHandler<> h; + Reader reader; + StringStream is("[null, true, -1, 1, -1234567890123456789, 1234567890123456789, 3.14, \"s\", { \"a\" : 1 }]"); + EXPECT_TRUE(reader.Parse(is, h)); +} + +template +struct TerminateHandler { + bool Null() { return e != 0; } + bool Bool(bool) { return e != 1; } + bool Int(int) { return e != 2; } + bool Uint(unsigned) { return e != 3; } + bool Int64(int64_t) { return e != 4; } + bool Uint64(uint64_t) { return e != 5; } + bool Double(double) { return e != 6; } + bool String(const char*, SizeType, bool) { return e != 7; } + bool StartObject() { return e != 8; } + bool Key(const char*, SizeType, bool) { return e != 9; } + bool EndObject(SizeType) { return e != 10; } + bool StartArray() { return e != 11; } + bool EndArray(SizeType) { return e != 12; } +}; + +#define TEST_TERMINATION(e, json)\ +{\ + Reader reader;\ + TerminateHandler h;\ + StringStream is(json);\ + EXPECT_FALSE(reader.Parse(is, h));\ + EXPECT_EQ(kParseErrorTermination, reader.GetParseErrorCode());\ +} + +TEST(Reader, ParseTerminationByHandler) { + TEST_TERMINATION(0, "[null"); + TEST_TERMINATION(1, "[true"); + TEST_TERMINATION(1, "[false"); + TEST_TERMINATION(2, "[-1"); + TEST_TERMINATION(3, "[1"); + TEST_TERMINATION(4, "[-1234567890123456789"); + TEST_TERMINATION(5, "[1234567890123456789"); + TEST_TERMINATION(6, "[0.5]"); + TEST_TERMINATION(7, "[\"a\""); + TEST_TERMINATION(8, "[{"); + TEST_TERMINATION(9, "[{\"a\""); + TEST_TERMINATION(10, "[{}"); + TEST_TERMINATION(10, "[{\"a\":1}"); // non-empty object + TEST_TERMINATION(11, "{\"a\":["); + TEST_TERMINATION(12, "{\"a\":[]"); + TEST_TERMINATION(12, "{\"a\":[1]"); // non-empty array +} + #ifdef __GNUC__ RAPIDJSON_DIAG_POP #endif diff --git a/test/unittest/simdtest.cpp b/test/unittest/simdtest.cpp new file mode 100644 index 0000000..217db07 --- /dev/null +++ b/test/unittest/simdtest.cpp @@ -0,0 +1,74 @@ +// Copyright (C) 2011 Milo Yip +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Since Travis CI installs old Valgrind 3.7.0, which fails with some SSE4.2 +// The unit tests prefix with SIMD should be skipped by Valgrind test + +// __SSE2__ and __SSE4_2__ are recognized by gcc, clang, and the Intel compiler. +// We use -march=native with gmake to enable -msse2 and -msse4.2, if supported. +#if defined(__SSE4_2__) +# define RAPIDJSON_SSE42 +#elif defined(__SSE2__) +# define RAPIDJSON_SSE2 +#endif + +#define RAPIDJSON_NAMESPACE rapidjson_simd + +#include "unittest.h" + +#include "rapidjson/reader.h" + +using namespace rapidjson_simd; + +#ifdef RAPIDJSON_SSE2 +#define SIMD_SUFFIX(name) name##_SSE2 +#elif defined(RAPIDJSON_SSE42) +#define SIMD_SUFFIX(name) name##_SSE42 +#else +#define SIMD_SUFFIX(name) name +#endif + +template +void TestSkipWhitespace() { + for (int step = 1; step < 32; step++) { + char buffer[1025]; + for (size_t i = 0; i < 1024; i++) + buffer[i] = " \t\r\n"[i % 4]; + for (size_t i = 0; i < 1024; i += step) + buffer[i] = 'X'; + buffer[1024] = '\0'; + + StreamType s(buffer); + size_t i = 0; + for (;;) { + SkipWhitespace(s); + if (s.Peek() == '\0') + break; + EXPECT_EQ(i, s.Tell()); + EXPECT_EQ('X', s.Take()); + i += step; + } + } +} + +TEST(SIMD, SIMD_SUFFIX(SkipWhitespace)) { + TestSkipWhitespace(); + TestSkipWhitespace(); +} diff --git a/test/unittest/stringbuffertest.cpp b/test/unittest/stringbuffertest.cpp index 71e0caf..7cfde4f 100644 --- a/test/unittest/stringbuffertest.cpp +++ b/test/unittest/stringbuffertest.cpp @@ -54,6 +54,10 @@ TEST(StringBuffer, Push) { buffer.Push(5); EXPECT_EQ(5u, buffer.GetSize()); + + // Causes sudden expansion to make the stack's capacity equal to size + buffer.Push(65536u); + EXPECT_EQ(5u + 65536u, buffer.GetSize()); } TEST(StringBuffer, Pop) { diff --git a/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp index 846b1c8..fa9547b 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -203,12 +203,28 @@ TEST(Value, EqualtoOperator) { EXPECT_TRUE(z.RemoveMember("t")); TestUnequal(x, z); TestEqual(y, z); - y.AddMember("t", true, crtAllocator); - z.AddMember("t", true, z.GetAllocator()); + y.AddMember("t", false, crtAllocator); + z.AddMember("t", false, z.GetAllocator()); + TestUnequal(x, y); + TestUnequal(z, x); + y["t"] = true; + z["t"] = true; TestEqual(x, y); TestEqual(y, z); TestEqual(z, x); + // Swapping element order is not OK + x["a"][0].Swap(x["a"][1]); + TestUnequal(x, y); + x["a"][0].Swap(x["a"][1]); + TestEqual(x, y); + + // Array of different size + x["a"].PushBack(4, allocator); + TestUnequal(x, y); + x["a"].PopBack(); + TestEqual(x, y); + // Issue #129: compare Uint64 x.SetUint64(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0xFFFFFFF0)); y.SetUint64(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0xFFFFFFFF)); @@ -229,6 +245,13 @@ void TestCopyFrom() { EXPECT_STREQ(v1.GetString(), v2.GetString()); EXPECT_EQ(v1.GetString(), v2.GetString()); // string NOT copied + v1.SetString("bar", a); // copy string + v2.CopyFrom(v1, a); + EXPECT_TRUE(v1.GetType() == v2.GetType()); + EXPECT_STREQ(v1.GetString(), v2.GetString()); + EXPECT_NE(v1.GetString(), v2.GetString()); // string copied + + v1.SetArray().PushBack(1234, a); v2.CopyFrom(v1, a); EXPECT_TRUE(v2.IsArray()); @@ -462,10 +485,19 @@ TEST(Value, Int64) { z.SetInt64(2147483648LL); // 2^31, cannot cast as int EXPECT_FALSE(z.IsInt()); EXPECT_TRUE(z.IsUint()); + EXPECT_NEAR(2147483648.0, z.GetDouble(), 0.0); z.SetInt64(4294967296LL); // 2^32, cannot cast as uint EXPECT_FALSE(z.IsInt()); EXPECT_FALSE(z.IsUint()); + EXPECT_NEAR(4294967296.0, z.GetDouble(), 0.0); + + z.SetInt64(-2147483649LL); // -2^31-1, cannot cast as int + EXPECT_FALSE(z.IsInt()); + EXPECT_NEAR(-2147483649.0, z.GetDouble(), 0.0); + + z.SetInt64(static_cast(RAPIDJSON_UINT64_C2(0x80000000, 00000000))); + EXPECT_DOUBLE_EQ(-9223372036854775808.0, z.GetDouble()); } TEST(Value, Uint64) { @@ -508,9 +540,8 @@ TEST(Value, Uint64) { z.SetUint64(9223372036854775808uLL); // 2^63 cannot cast as int64 EXPECT_FALSE(z.IsInt64()); - - // Issue 48 - EXPECT_EQ(9223372036854775808uLL, z.GetUint64()); + EXPECT_EQ(9223372036854775808uLL, z.GetUint64()); // Issue 48 + EXPECT_DOUBLE_EQ(9223372036854775808.0, z.GetDouble()); } TEST(Value, Double) { @@ -977,6 +1008,7 @@ TEST(Value, Object) { EXPECT_STREQ("Banana", x["B"].GetString()); EXPECT_STREQ("CherryD", x[C0D].GetString()); EXPECT_STREQ("CherryD", x[othername].GetString()); + EXPECT_THROW(x["nonexist"], AssertException); // const operator[] EXPECT_STREQ("Apple", y["A"].GetString()); @@ -1041,13 +1073,15 @@ TEST(Value, Object) { EXPECT_FALSE(citr >= itr); // RemoveMember() - x.RemoveMember("A"); + EXPECT_TRUE(x.RemoveMember("A")); EXPECT_FALSE(x.HasMember("A")); - x.RemoveMember("B"); + EXPECT_TRUE(x.RemoveMember("B")); EXPECT_FALSE(x.HasMember("B")); - x.RemoveMember(othername); + EXPECT_FALSE(x.RemoveMember("nonexist")); + + EXPECT_TRUE(x.RemoveMember(othername)); EXPECT_FALSE(x.HasMember(name)); EXPECT_TRUE(x.MemberBegin() == x.MemberEnd()); @@ -1237,3 +1271,46 @@ TEST(Value, AllocateShortString) { TestShortStringOptimization("123456789012345"); // edge case: 15 chars in 64-bit mode (=> short string) TestShortStringOptimization("1234567890123456"); // edge case: 16 chars in 64-bit mode (=> regular string) } + +template +struct TerminateHandler { + bool Null() { return e != 0; } + bool Bool(bool) { return e != 1; } + bool Int(int) { return e != 2; } + bool Uint(unsigned) { return e != 3; } + bool Int64(int64_t) { return e != 4; } + bool Uint64(uint64_t) { return e != 5; } + bool Double(double) { return e != 6; } + bool String(const char*, SizeType, bool) { return e != 7; } + bool StartObject() { return e != 8; } + bool Key(const char*, SizeType, bool) { return e != 9; } + bool EndObject(SizeType) { return e != 10; } + bool StartArray() { return e != 11; } + bool EndArray(SizeType) { return e != 12; } +}; + +#define TEST_TERMINATION(e, json)\ +{\ + Document d; \ + EXPECT_FALSE(d.Parse(json).HasParseError()); \ + Reader reader; \ + TerminateHandler h;\ + EXPECT_FALSE(d.Accept(h));\ +} + +TEST(Value, AcceptTerminationByHandler) { + TEST_TERMINATION(0, "[null]"); + TEST_TERMINATION(1, "[true]"); + TEST_TERMINATION(1, "[false]"); + TEST_TERMINATION(2, "[-1]"); + TEST_TERMINATION(3, "[2147483648]"); + TEST_TERMINATION(4, "[-1234567890123456789]"); + TEST_TERMINATION(5, "[9223372036854775808]"); + TEST_TERMINATION(6, "[0.5]"); + TEST_TERMINATION(7, "[\"a\"]"); + TEST_TERMINATION(8, "[{}]"); + TEST_TERMINATION(9, "[{\"a\":1}]"); + TEST_TERMINATION(10, "[{}]"); + TEST_TERMINATION(11, "{\"a\":[]}"); + TEST_TERMINATION(12, "{\"a\":[]}"); +} diff --git a/test/unittest/writertest.cpp b/test/unittest/writertest.cpp index 642161a..85d533d 100644 --- a/test/unittest/writertest.cpp +++ b/test/unittest/writertest.cpp @@ -46,7 +46,7 @@ TEST(Writer, Compact) { StringBuffer buffer; \ Writer writer(buffer); \ Reader reader; \ - reader.Parse<0>(s, writer); \ + reader.Parse(s, writer); \ EXPECT_STREQ(json, buffer.GetString()); \ EXPECT_TRUE(writer.IsComplete()); \ } @@ -89,11 +89,28 @@ TEST(Writer, String) { TEST_ROUNDTRIP("[\"Hello\"]"); TEST_ROUNDTRIP("[\"Hello\\u0000World\"]"); TEST_ROUNDTRIP("[\"\\\"\\\\/\\b\\f\\n\\r\\t\"]"); + +#if RAPIDJSON_HAS_STDSTRING + { + StringBuffer buffer; + Writer writer(buffer); + writer.String(std::string("Hello\n")); + EXPECT_STREQ("\"Hello\\n\"", buffer.GetString()); + } +#endif } TEST(Writer, Double) { TEST_ROUNDTRIP("[1.2345,1.2345678,0.123456789012,1234567.8]"); - TEST_ROUNDTRIP("[-0.0]"); // Issue #289 + TEST_ROUNDTRIP("0.0"); + TEST_ROUNDTRIP("-0.0"); // Issue #289 + TEST_ROUNDTRIP("1e30"); + TEST_ROUNDTRIP("1.0"); + TEST_ROUNDTRIP("5e-324"); // Min subnormal positive double + TEST_ROUNDTRIP("2.225073858507201e-308"); // Max subnormal positive double + TEST_ROUNDTRIP("2.2250738585072014e-308"); // Min normal positive double + TEST_ROUNDTRIP("1.7976931348623157e308"); // Max double + } TEST(Writer, Transcode) { @@ -152,7 +169,7 @@ private: }; TEST(Writer, OStreamWrapper) { - StringStream s("{ \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3] } "); + StringStream s("{ \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3], \"u64\": 1234567890123456789, \"i64\":-1234567890123456789 } "); std::stringstream ss; OStreamWrapper os(ss); @@ -163,7 +180,7 @@ TEST(Writer, OStreamWrapper) { reader.Parse<0>(s, writer); std::string actual = ss.str(); - EXPECT_STREQ("{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3]}", actual.c_str()); + EXPECT_STREQ("{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3],\"u64\":1234567890123456789,\"i64\":-1234567890123456789}", actual.c_str()); } TEST(Writer, AssertRootMayBeAnyValue) { @@ -306,3 +323,61 @@ TEST(Writer, RootValueIsComplete) { T(writer.String("")); #undef T } + +TEST(Writer, InvalidEncoding) { + // Fail in decoding invalid UTF-8 sequence http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt + { + GenericStringBuffer > buffer; + Writer >, UTF8<>, UTF16<> > writer(buffer); + writer.StartArray(); + EXPECT_FALSE(writer.String("\xfe")); + EXPECT_FALSE(writer.String("\xff")); + EXPECT_FALSE(writer.String("\xfe\xfe\xff\xff")); + writer.EndArray(); + } + + // Fail in encoding + { + StringBuffer buffer; + Writer > writer(buffer); + static const UTF32<>::Ch s[] = { 0x110000, 0 }; // Out of U+0000 to U+10FFFF + EXPECT_FALSE(writer.String(s)); + } + + // Fail in unicode escaping in ASCII output + { + StringBuffer buffer; + Writer, ASCII<> > writer(buffer); + static const UTF32<>::Ch s[] = { 0x110000, 0 }; // Out of U+0000 to U+10FFFF + EXPECT_FALSE(writer.String(s)); + } +} + +TEST(Writer, InvalidEventSequence) { + // {] + { + StringBuffer buffer; + Writer writer(buffer); + writer.StartObject(); + EXPECT_THROW(writer.EndArray(), AssertException); + EXPECT_FALSE(writer.IsComplete()); + } + + // [} + { + StringBuffer buffer; + Writer writer(buffer); + writer.StartArray(); + EXPECT_THROW(writer.EndObject(), AssertException); + EXPECT_FALSE(writer.IsComplete()); + } + + // { 1: + { + StringBuffer buffer; + Writer writer(buffer); + writer.StartObject(); + EXPECT_THROW(writer.Int(1), AssertException); + EXPECT_FALSE(writer.IsComplete()); + } +}