Commit 75ee552f authored by Milo Yip's avatar Milo Yip

Merge pull request #304 from miloyip/issue298_coverage

100% line of code coverage
parents 3f562e11 0edf27fa
......@@ -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
......@@ -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 <unsigned parseFlags, typename InputStream>
GenericDocument& ParseStream(InputStream& is) {
return ParseStream<parseFlags,Encoding,InputStream>(is);
return ParseStream<parseFlags, Encoding, InputStream>(is);
}
//! Parse JSON text from an input stream (with \ref kParseDefaultFlags)
......
......@@ -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_];
......
......@@ -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;
......
......@@ -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<uint64_t>(exponent + kDpExponentBias);
u.u64 = (significand & kDpSignificandMask) | (be << kDpSignificandSize);
const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 :
static_cast<uint64_t>(e + kDpExponentBias);
u.u64 = (f & kDpSignificandMask) | (be << kDpSignificandSize);
return u.d;
}
......
......@@ -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<uint32_t>(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<char>('0' + static_cast<char>(d));
......
......@@ -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; }
......
......@@ -15,6 +15,8 @@
#ifndef RAPIDJSON_ITOA_
#define RAPIDJSON_ITOA_
#include "../rapidjson.h"
RAPIDJSON_NAMESPACE_BEGIN
namespace internal {
......
......@@ -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);
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;
dS.Difference(bS, &delta);
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;
}
......
......@@ -78,7 +78,7 @@ public:
#if RAPIDJSON_HAS_STDSTRING
bool String(const std::basic_string<Ch>& 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;
......
......@@ -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
......
......@@ -1227,14 +1227,9 @@ private:
// May return a new state on state pop.
template <unsigned parseFlags, typename InputStream, typename Handler>
RAPIDJSON_FORCEINLINE IterativeParsingState Transit(IterativeParsingState src, Token token, IterativeParsingState dst, InputStream& is, Handler& handler) {
switch (dst) {
case IterativeParsingStartState:
RAPIDJSON_ASSERT(false);
return IterativeParsingErrorState;
case IterativeParsingFinishState:
return dst;
(void)token;
switch (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<parseFlags>(is, handler);
if (HasParseError()) {
return IterativeParsingErrorState;
}
return IterativeParsingFinishState;
default:
RAPIDJSON_ASSERT(false);
return IterativeParsingErrorState;
}
}
......
......@@ -127,7 +127,7 @@ public:
#if RAPIDJSON_HAS_STDSTRING
bool String(const std::basic_string<Ch>& 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<SourceEncoding, TargetEncoding>::Transcode(is, *os_);
if (!Transcoder<SourceEncoding, TargetEncoding>::Transcode(is, *os_))
return false;
}
os_->Put('\"');
return true;
......
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")
......
// 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 <typename Allocator>
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());
}
}
......@@ -28,13 +28,11 @@
using namespace rapidjson;
template <typename Allocator, typename StackAllocator>
void ParseTest() {
typedef GenericDocument<UTF8<>, Allocator, StackAllocator> DocumentType;
template <typename DocumentType>
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 <typename Allocator, typename StackAllocator>
void ParseTest() {
typedef GenericDocument<UTF8<>, 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<MemoryPoolAllocator<>, CrtAllocator>();
ParseTest<MemoryPoolAllocator<>, 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
......
......@@ -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<unsigned, FileReadStream> 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<unsigned, MemoryStream> 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<unsigned, MemoryStream> eis(ms, kUTF8);
EXPECT_FALSE(eis.HasBOM());
EXPECT_EQ(kUTF8, eis.GetType());
}
}
TEST_F(EncodedStreamTest, EncodedOutputStream) {
......
......@@ -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);
......
// 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 <typename T>
struct Traits {
};
template <>
struct Traits<uint32_t> {
enum { kBufferSize = 11 };
enum { kMaxDigit = 10 };
static uint32_t Negate(uint32_t x) { return x; };
};
template <>
struct Traits<int32_t> {
enum { kBufferSize = 12 };
enum { kMaxDigit = 10 };
static int32_t Negate(int32_t x) { return -x; };
};
template <>
struct Traits<uint64_t> {
enum { kBufferSize = 21 };
enum { kMaxDigit = 20 };
static uint64_t Negate(uint64_t x) { return x; };
};
template <>
struct Traits<int64_t> {
enum { kBufferSize = 22 };
enum { kMaxDigit = 20 };
static int64_t Negate(int64_t x) { return -x; };
};
template <typename T>
static void VerifyValue(T value, void(*f)(T, char*), char* (*g)(T, char*)) {
char buffer1[Traits<T>::kBufferSize];
char buffer2[Traits<T>::kBufferSize];
f(value, buffer1);
*g(value, buffer2) = '\0';
EXPECT_STREQ(buffer1, buffer2);
}
template <typename T>
static void Verify(void(*f)(T, char*), char* (*g)(T, char*)) {
// Boundary cases
VerifyValue<T>(0, f, g);
VerifyValue<T>(std::numeric_limits<T>::min(), f, g);
VerifyValue<T>(std::numeric_limits<T>::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<T>(i - 1, f, g);
VerifyValue<T>(i, f, g);
if (std::numeric_limits<T>::min() < 0) {
VerifyValue<T>(Traits<T>::Negate(i), f, g);
VerifyValue<T>(Traits<T>::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<uint32_t>(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<uint64_t>(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
......@@ -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<UTF8<>, 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<UTF8<>, CrtAllocator> document; // Use Crt allocator to check exception-safety (no memory leak)
......
// 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<StringBuffer> writer(buffer);
Reader reader;
StringStream s(kJson);
reader.Parse(s, writer);
EXPECT_STREQ(kPrettyJson, buffer.GetString());
}
TEST(PrettyWriter, SetIndent) {
StringBuffer buffer;
PrettyWriter<StringBuffer> 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<StringBuffer> 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<StringBuffer> 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 <sstream>
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<OStreamWrapper> 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<FileWriteStream> 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
This diff is collapsed.
// 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 <typename StreamType>
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<StringStream>();
TestSkipWhitespace<InsituStringStream>();
}
......@@ -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) {
......
......@@ -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<int64_t>(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 <int e>
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<e> 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\":[]}");
}
......@@ -46,7 +46,7 @@ TEST(Writer, Compact) {
StringBuffer buffer; \
Writer<StringBuffer> writer(buffer); \
Reader reader; \
reader.Parse<0>(s, writer); \
reader.Parse<kParseFullPrecisionFlag>(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<StringBuffer> 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<UTF16<> > buffer;
Writer<GenericStringBuffer<UTF16<> >, 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<StringBuffer, UTF32<> > 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<StringBuffer, UTF32<>, 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<StringBuffer> writer(buffer);
writer.StartObject();
EXPECT_THROW(writer.EndArray(), AssertException);
EXPECT_FALSE(writer.IsComplete());
}
// [}
{
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
writer.StartArray();
EXPECT_THROW(writer.EndObject(), AssertException);
EXPECT_FALSE(writer.IsComplete());
}
// { 1:
{
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
writer.StartObject();
EXPECT_THROW(writer.Int(1), AssertException);
EXPECT_FALSE(writer.IsComplete());
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment