summaryrefslogtreecommitdiffstats
path: root/src/s3select/rapidjson/test/unittest
diff options
context:
space:
mode:
Diffstat (limited to 'src/s3select/rapidjson/test/unittest')
-rw-r--r--src/s3select/rapidjson/test/unittest/CMakeLists.txt95
-rw-r--r--src/s3select/rapidjson/test/unittest/allocatorstest.cpp292
-rw-r--r--src/s3select/rapidjson/test/unittest/bigintegertest.cpp138
-rw-r--r--src/s3select/rapidjson/test/unittest/clzlltest.cpp34
-rw-r--r--src/s3select/rapidjson/test/unittest/cursorstreamwrappertest.cpp115
-rw-r--r--src/s3select/rapidjson/test/unittest/documenttest.cpp674
-rw-r--r--src/s3select/rapidjson/test/unittest/dtoatest.cpp99
-rw-r--r--src/s3select/rapidjson/test/unittest/encodedstreamtest.cpp313
-rw-r--r--src/s3select/rapidjson/test/unittest/encodingstest.cpp451
-rw-r--r--src/s3select/rapidjson/test/unittest/filestreamtest.cpp155
-rw-r--r--src/s3select/rapidjson/test/unittest/fwdtest.cpp230
-rw-r--r--src/s3select/rapidjson/test/unittest/istreamwrappertest.cpp181
-rw-r--r--src/s3select/rapidjson/test/unittest/itoatest.cpp160
-rw-r--r--src/s3select/rapidjson/test/unittest/jsoncheckertest.cpp143
-rw-r--r--src/s3select/rapidjson/test/unittest/namespacetest.cpp70
-rw-r--r--src/s3select/rapidjson/test/unittest/ostreamwrappertest.cpp92
-rw-r--r--src/s3select/rapidjson/test/unittest/platformtest.cpp40
-rw-r--r--src/s3select/rapidjson/test/unittest/pointertest.cpp1730
-rw-r--r--src/s3select/rapidjson/test/unittest/prettywritertest.cpp373
-rw-r--r--src/s3select/rapidjson/test/unittest/readertest.cpp2370
-rw-r--r--src/s3select/rapidjson/test/unittest/regextest.cpp639
-rw-r--r--src/s3select/rapidjson/test/unittest/schematest.cpp2952
-rw-r--r--src/s3select/rapidjson/test/unittest/simdtest.cpp219
-rw-r--r--src/s3select/rapidjson/test/unittest/strfunctest.cpp30
-rw-r--r--src/s3select/rapidjson/test/unittest/stringbuffertest.cpp192
-rw-r--r--src/s3select/rapidjson/test/unittest/strtodtest.cpp132
-rw-r--r--src/s3select/rapidjson/test/unittest/unittest.cpp51
-rw-r--r--src/s3select/rapidjson/test/unittest/unittest.h143
-rw-r--r--src/s3select/rapidjson/test/unittest/uritest.cpp718
-rw-r--r--src/s3select/rapidjson/test/unittest/valuetest.cpp1861
-rw-r--r--src/s3select/rapidjson/test/unittest/writertest.cpp598
31 files changed, 15290 insertions, 0 deletions
diff --git a/src/s3select/rapidjson/test/unittest/CMakeLists.txt b/src/s3select/rapidjson/test/unittest/CMakeLists.txt
new file mode 100644
index 000000000..565ed9823
--- /dev/null
+++ b/src/s3select/rapidjson/test/unittest/CMakeLists.txt
@@ -0,0 +1,95 @@
+include(CheckCXXCompilerFlag)
+
+set(UNITTEST_SOURCES
+ allocatorstest.cpp
+ bigintegertest.cpp
+ clzlltest.cpp
+ cursorstreamwrappertest.cpp
+ documenttest.cpp
+ dtoatest.cpp
+ encodedstreamtest.cpp
+ encodingstest.cpp
+ fwdtest.cpp
+ filestreamtest.cpp
+ itoatest.cpp
+ istreamwrappertest.cpp
+ jsoncheckertest.cpp
+ namespacetest.cpp
+ pointertest.cpp
+ platformtest.cpp
+ prettywritertest.cpp
+ ostreamwrappertest.cpp
+ readertest.cpp
+ regextest.cpp
+ schematest.cpp
+ simdtest.cpp
+ strfunctest.cpp
+ stringbuffertest.cpp
+ strtodtest.cpp
+ unittest.cpp
+ uritest.cpp
+ valuetest.cpp
+ writertest.cpp)
+
+find_program(CCACHE_FOUND ccache)
+if(CCACHE_FOUND)
+ set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
+ set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)
+ if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Qunused-arguments -fcolor-diagnostics")
+ endif()
+endif(CCACHE_FOUND)
+
+set_property(DIRECTORY PROPERTY COMPILE_OPTIONS ${EXTRA_CXX_FLAGS})
+
+if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+ # If the user is running a newer version of Clang that includes the
+ # -Wdouble-promotion, we will ignore that warning.
+ if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 3.7)
+ CHECK_CXX_COMPILER_FLAG("-Wno-double-promotion" HAS_NO_DOUBLE_PROMOTION)
+ if (HAS_NO_DOUBLE_PROMOTION)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-double-promotion")
+ endif()
+ endif()
+elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
+ # Force to always compile with /W4
+ if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
+ string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
+ else()
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
+ endif()
+
+ # Force to always compile with /WX
+ if(CMAKE_CXX_FLAGS MATCHES "/WX-")
+ string(REGEX REPLACE "/WX-" "/WX" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
+ else()
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /WX")
+ endif()
+endif()
+
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DRAPIDJSON_HAS_STDSTRING=1")
+
+add_library(namespacetest STATIC namespacetest.cpp)
+
+add_executable(unittest ${UNITTEST_SOURCES})
+target_link_libraries(unittest ${TEST_LIBRARIES} namespacetest)
+
+add_dependencies(tests unittest)
+
+add_test(NAME unittest
+ COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/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 --suppressions=${CMAKE_SOURCE_DIR}/test/valgrind.supp --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")
+ add_test(NAME symbol_check
+ COMMAND sh -c "objdump -t -C libnamespacetest.a | grep rapidjson ; test $? -ne 0"
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+ endif(CMAKE_BUILD_TYPE STREQUAL "Debug")
+
+endif(NOT MSVC)
diff --git a/src/s3select/rapidjson/test/unittest/allocatorstest.cpp b/src/s3select/rapidjson/test/unittest/allocatorstest.cpp
new file mode 100644
index 000000000..2ffc32542
--- /dev/null
+++ b/src/s3select/rapidjson/test/unittest/allocatorstest.cpp
@@ -0,0 +1,292 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#include "unittest.h"
+
+#include "rapidjson/allocators.h"
+
+#include <map>
+#include <string>
+#include <utility>
+#include <functional>
+
+using namespace rapidjson;
+
+template <typename Allocator>
+void TestAllocator(Allocator& a) {
+ EXPECT_TRUE(a.Malloc(0) == 0);
+
+ uint8_t* p = static_cast<uint8_t*>(a.Malloc(100));
+ EXPECT_TRUE(p != 0);
+ for (size_t i = 0; i < 100; i++)
+ p[i] = static_cast<uint8_t>(i);
+
+ // Expand
+ uint8_t* q = static_cast<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] = static_cast<uint8_t>(i);
+
+ // Shrink
+ uint8_t *r = static_cast<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);
+
+ // Realloc to zero size
+ EXPECT_TRUE(a.Realloc(a.Malloc(1), 1, 0) == 0);
+}
+
+struct TestStdAllocatorData {
+ TestStdAllocatorData(int &constructions, int &destructions) :
+ constructions_(&constructions),
+ destructions_(&destructions)
+ {
+ ++*constructions_;
+ }
+ TestStdAllocatorData(const TestStdAllocatorData& rhs) :
+ constructions_(rhs.constructions_),
+ destructions_(rhs.destructions_)
+ {
+ ++*constructions_;
+ }
+ TestStdAllocatorData& operator=(const TestStdAllocatorData& rhs)
+ {
+ this->~TestStdAllocatorData();
+ constructions_ = rhs.constructions_;
+ destructions_ = rhs.destructions_;
+ ++*constructions_;
+ return *this;
+ }
+ ~TestStdAllocatorData()
+ {
+ ++*destructions_;
+ }
+private:
+ TestStdAllocatorData();
+ int *constructions_,
+ *destructions_;
+};
+
+template <typename Allocator>
+void TestStdAllocator(const Allocator& a) {
+#if RAPIDJSON_HAS_CXX17
+ typedef StdAllocator<bool, Allocator> BoolAllocator;
+#else
+ typedef StdAllocator<void, Allocator> VoidAllocator;
+ typedef typename VoidAllocator::template rebind<bool>::other BoolAllocator;
+#endif
+ BoolAllocator ba(a), ba2(a);
+ EXPECT_TRUE(ba == ba2);
+ EXPECT_FALSE(ba!= ba2);
+ ba.deallocate(ba.allocate());
+ EXPECT_TRUE(ba == ba2);
+ EXPECT_FALSE(ba != ba2);
+
+ unsigned long long ll = 0, *llp = &ll;
+ const unsigned long long cll = 0, *cllp = &cll;
+ StdAllocator<unsigned long long, Allocator> lla(a);
+ EXPECT_EQ(lla.address(ll), llp);
+ EXPECT_EQ(lla.address(cll), cllp);
+ EXPECT_TRUE(lla.max_size() > 0 && lla.max_size() <= SIZE_MAX / sizeof(unsigned long long));
+
+ int *arr;
+ StdAllocator<int, Allocator> ia(a);
+ arr = ia.allocate(10 * sizeof(int));
+ EXPECT_TRUE(arr != 0);
+ for (int i = 0; i < 10; ++i) {
+ arr[i] = 0x0f0f0f0f;
+ }
+ ia.deallocate(arr, 10);
+ arr = Malloc<int>(ia, 10);
+ EXPECT_TRUE(arr != 0);
+ for (int i = 0; i < 10; ++i) {
+ arr[i] = 0x0f0f0f0f;
+ }
+ arr = Realloc<int>(ia, arr, 10, 20);
+ EXPECT_TRUE(arr != 0);
+ for (int i = 0; i < 10; ++i) {
+ EXPECT_EQ(arr[i], 0x0f0f0f0f);
+ }
+ for (int i = 10; i < 20; i++) {
+ arr[i] = 0x0f0f0f0f;
+ }
+ Free<int>(ia, arr, 20);
+
+ int cons = 0, dest = 0;
+ StdAllocator<TestStdAllocatorData, Allocator> da(a);
+ for (int i = 1; i < 10; i++) {
+ TestStdAllocatorData *d = da.allocate();
+ EXPECT_TRUE(d != 0);
+
+ da.destroy(new(d) TestStdAllocatorData(cons, dest));
+ EXPECT_EQ(cons, i);
+ EXPECT_EQ(dest, i);
+
+ da.deallocate(d);
+ }
+
+ typedef StdAllocator<char, Allocator> CharAllocator;
+ typedef std::basic_string<char, std::char_traits<char>, CharAllocator> String;
+#if RAPIDJSON_HAS_CXX11
+ String s(CharAllocator{a});
+#else
+ CharAllocator ca(a);
+ String s(ca);
+#endif
+ for (int i = 0; i < 26; i++) {
+ s.push_back(static_cast<char>('A' + i));
+ }
+ EXPECT_TRUE(s == "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+
+ typedef StdAllocator<std::pair<const int, bool>, Allocator> MapAllocator;
+ typedef std::map<int, bool, std::less<int>, MapAllocator> Map;
+#if RAPIDJSON_HAS_CXX11
+ Map map(std::less<int>(), MapAllocator{a});
+#else
+ MapAllocator ma(a);
+ Map map(std::less<int>(), ma);
+#endif
+ for (int i = 0; i < 10; i++) {
+ map.insert(std::make_pair(i, (i % 2) == 0));
+ }
+ EXPECT_TRUE(map.size() == 10);
+ for (int i = 0; i < 10; i++) {
+ typename Map::iterator it = map.find(i);
+ EXPECT_TRUE(it != map.end());
+ EXPECT_TRUE(it->second == ((i % 2) == 0));
+ }
+}
+
+TEST(Allocator, CrtAllocator) {
+ CrtAllocator a;
+
+ TestAllocator(a);
+ TestStdAllocator(a);
+
+ CrtAllocator a2;
+ EXPECT_TRUE(a == a2);
+ EXPECT_FALSE(a != a2);
+ a2.Free(a2.Malloc(1));
+ EXPECT_TRUE(a == a2);
+ EXPECT_FALSE(a != a2);
+}
+
+TEST(Allocator, MemoryPoolAllocator) {
+ const size_t capacity = RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY;
+ MemoryPoolAllocator<> a(capacity);
+
+ a.Clear(); // noop
+ EXPECT_EQ(a.Size(), 0u);
+ EXPECT_EQ(a.Capacity(), 0u);
+ EXPECT_EQ(a.Shared(), false);
+ {
+ MemoryPoolAllocator<> a2(a);
+ EXPECT_EQ(a2.Shared(), true);
+ EXPECT_EQ(a.Shared(), true);
+ EXPECT_TRUE(a == a2);
+ EXPECT_FALSE(a != a2);
+ a2.Free(a2.Malloc(1));
+ EXPECT_TRUE(a == a2);
+ EXPECT_FALSE(a != a2);
+ }
+ EXPECT_EQ(a.Shared(), false);
+ EXPECT_EQ(a.Capacity(), capacity);
+ EXPECT_EQ(a.Size(), 8u); // aligned
+ a.Clear();
+ EXPECT_EQ(a.Capacity(), 0u);
+ EXPECT_EQ(a.Size(), 0u);
+
+ TestAllocator(a);
+ TestStdAllocator(a);
+
+ for (size_t i = 1; i < 1000; i++) {
+ EXPECT_TRUE(a.Malloc(i) != 0);
+ EXPECT_LE(a.Size(), a.Capacity());
+ }
+
+ CrtAllocator baseAllocator;
+ a = MemoryPoolAllocator<>(capacity, &baseAllocator);
+ EXPECT_EQ(a.Capacity(), 0u);
+ EXPECT_EQ(a.Size(), 0u);
+ a.Free(a.Malloc(1));
+ EXPECT_EQ(a.Capacity(), capacity);
+ EXPECT_EQ(a.Size(), 8u); // aligned
+
+ {
+ a.Clear();
+ const size_t bufSize = 1024;
+ char *buffer = (char *)a.Malloc(bufSize);
+ MemoryPoolAllocator<> aligned_a(buffer, bufSize);
+ EXPECT_TRUE(aligned_a.Capacity() > 0 && aligned_a.Capacity() <= bufSize);
+ EXPECT_EQ(aligned_a.Size(), 0u);
+ aligned_a.Free(aligned_a.Malloc(1));
+ EXPECT_TRUE(aligned_a.Capacity() > 0 && aligned_a.Capacity() <= bufSize);
+ EXPECT_EQ(aligned_a.Size(), 8u); // aligned
+ }
+
+ {
+ a.Clear();
+ const size_t bufSize = 1024;
+ char *buffer = (char *)a.Malloc(bufSize);
+ RAPIDJSON_ASSERT(bufSize % sizeof(void*) == 0);
+ MemoryPoolAllocator<> unaligned_a(buffer + 1, bufSize - 1);
+ EXPECT_TRUE(unaligned_a.Capacity() > 0 && unaligned_a.Capacity() <= bufSize - sizeof(void*));
+ EXPECT_EQ(unaligned_a.Size(), 0u);
+ unaligned_a.Free(unaligned_a.Malloc(1));
+ EXPECT_TRUE(unaligned_a.Capacity() > 0 && unaligned_a.Capacity() <= bufSize - sizeof(void*));
+ EXPECT_EQ(unaligned_a.Size(), 8u); // aligned
+ }
+}
+
+TEST(Allocator, Alignment) {
+ if (sizeof(size_t) >= 8) {
+ EXPECT_EQ(RAPIDJSON_UINT64_C2(0x00000000, 0x00000000), RAPIDJSON_ALIGN(0));
+ for (uint64_t i = 1; i < 8; i++) {
+ EXPECT_EQ(RAPIDJSON_UINT64_C2(0x00000000, 0x00000008), RAPIDJSON_ALIGN(i));
+ EXPECT_EQ(RAPIDJSON_UINT64_C2(0x00000000, 0x00000010), RAPIDJSON_ALIGN(RAPIDJSON_UINT64_C2(0x00000000, 0x00000008) + i));
+ EXPECT_EQ(RAPIDJSON_UINT64_C2(0x00000001, 0x00000000), RAPIDJSON_ALIGN(RAPIDJSON_UINT64_C2(0x00000000, 0xFFFFFFF8) + i));
+ EXPECT_EQ(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0xFFFFFFF8), RAPIDJSON_ALIGN(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0xFFFFFFF0) + i));
+ }
+ }
+
+ EXPECT_EQ(0u, RAPIDJSON_ALIGN(0u));
+ for (uint32_t i = 1; i < 8; i++) {
+ EXPECT_EQ(8u, RAPIDJSON_ALIGN(i));
+ EXPECT_EQ(0xFFFFFFF8u, RAPIDJSON_ALIGN(0xFFFFFFF0u + i));
+ }
+}
+
+TEST(Allocator, Issue399) {
+ MemoryPoolAllocator<> a;
+ void* p = a.Malloc(100);
+ void* q = a.Realloc(p, 100, 200);
+ EXPECT_EQ(p, q);
+
+ // exhuasive testing
+ for (size_t j = 1; j < 32; j++) {
+ a.Clear();
+ a.Malloc(j); // some unaligned size
+ p = a.Malloc(1);
+ for (size_t i = 1; i < 1024; i++) {
+ q = a.Realloc(p, i, i + 1);
+ EXPECT_EQ(p, q);
+ p = q;
+ }
+ }
+}
diff --git a/src/s3select/rapidjson/test/unittest/bigintegertest.cpp b/src/s3select/rapidjson/test/unittest/bigintegertest.cpp
new file mode 100644
index 000000000..fad54382c
--- /dev/null
+++ b/src/s3select/rapidjson/test/unittest/bigintegertest.cpp
@@ -0,0 +1,138 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#include "unittest.h"
+
+#include "rapidjson/internal/biginteger.h"
+
+using namespace rapidjson::internal;
+
+#define BIGINTEGER_LITERAL(s) BigInteger(s, sizeof(s) - 1)
+
+static const BigInteger kZero(0);
+static const BigInteger kOne(1);
+static const BigInteger kUint64Max = BIGINTEGER_LITERAL("18446744073709551615");
+static const BigInteger kTwo64 = BIGINTEGER_LITERAL("18446744073709551616");
+
+TEST(BigInteger, Constructor) {
+ EXPECT_TRUE(kZero.IsZero());
+ EXPECT_TRUE(kZero == kZero);
+ EXPECT_TRUE(kZero == BIGINTEGER_LITERAL("0"));
+ EXPECT_TRUE(kZero == BIGINTEGER_LITERAL("00"));
+
+ const BigInteger a(123);
+ EXPECT_TRUE(a == a);
+ EXPECT_TRUE(a == BIGINTEGER_LITERAL("123"));
+ EXPECT_TRUE(a == BIGINTEGER_LITERAL("0123"));
+
+ EXPECT_EQ(2u, kTwo64.GetCount());
+ EXPECT_EQ(0u, kTwo64.GetDigit(0));
+ EXPECT_EQ(1u, kTwo64.GetDigit(1));
+}
+
+TEST(BigInteger, AddUint64) {
+ BigInteger a = kZero;
+ a += 0u;
+ EXPECT_TRUE(kZero == a);
+
+ a += 1u;
+ EXPECT_TRUE(kOne == a);
+
+ a += 1u;
+ EXPECT_TRUE(BigInteger(2) == a);
+
+ EXPECT_TRUE(BigInteger(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0xFFFFFFFF)) == kUint64Max);
+ BigInteger b = kUint64Max;
+ b += 1u;
+ EXPECT_TRUE(kTwo64 == b);
+ b += RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0xFFFFFFFF);
+ EXPECT_TRUE(BIGINTEGER_LITERAL("36893488147419103231") == b);
+}
+
+TEST(BigInteger, MultiplyUint64) {
+ BigInteger a = kZero;
+ a *= static_cast <uint64_t>(0);
+ EXPECT_TRUE(kZero == a);
+ a *= static_cast <uint64_t>(123);
+ EXPECT_TRUE(kZero == a);
+
+ BigInteger b = kOne;
+ b *= static_cast<uint64_t>(1);
+ EXPECT_TRUE(kOne == b);
+ b *= static_cast<uint64_t>(0);
+ EXPECT_TRUE(kZero == b);
+
+ BigInteger c(123);
+ c *= static_cast<uint64_t>(456u);
+ EXPECT_TRUE(BigInteger(123u * 456u) == c);
+ c *= RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0xFFFFFFFF);
+ EXPECT_TRUE(BIGINTEGER_LITERAL("1034640981606221330982120") == c);
+ c *= RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0xFFFFFFFF);
+ EXPECT_TRUE(BIGINTEGER_LITERAL("19085757395861596536664473018420572782123800") == c);
+}
+
+TEST(BigInteger, MultiplyUint32) {
+ BigInteger a = kZero;
+ a *= static_cast <uint32_t>(0);
+ EXPECT_TRUE(kZero == a);
+ a *= static_cast <uint32_t>(123);
+ EXPECT_TRUE(kZero == a);
+
+ BigInteger b = kOne;
+ b *= static_cast<uint32_t>(1);
+ EXPECT_TRUE(kOne == b);
+ b *= static_cast<uint32_t>(0);
+ EXPECT_TRUE(kZero == b);
+
+ BigInteger c(123);
+ c *= static_cast<uint32_t>(456u);
+ EXPECT_TRUE(BigInteger(123u * 456u) == c);
+ c *= 0xFFFFFFFFu;
+ EXPECT_TRUE(BIGINTEGER_LITERAL("240896125641960") == c);
+ c *= 0xFFFFFFFFu;
+ EXPECT_TRUE(BIGINTEGER_LITERAL("1034640981124429079698200") == c);
+}
+
+TEST(BigInteger, LeftShift) {
+ BigInteger a = kZero;
+ a <<= 1;
+ EXPECT_TRUE(kZero == a);
+ a <<= 64;
+ EXPECT_TRUE(kZero == a);
+
+ a = BigInteger(123);
+ a <<= 0;
+ EXPECT_TRUE(BigInteger(123) == a);
+ a <<= 1;
+ EXPECT_TRUE(BigInteger(246) == a);
+ a <<= 64;
+ EXPECT_TRUE(BIGINTEGER_LITERAL("4537899042132549697536") == a);
+ a <<= 99;
+ EXPECT_TRUE(BIGINTEGER_LITERAL("2876235222267216943024851750785644982682875244576768") == a);
+
+ a = 1;
+ a <<= 64; // a.count_ != 1
+ a <<= 256; // interShift == 0
+ EXPECT_TRUE(BIGINTEGER_LITERAL("2135987035920910082395021706169552114602704522356652769947041607822219725780640550022962086936576") == a);
+}
+
+TEST(BigInteger, Compare) {
+ EXPECT_EQ(0, kZero.Compare(kZero));
+ EXPECT_EQ(1, kOne.Compare(kZero));
+ EXPECT_EQ(-1, kZero.Compare(kOne));
+ EXPECT_EQ(0, kUint64Max.Compare(kUint64Max));
+ EXPECT_EQ(0, kTwo64.Compare(kTwo64));
+ EXPECT_EQ(-1, kUint64Max.Compare(kTwo64));
+ EXPECT_EQ(1, kTwo64.Compare(kUint64Max));
+}
diff --git a/src/s3select/rapidjson/test/unittest/clzlltest.cpp b/src/s3select/rapidjson/test/unittest/clzlltest.cpp
new file mode 100644
index 000000000..ad465e1f3
--- /dev/null
+++ b/src/s3select/rapidjson/test/unittest/clzlltest.cpp
@@ -0,0 +1,34 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#include "unittest.h"
+#include "rapidjson/internal/clzll.h"
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_PUSH
+#endif
+
+using namespace rapidjson::internal;
+
+TEST(clzll, normal) {
+ EXPECT_EQ(clzll(1), 63U);
+ EXPECT_EQ(clzll(2), 62U);
+ EXPECT_EQ(clzll(12), 60U);
+ EXPECT_EQ(clzll(0x0000000080000001UL), 32U);
+ EXPECT_EQ(clzll(0x8000000000000001UL), 0U);
+}
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_POP
+#endif
diff --git a/src/s3select/rapidjson/test/unittest/cursorstreamwrappertest.cpp b/src/s3select/rapidjson/test/unittest/cursorstreamwrappertest.cpp
new file mode 100644
index 000000000..dad335945
--- /dev/null
+++ b/src/s3select/rapidjson/test/unittest/cursorstreamwrappertest.cpp
@@ -0,0 +1,115 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#include "unittest.h"
+#include "rapidjson/document.h"
+#include "rapidjson/cursorstreamwrapper.h"
+
+using namespace rapidjson;
+
+// static const char json[] = "{\"string\"\n\n:\"my string\",\"array\"\n:[\"1\", \"2\", \"3\"]}";
+
+bool testJson(const char *json, size_t &line, size_t &col) {
+ StringStream ss(json);
+ CursorStreamWrapper<StringStream> csw(ss);
+ Document document;
+ document.ParseStream(csw);
+ bool ret = document.HasParseError();
+ if (ret) {
+ col = csw.GetColumn();
+ line = csw.GetLine();
+ }
+ return ret;
+}
+
+TEST(CursorStreamWrapper, MissingFirstBracket) {
+ const char json[] = "\"string\"\n\n:\"my string\",\"array\"\n:[\"1\", \"2\", \"3\"]}";
+ size_t col, line;
+ bool ret = testJson(json, line, col);
+ EXPECT_TRUE(ret);
+ EXPECT_EQ(line, 3u);
+ EXPECT_EQ(col, 0u);
+}
+
+TEST(CursorStreamWrapper, MissingQuotes) {
+ const char json[] = "{\"string\n\n:\"my string\",\"array\"\n:[\"1\", \"2\", \"3\"]}";
+ size_t col, line;
+ bool ret = testJson(json, line, col);
+ EXPECT_TRUE(ret);
+ EXPECT_EQ(line, 1u);
+ EXPECT_EQ(col, 8u);
+}
+
+TEST(CursorStreamWrapper, MissingColon) {
+ const char json[] = "{\"string\"\n\n\"my string\",\"array\"\n:[\"1\", \"2\", \"3\"]}";
+ size_t col, line;
+ bool ret = testJson(json, line, col);
+ EXPECT_TRUE(ret);
+ EXPECT_EQ(line, 3u);
+ EXPECT_EQ(col, 0u);
+}
+
+TEST(CursorStreamWrapper, MissingSecondQuotes) {
+ const char json[] = "{\"string\"\n\n:my string\",\"array\"\n:[\"1\", \"2\", \"3\"]}";
+ size_t col, line;
+ bool ret = testJson(json, line, col);
+ EXPECT_TRUE(ret);
+ EXPECT_EQ(line, 3u);
+ EXPECT_EQ(col, 1u);
+}
+
+TEST(CursorStreamWrapper, MissingComma) {
+ const char json[] = "{\"string\"\n\n:\"my string\"\"array\"\n:[\"1\", \"2\", \"3\"]}";
+ size_t col, line;
+ bool ret = testJson(json, line, col);
+ EXPECT_TRUE(ret);
+ EXPECT_EQ(line, 3u);
+ EXPECT_EQ(col, 12u);
+}
+
+TEST(CursorStreamWrapper, MissingArrayBracket) {
+ const char json[] = "{\"string\"\n\n:\"my string\",\"array\"\n:\"1\", \"2\", \"3\"]}";
+ size_t col, line;
+ bool ret = testJson(json, line, col);
+ EXPECT_TRUE(ret);
+ EXPECT_EQ(line, 4u);
+ EXPECT_EQ(col, 9u);
+}
+
+TEST(CursorStreamWrapper, MissingArrayComma) {
+ const char json[] = "{\"string\"\n\n:\"my string\",\"array\"\n:[\"1\" \"2\", \"3\"]}";
+ size_t col, line;
+ bool ret = testJson(json, line, col);
+ EXPECT_TRUE(ret);
+ EXPECT_EQ(line, 4u);
+ EXPECT_EQ(col, 6u);
+}
+
+TEST(CursorStreamWrapper, MissingLastArrayBracket) {
+ const char json8[] = "{\"string\"\n\n:\"my string\",\"array\"\n:[\"1\", \"2\", \"3\"}";
+ size_t col, line;
+ bool ret = testJson(json8, line, col);
+ EXPECT_TRUE(ret);
+ EXPECT_EQ(line, 4u);
+ EXPECT_EQ(col, 15u);
+}
+
+TEST(CursorStreamWrapper, MissingLastBracket) {
+ const char json9[] = "{\"string\"\n\n:\"my string\",\"array\"\n:[\"1\", \"2\", \"3\"]";
+ size_t col, line;
+ bool ret = testJson(json9, line, col);
+ EXPECT_TRUE(ret);
+ EXPECT_EQ(line, 4u);
+ EXPECT_EQ(col, 16u);
+}
diff --git a/src/s3select/rapidjson/test/unittest/documenttest.cpp b/src/s3select/rapidjson/test/unittest/documenttest.cpp
new file mode 100644
index 000000000..c3d1e484d
--- /dev/null
+++ b/src/s3select/rapidjson/test/unittest/documenttest.cpp
@@ -0,0 +1,674 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#include "unittest.h"
+#include "rapidjson/document.h"
+#include "rapidjson/writer.h"
+#include "rapidjson/filereadstream.h"
+#include "rapidjson/encodedstream.h"
+#include "rapidjson/stringbuffer.h"
+#include <sstream>
+#include <algorithm>
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(c++98-compat)
+RAPIDJSON_DIAG_OFF(missing-variable-declarations)
+#endif
+
+using namespace rapidjson;
+
+template <typename DocumentType>
+void ParseCheck(DocumentType& doc) {
+ typedef typename DocumentType::ValueType ValueType;
+
+ EXPECT_FALSE(doc.HasParseError());
+ if (doc.HasParseError())
+ printf("Error: %d at %zu\n", static_cast<int>(doc.GetParseError()), doc.GetErrorOffset());
+ EXPECT_TRUE(static_cast<ParseResult>(doc));
+
+ EXPECT_TRUE(doc.IsObject());
+
+ EXPECT_TRUE(doc.HasMember("hello"));
+ const ValueType& hello = doc["hello"];
+ EXPECT_TRUE(hello.IsString());
+ EXPECT_STREQ("world", hello.GetString());
+
+ EXPECT_TRUE(doc.HasMember("t"));
+ const ValueType& t = doc["t"];
+ EXPECT_TRUE(t.IsTrue());
+
+ EXPECT_TRUE(doc.HasMember("f"));
+ const ValueType& f = doc["f"];
+ EXPECT_TRUE(f.IsFalse());
+
+ EXPECT_TRUE(doc.HasMember("n"));
+ const ValueType& n = doc["n"];
+ EXPECT_TRUE(n.IsNull());
+
+ EXPECT_TRUE(doc.HasMember("i"));
+ const ValueType& i = doc["i"];
+ EXPECT_TRUE(i.IsNumber());
+ EXPECT_EQ(123, i.GetInt());
+
+ EXPECT_TRUE(doc.HasMember("pi"));
+ const ValueType& pi = doc["pi"];
+ EXPECT_TRUE(pi.IsNumber());
+ EXPECT_DOUBLE_EQ(3.1416, pi.GetDouble());
+
+ EXPECT_TRUE(doc.HasMember("a"));
+ const ValueType& a = doc["a"];
+ EXPECT_TRUE(a.IsArray());
+ EXPECT_EQ(4u, a.Size());
+ for (SizeType j = 0; j < 4; j++)
+ EXPECT_EQ(j + 1, a[j].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);
+
+ // Parse(const Ch*, size_t)
+ size_t length = strlen(json);
+ buffer = reinterpret_cast<char*>(malloc(length * 2));
+ memcpy(buffer, json, length);
+ memset(buffer + length, 'X', length);
+#if RAPIDJSON_HAS_STDSTRING
+ std::string s2(buffer, length); // backup buffer
+#endif
+ doc.SetNull();
+ doc.Parse(buffer, length);
+ free(buffer);
+ ParseCheck(doc);
+
+#if RAPIDJSON_HAS_STDSTRING
+ // Parse(std::string)
+ doc.SetNull();
+ doc.Parse(s2);
+ ParseCheck(doc);
+#endif
+}
+
+TEST(Document, Parse) {
+ ParseTest<MemoryPoolAllocator<>, CrtAllocator>();
+ ParseTest<MemoryPoolAllocator<>, MemoryPoolAllocator<> >();
+ ParseTest<CrtAllocator, MemoryPoolAllocator<> >();
+ ParseTest<CrtAllocator, CrtAllocator>();
+}
+
+TEST(Document, UnchangedOnParseError) {
+ Document doc;
+ doc.SetArray().PushBack(0, doc.GetAllocator());
+
+ ParseResult noError;
+ EXPECT_TRUE(noError);
+
+ ParseResult err = doc.Parse("{]");
+ EXPECT_TRUE(doc.HasParseError());
+ EXPECT_NE(err, noError);
+ EXPECT_NE(err.Code(), noError);
+ EXPECT_NE(noError, doc.GetParseError());
+ EXPECT_EQ(err.Code(), doc.GetParseError());
+ EXPECT_EQ(err.Offset(), doc.GetErrorOffset());
+ EXPECT_TRUE(doc.IsArray());
+ EXPECT_EQ(doc.Size(), 1u);
+
+ err = doc.Parse("{}");
+ EXPECT_FALSE(doc.HasParseError());
+ EXPECT_FALSE(err.IsError());
+ EXPECT_TRUE(err);
+ EXPECT_EQ(err, noError);
+ EXPECT_EQ(err.Code(), noError);
+ EXPECT_EQ(err.Code(), doc.GetParseError());
+ EXPECT_EQ(err.Offset(), doc.GetErrorOffset());
+ EXPECT_TRUE(doc.IsObject());
+ EXPECT_EQ(doc.MemberCount(), 0u);
+}
+
+static FILE* OpenEncodedFile(const char* filename) {
+ const char *paths[] = {
+ "encodings",
+ "bin/encodings",
+ "../bin/encodings",
+ "../../bin/encodings",
+ "../../../bin/encodings"
+ };
+ char buffer[1024];
+ for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) {
+ sprintf(buffer, "%s/%s", paths[i], filename);
+ FILE *fp = fopen(buffer, "rb");
+ if (fp)
+ return fp;
+ }
+ return 0;
+}
+
+TEST(Document, Parse_Encoding) {
+ const char* json = " { \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3, 4] } ";
+
+ typedef GenericDocument<UTF16<> > DocumentType;
+ DocumentType doc;
+
+ // Parse<unsigned, SourceEncoding>(const SourceEncoding::Ch*)
+ // doc.Parse<kParseDefaultFlags, UTF8<> >(json);
+ // EXPECT_FALSE(doc.HasParseError());
+ // EXPECT_EQ(0, StrCmp(doc[L"hello"].GetString(), L"world"));
+
+ // Parse<unsigned, SourceEncoding>(const SourceEncoding::Ch*, size_t)
+ size_t length = strlen(json);
+ char* buffer = reinterpret_cast<char*>(malloc(length * 2));
+ memcpy(buffer, json, length);
+ memset(buffer + length, 'X', length);
+#if RAPIDJSON_HAS_STDSTRING
+ std::string s2(buffer, length); // backup buffer
+#endif
+ doc.SetNull();
+ doc.Parse<kParseDefaultFlags, UTF8<> >(buffer, length);
+ free(buffer);
+ EXPECT_FALSE(doc.HasParseError());
+ if (doc.HasParseError())
+ printf("Error: %d at %zu\n", static_cast<int>(doc.GetParseError()), doc.GetErrorOffset());
+ EXPECT_EQ(0, StrCmp(doc[L"hello"].GetString(), L"world"));
+
+#if RAPIDJSON_HAS_STDSTRING
+ // Parse<unsigned, SourceEncoding>(std::string)
+ doc.SetNull();
+
+#if defined(_MSC_VER) && _MSC_VER < 1800
+ doc.Parse<kParseDefaultFlags, UTF8<> >(s2.c_str()); // VS2010 or below cannot handle templated function overloading. Use const char* instead.
+#else
+ doc.Parse<kParseDefaultFlags, UTF8<> >(s2);
+#endif
+ EXPECT_FALSE(doc.HasParseError());
+ EXPECT_EQ(0, StrCmp(doc[L"hello"].GetString(), L"world"));
+#endif
+}
+
+TEST(Document, ParseStream_EncodedInputStream) {
+ // UTF8 -> UTF16
+ FILE* fp = OpenEncodedFile("utf8.json");
+ char buffer[256];
+ FileReadStream bis(fp, buffer, sizeof(buffer));
+ EncodedInputStream<UTF8<>, FileReadStream> eis(bis);
+
+ GenericDocument<UTF16<> > d;
+ d.ParseStream<0, UTF8<> >(eis);
+ EXPECT_FALSE(d.HasParseError());
+
+ fclose(fp);
+
+ wchar_t expected[] = L"I can eat glass and it doesn't hurt me.";
+ GenericValue<UTF16<> >& v = d[L"en"];
+ EXPECT_TRUE(v.IsString());
+ EXPECT_EQ(sizeof(expected) / sizeof(wchar_t) - 1, v.GetStringLength());
+ EXPECT_EQ(0, StrCmp(expected, v.GetString()));
+
+ // UTF16 -> UTF8 in memory
+ StringBuffer bos;
+ typedef EncodedOutputStream<UTF8<>, StringBuffer> OutputStream;
+ OutputStream eos(bos, false); // Not writing BOM
+ {
+ Writer<OutputStream, UTF16<>, UTF8<> > writer(eos);
+ d.Accept(writer);
+ }
+
+ // Condense the original file and compare.
+ fp = OpenEncodedFile("utf8.json");
+ FileReadStream is(fp, buffer, sizeof(buffer));
+ Reader reader;
+ StringBuffer bos2;
+ Writer<StringBuffer> writer2(bos2);
+ reader.Parse(is, writer2);
+ fclose(fp);
+
+ EXPECT_EQ(bos.GetSize(), bos2.GetSize());
+ EXPECT_EQ(0, memcmp(bos.GetString(), bos2.GetString(), bos2.GetSize()));
+}
+
+TEST(Document, ParseStream_AutoUTFInputStream) {
+ // Any -> UTF8
+ FILE* fp = OpenEncodedFile("utf32be.json");
+ char buffer[256];
+ FileReadStream bis(fp, buffer, sizeof(buffer));
+ AutoUTFInputStream<unsigned, FileReadStream> eis(bis);
+
+ Document d;
+ d.ParseStream<0, AutoUTF<unsigned> >(eis);
+ EXPECT_FALSE(d.HasParseError());
+
+ fclose(fp);
+
+ char expected[] = "I can eat glass and it doesn't hurt me.";
+ Value& v = d["en"];
+ EXPECT_TRUE(v.IsString());
+ EXPECT_EQ(sizeof(expected) - 1, v.GetStringLength());
+ EXPECT_EQ(0, StrCmp(expected, v.GetString()));
+
+ // UTF8 -> UTF8 in memory
+ StringBuffer bos;
+ Writer<StringBuffer> writer(bos);
+ d.Accept(writer);
+
+ // Condense the original file and compare.
+ fp = OpenEncodedFile("utf8.json");
+ FileReadStream is(fp, buffer, sizeof(buffer));
+ Reader reader;
+ StringBuffer bos2;
+ Writer<StringBuffer> writer2(bos2);
+ reader.Parse(is, writer2);
+ fclose(fp);
+
+ EXPECT_EQ(bos.GetSize(), bos2.GetSize());
+ EXPECT_EQ(0, memcmp(bos.GetString(), bos2.GetString(), bos2.GetSize()));
+}
+
+TEST(Document, Swap) {
+ Document d1;
+ Document::AllocatorType& a = d1.GetAllocator();
+
+ d1.SetArray().PushBack(1, a).PushBack(2, a);
+
+ Value o;
+ o.SetObject().AddMember("a", 1, a);
+
+ // Swap between Document and Value
+ d1.Swap(o);
+ EXPECT_TRUE(d1.IsObject());
+ EXPECT_TRUE(o.IsArray());
+
+ d1.Swap(o);
+ EXPECT_TRUE(d1.IsArray());
+ EXPECT_TRUE(o.IsObject());
+
+ o.Swap(d1);
+ EXPECT_TRUE(d1.IsObject());
+ EXPECT_TRUE(o.IsArray());
+
+ // Swap between Document and Document
+ Document d2;
+ d2.SetArray().PushBack(3, a);
+ d1.Swap(d2);
+ EXPECT_TRUE(d1.IsArray());
+ EXPECT_TRUE(d2.IsObject());
+ EXPECT_EQ(&d2.GetAllocator(), &a);
+
+ // reset value
+ Value().Swap(d1);
+ EXPECT_TRUE(d1.IsNull());
+
+ // reset document, including allocator
+ // so clear o before so that it doesnt contain dangling elements
+ o.Clear();
+ Document().Swap(d2);
+ EXPECT_TRUE(d2.IsNull());
+ EXPECT_NE(&d2.GetAllocator(), &a);
+
+ // testing std::swap compatibility
+ d1.SetBool(true);
+ using std::swap;
+ swap(d1, d2);
+ EXPECT_TRUE(d1.IsNull());
+ EXPECT_TRUE(d2.IsTrue());
+
+ swap(o, d2);
+ EXPECT_TRUE(o.IsTrue());
+ EXPECT_TRUE(d2.IsArray());
+}
+
+
+// This should be slow due to assignment in inner-loop.
+struct OutputStringStream : public std::ostringstream {
+ typedef char Ch;
+
+ virtual ~OutputStringStream();
+
+ void Put(char c) {
+ put(c);
+ }
+ void Flush() {}
+};
+
+OutputStringStream::~OutputStringStream() {}
+
+TEST(Document, AcceptWriter) {
+ Document doc;
+ doc.Parse(" { \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3, 4] } ");
+
+ OutputStringStream os;
+ Writer<OutputStringStream> writer(os);
+ doc.Accept(writer);
+
+ EXPECT_EQ("{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3,4]}", os.str());
+}
+
+TEST(Document, UserBuffer) {
+ typedef GenericDocument<UTF8<>, MemoryPoolAllocator<>, MemoryPoolAllocator<> > DocumentType;
+ char valueBuffer[4096];
+ char parseBuffer[1024];
+ MemoryPoolAllocator<> valueAllocator(valueBuffer, sizeof(valueBuffer));
+ MemoryPoolAllocator<> parseAllocator(parseBuffer, sizeof(parseBuffer));
+ DocumentType doc(&valueAllocator, sizeof(parseBuffer) / 2, &parseAllocator);
+ 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_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
+TEST(Document, AssertAcceptInvalidNameType) {
+ Document doc;
+ doc.SetObject();
+ doc.AddMember("a", 0, doc.GetAllocator());
+ doc.FindMember("a")->name.SetNull(); // Change name to non-string type.
+
+ OutputStringStream os;
+ Writer<OutputStringStream> writer(os);
+ ASSERT_THROW(doc.Accept(writer), AssertException);
+}
+
+// Issue 44: SetStringRaw doesn't work with wchar_t
+TEST(Document, UTF16_Document) {
+ GenericDocument< UTF16<> > json;
+ json.Parse<kParseValidateEncodingFlag>(L"[{\"created_at\":\"Wed Oct 30 17:13:20 +0000 2012\"}]");
+
+ ASSERT_TRUE(json.IsArray());
+ GenericValue< UTF16<> >& v = json[0];
+ ASSERT_TRUE(v.IsObject());
+
+ GenericValue< UTF16<> >& s = v[L"created_at"];
+ ASSERT_TRUE(s.IsString());
+
+ EXPECT_EQ(0, memcmp(L"Wed Oct 30 17:13:20 +0000 2012", s.GetString(), (s.GetStringLength() + 1) * sizeof(wchar_t)));
+}
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+
+#if 0 // Many old compiler does not support these. Turn it off temporaily.
+
+#include <type_traits>
+
+TEST(Document, Traits) {
+ static_assert(std::is_constructible<Document>::value, "");
+ static_assert(std::is_default_constructible<Document>::value, "");
+#ifndef _MSC_VER
+ static_assert(!std::is_copy_constructible<Document>::value, "");
+#endif
+ static_assert(std::is_move_constructible<Document>::value, "");
+
+ static_assert(!std::is_nothrow_constructible<Document>::value, "");
+ static_assert(!std::is_nothrow_default_constructible<Document>::value, "");
+#ifndef _MSC_VER
+ static_assert(!std::is_nothrow_copy_constructible<Document>::value, "");
+ static_assert(std::is_nothrow_move_constructible<Document>::value, "");
+#endif
+
+ static_assert(std::is_assignable<Document,Document>::value, "");
+#ifndef _MSC_VER
+ static_assert(!std::is_copy_assignable<Document>::value, "");
+#endif
+ static_assert(std::is_move_assignable<Document>::value, "");
+
+#ifndef _MSC_VER
+ static_assert(std::is_nothrow_assignable<Document, Document>::value, "");
+#endif
+ static_assert(!std::is_nothrow_copy_assignable<Document>::value, "");
+#ifndef _MSC_VER
+ static_assert(std::is_nothrow_move_assignable<Document>::value, "");
+#endif
+
+ static_assert( std::is_destructible<Document>::value, "");
+#ifndef _MSC_VER
+ static_assert(std::is_nothrow_destructible<Document>::value, "");
+#endif
+}
+
+#endif
+
+template <typename Allocator>
+struct DocumentMove: public ::testing::Test {
+};
+
+typedef ::testing::Types< CrtAllocator, MemoryPoolAllocator<> > MoveAllocatorTypes;
+TYPED_TEST_CASE(DocumentMove, MoveAllocatorTypes);
+
+TYPED_TEST(DocumentMove, MoveConstructor) {
+ typedef TypeParam Allocator;
+ typedef GenericDocument<UTF8<>, Allocator> D;
+ Allocator allocator;
+
+ D a(&allocator);
+ a.Parse("[\"one\", \"two\", \"three\"]");
+ EXPECT_FALSE(a.HasParseError());
+ EXPECT_TRUE(a.IsArray());
+ EXPECT_EQ(3u, a.Size());
+ EXPECT_EQ(&a.GetAllocator(), &allocator);
+
+ // Document b(a); // does not compile (!is_copy_constructible)
+ D b(std::move(a));
+ EXPECT_TRUE(a.IsNull());
+ EXPECT_TRUE(b.IsArray());
+ EXPECT_EQ(3u, b.Size());
+ EXPECT_THROW(a.GetAllocator(), AssertException);
+ EXPECT_EQ(&b.GetAllocator(), &allocator);
+
+ b.Parse("{\"Foo\": \"Bar\", \"Baz\": 42}");
+ EXPECT_FALSE(b.HasParseError());
+ EXPECT_TRUE(b.IsObject());
+ EXPECT_EQ(2u, b.MemberCount());
+
+ // Document c = a; // does not compile (!is_copy_constructible)
+ D c = std::move(b);
+ EXPECT_TRUE(b.IsNull());
+ EXPECT_TRUE(c.IsObject());
+ EXPECT_EQ(2u, c.MemberCount());
+ EXPECT_THROW(b.GetAllocator(), AssertException);
+ EXPECT_EQ(&c.GetAllocator(), &allocator);
+}
+
+TYPED_TEST(DocumentMove, MoveConstructorParseError) {
+ typedef TypeParam Allocator;
+ typedef GenericDocument<UTF8<>, Allocator> D;
+
+ ParseResult noError;
+ D a;
+ a.Parse("{ 4 = 4]");
+ ParseResult error(a.GetParseError(), a.GetErrorOffset());
+ EXPECT_TRUE(a.HasParseError());
+ EXPECT_NE(error, noError);
+ EXPECT_NE(error.Code(), noError);
+ EXPECT_NE(error.Code(), noError.Code());
+ EXPECT_NE(error.Offset(), noError.Offset());
+
+ D b(std::move(a));
+ EXPECT_FALSE(a.HasParseError());
+ EXPECT_TRUE(b.HasParseError());
+ EXPECT_EQ(a.GetParseError(), noError);
+ EXPECT_EQ(a.GetParseError(), noError.Code());
+ EXPECT_EQ(a.GetErrorOffset(), noError.Offset());
+ EXPECT_EQ(b.GetParseError(), error);
+ EXPECT_EQ(b.GetParseError(), error.Code());
+ EXPECT_EQ(b.GetErrorOffset(), error.Offset());
+
+ D c(std::move(b));
+ EXPECT_FALSE(b.HasParseError());
+ EXPECT_TRUE(c.HasParseError());
+ EXPECT_EQ(b.GetParseError(), noError.Code());
+ EXPECT_EQ(c.GetParseError(), error.Code());
+ EXPECT_EQ(b.GetErrorOffset(), noError.Offset());
+ EXPECT_EQ(c.GetErrorOffset(), error.Offset());
+}
+
+// This test does not properly use parsing, just for testing.
+// It must call ClearStack() explicitly to prevent memory leak.
+// But here we cannot as ClearStack() is private.
+#if 0
+TYPED_TEST(DocumentMove, MoveConstructorStack) {
+ typedef TypeParam Allocator;
+ typedef UTF8<> Encoding;
+ typedef GenericDocument<Encoding, Allocator> Document;
+
+ Document a;
+ size_t defaultCapacity = a.GetStackCapacity();
+
+ // Trick Document into getting GetStackCapacity() to return non-zero
+ typedef GenericReader<Encoding, Encoding, Allocator> Reader;
+ Reader reader(&a.GetAllocator());
+ GenericStringStream<Encoding> is("[\"one\", \"two\", \"three\"]");
+ reader.template Parse<kParseDefaultFlags>(is, a);
+ size_t capacity = a.GetStackCapacity();
+ EXPECT_GT(capacity, 0u);
+
+ Document b(std::move(a));
+ EXPECT_EQ(a.GetStackCapacity(), defaultCapacity);
+ EXPECT_EQ(b.GetStackCapacity(), capacity);
+
+ Document c = std::move(b);
+ EXPECT_EQ(b.GetStackCapacity(), defaultCapacity);
+ EXPECT_EQ(c.GetStackCapacity(), capacity);
+}
+#endif
+
+TYPED_TEST(DocumentMove, MoveAssignment) {
+ typedef TypeParam Allocator;
+ typedef GenericDocument<UTF8<>, Allocator> D;
+ Allocator allocator;
+
+ D a(&allocator);
+ a.Parse("[\"one\", \"two\", \"three\"]");
+ EXPECT_FALSE(a.HasParseError());
+ EXPECT_TRUE(a.IsArray());
+ EXPECT_EQ(3u, a.Size());
+ EXPECT_EQ(&a.GetAllocator(), &allocator);
+
+ // Document b; b = a; // does not compile (!is_copy_assignable)
+ D b;
+ b = std::move(a);
+ EXPECT_TRUE(a.IsNull());
+ EXPECT_TRUE(b.IsArray());
+ EXPECT_EQ(3u, b.Size());
+ EXPECT_THROW(a.GetAllocator(), AssertException);
+ EXPECT_EQ(&b.GetAllocator(), &allocator);
+
+ b.Parse("{\"Foo\": \"Bar\", \"Baz\": 42}");
+ EXPECT_FALSE(b.HasParseError());
+ EXPECT_TRUE(b.IsObject());
+ EXPECT_EQ(2u, b.MemberCount());
+
+ // Document c; c = a; // does not compile (see static_assert)
+ D c;
+ c = std::move(b);
+ EXPECT_TRUE(b.IsNull());
+ EXPECT_TRUE(c.IsObject());
+ EXPECT_EQ(2u, c.MemberCount());
+ EXPECT_THROW(b.GetAllocator(), AssertException);
+ EXPECT_EQ(&c.GetAllocator(), &allocator);
+}
+
+TYPED_TEST(DocumentMove, MoveAssignmentParseError) {
+ typedef TypeParam Allocator;
+ typedef GenericDocument<UTF8<>, Allocator> D;
+
+ ParseResult noError;
+ D a;
+ a.Parse("{ 4 = 4]");
+ ParseResult error(a.GetParseError(), a.GetErrorOffset());
+ EXPECT_TRUE(a.HasParseError());
+ EXPECT_NE(error.Code(), noError.Code());
+ EXPECT_NE(error.Offset(), noError.Offset());
+
+ D b;
+ b = std::move(a);
+ EXPECT_FALSE(a.HasParseError());
+ EXPECT_TRUE(b.HasParseError());
+ EXPECT_EQ(a.GetParseError(), noError.Code());
+ EXPECT_EQ(b.GetParseError(), error.Code());
+ EXPECT_EQ(a.GetErrorOffset(), noError.Offset());
+ EXPECT_EQ(b.GetErrorOffset(), error.Offset());
+
+ D c;
+ c = std::move(b);
+ EXPECT_FALSE(b.HasParseError());
+ EXPECT_TRUE(c.HasParseError());
+ EXPECT_EQ(b.GetParseError(), noError.Code());
+ EXPECT_EQ(c.GetParseError(), error.Code());
+ EXPECT_EQ(b.GetErrorOffset(), noError.Offset());
+ EXPECT_EQ(c.GetErrorOffset(), error.Offset());
+}
+
+// This test does not properly use parsing, just for testing.
+// It must call ClearStack() explicitly to prevent memory leak.
+// But here we cannot as ClearStack() is private.
+#if 0
+TYPED_TEST(DocumentMove, MoveAssignmentStack) {
+ typedef TypeParam Allocator;
+ typedef UTF8<> Encoding;
+ typedef GenericDocument<Encoding, Allocator> D;
+
+ D a;
+ size_t defaultCapacity = a.GetStackCapacity();
+
+ // Trick Document into getting GetStackCapacity() to return non-zero
+ typedef GenericReader<Encoding, Encoding, Allocator> Reader;
+ Reader reader(&a.GetAllocator());
+ GenericStringStream<Encoding> is("[\"one\", \"two\", \"three\"]");
+ reader.template Parse<kParseDefaultFlags>(is, a);
+ size_t capacity = a.GetStackCapacity();
+ EXPECT_GT(capacity, 0u);
+
+ D b;
+ b = std::move(a);
+ EXPECT_EQ(a.GetStackCapacity(), defaultCapacity);
+ EXPECT_EQ(b.GetStackCapacity(), capacity);
+
+ D c;
+ c = std::move(b);
+ EXPECT_EQ(b.GetStackCapacity(), defaultCapacity);
+ EXPECT_EQ(c.GetStackCapacity(), capacity);
+}
+#endif
+
+#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
+
+// Issue 22: Memory corruption via operator=
+// Fixed by making unimplemented assignment operator private.
+//TEST(Document, Assignment) {
+// Document d1;
+// Document d2;
+// d1 = d2;
+//}
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
diff --git a/src/s3select/rapidjson/test/unittest/dtoatest.cpp b/src/s3select/rapidjson/test/unittest/dtoatest.cpp
new file mode 100644
index 000000000..3ec898289
--- /dev/null
+++ b/src/s3select/rapidjson/test/unittest/dtoatest.cpp
@@ -0,0 +1,99 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#include "unittest.h"
+#include "rapidjson/internal/dtoa.h"
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(type-limits)
+#endif
+
+using namespace rapidjson::internal;
+
+TEST(dtoa, normal) {
+ char buffer[30];
+
+#define TEST_DTOA(d, a)\
+ *dtoa(d, buffer) = '\0';\
+ EXPECT_STREQ(a, buffer)
+
+ TEST_DTOA(0.0, "0.0");
+ TEST_DTOA(-0.0, "-0.0");
+ TEST_DTOA(1.0, "1.0");
+ TEST_DTOA(-1.0, "-1.0");
+ TEST_DTOA(1.2345, "1.2345");
+ TEST_DTOA(1.2345678, "1.2345678");
+ TEST_DTOA(0.123456789012, "0.123456789012");
+ TEST_DTOA(1234567.8, "1234567.8");
+ TEST_DTOA(-79.39773355813419, "-79.39773355813419");
+ TEST_DTOA(-36.973846435546875, "-36.973846435546875");
+ TEST_DTOA(0.000001, "0.000001");
+ TEST_DTOA(0.0000001, "1e-7");
+ TEST_DTOA(1e30, "1e30");
+ TEST_DTOA(1.234567890123456e30, "1.234567890123456e30");
+ TEST_DTOA(5e-324, "5e-324"); // Min subnormal positive double
+ TEST_DTOA(2.225073858507201e-308, "2.225073858507201e-308"); // Max subnormal positive double
+ TEST_DTOA(2.2250738585072014e-308, "2.2250738585072014e-308"); // Min normal positive double
+ TEST_DTOA(1.7976931348623157e308, "1.7976931348623157e308"); // Max double
+
+#undef TEST_DTOA
+}
+
+TEST(dtoa, maxDecimalPlaces) {
+ char buffer[30];
+
+#define TEST_DTOA(m, d, a)\
+ *dtoa(d, buffer, m) = '\0';\
+ EXPECT_STREQ(a, buffer)
+
+ TEST_DTOA(3, 0.0, "0.0");
+ TEST_DTOA(1, 0.0, "0.0");
+ TEST_DTOA(3, -0.0, "-0.0");
+ TEST_DTOA(3, 1.0, "1.0");
+ TEST_DTOA(3, -1.0, "-1.0");
+ TEST_DTOA(3, 1.2345, "1.234");
+ TEST_DTOA(2, 1.2345, "1.23");
+ TEST_DTOA(1, 1.2345, "1.2");
+ TEST_DTOA(3, 1.2345678, "1.234");
+ TEST_DTOA(3, 1.0001, "1.0");
+ TEST_DTOA(2, 1.0001, "1.0");
+ TEST_DTOA(1, 1.0001, "1.0");
+ TEST_DTOA(3, 0.123456789012, "0.123");
+ TEST_DTOA(2, 0.123456789012, "0.12");
+ TEST_DTOA(1, 0.123456789012, "0.1");
+ TEST_DTOA(4, 0.0001, "0.0001");
+ TEST_DTOA(3, 0.0001, "0.0");
+ TEST_DTOA(2, 0.0001, "0.0");
+ TEST_DTOA(1, 0.0001, "0.0");
+ TEST_DTOA(3, 1234567.8, "1234567.8");
+ TEST_DTOA(3, 1e30, "1e30");
+ TEST_DTOA(3, 5e-324, "0.0"); // Min subnormal positive double
+ TEST_DTOA(3, 2.225073858507201e-308, "0.0"); // Max subnormal positive double
+ TEST_DTOA(3, 2.2250738585072014e-308, "0.0"); // Min normal positive double
+ TEST_DTOA(3, 1.7976931348623157e308, "1.7976931348623157e308"); // Max double
+ TEST_DTOA(5, -0.14000000000000001, "-0.14");
+ TEST_DTOA(4, -0.14000000000000001, "-0.14");
+ TEST_DTOA(3, -0.14000000000000001, "-0.14");
+ TEST_DTOA(3, -0.10000000000000001, "-0.1");
+ TEST_DTOA(2, -0.10000000000000001, "-0.1");
+ TEST_DTOA(1, -0.10000000000000001, "-0.1");
+
+#undef TEST_DTOA
+}
+
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_POP
+#endif
diff --git a/src/s3select/rapidjson/test/unittest/encodedstreamtest.cpp b/src/s3select/rapidjson/test/unittest/encodedstreamtest.cpp
new file mode 100644
index 000000000..d9b87e94c
--- /dev/null
+++ b/src/s3select/rapidjson/test/unittest/encodedstreamtest.cpp
@@ -0,0 +1,313 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#include "unittest.h"
+#include "rapidjson/filereadstream.h"
+#include "rapidjson/filewritestream.h"
+#include "rapidjson/encodedstream.h"
+#include "rapidjson/stringbuffer.h"
+#include "rapidjson/memorystream.h"
+#include "rapidjson/memorybuffer.h"
+
+using namespace rapidjson;
+
+class EncodedStreamTest : public ::testing::Test {
+public:
+ EncodedStreamTest() : json_(), length_() {}
+ virtual ~EncodedStreamTest();
+
+ virtual void SetUp() {
+ json_ = ReadFile("utf8.json", true, &length_);
+ }
+
+ virtual void TearDown() {
+ free(json_);
+ json_ = 0;
+ }
+
+private:
+ EncodedStreamTest(const EncodedStreamTest&);
+ EncodedStreamTest& operator=(const EncodedStreamTest&);
+
+protected:
+ static FILE* Open(const char* filename) {
+ const char *paths[] = {
+ "encodings",
+ "bin/encodings",
+ "../bin/encodings",
+ "../../bin/encodings",
+ "../../../bin/encodings"
+ };
+ char buffer[1024];
+ for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) {
+ sprintf(buffer, "%s/%s", paths[i], filename);
+ FILE *fp = fopen(buffer, "rb");
+ if (fp)
+ return fp;
+ }
+ return 0;
+ }
+
+ static char *ReadFile(const char* filename, bool appendPath, size_t* outLength) {
+ FILE *fp = appendPath ? Open(filename) : fopen(filename, "rb");
+
+ if (!fp) {
+ *outLength = 0;
+ return 0;
+ }
+
+ fseek(fp, 0, SEEK_END);
+ *outLength = static_cast<size_t>(ftell(fp));
+ fseek(fp, 0, SEEK_SET);
+ char* buffer = static_cast<char*>(malloc(*outLength + 1));
+ size_t readLength = fread(buffer, 1, *outLength, fp);
+ buffer[readLength] = '\0';
+ fclose(fp);
+ return buffer;
+ }
+
+ template <typename FileEncoding, typename MemoryEncoding>
+ void TestEncodedInputStream(const char* filename) {
+ // Test FileReadStream
+ {
+ char buffer[16];
+ FILE *fp = Open(filename);
+ ASSERT_TRUE(fp != 0);
+ FileReadStream fs(fp, buffer, sizeof(buffer));
+ EncodedInputStream<FileEncoding, FileReadStream> eis(fs);
+ StringStream s(json_);
+
+ while (eis.Peek() != '\0') {
+ unsigned expected, actual;
+ EXPECT_TRUE(UTF8<>::Decode(s, &expected));
+ EXPECT_TRUE(MemoryEncoding::Decode(eis, &actual));
+ EXPECT_EQ(expected, actual);
+ }
+ EXPECT_EQ('\0', s.Peek());
+ fclose(fp);
+ }
+
+ // Test MemoryStream
+ {
+ size_t size;
+ char* data = ReadFile(filename, true, &size);
+ MemoryStream ms(data, size);
+ EncodedInputStream<FileEncoding, MemoryStream> eis(ms);
+ StringStream s(json_);
+
+ while (eis.Peek() != '\0') {
+ unsigned expected, actual;
+ EXPECT_TRUE(UTF8<>::Decode(s, &expected));
+ EXPECT_TRUE(MemoryEncoding::Decode(eis, &actual));
+ EXPECT_EQ(expected, actual);
+ }
+ EXPECT_EQ('\0', s.Peek());
+ free(data);
+ EXPECT_EQ(size, eis.Tell());
+ }
+ }
+
+ void TestAutoUTFInputStream(const char *filename, bool expectHasBOM) {
+ // Test FileReadStream
+ {
+ char buffer[16];
+ FILE *fp = Open(filename);
+ 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;
+ EXPECT_TRUE(UTF8<>::Decode(s, &expected));
+ EXPECT_TRUE(AutoUTF<unsigned>::Decode(eis, &actual));
+ EXPECT_EQ(expected, actual);
+ }
+ EXPECT_EQ('\0', s.Peek());
+ fclose(fp);
+ }
+
+ // Test MemoryStream
+ {
+ size_t size;
+ 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') {
+ unsigned expected, actual;
+ EXPECT_TRUE(UTF8<>::Decode(s, &expected));
+ EXPECT_TRUE(AutoUTF<unsigned>::Decode(eis, &actual));
+ EXPECT_EQ(expected, actual);
+ }
+ EXPECT_EQ('\0', s.Peek());
+ free(data);
+ EXPECT_EQ(size, eis.Tell());
+ }
+ }
+
+ template <typename FileEncoding, typename MemoryEncoding>
+ void TestEncodedOutputStream(const char* expectedFilename, bool putBOM) {
+ // Test FileWriteStream
+ {
+ char filename[L_tmpnam];
+ FILE* fp = TempFile(filename);
+ char buffer[16];
+ FileWriteStream os(fp, buffer, sizeof(buffer));
+ EncodedOutputStream<FileEncoding, FileWriteStream> eos(os, putBOM);
+ StringStream s(json_);
+ while (s.Peek() != '\0') {
+ bool success = Transcoder<UTF8<>, MemoryEncoding>::Transcode(s, eos);
+ EXPECT_TRUE(success);
+ }
+ eos.Flush();
+ fclose(fp);
+ EXPECT_TRUE(CompareFile(filename, expectedFilename));
+ remove(filename);
+ }
+
+ // Test MemoryBuffer
+ {
+ MemoryBuffer mb;
+ EncodedOutputStream<FileEncoding, MemoryBuffer> eos(mb, putBOM);
+ StringStream s(json_);
+ while (s.Peek() != '\0') {
+ bool success = Transcoder<UTF8<>, MemoryEncoding>::Transcode(s, eos);
+ EXPECT_TRUE(success);
+ }
+ eos.Flush();
+ EXPECT_TRUE(CompareBufferFile(mb.GetBuffer(), mb.GetSize(), expectedFilename));
+ }
+ }
+
+ void TestAutoUTFOutputStream(UTFType type, bool putBOM, const char *expectedFilename) {
+ // Test FileWriteStream
+ {
+ char filename[L_tmpnam];
+ FILE* fp = TempFile(filename);
+
+ char buffer[16];
+ FileWriteStream os(fp, buffer, sizeof(buffer));
+ AutoUTFOutputStream<unsigned, FileWriteStream> eos(os, type, putBOM);
+ StringStream s(json_);
+ while (s.Peek() != '\0') {
+ bool success = Transcoder<UTF8<>, AutoUTF<unsigned> >::Transcode(s, eos);
+ EXPECT_TRUE(success);
+ }
+ eos.Flush();
+ fclose(fp);
+ EXPECT_TRUE(CompareFile(filename, expectedFilename));
+ remove(filename);
+ }
+
+ // Test MemoryBuffer
+ {
+ MemoryBuffer mb;
+ AutoUTFOutputStream<unsigned, MemoryBuffer> eos(mb, type, putBOM);
+ StringStream s(json_);
+ while (s.Peek() != '\0') {
+ bool success = Transcoder<UTF8<>, AutoUTF<unsigned> >::Transcode(s, eos);
+ EXPECT_TRUE(success);
+ }
+ eos.Flush();
+ EXPECT_TRUE(CompareBufferFile(mb.GetBuffer(), mb.GetSize(), expectedFilename));
+ }
+ }
+
+ bool CompareFile(const char* filename, const char* expectedFilename) {
+ size_t actualLength, expectedLength;
+ char* actualBuffer = ReadFile(filename, false, &actualLength);
+ char* expectedBuffer = ReadFile(expectedFilename, true, &expectedLength);
+ bool ret = (expectedLength == actualLength) && memcmp(expectedBuffer, actualBuffer, actualLength) == 0;
+ free(actualBuffer);
+ free(expectedBuffer);
+ return ret;
+ }
+
+ bool CompareBufferFile(const char* actualBuffer, size_t actualLength, const char* expectedFilename) {
+ size_t expectedLength;
+ char* expectedBuffer = ReadFile(expectedFilename, true, &expectedLength);
+ bool ret = (expectedLength == actualLength) && memcmp(expectedBuffer, actualBuffer, actualLength) == 0;
+ free(expectedBuffer);
+ return ret;
+ }
+
+ char *json_;
+ size_t length_;
+};
+
+EncodedStreamTest::~EncodedStreamTest() {}
+
+TEST_F(EncodedStreamTest, EncodedInputStream) {
+ TestEncodedInputStream<UTF8<>, UTF8<> >("utf8.json");
+ TestEncodedInputStream<UTF8<>, UTF8<> >("utf8bom.json");
+ TestEncodedInputStream<UTF16LE<>, UTF16<> >("utf16le.json");
+ TestEncodedInputStream<UTF16LE<>, UTF16<> >("utf16lebom.json");
+ TestEncodedInputStream<UTF16BE<>, UTF16<> >("utf16be.json");
+ TestEncodedInputStream<UTF16BE<>, UTF16<> >("utf16bebom.json");
+ TestEncodedInputStream<UTF32LE<>, UTF32<> >("utf32le.json");
+ TestEncodedInputStream<UTF32LE<>, UTF32<> >("utf32lebom.json");
+ TestEncodedInputStream<UTF32BE<>, UTF32<> >("utf32be.json");
+ TestEncodedInputStream<UTF32BE<>, UTF32<> >("utf32bebom.json");
+}
+
+TEST_F(EncodedStreamTest, AutoUTFInputStream) {
+ 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) {
+ TestEncodedOutputStream<UTF8<>, UTF8<> >("utf8.json", false);
+ TestEncodedOutputStream<UTF8<>, UTF8<> >("utf8bom.json", true);
+ TestEncodedOutputStream<UTF16LE<>, UTF16<> >("utf16le.json", false);
+ TestEncodedOutputStream<UTF16LE<>, UTF16<> >("utf16lebom.json",true);
+ TestEncodedOutputStream<UTF16BE<>, UTF16<> >("utf16be.json", false);
+ TestEncodedOutputStream<UTF16BE<>, UTF16<> >("utf16bebom.json",true);
+ TestEncodedOutputStream<UTF32LE<>, UTF32<> >("utf32le.json", false);
+ TestEncodedOutputStream<UTF32LE<>, UTF32<> >("utf32lebom.json",true);
+ TestEncodedOutputStream<UTF32BE<>, UTF32<> >("utf32be.json", false);
+ TestEncodedOutputStream<UTF32BE<>, UTF32<> >("utf32bebom.json",true);
+}
+
+TEST_F(EncodedStreamTest, AutoUTFOutputStream) {
+ TestAutoUTFOutputStream(kUTF8, false, "utf8.json");
+ TestAutoUTFOutputStream(kUTF8, true, "utf8bom.json");
+ TestAutoUTFOutputStream(kUTF16LE, false, "utf16le.json");
+ TestAutoUTFOutputStream(kUTF16LE, true, "utf16lebom.json");
+ TestAutoUTFOutputStream(kUTF16BE, false, "utf16be.json");
+ TestAutoUTFOutputStream(kUTF16BE, true, "utf16bebom.json");
+ TestAutoUTFOutputStream(kUTF32LE, false, "utf32le.json");
+ TestAutoUTFOutputStream(kUTF32LE, true, "utf32lebom.json");
+ TestAutoUTFOutputStream(kUTF32BE, false, "utf32be.json");
+ TestAutoUTFOutputStream(kUTF32BE, true, "utf32bebom.json");
+}
diff --git a/src/s3select/rapidjson/test/unittest/encodingstest.cpp b/src/s3select/rapidjson/test/unittest/encodingstest.cpp
new file mode 100644
index 000000000..455881e7e
--- /dev/null
+++ b/src/s3select/rapidjson/test/unittest/encodingstest.cpp
@@ -0,0 +1,451 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#include "unittest.h"
+#include "rapidjson/filereadstream.h"
+#include "rapidjson/filewritestream.h"
+#include "rapidjson/encodedstream.h"
+#include "rapidjson/stringbuffer.h"
+
+using namespace rapidjson;
+
+// Verification of encoders/decoders with Hoehrmann's UTF8 decoder
+
+// http://www.unicode.org/Public/UNIDATA/Blocks.txt
+static const unsigned kCodepointRanges[] = {
+ 0x0000, 0x007F, // Basic Latin
+ 0x0080, 0x00FF, // Latin-1 Supplement
+ 0x0100, 0x017F, // Latin Extended-A
+ 0x0180, 0x024F, // Latin Extended-B
+ 0x0250, 0x02AF, // IPA Extensions
+ 0x02B0, 0x02FF, // Spacing Modifier Letters
+ 0x0300, 0x036F, // Combining Diacritical Marks
+ 0x0370, 0x03FF, // Greek and Coptic
+ 0x0400, 0x04FF, // Cyrillic
+ 0x0500, 0x052F, // Cyrillic Supplement
+ 0x0530, 0x058F, // Armenian
+ 0x0590, 0x05FF, // Hebrew
+ 0x0600, 0x06FF, // Arabic
+ 0x0700, 0x074F, // Syriac
+ 0x0750, 0x077F, // Arabic Supplement
+ 0x0780, 0x07BF, // Thaana
+ 0x07C0, 0x07FF, // NKo
+ 0x0800, 0x083F, // Samaritan
+ 0x0840, 0x085F, // Mandaic
+ 0x0900, 0x097F, // Devanagari
+ 0x0980, 0x09FF, // Bengali
+ 0x0A00, 0x0A7F, // Gurmukhi
+ 0x0A80, 0x0AFF, // Gujarati
+ 0x0B00, 0x0B7F, // Oriya
+ 0x0B80, 0x0BFF, // Tamil
+ 0x0C00, 0x0C7F, // Telugu
+ 0x0C80, 0x0CFF, // Kannada
+ 0x0D00, 0x0D7F, // Malayalam
+ 0x0D80, 0x0DFF, // Sinhala
+ 0x0E00, 0x0E7F, // Thai
+ 0x0E80, 0x0EFF, // Lao
+ 0x0F00, 0x0FFF, // Tibetan
+ 0x1000, 0x109F, // Myanmar
+ 0x10A0, 0x10FF, // Georgian
+ 0x1100, 0x11FF, // Hangul Jamo
+ 0x1200, 0x137F, // Ethiopic
+ 0x1380, 0x139F, // Ethiopic Supplement
+ 0x13A0, 0x13FF, // Cherokee
+ 0x1400, 0x167F, // Unified Canadian Aboriginal Syllabics
+ 0x1680, 0x169F, // Ogham
+ 0x16A0, 0x16FF, // Runic
+ 0x1700, 0x171F, // Tagalog
+ 0x1720, 0x173F, // Hanunoo
+ 0x1740, 0x175F, // Buhid
+ 0x1760, 0x177F, // Tagbanwa
+ 0x1780, 0x17FF, // Khmer
+ 0x1800, 0x18AF, // Mongolian
+ 0x18B0, 0x18FF, // Unified Canadian Aboriginal Syllabics Extended
+ 0x1900, 0x194F, // Limbu
+ 0x1950, 0x197F, // Tai Le
+ 0x1980, 0x19DF, // New Tai Lue
+ 0x19E0, 0x19FF, // Khmer Symbols
+ 0x1A00, 0x1A1F, // Buginese
+ 0x1A20, 0x1AAF, // Tai Tham
+ 0x1B00, 0x1B7F, // Balinese
+ 0x1B80, 0x1BBF, // Sundanese
+ 0x1BC0, 0x1BFF, // Batak
+ 0x1C00, 0x1C4F, // Lepcha
+ 0x1C50, 0x1C7F, // Ol Chiki
+ 0x1CD0, 0x1CFF, // Vedic Extensions
+ 0x1D00, 0x1D7F, // Phonetic Extensions
+ 0x1D80, 0x1DBF, // Phonetic Extensions Supplement
+ 0x1DC0, 0x1DFF, // Combining Diacritical Marks Supplement
+ 0x1E00, 0x1EFF, // Latin Extended Additional
+ 0x1F00, 0x1FFF, // Greek Extended
+ 0x2000, 0x206F, // General Punctuation
+ 0x2070, 0x209F, // Superscripts and Subscripts
+ 0x20A0, 0x20CF, // Currency Symbols
+ 0x20D0, 0x20FF, // Combining Diacritical Marks for Symbols
+ 0x2100, 0x214F, // Letterlike Symbols
+ 0x2150, 0x218F, // Number Forms
+ 0x2190, 0x21FF, // Arrows
+ 0x2200, 0x22FF, // Mathematical Operators
+ 0x2300, 0x23FF, // Miscellaneous Technical
+ 0x2400, 0x243F, // Control Pictures
+ 0x2440, 0x245F, // Optical Character Recognition
+ 0x2460, 0x24FF, // Enclosed Alphanumerics
+ 0x2500, 0x257F, // Box Drawing
+ 0x2580, 0x259F, // Block Elements
+ 0x25A0, 0x25FF, // Geometric Shapes
+ 0x2600, 0x26FF, // Miscellaneous Symbols
+ 0x2700, 0x27BF, // Dingbats
+ 0x27C0, 0x27EF, // Miscellaneous Mathematical Symbols-A
+ 0x27F0, 0x27FF, // Supplemental Arrows-A
+ 0x2800, 0x28FF, // Braille Patterns
+ 0x2900, 0x297F, // Supplemental Arrows-B
+ 0x2980, 0x29FF, // Miscellaneous Mathematical Symbols-B
+ 0x2A00, 0x2AFF, // Supplemental Mathematical Operators
+ 0x2B00, 0x2BFF, // Miscellaneous Symbols and Arrows
+ 0x2C00, 0x2C5F, // Glagolitic
+ 0x2C60, 0x2C7F, // Latin Extended-C
+ 0x2C80, 0x2CFF, // Coptic
+ 0x2D00, 0x2D2F, // Georgian Supplement
+ 0x2D30, 0x2D7F, // Tifinagh
+ 0x2D80, 0x2DDF, // Ethiopic Extended
+ 0x2DE0, 0x2DFF, // Cyrillic Extended-A
+ 0x2E00, 0x2E7F, // Supplemental Punctuation
+ 0x2E80, 0x2EFF, // CJK Radicals Supplement
+ 0x2F00, 0x2FDF, // Kangxi Radicals
+ 0x2FF0, 0x2FFF, // Ideographic Description Characters
+ 0x3000, 0x303F, // CJK Symbols and Punctuation
+ 0x3040, 0x309F, // Hiragana
+ 0x30A0, 0x30FF, // Katakana
+ 0x3100, 0x312F, // Bopomofo
+ 0x3130, 0x318F, // Hangul Compatibility Jamo
+ 0x3190, 0x319F, // Kanbun
+ 0x31A0, 0x31BF, // Bopomofo Extended
+ 0x31C0, 0x31EF, // CJK Strokes
+ 0x31F0, 0x31FF, // Katakana Phonetic Extensions
+ 0x3200, 0x32FF, // Enclosed CJK Letters and Months
+ 0x3300, 0x33FF, // CJK Compatibility
+ 0x3400, 0x4DBF, // CJK Unified Ideographs Extension A
+ 0x4DC0, 0x4DFF, // Yijing Hexagram Symbols
+ 0x4E00, 0x9FFF, // CJK Unified Ideographs
+ 0xA000, 0xA48F, // Yi Syllables
+ 0xA490, 0xA4CF, // Yi Radicals
+ 0xA4D0, 0xA4FF, // Lisu
+ 0xA500, 0xA63F, // Vai
+ 0xA640, 0xA69F, // Cyrillic Extended-B
+ 0xA6A0, 0xA6FF, // Bamum
+ 0xA700, 0xA71F, // Modifier Tone Letters
+ 0xA720, 0xA7FF, // Latin Extended-D
+ 0xA800, 0xA82F, // Syloti Nagri
+ 0xA830, 0xA83F, // Common Indic Number Forms
+ 0xA840, 0xA87F, // Phags-pa
+ 0xA880, 0xA8DF, // Saurashtra
+ 0xA8E0, 0xA8FF, // Devanagari Extended
+ 0xA900, 0xA92F, // Kayah Li
+ 0xA930, 0xA95F, // Rejang
+ 0xA960, 0xA97F, // Hangul Jamo Extended-A
+ 0xA980, 0xA9DF, // Javanese
+ 0xAA00, 0xAA5F, // Cham
+ 0xAA60, 0xAA7F, // Myanmar Extended-A
+ 0xAA80, 0xAADF, // Tai Viet
+ 0xAB00, 0xAB2F, // Ethiopic Extended-A
+ 0xABC0, 0xABFF, // Meetei Mayek
+ 0xAC00, 0xD7AF, // Hangul Syllables
+ 0xD7B0, 0xD7FF, // Hangul Jamo Extended-B
+ //0xD800, 0xDB7F, // High Surrogates
+ //0xDB80, 0xDBFF, // High Private Use Surrogates
+ //0xDC00, 0xDFFF, // Low Surrogates
+ 0xE000, 0xF8FF, // Private Use Area
+ 0xF900, 0xFAFF, // CJK Compatibility Ideographs
+ 0xFB00, 0xFB4F, // Alphabetic Presentation Forms
+ 0xFB50, 0xFDFF, // Arabic Presentation Forms-A
+ 0xFE00, 0xFE0F, // Variation Selectors
+ 0xFE10, 0xFE1F, // Vertical Forms
+ 0xFE20, 0xFE2F, // Combining Half Marks
+ 0xFE30, 0xFE4F, // CJK Compatibility Forms
+ 0xFE50, 0xFE6F, // Small Form Variants
+ 0xFE70, 0xFEFF, // Arabic Presentation Forms-B
+ 0xFF00, 0xFFEF, // Halfwidth and Fullwidth Forms
+ 0xFFF0, 0xFFFF, // Specials
+ 0x10000, 0x1007F, // Linear B Syllabary
+ 0x10080, 0x100FF, // Linear B Ideograms
+ 0x10100, 0x1013F, // Aegean Numbers
+ 0x10140, 0x1018F, // Ancient Greek Numbers
+ 0x10190, 0x101CF, // Ancient Symbols
+ 0x101D0, 0x101FF, // Phaistos Disc
+ 0x10280, 0x1029F, // Lycian
+ 0x102A0, 0x102DF, // Carian
+ 0x10300, 0x1032F, // Old Italic
+ 0x10330, 0x1034F, // Gothic
+ 0x10380, 0x1039F, // Ugaritic
+ 0x103A0, 0x103DF, // Old Persian
+ 0x10400, 0x1044F, // Deseret
+ 0x10450, 0x1047F, // Shavian
+ 0x10480, 0x104AF, // Osmanya
+ 0x10800, 0x1083F, // Cypriot Syllabary
+ 0x10840, 0x1085F, // Imperial Aramaic
+ 0x10900, 0x1091F, // Phoenician
+ 0x10920, 0x1093F, // Lydian
+ 0x10A00, 0x10A5F, // Kharoshthi
+ 0x10A60, 0x10A7F, // Old South Arabian
+ 0x10B00, 0x10B3F, // Avestan
+ 0x10B40, 0x10B5F, // Inscriptional Parthian
+ 0x10B60, 0x10B7F, // Inscriptional Pahlavi
+ 0x10C00, 0x10C4F, // Old Turkic
+ 0x10E60, 0x10E7F, // Rumi Numeral Symbols
+ 0x11000, 0x1107F, // Brahmi
+ 0x11080, 0x110CF, // Kaithi
+ 0x12000, 0x123FF, // Cuneiform
+ 0x12400, 0x1247F, // Cuneiform Numbers and Punctuation
+ 0x13000, 0x1342F, // Egyptian Hieroglyphs
+ 0x16800, 0x16A3F, // Bamum Supplement
+ 0x1B000, 0x1B0FF, // Kana Supplement
+ 0x1D000, 0x1D0FF, // Byzantine Musical Symbols
+ 0x1D100, 0x1D1FF, // Musical Symbols
+ 0x1D200, 0x1D24F, // Ancient Greek Musical Notation
+ 0x1D300, 0x1D35F, // Tai Xuan Jing Symbols
+ 0x1D360, 0x1D37F, // Counting Rod Numerals
+ 0x1D400, 0x1D7FF, // Mathematical Alphanumeric Symbols
+ 0x1F000, 0x1F02F, // Mahjong Tiles
+ 0x1F030, 0x1F09F, // Domino Tiles
+ 0x1F0A0, 0x1F0FF, // Playing Cards
+ 0x1F100, 0x1F1FF, // Enclosed Alphanumeric Supplement
+ 0x1F200, 0x1F2FF, // Enclosed Ideographic Supplement
+ 0x1F300, 0x1F5FF, // Miscellaneous Symbols And Pictographs
+ 0x1F600, 0x1F64F, // Emoticons
+ 0x1F680, 0x1F6FF, // Transport And Map Symbols
+ 0x1F700, 0x1F77F, // Alchemical Symbols
+ 0x20000, 0x2A6DF, // CJK Unified Ideographs Extension B
+ 0x2A700, 0x2B73F, // CJK Unified Ideographs Extension C
+ 0x2B740, 0x2B81F, // CJK Unified Ideographs Extension D
+ 0x2F800, 0x2FA1F, // CJK Compatibility Ideographs Supplement
+ 0xE0000, 0xE007F, // Tags
+ 0xE0100, 0xE01EF, // Variation Selectors Supplement
+ 0xF0000, 0xFFFFF, // Supplementary Private Use Area-A
+ 0x100000, 0x10FFFF, // Supplementary Private Use Area-B
+ 0xFFFFFFFF
+};
+
+// Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de>
+// See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
+
+#define UTF8_ACCEPT 0u
+
+static const unsigned char utf8d[] = {
+ // The first part of the table maps bytes to character classes that
+ // to reduce the size of the transition table and create bitmasks.
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+ 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
+
+ // The second part is a transition table that maps a combination
+ // of a state of the automaton and a character class to a state.
+ 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
+ 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
+ 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
+ 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
+ 12,36,12,12,12,12,12,12,12,12,12,12,
+};
+
+static unsigned inline decode(unsigned* state, unsigned* codep, unsigned byte) {
+ unsigned type = utf8d[byte];
+
+ *codep = (*state != UTF8_ACCEPT) ?
+ (byte & 0x3fu) | (*codep << 6) :
+ (0xffu >> type) & (byte);
+
+ *state = utf8d[256 + *state + type];
+ return *state;
+}
+
+//static bool IsUTF8(unsigned char* s) {
+// unsigned codepoint, state = 0;
+//
+// while (*s)
+// decode(&state, &codepoint, *s++);
+//
+// return state == UTF8_ACCEPT;
+//}
+
+TEST(EncodingsTest, UTF8) {
+ StringBuffer os, os2;
+ for (const unsigned* range = kCodepointRanges; *range != 0xFFFFFFFF; range += 2) {
+ for (unsigned codepoint = range[0]; codepoint <= range[1]; ++codepoint) {
+ os.Clear();
+ UTF8<>::Encode(os, codepoint);
+ const char* encodedStr = os.GetString();
+
+ // Decode with Hoehrmann
+ {
+ unsigned decodedCodepoint = 0;
+ unsigned state = 0;
+
+ unsigned decodedCount = 0;
+ for (const char* s = encodedStr; *s; ++s)
+ if (!decode(&state, &decodedCodepoint, static_cast<unsigned char>(*s))) {
+ EXPECT_EQ(codepoint, decodedCodepoint);
+ decodedCount++;
+ }
+
+ if (*encodedStr) { // This decoder cannot handle U+0000
+ EXPECT_EQ(1u, decodedCount); // Should only contain one code point
+ }
+
+ EXPECT_EQ(UTF8_ACCEPT, state);
+ if (UTF8_ACCEPT != state)
+ std::cout << std::hex << codepoint << " " << decodedCodepoint << std::endl;
+ }
+
+ // Decode
+ {
+ StringStream is(encodedStr);
+ unsigned decodedCodepoint;
+ bool result = UTF8<>::Decode(is, &decodedCodepoint);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(codepoint, decodedCodepoint);
+ if (!result || codepoint != decodedCodepoint)
+ std::cout << std::hex << codepoint << " " << decodedCodepoint << std::endl;
+ }
+
+ // Validate
+ {
+ StringStream is(encodedStr);
+ os2.Clear();
+ bool result = UTF8<>::Validate(is, os2);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(0, StrCmp(encodedStr, os2.GetString()));
+ }
+ }
+ }
+}
+
+TEST(EncodingsTest, UTF16) {
+ GenericStringBuffer<UTF16<> > os, os2;
+ GenericStringBuffer<UTF8<> > utf8os;
+ for (const unsigned* range = kCodepointRanges; *range != 0xFFFFFFFF; range += 2) {
+ for (unsigned codepoint = range[0]; codepoint <= range[1]; ++codepoint) {
+ os.Clear();
+ UTF16<>::Encode(os, codepoint);
+ const UTF16<>::Ch* encodedStr = os.GetString();
+
+ // Encode with Hoehrmann's code
+ if (codepoint != 0) // cannot handle U+0000
+ {
+ // encode with UTF8<> first
+ utf8os.Clear();
+ UTF8<>::Encode(utf8os, codepoint);
+
+ // transcode from UTF8 to UTF16 with Hoehrmann's code
+ unsigned decodedCodepoint = 0;
+ unsigned state = 0;
+ UTF16<>::Ch buffer[3], *p = &buffer[0];
+ for (const char* s = utf8os.GetString(); *s; ++s) {
+ if (!decode(&state, &decodedCodepoint, static_cast<unsigned char>(*s)))
+ break;
+ }
+
+ if (codepoint <= 0xFFFF)
+ *p++ = static_cast<UTF16<>::Ch>(decodedCodepoint);
+ else {
+ // Encode code points above U+FFFF as surrogate pair.
+ *p++ = static_cast<UTF16<>::Ch>(0xD7C0 + (decodedCodepoint >> 10));
+ *p++ = static_cast<UTF16<>::Ch>(0xDC00 + (decodedCodepoint & 0x3FF));
+ }
+ *p++ = '\0';
+
+ EXPECT_EQ(0, StrCmp(buffer, encodedStr));
+ }
+
+ // Decode
+ {
+ GenericStringStream<UTF16<> > is(encodedStr);
+ unsigned decodedCodepoint;
+ bool result = UTF16<>::Decode(is, &decodedCodepoint);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(codepoint, decodedCodepoint);
+ if (!result || codepoint != decodedCodepoint)
+ std::cout << std::hex << codepoint << " " << decodedCodepoint << std::endl;
+ }
+
+ // Validate
+ {
+ GenericStringStream<UTF16<> > is(encodedStr);
+ os2.Clear();
+ bool result = UTF16<>::Validate(is, os2);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(0, StrCmp(encodedStr, os2.GetString()));
+ }
+ }
+ }
+}
+
+TEST(EncodingsTest, UTF32) {
+ GenericStringBuffer<UTF32<> > os, os2;
+ for (const unsigned* range = kCodepointRanges; *range != 0xFFFFFFFF; range += 2) {
+ for (unsigned codepoint = range[0]; codepoint <= range[1]; ++codepoint) {
+ os.Clear();
+ UTF32<>::Encode(os, codepoint);
+ const UTF32<>::Ch* encodedStr = os.GetString();
+
+ // Decode
+ {
+ GenericStringStream<UTF32<> > is(encodedStr);
+ unsigned decodedCodepoint;
+ bool result = UTF32<>::Decode(is, &decodedCodepoint);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(codepoint, decodedCodepoint);
+ if (!result || codepoint != decodedCodepoint)
+ std::cout << std::hex << codepoint << " " << decodedCodepoint << std::endl;
+ }
+
+ // Validate
+ {
+ GenericStringStream<UTF32<> > is(encodedStr);
+ os2.Clear();
+ bool result = UTF32<>::Validate(is, os2);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(0, StrCmp(encodedStr, os2.GetString()));
+ }
+ }
+ }
+}
+
+TEST(EncodingsTest, ASCII) {
+ StringBuffer os, os2;
+ for (unsigned codepoint = 0; codepoint < 128; codepoint++) {
+ os.Clear();
+ ASCII<>::Encode(os, codepoint);
+ const ASCII<>::Ch* encodedStr = os.GetString();
+ {
+ StringStream is(encodedStr);
+ unsigned decodedCodepoint;
+ bool result = ASCII<>::Decode(is, &decodedCodepoint);
+ if (!result || codepoint != decodedCodepoint)
+ std::cout << std::hex << codepoint << " " << decodedCodepoint << std::endl;
+ }
+
+ // Validate
+ {
+ StringStream is(encodedStr);
+ os2.Clear();
+ bool result = ASCII<>::Validate(is, os2);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(0, StrCmp(encodedStr, os2.GetString()));
+ }
+ }
+}
diff --git a/src/s3select/rapidjson/test/unittest/filestreamtest.cpp b/src/s3select/rapidjson/test/unittest/filestreamtest.cpp
new file mode 100644
index 000000000..de0b4d1a4
--- /dev/null
+++ b/src/s3select/rapidjson/test/unittest/filestreamtest.cpp
@@ -0,0 +1,155 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#include "unittest.h"
+#include "rapidjson/filereadstream.h"
+#include "rapidjson/filewritestream.h"
+#include "rapidjson/encodedstream.h"
+
+using namespace rapidjson;
+
+class FileStreamTest : public ::testing::Test {
+public:
+ FileStreamTest() : filename_(), json_(), length_(), abcde_() {}
+ virtual ~FileStreamTest();
+
+ virtual void SetUp() {
+ 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);
+ length_ = static_cast<size_t>(ftell(fp));
+ fseek(fp, 0, SEEK_SET);
+ json_ = static_cast<char*>(malloc(length_ + 1));
+ size_t readLength = fread(json_, 1, length_, fp);
+ json_[readLength] = '\0';
+ fclose(fp);
+
+ const char *abcde_paths[] = {
+ "data/abcde.txt",
+ "bin/data/abcde.txt",
+ "../bin/data/abcde.txt",
+ "../../bin/data/abcde.txt",
+ "../../../bin/data/abcde.txt"
+ };
+ fp = 0;
+ for (size_t i = 0; i < sizeof(abcde_paths) / sizeof(abcde_paths[0]); i++) {
+ fp = fopen(abcde_paths[i], "rb");
+ if (fp) {
+ abcde_ = abcde_paths[i];
+ break;
+ }
+ }
+ ASSERT_TRUE(fp != 0);
+ fclose(fp);
+ }
+
+ virtual void TearDown() {
+ free(json_);
+ json_ = 0;
+ }
+
+private:
+ FileStreamTest(const FileStreamTest&);
+ FileStreamTest& operator=(const FileStreamTest&);
+
+protected:
+ const char* filename_;
+ char *json_;
+ size_t length_;
+ const char* abcde_;
+};
+
+FileStreamTest::~FileStreamTest() {}
+
+TEST_F(FileStreamTest, FileReadStream) {
+ FILE *fp = fopen(filename_, "rb");
+ ASSERT_TRUE(fp != 0);
+ char buffer[65536];
+ FileReadStream s(fp, buffer, sizeof(buffer));
+
+ for (size_t i = 0; i < length_; i++) {
+ EXPECT_EQ(json_[i], s.Peek());
+ EXPECT_EQ(json_[i], s.Peek()); // 2nd time should be the same
+ EXPECT_EQ(json_[i], s.Take());
+ }
+
+ EXPECT_EQ(length_, s.Tell());
+ EXPECT_EQ('\0', s.Peek());
+
+ fclose(fp);
+}
+
+TEST_F(FileStreamTest, FileReadStream_Peek4) {
+ FILE *fp = fopen(abcde_, "rb");
+ ASSERT_TRUE(fp != 0);
+ char buffer[4];
+ FileReadStream s(fp, buffer, sizeof(buffer));
+
+ const char* c = s.Peek4();
+ for (int i = 0; i < 4; i++)
+ EXPECT_EQ('a' + i, c[i]);
+ EXPECT_EQ(0u, s.Tell());
+
+ for (int i = 0; i < 5; i++) {
+ EXPECT_EQ(static_cast<size_t>(i), s.Tell());
+ EXPECT_EQ('a' + i, s.Peek());
+ EXPECT_EQ('a' + i, s.Peek());
+ EXPECT_EQ('a' + i, s.Take());
+ }
+ EXPECT_EQ(5u, s.Tell());
+ EXPECT_EQ(0, s.Peek());
+ EXPECT_EQ(0, s.Take());
+
+ fclose(fp);
+}
+
+TEST_F(FileStreamTest, FileWriteStream) {
+ char filename[L_tmpnam];
+ FILE* fp = TempFile(filename);
+
+ char buffer[65536];
+ FileWriteStream os(fp, buffer, sizeof(buffer));
+ for (size_t i = 0; i < length_; i++)
+ os.Put(json_[i]);
+ os.Flush();
+ fclose(fp);
+
+ // Read it back to verify
+ fp = fopen(filename, "rb");
+ FileReadStream is(fp, buffer, sizeof(buffer));
+
+ for (size_t i = 0; i < length_; i++)
+ EXPECT_EQ(json_[i], is.Take());
+
+ EXPECT_EQ(length_, is.Tell());
+ fclose(fp);
+
+ //std::cout << filename << std::endl;
+ remove(filename);
+}
diff --git a/src/s3select/rapidjson/test/unittest/fwdtest.cpp b/src/s3select/rapidjson/test/unittest/fwdtest.cpp
new file mode 100644
index 000000000..e9c707805
--- /dev/null
+++ b/src/s3select/rapidjson/test/unittest/fwdtest.cpp
@@ -0,0 +1,230 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#include "unittest.h"
+
+// Using forward declared types here.
+
+#include "rapidjson/fwd.h"
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(effc++)
+#endif
+
+using namespace rapidjson;
+
+struct Foo {
+ Foo();
+ ~Foo();
+
+ // encodings.h
+ UTF8<char>* utf8;
+ UTF16<wchar_t>* utf16;
+ UTF16BE<wchar_t>* utf16be;
+ UTF16LE<wchar_t>* utf16le;
+ UTF32<unsigned>* utf32;
+ UTF32BE<unsigned>* utf32be;
+ UTF32LE<unsigned>* utf32le;
+ ASCII<char>* ascii;
+ AutoUTF<unsigned>* autoutf;
+ Transcoder<UTF8<char>, UTF8<char> >* transcoder;
+
+ // allocators.h
+ CrtAllocator* crtallocator;
+ MemoryPoolAllocator<CrtAllocator>* memorypoolallocator;
+
+ // stream.h
+ StringStream* stringstream;
+ InsituStringStream* insitustringstream;
+
+ // stringbuffer.h
+ StringBuffer* stringbuffer;
+
+ // // filereadstream.h
+ // FileReadStream* filereadstream;
+
+ // // filewritestream.h
+ // FileWriteStream* filewritestream;
+
+ // memorybuffer.h
+ MemoryBuffer* memorybuffer;
+
+ // memorystream.h
+ MemoryStream* memorystream;
+
+ // reader.h
+ BaseReaderHandler<UTF8<char>, void>* basereaderhandler;
+ Reader* reader;
+
+ // writer.h
+ Writer<StringBuffer, UTF8<char>, UTF8<char>, CrtAllocator, 0>* writer;
+
+ // prettywriter.h
+ PrettyWriter<StringBuffer, UTF8<char>, UTF8<char>, CrtAllocator, 0>* prettywriter;
+
+ // document.h
+ Value* value;
+ Document* document;
+
+ // pointer.h
+ Pointer* pointer;
+
+ // schema.h
+ SchemaDocument* schemadocument;
+ SchemaValidator* schemavalidator;
+
+ // char buffer[16];
+};
+
+// Using type definitions here.
+
+#include "rapidjson/stringbuffer.h"
+#include "rapidjson/filereadstream.h"
+#include "rapidjson/filewritestream.h"
+#include "rapidjson/memorybuffer.h"
+#include "rapidjson/memorystream.h"
+#include "rapidjson/document.h" // -> reader.h
+#include "rapidjson/writer.h"
+#include "rapidjson/prettywriter.h"
+#include "rapidjson/schema.h" // -> pointer.h
+
+typedef Transcoder<UTF8<>, UTF8<> > TranscoderUtf8ToUtf8;
+typedef BaseReaderHandler<UTF8<>, void> BaseReaderHandlerUtf8Void;
+
+Foo::Foo() :
+ // encodings.h
+ utf8(RAPIDJSON_NEW(UTF8<>)),
+ utf16(RAPIDJSON_NEW(UTF16<>)),
+ utf16be(RAPIDJSON_NEW(UTF16BE<>)),
+ utf16le(RAPIDJSON_NEW(UTF16LE<>)),
+ utf32(RAPIDJSON_NEW(UTF32<>)),
+ utf32be(RAPIDJSON_NEW(UTF32BE<>)),
+ utf32le(RAPIDJSON_NEW(UTF32LE<>)),
+ ascii(RAPIDJSON_NEW(ASCII<>)),
+ autoutf(RAPIDJSON_NEW(AutoUTF<unsigned>)),
+ transcoder(RAPIDJSON_NEW(TranscoderUtf8ToUtf8)),
+
+ // allocators.h
+ crtallocator(RAPIDJSON_NEW(CrtAllocator)),
+ memorypoolallocator(RAPIDJSON_NEW(MemoryPoolAllocator<>)),
+
+ // stream.h
+ stringstream(RAPIDJSON_NEW(StringStream)(NULL)),
+ insitustringstream(RAPIDJSON_NEW(InsituStringStream)(NULL)),
+
+ // stringbuffer.h
+ stringbuffer(RAPIDJSON_NEW(StringBuffer)),
+
+ // // filereadstream.h
+ // filereadstream(RAPIDJSON_NEW(FileReadStream)(stdout, buffer, sizeof(buffer))),
+
+ // // filewritestream.h
+ // filewritestream(RAPIDJSON_NEW(FileWriteStream)(stdout, buffer, sizeof(buffer))),
+
+ // memorybuffer.h
+ memorybuffer(RAPIDJSON_NEW(MemoryBuffer)),
+
+ // memorystream.h
+ memorystream(RAPIDJSON_NEW(MemoryStream)(NULL, 0)),
+
+ // reader.h
+ basereaderhandler(RAPIDJSON_NEW(BaseReaderHandlerUtf8Void)),
+ reader(RAPIDJSON_NEW(Reader)),
+
+ // writer.h
+ writer(RAPIDJSON_NEW(Writer<StringBuffer>)),
+
+ // prettywriter.h
+ prettywriter(RAPIDJSON_NEW(PrettyWriter<StringBuffer>)),
+
+ // document.h
+ value(RAPIDJSON_NEW(Value)),
+ document(RAPIDJSON_NEW(Document)),
+
+ // pointer.h
+ pointer(RAPIDJSON_NEW(Pointer)),
+
+ // schema.h
+ schemadocument(RAPIDJSON_NEW(SchemaDocument)(*document)),
+ schemavalidator(RAPIDJSON_NEW(SchemaValidator)(*schemadocument))
+{
+
+}
+
+Foo::~Foo() {
+ // encodings.h
+ RAPIDJSON_DELETE(utf8);
+ RAPIDJSON_DELETE(utf16);
+ RAPIDJSON_DELETE(utf16be);
+ RAPIDJSON_DELETE(utf16le);
+ RAPIDJSON_DELETE(utf32);
+ RAPIDJSON_DELETE(utf32be);
+ RAPIDJSON_DELETE(utf32le);
+ RAPIDJSON_DELETE(ascii);
+ RAPIDJSON_DELETE(autoutf);
+ RAPIDJSON_DELETE(transcoder);
+
+ // allocators.h
+ RAPIDJSON_DELETE(crtallocator);
+ RAPIDJSON_DELETE(memorypoolallocator);
+
+ // stream.h
+ RAPIDJSON_DELETE(stringstream);
+ RAPIDJSON_DELETE(insitustringstream);
+
+ // stringbuffer.h
+ RAPIDJSON_DELETE(stringbuffer);
+
+ // // filereadstream.h
+ // RAPIDJSON_DELETE(filereadstream);
+
+ // // filewritestream.h
+ // RAPIDJSON_DELETE(filewritestream);
+
+ // memorybuffer.h
+ RAPIDJSON_DELETE(memorybuffer);
+
+ // memorystream.h
+ RAPIDJSON_DELETE(memorystream);
+
+ // reader.h
+ RAPIDJSON_DELETE(basereaderhandler);
+ RAPIDJSON_DELETE(reader);
+
+ // writer.h
+ RAPIDJSON_DELETE(writer);
+
+ // prettywriter.h
+ RAPIDJSON_DELETE(prettywriter);
+
+ // document.h
+ RAPIDJSON_DELETE(value);
+ RAPIDJSON_DELETE(document);
+
+ // pointer.h
+ RAPIDJSON_DELETE(pointer);
+
+ // schema.h
+ RAPIDJSON_DELETE(schemadocument);
+ RAPIDJSON_DELETE(schemavalidator);
+}
+
+TEST(Fwd, Fwd) {
+ Foo f;
+}
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_POP
+#endif
diff --git a/src/s3select/rapidjson/test/unittest/istreamwrappertest.cpp b/src/s3select/rapidjson/test/unittest/istreamwrappertest.cpp
new file mode 100644
index 000000000..f0cdb2d38
--- /dev/null
+++ b/src/s3select/rapidjson/test/unittest/istreamwrappertest.cpp
@@ -0,0 +1,181 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#include "unittest.h"
+
+#include "rapidjson/istreamwrapper.h"
+#include "rapidjson/encodedstream.h"
+#include "rapidjson/document.h"
+#include <sstream>
+#include <fstream>
+
+#if defined(_MSC_VER) && !defined(__clang__)
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(4702) // unreachable code
+#endif
+
+using namespace rapidjson;
+using namespace std;
+
+template <typename StringStreamType>
+static void TestStringStream() {
+ typedef typename StringStreamType::char_type Ch;
+
+ {
+ StringStreamType iss;
+ BasicIStreamWrapper<StringStreamType> is(iss);
+ EXPECT_EQ(0u, is.Tell());
+ if (sizeof(Ch) == 1) {
+ EXPECT_EQ(0, is.Peek4());
+ EXPECT_EQ(0u, is.Tell());
+ }
+ EXPECT_EQ(0, is.Peek());
+ EXPECT_EQ(0, is.Take());
+ EXPECT_EQ(0u, is.Tell());
+ }
+
+ {
+ Ch s[] = { 'A', 'B', 'C', '\0' };
+ StringStreamType iss(s);
+ BasicIStreamWrapper<StringStreamType> is(iss);
+ EXPECT_EQ(0u, is.Tell());
+ if (sizeof(Ch) == 1) {
+ EXPECT_EQ(0, is.Peek4()); // less than 4 bytes
+ }
+ for (int i = 0; i < 3; i++) {
+ EXPECT_EQ(static_cast<size_t>(i), is.Tell());
+ EXPECT_EQ('A' + i, is.Peek());
+ EXPECT_EQ('A' + i, is.Peek());
+ EXPECT_EQ('A' + i, is.Take());
+ }
+ EXPECT_EQ(3u, is.Tell());
+ EXPECT_EQ(0, is.Peek());
+ EXPECT_EQ(0, is.Take());
+ }
+
+ {
+ Ch s[] = { 'A', 'B', 'C', 'D', 'E', '\0' };
+ StringStreamType iss(s);
+ BasicIStreamWrapper<StringStreamType> is(iss);
+ if (sizeof(Ch) == 1) {
+ const Ch* c = is.Peek4();
+ for (int i = 0; i < 4; i++)
+ EXPECT_EQ('A' + i, c[i]);
+ EXPECT_EQ(0u, is.Tell());
+ }
+ for (int i = 0; i < 5; i++) {
+ EXPECT_EQ(static_cast<size_t>(i), is.Tell());
+ EXPECT_EQ('A' + i, is.Peek());
+ EXPECT_EQ('A' + i, is.Peek());
+ EXPECT_EQ('A' + i, is.Take());
+ }
+ EXPECT_EQ(5u, is.Tell());
+ EXPECT_EQ(0, is.Peek());
+ EXPECT_EQ(0, is.Take());
+ }
+}
+
+TEST(IStreamWrapper, istringstream) {
+ TestStringStream<istringstream>();
+}
+
+TEST(IStreamWrapper, stringstream) {
+ TestStringStream<stringstream>();
+}
+
+TEST(IStreamWrapper, wistringstream) {
+ TestStringStream<wistringstream>();
+}
+
+TEST(IStreamWrapper, wstringstream) {
+ TestStringStream<wstringstream>();
+}
+
+template <typename FileStreamType>
+static bool Open(FileStreamType& fs, const char* filename) {
+ const char *paths[] = {
+ "encodings",
+ "bin/encodings",
+ "../bin/encodings",
+ "../../bin/encodings",
+ "../../../bin/encodings"
+ };
+ char buffer[1024];
+ for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) {
+ sprintf(buffer, "%s/%s", paths[i], filename);
+ fs.open(buffer, ios_base::in | ios_base::binary);
+ if (fs.is_open())
+ return true;
+ }
+ return false;
+}
+
+TEST(IStreamWrapper, ifstream) {
+ ifstream ifs;
+ ASSERT_TRUE(Open(ifs, "utf8bom.json"));
+ IStreamWrapper isw(ifs);
+ EncodedInputStream<UTF8<>, IStreamWrapper> eis(isw);
+ Document d;
+ EXPECT_TRUE(!d.ParseStream(eis).HasParseError());
+ EXPECT_TRUE(d.IsObject());
+ EXPECT_EQ(5u, d.MemberCount());
+}
+
+TEST(IStreamWrapper, fstream) {
+ fstream fs;
+ ASSERT_TRUE(Open(fs, "utf8bom.json"));
+ IStreamWrapper isw(fs);
+ EncodedInputStream<UTF8<>, IStreamWrapper> eis(isw);
+ Document d;
+ EXPECT_TRUE(!d.ParseStream(eis).HasParseError());
+ EXPECT_TRUE(d.IsObject());
+ EXPECT_EQ(5u, d.MemberCount());
+}
+
+// wifstream/wfstream only works on C++11 with codecvt_utf16
+// But many C++11 library still not have it.
+#if 0
+#include <codecvt>
+
+TEST(IStreamWrapper, wifstream) {
+ wifstream ifs;
+ ASSERT_TRUE(Open(ifs, "utf16bebom.json"));
+ ifs.imbue(std::locale(ifs.getloc(),
+ new std::codecvt_utf16<wchar_t, 0x10ffff, std::consume_header>));
+ WIStreamWrapper isw(ifs);
+ GenericDocument<UTF16<> > d;
+ d.ParseStream<kParseDefaultFlags, UTF16<>, WIStreamWrapper>(isw);
+ EXPECT_TRUE(!d.HasParseError());
+ EXPECT_TRUE(d.IsObject());
+ EXPECT_EQ(5, d.MemberCount());
+}
+
+TEST(IStreamWrapper, wfstream) {
+ wfstream fs;
+ ASSERT_TRUE(Open(fs, "utf16bebom.json"));
+ fs.imbue(std::locale(fs.getloc(),
+ new std::codecvt_utf16<wchar_t, 0x10ffff, std::consume_header>));
+ WIStreamWrapper isw(fs);
+ GenericDocument<UTF16<> > d;
+ d.ParseStream<kParseDefaultFlags, UTF16<>, WIStreamWrapper>(isw);
+ EXPECT_TRUE(!d.HasParseError());
+ EXPECT_TRUE(d.IsObject());
+ EXPECT_EQ(5, d.MemberCount());
+}
+
+#endif
+
+#if defined(_MSC_VER) && !defined(__clang__)
+RAPIDJSON_DIAG_POP
+#endif
diff --git a/src/s3select/rapidjson/test/unittest/itoatest.cpp b/src/s3select/rapidjson/test/unittest/itoatest.cpp
new file mode 100644
index 000000000..4c834de37
--- /dev/null
+++ b/src/s3select/rapidjson/test/unittest/itoatest.cpp
@@ -0,0 +1,160 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#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 (int 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;
+ if (i > static_cast<T>((std::numeric_limits<T>::max)() / static_cast<T>(power)))
+ break;
+ i *= static_cast<T>(power);
+ } while (last < i);
+ }
+}
+
+static void u32toa_naive(uint32_t value, char* buffer) {
+ char temp[10];
+ char *p = temp;
+ do {
+ *p++ = static_cast<char>(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++ = static_cast<char>(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
diff --git a/src/s3select/rapidjson/test/unittest/jsoncheckertest.cpp b/src/s3select/rapidjson/test/unittest/jsoncheckertest.cpp
new file mode 100644
index 000000000..19e1f1c47
--- /dev/null
+++ b/src/s3select/rapidjson/test/unittest/jsoncheckertest.cpp
@@ -0,0 +1,143 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#include "unittest.h"
+
+#include "rapidjson/document.h"
+
+using namespace rapidjson;
+
+static char* ReadFile(const char* filename, size_t& length) {
+ const char *paths[] = {
+ "jsonchecker",
+ "bin/jsonchecker",
+ "../bin/jsonchecker",
+ "../../bin/jsonchecker",
+ "../../../bin/jsonchecker"
+ };
+ char buffer[1024];
+ FILE *fp = 0;
+ for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) {
+ sprintf(buffer, "%s/%s", paths[i], filename);
+ fp = fopen(buffer, "rb");
+ if (fp)
+ break;
+ }
+
+ if (!fp)
+ return 0;
+
+ fseek(fp, 0, SEEK_END);
+ length = static_cast<size_t>(ftell(fp));
+ fseek(fp, 0, SEEK_SET);
+ char* json = static_cast<char*>(malloc(length + 1));
+ size_t readLength = fread(json, 1, length, fp);
+ json[readLength] = '\0';
+ fclose(fp);
+ return json;
+}
+
+struct NoOpHandler {
+ bool Null() { return true; }
+ bool Bool(bool) { return true; }
+ bool Int(int) { return true; }
+ bool Uint(unsigned) { return true; }
+ bool Int64(int64_t) { return true; }
+ bool Uint64(uint64_t) { return true; }
+ bool Double(double) { return true; }
+ bool RawNumber(const char*, SizeType, bool) { return true; }
+ bool String(const char*, SizeType, bool) { return true; }
+ bool StartObject() { return true; }
+ bool Key(const char*, SizeType, bool) { return true; }
+ bool EndObject(SizeType) { return true; }
+ bool StartArray() { return true; }
+ bool EndArray(SizeType) { return true; }
+};
+
+
+TEST(JsonChecker, Reader) {
+ char filename[256];
+
+ // jsonchecker/failXX.json
+ for (int i = 1; i <= 33; i++) {
+ if (i == 1) // fail1.json is valid in rapidjson, which has no limitation on type of root element (RFC 7159).
+ continue;
+ if (i == 18) // fail18.json is valid in rapidjson, which has no limitation on depth of nesting.
+ continue;
+
+ sprintf(filename, "fail%d.json", i);
+ size_t length;
+ char* json = ReadFile(filename, length);
+ if (!json) {
+ printf("jsonchecker file %s not found", filename);
+ ADD_FAILURE();
+ continue;
+ }
+
+ // Test stack-based parsing.
+ GenericDocument<UTF8<>, CrtAllocator> document; // Use Crt allocator to check exception-safety (no memory leak)
+ document.Parse(json);
+ EXPECT_TRUE(document.HasParseError()) << filename;
+
+ // Test iterative parsing.
+ document.Parse<kParseIterativeFlag>(json);
+ EXPECT_TRUE(document.HasParseError()) << filename;
+
+ // Test iterative pull-parsing.
+ Reader reader;
+ StringStream ss(json);
+ NoOpHandler h;
+ reader.IterativeParseInit();
+ while (!reader.IterativeParseComplete()) {
+ if (!reader.IterativeParseNext<kParseDefaultFlags>(ss, h))
+ break;
+ }
+ EXPECT_TRUE(reader.HasParseError()) << filename;
+
+ free(json);
+ }
+
+ // passX.json
+ for (int i = 1; i <= 3; i++) {
+ sprintf(filename, "pass%d.json", i);
+ size_t length;
+ char* json = ReadFile(filename, length);
+ if (!json) {
+ printf("jsonchecker file %s not found", filename);
+ continue;
+ }
+
+ // Test stack-based parsing.
+ GenericDocument<UTF8<>, CrtAllocator> document; // Use Crt allocator to check exception-safety (no memory leak)
+ document.Parse(json);
+ EXPECT_FALSE(document.HasParseError()) << filename;
+
+ // Test iterative parsing.
+ document.Parse<kParseIterativeFlag>(json);
+ EXPECT_FALSE(document.HasParseError()) << filename;
+
+ // Test iterative pull-parsing.
+ Reader reader;
+ StringStream ss(json);
+ NoOpHandler h;
+ reader.IterativeParseInit();
+ while (!reader.IterativeParseComplete()) {
+ if (!reader.IterativeParseNext<kParseDefaultFlags>(ss, h))
+ break;
+ }
+ EXPECT_FALSE(reader.HasParseError()) << filename;
+
+ free(json);
+ }
+}
diff --git a/src/s3select/rapidjson/test/unittest/namespacetest.cpp b/src/s3select/rapidjson/test/unittest/namespacetest.cpp
new file mode 100644
index 000000000..e33e6d5f5
--- /dev/null
+++ b/src/s3select/rapidjson/test/unittest/namespacetest.cpp
@@ -0,0 +1,70 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#include "unittest.h"
+
+// test another instantiation of RapidJSON in a different namespace
+
+#define RAPIDJSON_NAMESPACE my::rapid::json
+#define RAPIDJSON_NAMESPACE_BEGIN namespace my { namespace rapid { namespace json {
+#define RAPIDJSON_NAMESPACE_END } } }
+
+// include lots of RapidJSON files
+
+#include "rapidjson/document.h"
+#include "rapidjson/writer.h"
+#include "rapidjson/filereadstream.h"
+#include "rapidjson/filewritestream.h"
+#include "rapidjson/encodedstream.h"
+#include "rapidjson/stringbuffer.h"
+
+static const char json[] = "{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3,4]}";
+
+TEST(NamespaceTest,Using) {
+ using namespace RAPIDJSON_NAMESPACE;
+ typedef GenericDocument<UTF8<>, CrtAllocator> DocumentType;
+ DocumentType doc;
+
+ doc.Parse(json);
+ EXPECT_TRUE(!doc.HasParseError());
+}
+
+TEST(NamespaceTest,Direct) {
+ typedef RAPIDJSON_NAMESPACE::Document Document;
+ typedef RAPIDJSON_NAMESPACE::Reader Reader;
+ typedef RAPIDJSON_NAMESPACE::StringStream StringStream;
+ typedef RAPIDJSON_NAMESPACE::StringBuffer StringBuffer;
+ typedef RAPIDJSON_NAMESPACE::Writer<StringBuffer> WriterType;
+
+ StringStream s(json);
+ StringBuffer buffer;
+ WriterType writer(buffer);
+ buffer.ShrinkToFit();
+ Reader reader;
+ reader.Parse(s, writer);
+
+ EXPECT_STREQ(json, buffer.GetString());
+ EXPECT_EQ(sizeof(json)-1, buffer.GetSize());
+ EXPECT_TRUE(writer.IsComplete());
+
+ Document doc;
+ doc.Parse(buffer.GetString());
+ EXPECT_TRUE(!doc.HasParseError());
+
+ buffer.Clear();
+ writer.Reset(buffer);
+ doc.Accept(writer);
+ EXPECT_STREQ(json, buffer.GetString());
+ EXPECT_TRUE(writer.IsComplete());
+}
diff --git a/src/s3select/rapidjson/test/unittest/ostreamwrappertest.cpp b/src/s3select/rapidjson/test/unittest/ostreamwrappertest.cpp
new file mode 100644
index 000000000..c9bc5f4bf
--- /dev/null
+++ b/src/s3select/rapidjson/test/unittest/ostreamwrappertest.cpp
@@ -0,0 +1,92 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#include "unittest.h"
+
+#include "rapidjson/ostreamwrapper.h"
+#include "rapidjson/encodedstream.h"
+#include "rapidjson/document.h"
+#include <sstream>
+#include <fstream>
+
+using namespace rapidjson;
+using namespace std;
+
+template <typename StringStreamType>
+static void TestStringStream() {
+ typedef typename StringStreamType::char_type Ch;
+
+ Ch s[] = { 'A', 'B', 'C', '\0' };
+ StringStreamType oss(s);
+ BasicOStreamWrapper<StringStreamType> os(oss);
+ for (size_t i = 0; i < 3; i++)
+ os.Put(s[i]);
+ os.Flush();
+ for (size_t i = 0; i < 3; i++)
+ EXPECT_EQ(s[i], oss.str()[i]);
+}
+
+TEST(OStreamWrapper, ostringstream) {
+ TestStringStream<ostringstream>();
+}
+
+TEST(OStreamWrapper, stringstream) {
+ TestStringStream<stringstream>();
+}
+
+TEST(OStreamWrapper, wostringstream) {
+ TestStringStream<wostringstream>();
+}
+
+TEST(OStreamWrapper, wstringstream) {
+ TestStringStream<wstringstream>();
+}
+
+TEST(OStreamWrapper, cout) {
+ OStreamWrapper os(cout);
+ const char* s = "Hello World!\n";
+ while (*s)
+ os.Put(*s++);
+ os.Flush();
+}
+
+template <typename FileStreamType>
+static void TestFileStream() {
+ char filename[L_tmpnam];
+ FILE* fp = TempFile(filename);
+ fclose(fp);
+
+ const char* s = "Hello World!\n";
+ {
+ FileStreamType ofs(filename, ios::out | ios::binary);
+ BasicOStreamWrapper<FileStreamType> osw(ofs);
+ for (const char* p = s; *p; p++)
+ osw.Put(*p);
+ osw.Flush();
+ }
+
+ fp = fopen(filename, "r");
+ ASSERT_TRUE( fp != NULL );
+ for (const char* p = s; *p; p++)
+ EXPECT_EQ(*p, static_cast<char>(fgetc(fp)));
+ fclose(fp);
+}
+
+TEST(OStreamWrapper, ofstream) {
+ TestFileStream<ofstream>();
+}
+
+TEST(OStreamWrapper, fstream) {
+ TestFileStream<fstream>();
+}
diff --git a/src/s3select/rapidjson/test/unittest/platformtest.cpp b/src/s3select/rapidjson/test/unittest/platformtest.cpp
new file mode 100644
index 000000000..05eba3f5b
--- /dev/null
+++ b/src/s3select/rapidjson/test/unittest/platformtest.cpp
@@ -0,0 +1,40 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2021 THL A29 Limited, a Tencent company, and Milo Yip.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#include "unittest.h"
+
+// see https://github.com/Tencent/rapidjson/issues/1448
+// including windows.h on purpose to provoke a compile time problem as GetObject is a
+// macro that gets defined when windows.h is included
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+#include "rapidjson/document.h"
+#undef GetObject
+
+using namespace rapidjson;
+
+TEST(Platform, GetObject) {
+ Document doc;
+ doc.Parse(" { \"object\" : { \"pi\": 3.1416} } ");
+ EXPECT_TRUE(doc.IsObject());
+ EXPECT_TRUE(doc.HasMember("object"));
+ const Document::ValueType& o = doc["object"];
+ EXPECT_TRUE(o.IsObject());
+ Value::ConstObject sub = o.GetObject();
+ EXPECT_TRUE(sub.HasMember("pi"));
+ Value::ConstObject sub2 = o.GetObj();
+ EXPECT_TRUE(sub2.HasMember("pi"));
+}
diff --git a/src/s3select/rapidjson/test/unittest/pointertest.cpp b/src/s3select/rapidjson/test/unittest/pointertest.cpp
new file mode 100644
index 000000000..342086dd1
--- /dev/null
+++ b/src/s3select/rapidjson/test/unittest/pointertest.cpp
@@ -0,0 +1,1730 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#include "unittest.h"
+#include "rapidjson/pointer.h"
+#include "rapidjson/stringbuffer.h"
+#include "rapidjson/ostreamwrapper.h"
+#include <sstream>
+#include <map>
+#include <algorithm>
+
+using namespace rapidjson;
+
+static const char kJson[] = "{\n"
+" \"foo\":[\"bar\", \"baz\"],\n"
+" \"\" : 0,\n"
+" \"a/b\" : 1,\n"
+" \"c%d\" : 2,\n"
+" \"e^f\" : 3,\n"
+" \"g|h\" : 4,\n"
+" \"i\\\\j\" : 5,\n"
+" \"k\\\"l\" : 6,\n"
+" \" \" : 7,\n"
+" \"m~n\" : 8\n"
+"}";
+
+TEST(Pointer, DefaultConstructor) {
+ Pointer p;
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(0u, p.GetTokenCount());
+}
+
+TEST(Pointer, Parse) {
+ {
+ Pointer p("");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(0u, p.GetTokenCount());
+ }
+
+ {
+ Pointer p("/");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_EQ(0u, p.GetTokens()[0].length);
+ EXPECT_STREQ("", p.GetTokens()[0].name);
+ EXPECT_EQ(kPointerInvalidIndex, p.GetTokens()[0].index);
+ }
+
+ {
+ Pointer p("/foo");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_EQ(3u, p.GetTokens()[0].length);
+ EXPECT_STREQ("foo", p.GetTokens()[0].name);
+ EXPECT_EQ(kPointerInvalidIndex, p.GetTokens()[0].index);
+ }
+
+ #if RAPIDJSON_HAS_STDSTRING
+ {
+ Pointer p(std::string("/foo"));
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_EQ(3u, p.GetTokens()[0].length);
+ EXPECT_STREQ("foo", p.GetTokens()[0].name);
+ EXPECT_EQ(kPointerInvalidIndex, p.GetTokens()[0].index);
+ }
+ #endif
+
+ {
+ Pointer p("/foo/0");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(2u, p.GetTokenCount());
+ EXPECT_EQ(3u, p.GetTokens()[0].length);
+ EXPECT_STREQ("foo", p.GetTokens()[0].name);
+ EXPECT_EQ(kPointerInvalidIndex, p.GetTokens()[0].index);
+ EXPECT_EQ(1u, p.GetTokens()[1].length);
+ EXPECT_STREQ("0", p.GetTokens()[1].name);
+ EXPECT_EQ(0u, p.GetTokens()[1].index);
+ }
+
+ {
+ // Unescape ~1
+ Pointer p("/a~1b");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_EQ(3u, p.GetTokens()[0].length);
+ EXPECT_STREQ("a/b", p.GetTokens()[0].name);
+ }
+
+ {
+ // Unescape ~0
+ Pointer p("/m~0n");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_EQ(3u, p.GetTokens()[0].length);
+ EXPECT_STREQ("m~n", p.GetTokens()[0].name);
+ }
+
+ {
+ // empty name
+ Pointer p("/");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_EQ(0u, p.GetTokens()[0].length);
+ EXPECT_STREQ("", p.GetTokens()[0].name);
+ }
+
+ {
+ // empty and non-empty name
+ Pointer p("//a");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(2u, p.GetTokenCount());
+ EXPECT_EQ(0u, p.GetTokens()[0].length);
+ EXPECT_STREQ("", p.GetTokens()[0].name);
+ EXPECT_EQ(1u, p.GetTokens()[1].length);
+ EXPECT_STREQ("a", p.GetTokens()[1].name);
+ }
+
+ {
+ // Null characters
+ Pointer p("/\0\0", 3);
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_EQ(2u, p.GetTokens()[0].length);
+ EXPECT_EQ('\0', p.GetTokens()[0].name[0]);
+ EXPECT_EQ('\0', p.GetTokens()[0].name[1]);
+ EXPECT_EQ('\0', p.GetTokens()[0].name[2]);
+ }
+
+ {
+ // Valid index
+ Pointer p("/123");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_STREQ("123", p.GetTokens()[0].name);
+ EXPECT_EQ(123u, p.GetTokens()[0].index);
+ }
+
+ {
+ // Invalid index (with leading zero)
+ Pointer p("/01");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_STREQ("01", p.GetTokens()[0].name);
+ EXPECT_EQ(kPointerInvalidIndex, p.GetTokens()[0].index);
+ }
+
+ if (sizeof(SizeType) == 4) {
+ // Invalid index (overflow)
+ Pointer p("/4294967296");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_STREQ("4294967296", p.GetTokens()[0].name);
+ EXPECT_EQ(kPointerInvalidIndex, p.GetTokens()[0].index);
+ }
+
+ {
+ // kPointerParseErrorTokenMustBeginWithSolidus
+ Pointer p(" ");
+ EXPECT_FALSE(p.IsValid());
+ EXPECT_EQ(kPointerParseErrorTokenMustBeginWithSolidus, p.GetParseErrorCode());
+ EXPECT_EQ(0u, p.GetParseErrorOffset());
+ }
+
+ {
+ // kPointerParseErrorInvalidEscape
+ Pointer p("/~");
+ EXPECT_FALSE(p.IsValid());
+ EXPECT_EQ(kPointerParseErrorInvalidEscape, p.GetParseErrorCode());
+ EXPECT_EQ(2u, p.GetParseErrorOffset());
+ }
+
+ {
+ // kPointerParseErrorInvalidEscape
+ Pointer p("/~2");
+ EXPECT_FALSE(p.IsValid());
+ EXPECT_EQ(kPointerParseErrorInvalidEscape, p.GetParseErrorCode());
+ EXPECT_EQ(2u, p.GetParseErrorOffset());
+ }
+}
+
+TEST(Pointer, Parse_URIFragment) {
+ {
+ Pointer p("#");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(0u, p.GetTokenCount());
+ }
+
+ {
+ Pointer p("#/foo");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_EQ(3u, p.GetTokens()[0].length);
+ EXPECT_STREQ("foo", p.GetTokens()[0].name);
+ }
+
+ {
+ Pointer p("#/foo/0");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(2u, p.GetTokenCount());
+ EXPECT_EQ(3u, p.GetTokens()[0].length);
+ EXPECT_STREQ("foo", p.GetTokens()[0].name);
+ EXPECT_EQ(1u, p.GetTokens()[1].length);
+ EXPECT_STREQ("0", p.GetTokens()[1].name);
+ EXPECT_EQ(0u, p.GetTokens()[1].index);
+ }
+
+ {
+ // Unescape ~1
+ Pointer p("#/a~1b");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_EQ(3u, p.GetTokens()[0].length);
+ EXPECT_STREQ("a/b", p.GetTokens()[0].name);
+ }
+
+ {
+ // Unescape ~0
+ Pointer p("#/m~0n");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_EQ(3u, p.GetTokens()[0].length);
+ EXPECT_STREQ("m~n", p.GetTokens()[0].name);
+ }
+
+ {
+ // empty name
+ Pointer p("#/");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_EQ(0u, p.GetTokens()[0].length);
+ EXPECT_STREQ("", p.GetTokens()[0].name);
+ }
+
+ {
+ // empty and non-empty name
+ Pointer p("#//a");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(2u, p.GetTokenCount());
+ EXPECT_EQ(0u, p.GetTokens()[0].length);
+ EXPECT_STREQ("", p.GetTokens()[0].name);
+ EXPECT_EQ(1u, p.GetTokens()[1].length);
+ EXPECT_STREQ("a", p.GetTokens()[1].name);
+ }
+
+ {
+ // Null characters
+ Pointer p("#/%00%00");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_EQ(2u, p.GetTokens()[0].length);
+ EXPECT_EQ('\0', p.GetTokens()[0].name[0]);
+ EXPECT_EQ('\0', p.GetTokens()[0].name[1]);
+ EXPECT_EQ('\0', p.GetTokens()[0].name[2]);
+ }
+
+ {
+ // Percentage Escapes
+ EXPECT_STREQ("c%d", Pointer("#/c%25d").GetTokens()[0].name);
+ EXPECT_STREQ("e^f", Pointer("#/e%5Ef").GetTokens()[0].name);
+ EXPECT_STREQ("g|h", Pointer("#/g%7Ch").GetTokens()[0].name);
+ EXPECT_STREQ("i\\j", Pointer("#/i%5Cj").GetTokens()[0].name);
+ EXPECT_STREQ("k\"l", Pointer("#/k%22l").GetTokens()[0].name);
+ EXPECT_STREQ(" ", Pointer("#/%20").GetTokens()[0].name);
+ }
+
+ {
+ // Valid index
+ Pointer p("#/123");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_STREQ("123", p.GetTokens()[0].name);
+ EXPECT_EQ(123u, p.GetTokens()[0].index);
+ }
+
+ {
+ // Invalid index (with leading zero)
+ Pointer p("#/01");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_STREQ("01", p.GetTokens()[0].name);
+ EXPECT_EQ(kPointerInvalidIndex, p.GetTokens()[0].index);
+ }
+
+ if (sizeof(SizeType) == 4) {
+ // Invalid index (overflow)
+ Pointer p("#/4294967296");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_STREQ("4294967296", p.GetTokens()[0].name);
+ EXPECT_EQ(kPointerInvalidIndex, p.GetTokens()[0].index);
+ }
+
+ {
+ // Decode UTF-8 perecent encoding to UTF-8
+ Pointer p("#/%C2%A2");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_STREQ("\xC2\xA2", p.GetTokens()[0].name);
+ }
+
+ {
+ // Decode UTF-8 perecent encoding to UTF-16
+ GenericPointer<GenericValue<UTF16<> > > p(L"#/%C2%A2");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_EQ(static_cast<UTF16<>::Ch>(0x00A2), p.GetTokens()[0].name[0]);
+ EXPECT_EQ(1u, p.GetTokens()[0].length);
+ }
+
+ {
+ // Decode UTF-8 perecent encoding to UTF-16
+ GenericPointer<GenericValue<UTF16<> > > p(L"#/%E2%82%AC");
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(1u, p.GetTokenCount());
+ EXPECT_EQ(static_cast<UTF16<>::Ch>(0x20AC), p.GetTokens()[0].name[0]);
+ EXPECT_EQ(1u, p.GetTokens()[0].length);
+ }
+
+ {
+ // kPointerParseErrorTokenMustBeginWithSolidus
+ Pointer p("# ");
+ EXPECT_FALSE(p.IsValid());
+ EXPECT_EQ(kPointerParseErrorTokenMustBeginWithSolidus, p.GetParseErrorCode());
+ EXPECT_EQ(1u, p.GetParseErrorOffset());
+ }
+
+ {
+ // kPointerParseErrorInvalidEscape
+ Pointer p("#/~");
+ EXPECT_FALSE(p.IsValid());
+ EXPECT_EQ(kPointerParseErrorInvalidEscape, p.GetParseErrorCode());
+ EXPECT_EQ(3u, p.GetParseErrorOffset());
+ }
+
+ {
+ // kPointerParseErrorInvalidEscape
+ Pointer p("#/~2");
+ EXPECT_FALSE(p.IsValid());
+ EXPECT_EQ(kPointerParseErrorInvalidEscape, p.GetParseErrorCode());
+ EXPECT_EQ(3u, p.GetParseErrorOffset());
+ }
+
+ {
+ // kPointerParseErrorInvalidPercentEncoding
+ Pointer p("#/%");
+ EXPECT_FALSE(p.IsValid());
+ EXPECT_EQ(kPointerParseErrorInvalidPercentEncoding, p.GetParseErrorCode());
+ EXPECT_EQ(2u, p.GetParseErrorOffset());
+ }
+
+ {
+ // kPointerParseErrorInvalidPercentEncoding (invalid hex)
+ Pointer p("#/%g0");
+ EXPECT_FALSE(p.IsValid());
+ EXPECT_EQ(kPointerParseErrorInvalidPercentEncoding, p.GetParseErrorCode());
+ EXPECT_EQ(2u, p.GetParseErrorOffset());
+ }
+
+ {
+ // kPointerParseErrorInvalidPercentEncoding (invalid hex)
+ Pointer p("#/%0g");
+ EXPECT_FALSE(p.IsValid());
+ EXPECT_EQ(kPointerParseErrorInvalidPercentEncoding, p.GetParseErrorCode());
+ EXPECT_EQ(2u, p.GetParseErrorOffset());
+ }
+
+ {
+ // kPointerParseErrorInvalidPercentEncoding (incomplete UTF-8 sequence)
+ Pointer p("#/%C2");
+ EXPECT_FALSE(p.IsValid());
+ EXPECT_EQ(kPointerParseErrorInvalidPercentEncoding, p.GetParseErrorCode());
+ EXPECT_EQ(2u, p.GetParseErrorOffset());
+ }
+
+ {
+ // kPointerParseErrorCharacterMustPercentEncode
+ Pointer p("#/ ");
+ EXPECT_FALSE(p.IsValid());
+ EXPECT_EQ(kPointerParseErrorCharacterMustPercentEncode, p.GetParseErrorCode());
+ EXPECT_EQ(2u, p.GetParseErrorOffset());
+ }
+
+ {
+ // kPointerParseErrorCharacterMustPercentEncode
+ Pointer p("#/\n");
+ EXPECT_FALSE(p.IsValid());
+ EXPECT_EQ(kPointerParseErrorCharacterMustPercentEncode, p.GetParseErrorCode());
+ EXPECT_EQ(2u, p.GetParseErrorOffset());
+ }
+}
+
+TEST(Pointer, Stringify) {
+ // Test by roundtrip
+ const char* sources[] = {
+ "",
+ "/foo",
+ "/foo/0",
+ "/",
+ "/a~1b",
+ "/c%d",
+ "/e^f",
+ "/g|h",
+ "/i\\j",
+ "/k\"l",
+ "/ ",
+ "/m~0n",
+ "/\xC2\xA2",
+ "/\xE2\x82\xAC",
+ "/\xF0\x9D\x84\x9E"
+ };
+
+ for (size_t i = 0; i < sizeof(sources) / sizeof(sources[0]); i++) {
+ Pointer p(sources[i]);
+ StringBuffer s;
+ EXPECT_TRUE(p.Stringify(s));
+ EXPECT_STREQ(sources[i], s.GetString());
+
+ // Stringify to URI fragment
+ StringBuffer s2;
+ EXPECT_TRUE(p.StringifyUriFragment(s2));
+ Pointer p2(s2.GetString(), s2.GetSize());
+ EXPECT_TRUE(p2.IsValid());
+ EXPECT_TRUE(p == p2);
+ }
+
+ {
+ // Strigify to URI fragment with an invalid UTF-8 sequence
+ Pointer p("/\xC2");
+ StringBuffer s;
+ EXPECT_FALSE(p.StringifyUriFragment(s));
+ }
+}
+
+// Construct a Pointer with static tokens, no dynamic allocation involved.
+#define NAME(s) { s, static_cast<SizeType>(sizeof(s) / sizeof(s[0]) - 1), kPointerInvalidIndex }
+#define INDEX(i) { #i, static_cast<SizeType>(sizeof(#i) - 1), i }
+
+static const Pointer::Token kTokens[] = { NAME("foo"), INDEX(0) }; // equivalent to "/foo/0"
+
+#undef NAME
+#undef INDEX
+
+TEST(Pointer, ConstructorWithToken) {
+ Pointer p(kTokens, sizeof(kTokens) / sizeof(kTokens[0]));
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(2u, p.GetTokenCount());
+ EXPECT_EQ(3u, p.GetTokens()[0].length);
+ EXPECT_STREQ("foo", p.GetTokens()[0].name);
+ EXPECT_EQ(1u, p.GetTokens()[1].length);
+ EXPECT_STREQ("0", p.GetTokens()[1].name);
+ EXPECT_EQ(0u, p.GetTokens()[1].index);
+}
+
+TEST(Pointer, CopyConstructor) {
+ {
+ CrtAllocator allocator;
+ Pointer p("/foo/0", &allocator);
+ Pointer q(p);
+ EXPECT_TRUE(q.IsValid());
+ EXPECT_EQ(2u, q.GetTokenCount());
+ EXPECT_EQ(3u, q.GetTokens()[0].length);
+ EXPECT_STREQ("foo", q.GetTokens()[0].name);
+ EXPECT_EQ(1u, q.GetTokens()[1].length);
+ EXPECT_STREQ("0", q.GetTokens()[1].name);
+ EXPECT_EQ(0u, q.GetTokens()[1].index);
+
+ // Copied pointer needs to have its own allocator
+ EXPECT_NE(&p.GetAllocator(), &q.GetAllocator());
+ }
+
+ // Static tokens
+ {
+ Pointer p(kTokens, sizeof(kTokens) / sizeof(kTokens[0]));
+ Pointer q(p);
+ EXPECT_TRUE(q.IsValid());
+ EXPECT_EQ(2u, q.GetTokenCount());
+ EXPECT_EQ(3u, q.GetTokens()[0].length);
+ EXPECT_STREQ("foo", q.GetTokens()[0].name);
+ EXPECT_EQ(1u, q.GetTokens()[1].length);
+ EXPECT_STREQ("0", q.GetTokens()[1].name);
+ EXPECT_EQ(0u, q.GetTokens()[1].index);
+ }
+}
+
+TEST(Pointer, Assignment) {
+ {
+ CrtAllocator allocator;
+ Pointer p("/foo/0", &allocator);
+ Pointer q;
+ q = p;
+ EXPECT_TRUE(q.IsValid());
+ EXPECT_EQ(2u, q.GetTokenCount());
+ EXPECT_EQ(3u, q.GetTokens()[0].length);
+ EXPECT_STREQ("foo", q.GetTokens()[0].name);
+ EXPECT_EQ(1u, q.GetTokens()[1].length);
+ EXPECT_STREQ("0", q.GetTokens()[1].name);
+ EXPECT_EQ(0u, q.GetTokens()[1].index);
+ EXPECT_NE(&p.GetAllocator(), &q.GetAllocator());
+ q = static_cast<const Pointer &>(q);
+ EXPECT_TRUE(q.IsValid());
+ EXPECT_EQ(2u, q.GetTokenCount());
+ EXPECT_EQ(3u, q.GetTokens()[0].length);
+ EXPECT_STREQ("foo", q.GetTokens()[0].name);
+ EXPECT_EQ(1u, q.GetTokens()[1].length);
+ EXPECT_STREQ("0", q.GetTokens()[1].name);
+ EXPECT_EQ(0u, q.GetTokens()[1].index);
+ EXPECT_NE(&p.GetAllocator(), &q.GetAllocator());
+ }
+
+ // Static tokens
+ {
+ Pointer p(kTokens, sizeof(kTokens) / sizeof(kTokens[0]));
+ Pointer q;
+ q = p;
+ EXPECT_TRUE(q.IsValid());
+ EXPECT_EQ(2u, q.GetTokenCount());
+ EXPECT_EQ(3u, q.GetTokens()[0].length);
+ EXPECT_STREQ("foo", q.GetTokens()[0].name);
+ EXPECT_EQ(1u, q.GetTokens()[1].length);
+ EXPECT_STREQ("0", q.GetTokens()[1].name);
+ EXPECT_EQ(0u, q.GetTokens()[1].index);
+ }
+}
+
+TEST(Pointer, Swap) {
+ Pointer p("/foo/0");
+ Pointer q(&p.GetAllocator());
+
+ q.Swap(p);
+ EXPECT_EQ(&q.GetAllocator(), &p.GetAllocator());
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_TRUE(q.IsValid());
+ EXPECT_EQ(0u, p.GetTokenCount());
+ EXPECT_EQ(2u, q.GetTokenCount());
+ EXPECT_EQ(3u, q.GetTokens()[0].length);
+ EXPECT_STREQ("foo", q.GetTokens()[0].name);
+ EXPECT_EQ(1u, q.GetTokens()[1].length);
+ EXPECT_STREQ("0", q.GetTokens()[1].name);
+ EXPECT_EQ(0u, q.GetTokens()[1].index);
+
+ // std::swap compatibility
+ std::swap(p, q);
+ EXPECT_EQ(&p.GetAllocator(), &q.GetAllocator());
+ EXPECT_TRUE(q.IsValid());
+ EXPECT_TRUE(p.IsValid());
+ EXPECT_EQ(0u, q.GetTokenCount());
+ EXPECT_EQ(2u, p.GetTokenCount());
+ EXPECT_EQ(3u, p.GetTokens()[0].length);
+ EXPECT_STREQ("foo", p.GetTokens()[0].name);
+ EXPECT_EQ(1u, p.GetTokens()[1].length);
+ EXPECT_STREQ("0", p.GetTokens()[1].name);
+ EXPECT_EQ(0u, p.GetTokens()[1].index);
+}
+
+TEST(Pointer, Append) {
+ {
+ Pointer p;
+ Pointer q = p.Append("foo");
+ EXPECT_TRUE(Pointer("/foo") == q);
+ q = q.Append(1234);
+ EXPECT_TRUE(Pointer("/foo/1234") == q);
+ q = q.Append("");
+ EXPECT_TRUE(Pointer("/foo/1234/") == q);
+ }
+
+ {
+ Pointer p;
+ Pointer q = p.Append(Value("foo").Move());
+ EXPECT_TRUE(Pointer("/foo") == q);
+ q = q.Append(Value(1234).Move());
+ EXPECT_TRUE(Pointer("/foo/1234") == q);
+ q = q.Append(Value(kStringType).Move());
+ EXPECT_TRUE(Pointer("/foo/1234/") == q);
+ }
+
+#if RAPIDJSON_HAS_STDSTRING
+ {
+ Pointer p;
+ Pointer q = p.Append(std::string("foo"));
+ EXPECT_TRUE(Pointer("/foo") == q);
+ }
+#endif
+}
+
+TEST(Pointer, Equality) {
+ EXPECT_TRUE(Pointer("/foo/0") == Pointer("/foo/0"));
+ EXPECT_FALSE(Pointer("/foo/0") == Pointer("/foo/1"));
+ EXPECT_FALSE(Pointer("/foo/0") == Pointer("/foo/0/1"));
+ EXPECT_FALSE(Pointer("/foo/0") == Pointer("a"));
+ EXPECT_FALSE(Pointer("a") == Pointer("a")); // Invalid always not equal
+}
+
+TEST(Pointer, Inequality) {
+ EXPECT_FALSE(Pointer("/foo/0") != Pointer("/foo/0"));
+ EXPECT_TRUE(Pointer("/foo/0") != Pointer("/foo/1"));
+ EXPECT_TRUE(Pointer("/foo/0") != Pointer("/foo/0/1"));
+ EXPECT_TRUE(Pointer("/foo/0") != Pointer("a"));
+ EXPECT_TRUE(Pointer("a") != Pointer("a")); // Invalid always not equal
+}
+
+TEST(Pointer, Create) {
+ Document d;
+ {
+ Value* v = &Pointer("").Create(d, d.GetAllocator());
+ EXPECT_EQ(&d, v);
+ }
+ {
+ Value* v = &Pointer("/foo").Create(d, d.GetAllocator());
+ EXPECT_EQ(&d["foo"], v);
+ }
+ {
+ Value* v = &Pointer("/foo/0").Create(d, d.GetAllocator());
+ EXPECT_EQ(&d["foo"][0], v);
+ }
+ {
+ Value* v = &Pointer("/foo/-").Create(d, d.GetAllocator());
+ EXPECT_EQ(&d["foo"][1], v);
+ }
+
+ {
+ Value* v = &Pointer("/foo/-/-").Create(d, d.GetAllocator());
+ // "foo/-" is a newly created null value x.
+ // "foo/-/-" finds that x is not an array, it converts x to empty object
+ // and treats - as "-" member name
+ EXPECT_EQ(&d["foo"][2]["-"], v);
+ }
+
+ {
+ // Document with no allocator
+ Value* v = &Pointer("/foo/-").Create(d);
+ EXPECT_EQ(&d["foo"][3], v);
+ }
+
+ {
+ // Value (not document) must give allocator
+ Value* v = &Pointer("/-").Create(d["foo"], d.GetAllocator());
+ EXPECT_EQ(&d["foo"][4], v);
+ }
+}
+
+static const char kJsonIds[] = "{\n"
+ " \"id\": \"/root/\","
+ " \"foo\":[\"bar\", \"baz\", {\"id\": \"inarray\", \"child\": 1}],\n"
+ " \"int\" : 2,\n"
+ " \"str\" : \"val\",\n"
+ " \"obj\": {\"id\": \"inobj\", \"child\": 3},\n"
+ " \"jbo\": {\"id\": true, \"child\": 4}\n"
+ "}";
+
+
+TEST(Pointer, GetUri) {
+ CrtAllocator allocator;
+ Document d;
+ d.Parse(kJsonIds);
+ Pointer::UriType doc("http://doc");
+ Pointer::UriType root("http://doc/root/");
+ Pointer::UriType empty = Pointer::UriType();
+
+ EXPECT_TRUE(Pointer("").GetUri(d, doc) == doc);
+ EXPECT_TRUE(Pointer("/foo").GetUri(d, doc) == root);
+ EXPECT_TRUE(Pointer("/foo/0").GetUri(d, doc) == root);
+ EXPECT_TRUE(Pointer("/foo/2").GetUri(d, doc) == root);
+ EXPECT_TRUE(Pointer("/foo/2/child").GetUri(d, doc) == Pointer::UriType("http://doc/root/inarray"));
+ EXPECT_TRUE(Pointer("/int").GetUri(d, doc) == root);
+ EXPECT_TRUE(Pointer("/str").GetUri(d, doc) == root);
+ EXPECT_TRUE(Pointer("/obj").GetUri(d, doc) == root);
+ EXPECT_TRUE(Pointer("/obj/child").GetUri(d, doc) == Pointer::UriType("http://doc/root/inobj"));
+ EXPECT_TRUE(Pointer("/jbo").GetUri(d, doc) == root);
+ EXPECT_TRUE(Pointer("/jbo/child").GetUri(d, doc) == root); // id not string
+
+ size_t unresolvedTokenIndex;
+ EXPECT_TRUE(Pointer("/abc").GetUri(d, doc, &unresolvedTokenIndex, &allocator) == empty); // Out of boundary
+ EXPECT_EQ(0u, unresolvedTokenIndex);
+ EXPECT_TRUE(Pointer("/foo/3").GetUri(d, doc, &unresolvedTokenIndex, &allocator) == empty); // Out of boundary
+ EXPECT_EQ(1u, unresolvedTokenIndex);
+ EXPECT_TRUE(Pointer("/foo/a").GetUri(d, doc, &unresolvedTokenIndex, &allocator) == empty); // "/foo" is an array, cannot query by "a"
+ EXPECT_EQ(1u, unresolvedTokenIndex);
+ EXPECT_TRUE(Pointer("/foo/0/0").GetUri(d, doc, &unresolvedTokenIndex, &allocator) == empty); // "/foo/0" is an string, cannot further query
+ EXPECT_EQ(2u, unresolvedTokenIndex);
+ EXPECT_TRUE(Pointer("/foo/0/a").GetUri(d, doc, &unresolvedTokenIndex, &allocator) == empty); // "/foo/0" is an string, cannot further query
+ EXPECT_EQ(2u, unresolvedTokenIndex);
+
+ Pointer::Token tokens[] = { { "foo ...", 3, kPointerInvalidIndex } };
+ EXPECT_TRUE(Pointer(tokens, 1).GetUri(d, doc) == root);
+}
+
+TEST(Pointer, Get) {
+ Document d;
+ d.Parse(kJson);
+
+ EXPECT_EQ(&d, Pointer("").Get(d));
+ EXPECT_EQ(&d["foo"], Pointer("/foo").Get(d));
+ EXPECT_EQ(&d["foo"][0], Pointer("/foo/0").Get(d));
+ EXPECT_EQ(&d[""], Pointer("/").Get(d));
+ EXPECT_EQ(&d["a/b"], Pointer("/a~1b").Get(d));
+ EXPECT_EQ(&d["c%d"], Pointer("/c%d").Get(d));
+ EXPECT_EQ(&d["e^f"], Pointer("/e^f").Get(d));
+ EXPECT_EQ(&d["g|h"], Pointer("/g|h").Get(d));
+ EXPECT_EQ(&d["i\\j"], Pointer("/i\\j").Get(d));
+ EXPECT_EQ(&d["k\"l"], Pointer("/k\"l").Get(d));
+ EXPECT_EQ(&d[" "], Pointer("/ ").Get(d));
+ EXPECT_EQ(&d["m~n"], Pointer("/m~0n").Get(d));
+
+ EXPECT_TRUE(Pointer("/abc").Get(d) == 0); // Out of boundary
+ size_t unresolvedTokenIndex;
+ EXPECT_TRUE(Pointer("/foo/2").Get(d, &unresolvedTokenIndex) == 0); // Out of boundary
+ EXPECT_EQ(1u, unresolvedTokenIndex);
+ EXPECT_TRUE(Pointer("/foo/a").Get(d, &unresolvedTokenIndex) == 0); // "/foo" is an array, cannot query by "a"
+ EXPECT_EQ(1u, unresolvedTokenIndex);
+ EXPECT_TRUE(Pointer("/foo/0/0").Get(d, &unresolvedTokenIndex) == 0); // "/foo/0" is an string, cannot further query
+ EXPECT_EQ(2u, unresolvedTokenIndex);
+ EXPECT_TRUE(Pointer("/foo/0/a").Get(d, &unresolvedTokenIndex) == 0); // "/foo/0" is an string, cannot further query
+ EXPECT_EQ(2u, unresolvedTokenIndex);
+
+ Pointer::Token tokens[] = { { "foo ...", 3, kPointerInvalidIndex } };
+ EXPECT_EQ(&d["foo"], Pointer(tokens, 1).Get(d));
+}
+
+TEST(Pointer, GetWithDefault) {
+ Document d;
+ d.Parse(kJson);
+
+ // Value version
+ Document::AllocatorType& a = d.GetAllocator();
+ const Value v("qux");
+ EXPECT_TRUE(Value("bar") == Pointer("/foo/0").GetWithDefault(d, v, a));
+ EXPECT_TRUE(Value("baz") == Pointer("/foo/1").GetWithDefault(d, v, a));
+ EXPECT_TRUE(Value("qux") == Pointer("/foo/2").GetWithDefault(d, v, a));
+ EXPECT_TRUE(Value("last") == Pointer("/foo/-").GetWithDefault(d, Value("last").Move(), a));
+ EXPECT_STREQ("last", d["foo"][3].GetString());
+
+ EXPECT_TRUE(Pointer("/foo/null").GetWithDefault(d, Value().Move(), a).IsNull());
+ EXPECT_TRUE(Pointer("/foo/null").GetWithDefault(d, "x", a).IsNull());
+
+ // Generic version
+ EXPECT_EQ(-1, Pointer("/foo/int").GetWithDefault(d, -1, a).GetInt());
+ EXPECT_EQ(-1, Pointer("/foo/int").GetWithDefault(d, -2, a).GetInt());
+ EXPECT_EQ(0x87654321, Pointer("/foo/uint").GetWithDefault(d, 0x87654321, a).GetUint());
+ EXPECT_EQ(0x87654321, Pointer("/foo/uint").GetWithDefault(d, 0x12345678, a).GetUint());
+
+ const int64_t i64 = static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x80000000, 0));
+ EXPECT_EQ(i64, Pointer("/foo/int64").GetWithDefault(d, i64, a).GetInt64());
+ EXPECT_EQ(i64, Pointer("/foo/int64").GetWithDefault(d, i64 + 1, a).GetInt64());
+
+ const uint64_t u64 = RAPIDJSON_UINT64_C2(0xFFFFFFFFF, 0xFFFFFFFFF);
+ EXPECT_EQ(u64, Pointer("/foo/uint64").GetWithDefault(d, u64, a).GetUint64());
+ EXPECT_EQ(u64, Pointer("/foo/uint64").GetWithDefault(d, u64 - 1, a).GetUint64());
+
+ EXPECT_TRUE(Pointer("/foo/true").GetWithDefault(d, true, a).IsTrue());
+ EXPECT_TRUE(Pointer("/foo/true").GetWithDefault(d, false, a).IsTrue());
+
+ EXPECT_TRUE(Pointer("/foo/false").GetWithDefault(d, false, a).IsFalse());
+ EXPECT_TRUE(Pointer("/foo/false").GetWithDefault(d, true, a).IsFalse());
+
+ // StringRef version
+ EXPECT_STREQ("Hello", Pointer("/foo/hello").GetWithDefault(d, "Hello", a).GetString());
+
+ // Copy string version
+ {
+ char buffer[256];
+ strcpy(buffer, "World");
+ EXPECT_STREQ("World", Pointer("/foo/world").GetWithDefault(d, buffer, a).GetString());
+ memset(buffer, 0, sizeof(buffer));
+ }
+ EXPECT_STREQ("World", GetValueByPointer(d, "/foo/world")->GetString());
+
+#if RAPIDJSON_HAS_STDSTRING
+ EXPECT_STREQ("C++", Pointer("/foo/C++").GetWithDefault(d, std::string("C++"), a).GetString());
+#endif
+}
+
+TEST(Pointer, GetWithDefault_NoAllocator) {
+ Document d;
+ d.Parse(kJson);
+
+ // Value version
+ const Value v("qux");
+ EXPECT_TRUE(Value("bar") == Pointer("/foo/0").GetWithDefault(d, v));
+ EXPECT_TRUE(Value("baz") == Pointer("/foo/1").GetWithDefault(d, v));
+ EXPECT_TRUE(Value("qux") == Pointer("/foo/2").GetWithDefault(d, v));
+ EXPECT_TRUE(Value("last") == Pointer("/foo/-").GetWithDefault(d, Value("last").Move()));
+ EXPECT_STREQ("last", d["foo"][3].GetString());
+
+ EXPECT_TRUE(Pointer("/foo/null").GetWithDefault(d, Value().Move()).IsNull());
+ EXPECT_TRUE(Pointer("/foo/null").GetWithDefault(d, "x").IsNull());
+
+ // Generic version
+ EXPECT_EQ(-1, Pointer("/foo/int").GetWithDefault(d, -1).GetInt());
+ EXPECT_EQ(-1, Pointer("/foo/int").GetWithDefault(d, -2).GetInt());
+ EXPECT_EQ(0x87654321, Pointer("/foo/uint").GetWithDefault(d, 0x87654321).GetUint());
+ EXPECT_EQ(0x87654321, Pointer("/foo/uint").GetWithDefault(d, 0x12345678).GetUint());
+
+ const int64_t i64 = static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x80000000, 0));
+ EXPECT_EQ(i64, Pointer("/foo/int64").GetWithDefault(d, i64).GetInt64());
+ EXPECT_EQ(i64, Pointer("/foo/int64").GetWithDefault(d, i64 + 1).GetInt64());
+
+ const uint64_t u64 = RAPIDJSON_UINT64_C2(0xFFFFFFFFF, 0xFFFFFFFFF);
+ EXPECT_EQ(u64, Pointer("/foo/uint64").GetWithDefault(d, u64).GetUint64());
+ EXPECT_EQ(u64, Pointer("/foo/uint64").GetWithDefault(d, u64 - 1).GetUint64());
+
+ EXPECT_TRUE(Pointer("/foo/true").GetWithDefault(d, true).IsTrue());
+ EXPECT_TRUE(Pointer("/foo/true").GetWithDefault(d, false).IsTrue());
+
+ EXPECT_TRUE(Pointer("/foo/false").GetWithDefault(d, false).IsFalse());
+ EXPECT_TRUE(Pointer("/foo/false").GetWithDefault(d, true).IsFalse());
+
+ // StringRef version
+ EXPECT_STREQ("Hello", Pointer("/foo/hello").GetWithDefault(d, "Hello").GetString());
+
+ // Copy string version
+ {
+ char buffer[256];
+ strcpy(buffer, "World");
+ EXPECT_STREQ("World", Pointer("/foo/world").GetWithDefault(d, buffer).GetString());
+ memset(buffer, 0, sizeof(buffer));
+ }
+ EXPECT_STREQ("World", GetValueByPointer(d, "/foo/world")->GetString());
+
+#if RAPIDJSON_HAS_STDSTRING
+ EXPECT_STREQ("C++", Pointer("/foo/C++").GetWithDefault(d, std::string("C++")).GetString());
+#endif
+}
+
+TEST(Pointer, Set) {
+ Document d;
+ d.Parse(kJson);
+ Document::AllocatorType& a = d.GetAllocator();
+
+ // Value version
+ Pointer("/foo/0").Set(d, Value(123).Move(), a);
+ EXPECT_EQ(123, d["foo"][0].GetInt());
+
+ Pointer("/foo/-").Set(d, Value(456).Move(), a);
+ EXPECT_EQ(456, d["foo"][2].GetInt());
+
+ Pointer("/foo/null").Set(d, Value().Move(), a);
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/null")->IsNull());
+
+ // Const Value version
+ const Value foo(d["foo"], a);
+ Pointer("/clone").Set(d, foo, a);
+ EXPECT_EQ(foo, *GetValueByPointer(d, "/clone"));
+
+ // Generic version
+ Pointer("/foo/int").Set(d, -1, a);
+ EXPECT_EQ(-1, GetValueByPointer(d, "/foo/int")->GetInt());
+
+ Pointer("/foo/uint").Set(d, 0x87654321, a);
+ EXPECT_EQ(0x87654321, GetValueByPointer(d, "/foo/uint")->GetUint());
+
+ const int64_t i64 = static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x80000000, 0));
+ Pointer("/foo/int64").Set(d, i64, a);
+ EXPECT_EQ(i64, GetValueByPointer(d, "/foo/int64")->GetInt64());
+
+ const uint64_t u64 = RAPIDJSON_UINT64_C2(0xFFFFFFFFF, 0xFFFFFFFFF);
+ Pointer("/foo/uint64").Set(d, u64, a);
+ EXPECT_EQ(u64, GetValueByPointer(d, "/foo/uint64")->GetUint64());
+
+ Pointer("/foo/true").Set(d, true, a);
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/true")->IsTrue());
+
+ Pointer("/foo/false").Set(d, false, a);
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/false")->IsFalse());
+
+ // StringRef version
+ Pointer("/foo/hello").Set(d, "Hello", a);
+ EXPECT_STREQ("Hello", GetValueByPointer(d, "/foo/hello")->GetString());
+
+ // Copy string version
+ {
+ char buffer[256];
+ strcpy(buffer, "World");
+ Pointer("/foo/world").Set(d, buffer, a);
+ memset(buffer, 0, sizeof(buffer));
+ }
+ EXPECT_STREQ("World", GetValueByPointer(d, "/foo/world")->GetString());
+
+#if RAPIDJSON_HAS_STDSTRING
+ Pointer("/foo/c++").Set(d, std::string("C++"), a);
+ EXPECT_STREQ("C++", GetValueByPointer(d, "/foo/c++")->GetString());
+#endif
+}
+
+TEST(Pointer, Set_NoAllocator) {
+ Document d;
+ d.Parse(kJson);
+
+ // Value version
+ Pointer("/foo/0").Set(d, Value(123).Move());
+ EXPECT_EQ(123, d["foo"][0].GetInt());
+
+ Pointer("/foo/-").Set(d, Value(456).Move());
+ EXPECT_EQ(456, d["foo"][2].GetInt());
+
+ Pointer("/foo/null").Set(d, Value().Move());
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/null")->IsNull());
+
+ // Const Value version
+ const Value foo(d["foo"], d.GetAllocator());
+ Pointer("/clone").Set(d, foo);
+ EXPECT_EQ(foo, *GetValueByPointer(d, "/clone"));
+
+ // Generic version
+ Pointer("/foo/int").Set(d, -1);
+ EXPECT_EQ(-1, GetValueByPointer(d, "/foo/int")->GetInt());
+
+ Pointer("/foo/uint").Set(d, 0x87654321);
+ EXPECT_EQ(0x87654321, GetValueByPointer(d, "/foo/uint")->GetUint());
+
+ const int64_t i64 = static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x80000000, 0));
+ Pointer("/foo/int64").Set(d, i64);
+ EXPECT_EQ(i64, GetValueByPointer(d, "/foo/int64")->GetInt64());
+
+ const uint64_t u64 = RAPIDJSON_UINT64_C2(0xFFFFFFFFF, 0xFFFFFFFFF);
+ Pointer("/foo/uint64").Set(d, u64);
+ EXPECT_EQ(u64, GetValueByPointer(d, "/foo/uint64")->GetUint64());
+
+ Pointer("/foo/true").Set(d, true);
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/true")->IsTrue());
+
+ Pointer("/foo/false").Set(d, false);
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/false")->IsFalse());
+
+ // StringRef version
+ Pointer("/foo/hello").Set(d, "Hello");
+ EXPECT_STREQ("Hello", GetValueByPointer(d, "/foo/hello")->GetString());
+
+ // Copy string version
+ {
+ char buffer[256];
+ strcpy(buffer, "World");
+ Pointer("/foo/world").Set(d, buffer);
+ memset(buffer, 0, sizeof(buffer));
+ }
+ EXPECT_STREQ("World", GetValueByPointer(d, "/foo/world")->GetString());
+
+#if RAPIDJSON_HAS_STDSTRING
+ Pointer("/foo/c++").Set(d, std::string("C++"));
+ EXPECT_STREQ("C++", GetValueByPointer(d, "/foo/c++")->GetString());
+#endif
+}
+
+TEST(Pointer, Swap_Value) {
+ Document d;
+ d.Parse(kJson);
+ Document::AllocatorType& a = d.GetAllocator();
+ Pointer("/foo/0").Swap(d, *Pointer("/foo/1").Get(d), a);
+ EXPECT_STREQ("baz", d["foo"][0].GetString());
+ EXPECT_STREQ("bar", d["foo"][1].GetString());
+}
+
+TEST(Pointer, Swap_Value_NoAllocator) {
+ Document d;
+ d.Parse(kJson);
+ Pointer("/foo/0").Swap(d, *Pointer("/foo/1").Get(d));
+ EXPECT_STREQ("baz", d["foo"][0].GetString());
+ EXPECT_STREQ("bar", d["foo"][1].GetString());
+}
+
+TEST(Pointer, Erase) {
+ Document d;
+ d.Parse(kJson);
+
+ EXPECT_FALSE(Pointer("").Erase(d));
+ EXPECT_FALSE(Pointer("/nonexist").Erase(d));
+ EXPECT_FALSE(Pointer("/nonexist/nonexist").Erase(d));
+ EXPECT_FALSE(Pointer("/foo/nonexist").Erase(d));
+ EXPECT_FALSE(Pointer("/foo/nonexist/nonexist").Erase(d));
+ EXPECT_FALSE(Pointer("/foo/0/nonexist").Erase(d));
+ EXPECT_FALSE(Pointer("/foo/0/nonexist/nonexist").Erase(d));
+ EXPECT_FALSE(Pointer("/foo/2/nonexist").Erase(d));
+ EXPECT_TRUE(Pointer("/foo/0").Erase(d));
+ EXPECT_EQ(1u, d["foo"].Size());
+ EXPECT_STREQ("baz", d["foo"][0].GetString());
+ EXPECT_TRUE(Pointer("/foo/0").Erase(d));
+ EXPECT_TRUE(d["foo"].Empty());
+ EXPECT_TRUE(Pointer("/foo").Erase(d));
+ EXPECT_TRUE(Pointer("/foo").Get(d) == 0);
+
+ Pointer("/a/0/b/0").Create(d);
+
+ EXPECT_TRUE(Pointer("/a/0/b/0").Get(d) != 0);
+ EXPECT_TRUE(Pointer("/a/0/b/0").Erase(d));
+ EXPECT_TRUE(Pointer("/a/0/b/0").Get(d) == 0);
+
+ EXPECT_TRUE(Pointer("/a/0/b").Get(d) != 0);
+ EXPECT_TRUE(Pointer("/a/0/b").Erase(d));
+ EXPECT_TRUE(Pointer("/a/0/b").Get(d) == 0);
+
+ EXPECT_TRUE(Pointer("/a/0").Get(d) != 0);
+ EXPECT_TRUE(Pointer("/a/0").Erase(d));
+ EXPECT_TRUE(Pointer("/a/0").Get(d) == 0);
+
+ EXPECT_TRUE(Pointer("/a").Get(d) != 0);
+ EXPECT_TRUE(Pointer("/a").Erase(d));
+ EXPECT_TRUE(Pointer("/a").Get(d) == 0);
+}
+
+TEST(Pointer, CreateValueByPointer) {
+ Document d;
+ Document::AllocatorType& a = d.GetAllocator();
+
+ {
+ Value& v = CreateValueByPointer(d, Pointer("/foo/0"), a);
+ EXPECT_EQ(&d["foo"][0], &v);
+ }
+ {
+ Value& v = CreateValueByPointer(d, "/foo/1", a);
+ EXPECT_EQ(&d["foo"][1], &v);
+ }
+}
+
+TEST(Pointer, CreateValueByPointer_NoAllocator) {
+ Document d;
+
+ {
+ Value& v = CreateValueByPointer(d, Pointer("/foo/0"));
+ EXPECT_EQ(&d["foo"][0], &v);
+ }
+ {
+ Value& v = CreateValueByPointer(d, "/foo/1");
+ EXPECT_EQ(&d["foo"][1], &v);
+ }
+}
+
+TEST(Pointer, GetValueByPointer) {
+ Document d;
+ d.Parse(kJson);
+
+ EXPECT_EQ(&d["foo"][0], GetValueByPointer(d, Pointer("/foo/0")));
+ EXPECT_EQ(&d["foo"][0], GetValueByPointer(d, "/foo/0"));
+
+ size_t unresolvedTokenIndex;
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/2", &unresolvedTokenIndex) == 0); // Out of boundary
+ EXPECT_EQ(1u, unresolvedTokenIndex);
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/a", &unresolvedTokenIndex) == 0); // "/foo" is an array, cannot query by "a"
+ EXPECT_EQ(1u, unresolvedTokenIndex);
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/0/0", &unresolvedTokenIndex) == 0); // "/foo/0" is an string, cannot further query
+ EXPECT_EQ(2u, unresolvedTokenIndex);
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/0/a", &unresolvedTokenIndex) == 0); // "/foo/0" is an string, cannot further query
+ EXPECT_EQ(2u, unresolvedTokenIndex);
+
+ // const version
+ const Value& v = d;
+ EXPECT_EQ(&d["foo"][0], GetValueByPointer(v, Pointer("/foo/0")));
+ EXPECT_EQ(&d["foo"][0], GetValueByPointer(v, "/foo/0"));
+
+ EXPECT_TRUE(GetValueByPointer(v, "/foo/2", &unresolvedTokenIndex) == 0); // Out of boundary
+ EXPECT_EQ(1u, unresolvedTokenIndex);
+ EXPECT_TRUE(GetValueByPointer(v, "/foo/a", &unresolvedTokenIndex) == 0); // "/foo" is an array, cannot query by "a"
+ EXPECT_EQ(1u, unresolvedTokenIndex);
+ EXPECT_TRUE(GetValueByPointer(v, "/foo/0/0", &unresolvedTokenIndex) == 0); // "/foo/0" is an string, cannot further query
+ EXPECT_EQ(2u, unresolvedTokenIndex);
+ EXPECT_TRUE(GetValueByPointer(v, "/foo/0/a", &unresolvedTokenIndex) == 0); // "/foo/0" is an string, cannot further query
+ EXPECT_EQ(2u, unresolvedTokenIndex);
+
+}
+
+TEST(Pointer, GetValueByPointerWithDefault_Pointer) {
+ Document d;
+ d.Parse(kJson);
+
+ Document::AllocatorType& a = d.GetAllocator();
+ const Value v("qux");
+ EXPECT_TRUE(Value("bar") == GetValueByPointerWithDefault(d, Pointer("/foo/0"), v, a));
+ EXPECT_TRUE(Value("bar") == GetValueByPointerWithDefault(d, Pointer("/foo/0"), v, a));
+ EXPECT_TRUE(Value("baz") == GetValueByPointerWithDefault(d, Pointer("/foo/1"), v, a));
+ EXPECT_TRUE(Value("qux") == GetValueByPointerWithDefault(d, Pointer("/foo/2"), v, a));
+ EXPECT_TRUE(Value("last") == GetValueByPointerWithDefault(d, Pointer("/foo/-"), Value("last").Move(), a));
+ EXPECT_STREQ("last", d["foo"][3].GetString());
+
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, Pointer("/foo/null"), Value().Move(), a).IsNull());
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, Pointer("/foo/null"), "x", a).IsNull());
+
+ // Generic version
+ EXPECT_EQ(-1, GetValueByPointerWithDefault(d, Pointer("/foo/int"), -1, a).GetInt());
+ EXPECT_EQ(-1, GetValueByPointerWithDefault(d, Pointer("/foo/int"), -2, a).GetInt());
+ EXPECT_EQ(0x87654321, GetValueByPointerWithDefault(d, Pointer("/foo/uint"), 0x87654321, a).GetUint());
+ EXPECT_EQ(0x87654321, GetValueByPointerWithDefault(d, Pointer("/foo/uint"), 0x12345678, a).GetUint());
+
+ const int64_t i64 = static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x80000000, 0));
+ EXPECT_EQ(i64, GetValueByPointerWithDefault(d, Pointer("/foo/int64"), i64, a).GetInt64());
+ EXPECT_EQ(i64, GetValueByPointerWithDefault(d, Pointer("/foo/int64"), i64 + 1, a).GetInt64());
+
+ const uint64_t u64 = RAPIDJSON_UINT64_C2(0xFFFFFFFFF, 0xFFFFFFFFF);
+ EXPECT_EQ(u64, GetValueByPointerWithDefault(d, Pointer("/foo/uint64"), u64, a).GetUint64());
+ EXPECT_EQ(u64, GetValueByPointerWithDefault(d, Pointer("/foo/uint64"), u64 - 1, a).GetUint64());
+
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, Pointer("/foo/true"), true, a).IsTrue());
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, Pointer("/foo/true"), false, a).IsTrue());
+
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, Pointer("/foo/false"), false, a).IsFalse());
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, Pointer("/foo/false"), true, a).IsFalse());
+
+ // StringRef version
+ EXPECT_STREQ("Hello", GetValueByPointerWithDefault(d, Pointer("/foo/hello"), "Hello", a).GetString());
+
+ // Copy string version
+ {
+ char buffer[256];
+ strcpy(buffer, "World");
+ EXPECT_STREQ("World", GetValueByPointerWithDefault(d, Pointer("/foo/world"), buffer, a).GetString());
+ memset(buffer, 0, sizeof(buffer));
+ }
+ EXPECT_STREQ("World", GetValueByPointer(d, Pointer("/foo/world"))->GetString());
+
+#if RAPIDJSON_HAS_STDSTRING
+ EXPECT_STREQ("C++", GetValueByPointerWithDefault(d, Pointer("/foo/C++"), std::string("C++"), a).GetString());
+#endif
+}
+
+TEST(Pointer, GetValueByPointerWithDefault_String) {
+ Document d;
+ d.Parse(kJson);
+
+ Document::AllocatorType& a = d.GetAllocator();
+ const Value v("qux");
+ EXPECT_TRUE(Value("bar") == GetValueByPointerWithDefault(d, "/foo/0", v, a));
+ EXPECT_TRUE(Value("bar") == GetValueByPointerWithDefault(d, "/foo/0", v, a));
+ EXPECT_TRUE(Value("baz") == GetValueByPointerWithDefault(d, "/foo/1", v, a));
+ EXPECT_TRUE(Value("qux") == GetValueByPointerWithDefault(d, "/foo/2", v, a));
+ EXPECT_TRUE(Value("last") == GetValueByPointerWithDefault(d, "/foo/-", Value("last").Move(), a));
+ EXPECT_STREQ("last", d["foo"][3].GetString());
+
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, "/foo/null", Value().Move(), a).IsNull());
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, "/foo/null", "x", a).IsNull());
+
+ // Generic version
+ EXPECT_EQ(-1, GetValueByPointerWithDefault(d, "/foo/int", -1, a).GetInt());
+ EXPECT_EQ(-1, GetValueByPointerWithDefault(d, "/foo/int", -2, a).GetInt());
+ EXPECT_EQ(0x87654321, GetValueByPointerWithDefault(d, "/foo/uint", 0x87654321, a).GetUint());
+ EXPECT_EQ(0x87654321, GetValueByPointerWithDefault(d, "/foo/uint", 0x12345678, a).GetUint());
+
+ const int64_t i64 = static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x80000000, 0));
+ EXPECT_EQ(i64, GetValueByPointerWithDefault(d, "/foo/int64", i64, a).GetInt64());
+ EXPECT_EQ(i64, GetValueByPointerWithDefault(d, "/foo/int64", i64 + 1, a).GetInt64());
+
+ const uint64_t u64 = RAPIDJSON_UINT64_C2(0xFFFFFFFFF, 0xFFFFFFFFF);
+ EXPECT_EQ(u64, GetValueByPointerWithDefault(d, "/foo/uint64", u64, a).GetUint64());
+ EXPECT_EQ(u64, GetValueByPointerWithDefault(d, "/foo/uint64", u64 - 1, a).GetUint64());
+
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, "/foo/true", true, a).IsTrue());
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, "/foo/true", false, a).IsTrue());
+
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, "/foo/false", false, a).IsFalse());
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, "/foo/false", true, a).IsFalse());
+
+ // StringRef version
+ EXPECT_STREQ("Hello", GetValueByPointerWithDefault(d, "/foo/hello", "Hello", a).GetString());
+
+ // Copy string version
+ {
+ char buffer[256];
+ strcpy(buffer, "World");
+ EXPECT_STREQ("World", GetValueByPointerWithDefault(d, "/foo/world", buffer, a).GetString());
+ memset(buffer, 0, sizeof(buffer));
+ }
+ EXPECT_STREQ("World", GetValueByPointer(d, "/foo/world")->GetString());
+
+#if RAPIDJSON_HAS_STDSTRING
+ EXPECT_STREQ("C++", GetValueByPointerWithDefault(d, "/foo/C++", std::string("C++"), a).GetString());
+#endif
+}
+
+TEST(Pointer, GetValueByPointerWithDefault_Pointer_NoAllocator) {
+ Document d;
+ d.Parse(kJson);
+
+ const Value v("qux");
+ EXPECT_TRUE(Value("bar") == GetValueByPointerWithDefault(d, Pointer("/foo/0"), v));
+ EXPECT_TRUE(Value("bar") == GetValueByPointerWithDefault(d, Pointer("/foo/0"), v));
+ EXPECT_TRUE(Value("baz") == GetValueByPointerWithDefault(d, Pointer("/foo/1"), v));
+ EXPECT_TRUE(Value("qux") == GetValueByPointerWithDefault(d, Pointer("/foo/2"), v));
+ EXPECT_TRUE(Value("last") == GetValueByPointerWithDefault(d, Pointer("/foo/-"), Value("last").Move()));
+ EXPECT_STREQ("last", d["foo"][3].GetString());
+
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, Pointer("/foo/null"), Value().Move()).IsNull());
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, Pointer("/foo/null"), "x").IsNull());
+
+ // Generic version
+ EXPECT_EQ(-1, GetValueByPointerWithDefault(d, Pointer("/foo/int"), -1).GetInt());
+ EXPECT_EQ(-1, GetValueByPointerWithDefault(d, Pointer("/foo/int"), -2).GetInt());
+ EXPECT_EQ(0x87654321, GetValueByPointerWithDefault(d, Pointer("/foo/uint"), 0x87654321).GetUint());
+ EXPECT_EQ(0x87654321, GetValueByPointerWithDefault(d, Pointer("/foo/uint"), 0x12345678).GetUint());
+
+ const int64_t i64 = static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x80000000, 0));
+ EXPECT_EQ(i64, GetValueByPointerWithDefault(d, Pointer("/foo/int64"), i64).GetInt64());
+ EXPECT_EQ(i64, GetValueByPointerWithDefault(d, Pointer("/foo/int64"), i64 + 1).GetInt64());
+
+ const uint64_t u64 = RAPIDJSON_UINT64_C2(0xFFFFFFFFF, 0xFFFFFFFFF);
+ EXPECT_EQ(u64, GetValueByPointerWithDefault(d, Pointer("/foo/uint64"), u64).GetUint64());
+ EXPECT_EQ(u64, GetValueByPointerWithDefault(d, Pointer("/foo/uint64"), u64 - 1).GetUint64());
+
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, Pointer("/foo/true"), true).IsTrue());
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, Pointer("/foo/true"), false).IsTrue());
+
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, Pointer("/foo/false"), false).IsFalse());
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, Pointer("/foo/false"), true).IsFalse());
+
+ // StringRef version
+ EXPECT_STREQ("Hello", GetValueByPointerWithDefault(d, Pointer("/foo/hello"), "Hello").GetString());
+
+ // Copy string version
+ {
+ char buffer[256];
+ strcpy(buffer, "World");
+ EXPECT_STREQ("World", GetValueByPointerWithDefault(d, Pointer("/foo/world"), buffer).GetString());
+ memset(buffer, 0, sizeof(buffer));
+ }
+ EXPECT_STREQ("World", GetValueByPointer(d, Pointer("/foo/world"))->GetString());
+
+#if RAPIDJSON_HAS_STDSTRING
+ EXPECT_STREQ("C++", GetValueByPointerWithDefault(d, Pointer("/foo/C++"), std::string("C++")).GetString());
+#endif
+}
+
+TEST(Pointer, GetValueByPointerWithDefault_String_NoAllocator) {
+ Document d;
+ d.Parse(kJson);
+
+ const Value v("qux");
+ EXPECT_TRUE(Value("bar") == GetValueByPointerWithDefault(d, "/foo/0", v));
+ EXPECT_TRUE(Value("bar") == GetValueByPointerWithDefault(d, "/foo/0", v));
+ EXPECT_TRUE(Value("baz") == GetValueByPointerWithDefault(d, "/foo/1", v));
+ EXPECT_TRUE(Value("qux") == GetValueByPointerWithDefault(d, "/foo/2", v));
+ EXPECT_TRUE(Value("last") == GetValueByPointerWithDefault(d, "/foo/-", Value("last").Move()));
+ EXPECT_STREQ("last", d["foo"][3].GetString());
+
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, "/foo/null", Value().Move()).IsNull());
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, "/foo/null", "x").IsNull());
+
+ // Generic version
+ EXPECT_EQ(-1, GetValueByPointerWithDefault(d, "/foo/int", -1).GetInt());
+ EXPECT_EQ(-1, GetValueByPointerWithDefault(d, "/foo/int", -2).GetInt());
+ EXPECT_EQ(0x87654321, GetValueByPointerWithDefault(d, "/foo/uint", 0x87654321).GetUint());
+ EXPECT_EQ(0x87654321, GetValueByPointerWithDefault(d, "/foo/uint", 0x12345678).GetUint());
+
+ const int64_t i64 = static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x80000000, 0));
+ EXPECT_EQ(i64, GetValueByPointerWithDefault(d, "/foo/int64", i64).GetInt64());
+ EXPECT_EQ(i64, GetValueByPointerWithDefault(d, "/foo/int64", i64 + 1).GetInt64());
+
+ const uint64_t u64 = RAPIDJSON_UINT64_C2(0xFFFFFFFFF, 0xFFFFFFFFF);
+ EXPECT_EQ(u64, GetValueByPointerWithDefault(d, "/foo/uint64", u64).GetUint64());
+ EXPECT_EQ(u64, GetValueByPointerWithDefault(d, "/foo/uint64", u64 - 1).GetUint64());
+
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, "/foo/true", true).IsTrue());
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, "/foo/true", false).IsTrue());
+
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, "/foo/false", false).IsFalse());
+ EXPECT_TRUE(GetValueByPointerWithDefault(d, "/foo/false", true).IsFalse());
+
+ // StringRef version
+ EXPECT_STREQ("Hello", GetValueByPointerWithDefault(d, "/foo/hello", "Hello").GetString());
+
+ // Copy string version
+ {
+ char buffer[256];
+ strcpy(buffer, "World");
+ EXPECT_STREQ("World", GetValueByPointerWithDefault(d, "/foo/world", buffer).GetString());
+ memset(buffer, 0, sizeof(buffer));
+ }
+ EXPECT_STREQ("World", GetValueByPointer(d, "/foo/world")->GetString());
+
+#if RAPIDJSON_HAS_STDSTRING
+ EXPECT_STREQ("C++", GetValueByPointerWithDefault(d, Pointer("/foo/C++"), std::string("C++")).GetString());
+#endif
+}
+
+TEST(Pointer, SetValueByPointer_Pointer) {
+ Document d;
+ d.Parse(kJson);
+ Document::AllocatorType& a = d.GetAllocator();
+
+ // Value version
+ SetValueByPointer(d, Pointer("/foo/0"), Value(123).Move(), a);
+ EXPECT_EQ(123, d["foo"][0].GetInt());
+
+ SetValueByPointer(d, Pointer("/foo/null"), Value().Move(), a);
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/null")->IsNull());
+
+ // Const Value version
+ const Value foo(d["foo"], d.GetAllocator());
+ SetValueByPointer(d, Pointer("/clone"), foo, a);
+ EXPECT_EQ(foo, *GetValueByPointer(d, "/clone"));
+
+ // Generic version
+ SetValueByPointer(d, Pointer("/foo/int"), -1, a);
+ EXPECT_EQ(-1, GetValueByPointer(d, "/foo/int")->GetInt());
+
+ SetValueByPointer(d, Pointer("/foo/uint"), 0x87654321, a);
+ EXPECT_EQ(0x87654321, GetValueByPointer(d, "/foo/uint")->GetUint());
+
+ const int64_t i64 = static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x80000000, 0));
+ SetValueByPointer(d, Pointer("/foo/int64"), i64, a);
+ EXPECT_EQ(i64, GetValueByPointer(d, "/foo/int64")->GetInt64());
+
+ const uint64_t u64 = RAPIDJSON_UINT64_C2(0xFFFFFFFFF, 0xFFFFFFFFF);
+ SetValueByPointer(d, Pointer("/foo/uint64"), u64, a);
+ EXPECT_EQ(u64, GetValueByPointer(d, "/foo/uint64")->GetUint64());
+
+ SetValueByPointer(d, Pointer("/foo/true"), true, a);
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/true")->IsTrue());
+
+ SetValueByPointer(d, Pointer("/foo/false"), false, a);
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/false")->IsFalse());
+
+ // StringRef version
+ SetValueByPointer(d, Pointer("/foo/hello"), "Hello", a);
+ EXPECT_STREQ("Hello", GetValueByPointer(d, "/foo/hello")->GetString());
+
+ // Copy string version
+ {
+ char buffer[256];
+ strcpy(buffer, "World");
+ SetValueByPointer(d, Pointer("/foo/world"), buffer, a);
+ memset(buffer, 0, sizeof(buffer));
+ }
+ EXPECT_STREQ("World", GetValueByPointer(d, "/foo/world")->GetString());
+
+#if RAPIDJSON_HAS_STDSTRING
+ SetValueByPointer(d, Pointer("/foo/c++"), std::string("C++"), a);
+ EXPECT_STREQ("C++", GetValueByPointer(d, "/foo/c++")->GetString());
+#endif
+}
+
+TEST(Pointer, SetValueByPointer_String) {
+ Document d;
+ d.Parse(kJson);
+ Document::AllocatorType& a = d.GetAllocator();
+
+ // Value version
+ SetValueByPointer(d, "/foo/0", Value(123).Move(), a);
+ EXPECT_EQ(123, d["foo"][0].GetInt());
+
+ SetValueByPointer(d, "/foo/null", Value().Move(), a);
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/null")->IsNull());
+
+ // Const Value version
+ const Value foo(d["foo"], d.GetAllocator());
+ SetValueByPointer(d, "/clone", foo, a);
+ EXPECT_EQ(foo, *GetValueByPointer(d, "/clone"));
+
+ // Generic version
+ SetValueByPointer(d, "/foo/int", -1, a);
+ EXPECT_EQ(-1, GetValueByPointer(d, "/foo/int")->GetInt());
+
+ SetValueByPointer(d, "/foo/uint", 0x87654321, a);
+ EXPECT_EQ(0x87654321, GetValueByPointer(d, "/foo/uint")->GetUint());
+
+ const int64_t i64 = static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x80000000, 0));
+ SetValueByPointer(d, "/foo/int64", i64, a);
+ EXPECT_EQ(i64, GetValueByPointer(d, "/foo/int64")->GetInt64());
+
+ const uint64_t u64 = RAPIDJSON_UINT64_C2(0xFFFFFFFFF, 0xFFFFFFFFF);
+ SetValueByPointer(d, "/foo/uint64", u64, a);
+ EXPECT_EQ(u64, GetValueByPointer(d, "/foo/uint64")->GetUint64());
+
+ SetValueByPointer(d, "/foo/true", true, a);
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/true")->IsTrue());
+
+ SetValueByPointer(d, "/foo/false", false, a);
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/false")->IsFalse());
+
+ // StringRef version
+ SetValueByPointer(d, "/foo/hello", "Hello", a);
+ EXPECT_STREQ("Hello", GetValueByPointer(d, "/foo/hello")->GetString());
+
+ // Copy string version
+ {
+ char buffer[256];
+ strcpy(buffer, "World");
+ SetValueByPointer(d, "/foo/world", buffer, a);
+ memset(buffer, 0, sizeof(buffer));
+ }
+ EXPECT_STREQ("World", GetValueByPointer(d, "/foo/world")->GetString());
+
+#if RAPIDJSON_HAS_STDSTRING
+ SetValueByPointer(d, "/foo/c++", std::string("C++"), a);
+ EXPECT_STREQ("C++", GetValueByPointer(d, "/foo/c++")->GetString());
+#endif
+}
+
+TEST(Pointer, SetValueByPointer_Pointer_NoAllocator) {
+ Document d;
+ d.Parse(kJson);
+
+ // Value version
+ SetValueByPointer(d, Pointer("/foo/0"), Value(123).Move());
+ EXPECT_EQ(123, d["foo"][0].GetInt());
+
+ SetValueByPointer(d, Pointer("/foo/null"), Value().Move());
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/null")->IsNull());
+
+ // Const Value version
+ const Value foo(d["foo"], d.GetAllocator());
+ SetValueByPointer(d, Pointer("/clone"), foo);
+ EXPECT_EQ(foo, *GetValueByPointer(d, "/clone"));
+
+ // Generic version
+ SetValueByPointer(d, Pointer("/foo/int"), -1);
+ EXPECT_EQ(-1, GetValueByPointer(d, "/foo/int")->GetInt());
+
+ SetValueByPointer(d, Pointer("/foo/uint"), 0x87654321);
+ EXPECT_EQ(0x87654321, GetValueByPointer(d, "/foo/uint")->GetUint());
+
+ const int64_t i64 = static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x80000000, 0));
+ SetValueByPointer(d, Pointer("/foo/int64"), i64);
+ EXPECT_EQ(i64, GetValueByPointer(d, "/foo/int64")->GetInt64());
+
+ const uint64_t u64 = RAPIDJSON_UINT64_C2(0xFFFFFFFFF, 0xFFFFFFFFF);
+ SetValueByPointer(d, Pointer("/foo/uint64"), u64);
+ EXPECT_EQ(u64, GetValueByPointer(d, "/foo/uint64")->GetUint64());
+
+ SetValueByPointer(d, Pointer("/foo/true"), true);
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/true")->IsTrue());
+
+ SetValueByPointer(d, Pointer("/foo/false"), false);
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/false")->IsFalse());
+
+ // StringRef version
+ SetValueByPointer(d, Pointer("/foo/hello"), "Hello");
+ EXPECT_STREQ("Hello", GetValueByPointer(d, "/foo/hello")->GetString());
+
+ // Copy string version
+ {
+ char buffer[256];
+ strcpy(buffer, "World");
+ SetValueByPointer(d, Pointer("/foo/world"), buffer);
+ memset(buffer, 0, sizeof(buffer));
+ }
+ EXPECT_STREQ("World", GetValueByPointer(d, "/foo/world")->GetString());
+
+#if RAPIDJSON_HAS_STDSTRING
+ SetValueByPointer(d, Pointer("/foo/c++"), std::string("C++"));
+ EXPECT_STREQ("C++", GetValueByPointer(d, "/foo/c++")->GetString());
+#endif
+}
+
+TEST(Pointer, SetValueByPointer_String_NoAllocator) {
+ Document d;
+ d.Parse(kJson);
+
+ // Value version
+ SetValueByPointer(d, "/foo/0", Value(123).Move());
+ EXPECT_EQ(123, d["foo"][0].GetInt());
+
+ SetValueByPointer(d, "/foo/null", Value().Move());
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/null")->IsNull());
+
+ // Const Value version
+ const Value foo(d["foo"], d.GetAllocator());
+ SetValueByPointer(d, "/clone", foo);
+ EXPECT_EQ(foo, *GetValueByPointer(d, "/clone"));
+
+ // Generic version
+ SetValueByPointer(d, "/foo/int", -1);
+ EXPECT_EQ(-1, GetValueByPointer(d, "/foo/int")->GetInt());
+
+ SetValueByPointer(d, "/foo/uint", 0x87654321);
+ EXPECT_EQ(0x87654321, GetValueByPointer(d, "/foo/uint")->GetUint());
+
+ const int64_t i64 = static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x80000000, 0));
+ SetValueByPointer(d, "/foo/int64", i64);
+ EXPECT_EQ(i64, GetValueByPointer(d, "/foo/int64")->GetInt64());
+
+ const uint64_t u64 = RAPIDJSON_UINT64_C2(0xFFFFFFFFF, 0xFFFFFFFFF);
+ SetValueByPointer(d, "/foo/uint64", u64);
+ EXPECT_EQ(u64, GetValueByPointer(d, "/foo/uint64")->GetUint64());
+
+ SetValueByPointer(d, "/foo/true", true);
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/true")->IsTrue());
+
+ SetValueByPointer(d, "/foo/false", false);
+ EXPECT_TRUE(GetValueByPointer(d, "/foo/false")->IsFalse());
+
+ // StringRef version
+ SetValueByPointer(d, "/foo/hello", "Hello");
+ EXPECT_STREQ("Hello", GetValueByPointer(d, "/foo/hello")->GetString());
+
+ // Copy string version
+ {
+ char buffer[256];
+ strcpy(buffer, "World");
+ SetValueByPointer(d, "/foo/world", buffer);
+ memset(buffer, 0, sizeof(buffer));
+ }
+ EXPECT_STREQ("World", GetValueByPointer(d, "/foo/world")->GetString());
+
+#if RAPIDJSON_HAS_STDSTRING
+ SetValueByPointer(d, "/foo/c++", std::string("C++"));
+ EXPECT_STREQ("C++", GetValueByPointer(d, "/foo/c++")->GetString());
+#endif
+}
+
+TEST(Pointer, SwapValueByPointer) {
+ Document d;
+ d.Parse(kJson);
+ Document::AllocatorType& a = d.GetAllocator();
+ SwapValueByPointer(d, Pointer("/foo/0"), *GetValueByPointer(d, "/foo/1"), a);
+ EXPECT_STREQ("baz", d["foo"][0].GetString());
+ EXPECT_STREQ("bar", d["foo"][1].GetString());
+
+ SwapValueByPointer(d, "/foo/0", *GetValueByPointer(d, "/foo/1"), a);
+ EXPECT_STREQ("bar", d["foo"][0].GetString());
+ EXPECT_STREQ("baz", d["foo"][1].GetString());
+}
+
+TEST(Pointer, SwapValueByPointer_NoAllocator) {
+ Document d;
+ d.Parse(kJson);
+ SwapValueByPointer(d, Pointer("/foo/0"), *GetValueByPointer(d, "/foo/1"));
+ EXPECT_STREQ("baz", d["foo"][0].GetString());
+ EXPECT_STREQ("bar", d["foo"][1].GetString());
+
+ SwapValueByPointer(d, "/foo/0", *GetValueByPointer(d, "/foo/1"));
+ EXPECT_STREQ("bar", d["foo"][0].GetString());
+ EXPECT_STREQ("baz", d["foo"][1].GetString());
+}
+
+TEST(Pointer, EraseValueByPointer_Pointer) {
+ Document d;
+ d.Parse(kJson);
+
+ EXPECT_FALSE(EraseValueByPointer(d, Pointer("")));
+ EXPECT_FALSE(Pointer("/foo/nonexist").Erase(d));
+ EXPECT_TRUE(EraseValueByPointer(d, Pointer("/foo/0")));
+ EXPECT_EQ(1u, d["foo"].Size());
+ EXPECT_STREQ("baz", d["foo"][0].GetString());
+ EXPECT_TRUE(EraseValueByPointer(d, Pointer("/foo/0")));
+ EXPECT_TRUE(d["foo"].Empty());
+ EXPECT_TRUE(EraseValueByPointer(d, Pointer("/foo")));
+ EXPECT_TRUE(Pointer("/foo").Get(d) == 0);
+}
+
+TEST(Pointer, EraseValueByPointer_String) {
+ Document d;
+ d.Parse(kJson);
+
+ EXPECT_FALSE(EraseValueByPointer(d, ""));
+ EXPECT_FALSE(Pointer("/foo/nonexist").Erase(d));
+ EXPECT_TRUE(EraseValueByPointer(d, "/foo/0"));
+ EXPECT_EQ(1u, d["foo"].Size());
+ EXPECT_STREQ("baz", d["foo"][0].GetString());
+ EXPECT_TRUE(EraseValueByPointer(d, "/foo/0"));
+ EXPECT_TRUE(d["foo"].Empty());
+ EXPECT_TRUE(EraseValueByPointer(d, "/foo"));
+ EXPECT_TRUE(Pointer("/foo").Get(d) == 0);
+}
+
+TEST(Pointer, Ambiguity) {
+ {
+ Document d;
+ d.Parse("{\"0\" : [123]}");
+ EXPECT_EQ(123, Pointer("/0/0").Get(d)->GetInt());
+ Pointer("/0/a").Set(d, 456); // Change array [123] to object {456}
+ EXPECT_EQ(456, Pointer("/0/a").Get(d)->GetInt());
+ }
+
+ {
+ Document d;
+ EXPECT_FALSE(d.Parse("[{\"0\": 123}]").HasParseError());
+ EXPECT_EQ(123, Pointer("/0/0").Get(d)->GetInt());
+ Pointer("/0/1").Set(d, 456); // 1 is treated as "1" to index object
+ EXPECT_EQ(123, Pointer("/0/0").Get(d)->GetInt());
+ EXPECT_EQ(456, Pointer("/0/1").Get(d)->GetInt());
+ }
+}
+
+TEST(Pointer, ResolveOnObject) {
+ Document d;
+ EXPECT_FALSE(d.Parse("{\"a\": 123}").HasParseError());
+
+ {
+ Value::ConstObject o = static_cast<const Document&>(d).GetObject();
+ EXPECT_EQ(123, Pointer("/a").Get(o)->GetInt());
+ }
+
+ {
+ Value::Object o = d.GetObject();
+ Pointer("/a").Set(o, 456, d.GetAllocator());
+ EXPECT_EQ(456, Pointer("/a").Get(o)->GetInt());
+ }
+}
+
+TEST(Pointer, ResolveOnArray) {
+ Document d;
+ EXPECT_FALSE(d.Parse("[1, 2, 3]").HasParseError());
+
+ {
+ Value::ConstArray a = static_cast<const Document&>(d).GetArray();
+ EXPECT_EQ(2, Pointer("/1").Get(a)->GetInt());
+ }
+
+ {
+ Value::Array a = d.GetArray();
+ Pointer("/1").Set(a, 123, d.GetAllocator());
+ EXPECT_EQ(123, Pointer("/1").Get(a)->GetInt());
+ }
+}
+
+TEST(Pointer, LessThan) {
+ static const struct {
+ const char *str;
+ bool valid;
+ } pointers[] = {
+ { "/a/b", true },
+ { "/a", true },
+ { "/d/1", true },
+ { "/d/2/z", true },
+ { "/d/2/3", true },
+ { "/d/2", true },
+ { "/a/c", true },
+ { "/e/f~g", false },
+ { "/d/2/zz", true },
+ { "/d/1", true },
+ { "/d/2/z", true },
+ { "/e/f~~g", false },
+ { "/e/f~0g", true },
+ { "/e/f~1g", true },
+ { "/e/f.g", true },
+ { "", true }
+ };
+ static const char *ordered_pointers[] = {
+ "",
+ "/a",
+ "/a/b",
+ "/a/c",
+ "/d/1",
+ "/d/1",
+ "/d/2",
+ "/e/f.g",
+ "/e/f~1g",
+ "/e/f~0g",
+ "/d/2/3",
+ "/d/2/z",
+ "/d/2/z",
+ "/d/2/zz",
+ NULL, // was invalid "/e/f~g"
+ NULL // was invalid "/e/f~~g"
+ };
+ typedef MemoryPoolAllocator<> AllocatorType;
+ typedef GenericPointer<Value, AllocatorType> PointerType;
+ typedef std::multimap<PointerType, size_t> PointerMap;
+ PointerMap map;
+ PointerMap::iterator it;
+ AllocatorType allocator;
+ size_t i;
+
+ EXPECT_EQ(sizeof(pointers) / sizeof(pointers[0]),
+ sizeof(ordered_pointers) / sizeof(ordered_pointers[0]));
+
+ for (i = 0; i < sizeof(pointers) / sizeof(pointers[0]); ++i) {
+ it = map.insert(PointerMap::value_type(PointerType(pointers[i].str, &allocator), i));
+ if (!it->first.IsValid()) {
+ EXPECT_EQ(++it, map.end());
+ }
+ }
+
+ for (i = 0, it = map.begin(); it != map.end(); ++it, ++i) {
+ EXPECT_TRUE(it->second < sizeof(pointers) / sizeof(pointers[0]));
+ EXPECT_EQ(it->first.IsValid(), pointers[it->second].valid);
+ EXPECT_TRUE(i < sizeof(ordered_pointers) / sizeof(ordered_pointers[0]));
+ EXPECT_EQ(it->first.IsValid(), !!ordered_pointers[i]);
+ if (it->first.IsValid()) {
+ std::stringstream ss;
+ OStreamWrapper os(ss);
+ EXPECT_TRUE(it->first.Stringify(os));
+ EXPECT_EQ(ss.str(), pointers[it->second].str);
+ EXPECT_EQ(ss.str(), ordered_pointers[i]);
+ }
+ }
+}
+
+// https://github.com/Tencent/rapidjson/issues/483
+namespace myjson {
+
+class MyAllocator
+{
+public:
+ static const bool kNeedFree = true;
+ void * Malloc(size_t _size) { return malloc(_size); }
+ void * Realloc(void *_org_p, size_t _org_size, size_t _new_size) { (void)_org_size; return realloc(_org_p, _new_size); }
+ static void Free(void *_p) { return free(_p); }
+};
+
+typedef rapidjson::GenericDocument<
+ rapidjson::UTF8<>,
+ rapidjson::MemoryPoolAllocator< MyAllocator >,
+ MyAllocator
+ > Document;
+
+typedef rapidjson::GenericPointer<
+ ::myjson::Document::ValueType,
+ MyAllocator
+ > Pointer;
+
+typedef ::myjson::Document::ValueType Value;
+
+}
+
+TEST(Pointer, Issue483) {
+ std::string mystr, path;
+ myjson::Document document;
+ myjson::Value value(rapidjson::kStringType);
+ value.SetString(mystr.c_str(), static_cast<SizeType>(mystr.length()), document.GetAllocator());
+ myjson::Pointer(path.c_str()).Set(document, value, document.GetAllocator());
+}
+
+TEST(Pointer, Issue1899) {
+ typedef GenericPointer<Value, MemoryPoolAllocator<> > PointerType;
+ PointerType p;
+ PointerType q = p.Append("foo");
+ EXPECT_TRUE(PointerType("/foo") == q);
+ q = q.Append(1234);
+ EXPECT_TRUE(PointerType("/foo/1234") == q);
+ q = q.Append("");
+ EXPECT_TRUE(PointerType("/foo/1234/") == q);
+} \ No newline at end of file
diff --git a/src/s3select/rapidjson/test/unittest/prettywritertest.cpp b/src/s3select/rapidjson/test/unittest/prettywritertest.cpp
new file mode 100644
index 000000000..0b7feef3b
--- /dev/null
+++ b/src/s3select/rapidjson/test/unittest/prettywritertest.cpp
@@ -0,0 +1,373 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#include "unittest.h"
+#include "rapidjson/reader.h"
+#include "rapidjson/prettywriter.h"
+#include "rapidjson/stringbuffer.h"
+#include "rapidjson/filewritestream.h"
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(c++98-compat)
+#endif
+
+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"
+"}";
+
+static const char kPrettyJson_FormatOptions_SLA[] =
+"{\n"
+" \"hello\": \"world\",\n"
+" \"t\": true,\n"
+" \"f\": false,\n"
+" \"n\": null,\n"
+" \"i\": 123,\n"
+" \"pi\": 3.1416,\n"
+" \"a\": [1, 2, 3, -1],\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, FormatOptions) {
+ StringBuffer buffer;
+ PrettyWriter<StringBuffer> writer(buffer);
+ writer.SetFormatOptions(kFormatSingleLineArray);
+ Reader reader;
+ StringStream s(kJson);
+ reader.Parse(s, writer);
+ EXPECT_STREQ(kPrettyJson_FormatOptions_SLA, 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);
+ ASSERT_TRUE(fp!=NULL);
+ 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 = static_cast<size_t>(ftell(fp));
+ fseek(fp, 0, SEEK_SET);
+ char* json = static_cast<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);
+}
+
+TEST(PrettyWriter, RawValue) {
+ StringBuffer buffer;
+ PrettyWriter<StringBuffer> writer(buffer);
+ writer.StartObject();
+ writer.Key("a");
+ writer.Int(1);
+ writer.Key("raw");
+ const char json[] = "[\"Hello\\nWorld\", 123.456]";
+ writer.RawValue(json, strlen(json), kArrayType);
+ writer.EndObject();
+ EXPECT_TRUE(writer.IsComplete());
+ EXPECT_STREQ(
+ "{\n"
+ " \"a\": 1,\n"
+ " \"raw\": [\"Hello\\nWorld\", 123.456]\n" // no indentation within raw value
+ "}",
+ buffer.GetString());
+}
+
+TEST(PrettyWriter, InvalidEventSequence) {
+ // {]
+ {
+ StringBuffer buffer;
+ PrettyWriter<StringBuffer> writer(buffer);
+ writer.StartObject();
+ EXPECT_THROW(writer.EndArray(), AssertException);
+ EXPECT_FALSE(writer.IsComplete());
+ }
+
+ // [}
+ {
+ StringBuffer buffer;
+ PrettyWriter<StringBuffer> writer(buffer);
+ writer.StartArray();
+ EXPECT_THROW(writer.EndObject(), AssertException);
+ EXPECT_FALSE(writer.IsComplete());
+ }
+
+ // { 1:
+ {
+ StringBuffer buffer;
+ PrettyWriter<StringBuffer> writer(buffer);
+ writer.StartObject();
+ EXPECT_THROW(writer.Int(1), AssertException);
+ EXPECT_FALSE(writer.IsComplete());
+ }
+
+ // { 'a' }
+ {
+ StringBuffer buffer;
+ PrettyWriter<StringBuffer> writer(buffer);
+ writer.StartObject();
+ writer.Key("a");
+ EXPECT_THROW(writer.EndObject(), AssertException);
+ EXPECT_FALSE(writer.IsComplete());
+ }
+
+ // { 'a':'b','c' }
+ {
+ StringBuffer buffer;
+ PrettyWriter<StringBuffer> writer(buffer);
+ writer.StartObject();
+ writer.Key("a");
+ writer.String("b");
+ writer.Key("c");
+ EXPECT_THROW(writer.EndObject(), AssertException);
+ EXPECT_FALSE(writer.IsComplete());
+ }
+}
+
+TEST(PrettyWriter, NaN) {
+ double nan = std::numeric_limits<double>::quiet_NaN();
+
+ EXPECT_TRUE(internal::Double(nan).IsNan());
+ StringBuffer buffer;
+ {
+ PrettyWriter<StringBuffer> writer(buffer);
+ EXPECT_FALSE(writer.Double(nan));
+ }
+ {
+ PrettyWriter<StringBuffer, UTF8<>, UTF8<>, CrtAllocator, kWriteNanAndInfFlag> writer(buffer);
+ EXPECT_TRUE(writer.Double(nan));
+ EXPECT_STREQ("NaN", buffer.GetString());
+ }
+ GenericStringBuffer<UTF16<> > buffer2;
+ PrettyWriter<GenericStringBuffer<UTF16<> > > writer2(buffer2);
+ EXPECT_FALSE(writer2.Double(nan));
+}
+
+TEST(PrettyWriter, Inf) {
+ double inf = std::numeric_limits<double>::infinity();
+
+ EXPECT_TRUE(internal::Double(inf).IsInf());
+ StringBuffer buffer;
+ {
+ PrettyWriter<StringBuffer> writer(buffer);
+ EXPECT_FALSE(writer.Double(inf));
+ }
+ {
+ PrettyWriter<StringBuffer> writer(buffer);
+ EXPECT_FALSE(writer.Double(-inf));
+ }
+ {
+ PrettyWriter<StringBuffer, UTF8<>, UTF8<>, CrtAllocator, kWriteNanAndInfFlag> writer(buffer);
+ EXPECT_TRUE(writer.Double(inf));
+ }
+ {
+ PrettyWriter<StringBuffer, UTF8<>, UTF8<>, CrtAllocator, kWriteNanAndInfFlag> writer(buffer);
+ EXPECT_TRUE(writer.Double(-inf));
+ }
+ EXPECT_STREQ("Infinity-Infinity", buffer.GetString());
+}
+
+TEST(PrettyWriter, Issue_889) {
+ char buf[100] = "Hello";
+
+ StringBuffer buffer;
+ PrettyWriter<StringBuffer> writer(buffer);
+ writer.StartArray();
+ writer.String(buf);
+ writer.EndArray();
+
+ EXPECT_STREQ("[\n \"Hello\"\n]", buffer.GetString());
+ EXPECT_TRUE(writer.IsComplete()); \
+}
+
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+
+static PrettyWriter<StringBuffer> WriterGen(StringBuffer &target) {
+ PrettyWriter<StringBuffer> writer(target);
+ writer.StartObject();
+ writer.Key("a");
+ writer.Int(1);
+ return writer;
+}
+
+TEST(PrettyWriter, MoveCtor) {
+ StringBuffer buffer;
+ PrettyWriter<StringBuffer> writer(WriterGen(buffer));
+ writer.EndObject();
+ EXPECT_TRUE(writer.IsComplete());
+ EXPECT_STREQ(
+ "{\n"
+ " \"a\": 1\n"
+ "}",
+ buffer.GetString());
+}
+#endif
+
+TEST(PrettyWriter, Issue_1336) {
+#define T(meth, val, expected) \
+ { \
+ StringBuffer buffer; \
+ PrettyWriter<StringBuffer> writer(buffer); \
+ writer.meth(val); \
+ \
+ EXPECT_STREQ(expected, buffer.GetString()); \
+ EXPECT_TRUE(writer.IsComplete()); \
+ }
+
+ T(Bool, false, "false");
+ T(Bool, true, "true");
+ T(Int, 0, "0");
+ T(Uint, 0, "0");
+ T(Int64, 0, "0");
+ T(Uint64, 0, "0");
+ T(Double, 0, "0.0");
+ T(String, "Hello", "\"Hello\"");
+#undef T
+
+ StringBuffer buffer;
+ PrettyWriter<StringBuffer> writer(buffer);
+ writer.Null();
+
+ EXPECT_STREQ("null", buffer.GetString());
+ EXPECT_TRUE(writer.IsComplete());
+}
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
diff --git a/src/s3select/rapidjson/test/unittest/readertest.cpp b/src/s3select/rapidjson/test/unittest/readertest.cpp
new file mode 100644
index 000000000..f828dbbe2
--- /dev/null
+++ b/src/s3select/rapidjson/test/unittest/readertest.cpp
@@ -0,0 +1,2370 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#include "unittest.h"
+
+#include "rapidjson/reader.h"
+#include "rapidjson/internal/dtoa.h"
+#include "rapidjson/internal/itoa.h"
+#include "rapidjson/memorystream.h"
+
+#include <limits>
+
+using namespace rapidjson;
+
+RAPIDJSON_DIAG_PUSH
+#ifdef __GNUC__
+RAPIDJSON_DIAG_OFF(effc++)
+RAPIDJSON_DIAG_OFF(float-equal)
+RAPIDJSON_DIAG_OFF(missing-noreturn)
+#if __GNUC__ >= 7
+RAPIDJSON_DIAG_OFF(dangling-else)
+#endif
+#endif // __GNUC__
+
+#ifdef __clang__
+RAPIDJSON_DIAG_OFF(variadic-macros)
+RAPIDJSON_DIAG_OFF(c++98-compat-pedantic)
+#endif
+
+template<bool expect>
+struct ParseBoolHandler : BaseReaderHandler<UTF8<>, ParseBoolHandler<expect> > {
+ ParseBoolHandler() : step_(0) {}
+ bool Default() { ADD_FAILURE(); return false; }
+ // gcc 4.8.x generates warning in EXPECT_EQ(bool, bool) on this gtest version.
+ // Workaround with EXPECT_TRUE().
+ bool Bool(bool b) { /*EXPECT_EQ(expect, b); */EXPECT_TRUE(expect == b); ++step_; return true; }
+
+ unsigned step_;
+};
+
+TEST(Reader, ParseTrue) {
+ StringStream s("true");
+ ParseBoolHandler<true> h;
+ Reader reader;
+ reader.Parse(s, h);
+ EXPECT_EQ(1u, h.step_);
+}
+
+TEST(Reader, ParseFalse) {
+ StringStream s("false");
+ ParseBoolHandler<false> h;
+ Reader reader;
+ reader.Parse(s, h);
+ EXPECT_EQ(1u, h.step_);
+}
+
+struct ParseIntHandler : BaseReaderHandler<UTF8<>, ParseIntHandler> {
+ ParseIntHandler() : step_(0), actual_() {}
+ bool Default() { ADD_FAILURE(); return false; }
+ bool Int(int i) { actual_ = i; step_++; return true; }
+
+ unsigned step_;
+ int actual_;
+};
+
+struct ParseUintHandler : BaseReaderHandler<UTF8<>, ParseUintHandler> {
+ ParseUintHandler() : step_(0), actual_() {}
+ bool Default() { ADD_FAILURE(); return false; }
+ bool Uint(unsigned i) { actual_ = i; step_++; return true; }
+
+ unsigned step_;
+ unsigned actual_;
+};
+
+struct ParseInt64Handler : BaseReaderHandler<UTF8<>, ParseInt64Handler> {
+ ParseInt64Handler() : step_(0), actual_() {}
+ bool Default() { ADD_FAILURE(); return false; }
+ bool Int64(int64_t i) { actual_ = i; step_++; return true; }
+
+ unsigned step_;
+ int64_t actual_;
+};
+
+struct ParseUint64Handler : BaseReaderHandler<UTF8<>, ParseUint64Handler> {
+ ParseUint64Handler() : step_(0), actual_() {}
+ bool Default() { ADD_FAILURE(); return false; }
+ bool Uint64(uint64_t i) { actual_ = i; step_++; return true; }
+
+ unsigned step_;
+ uint64_t actual_;
+};
+
+struct ParseDoubleHandler : BaseReaderHandler<UTF8<>, ParseDoubleHandler> {
+ ParseDoubleHandler() : step_(0), actual_() {}
+ bool Default() { ADD_FAILURE(); return false; }
+ bool Double(double d) { actual_ = d; step_++; return true; }
+
+ unsigned step_;
+ double actual_;
+};
+
+TEST(Reader, ParseNumber_Integer) {
+#define TEST_INTEGER(Handler, str, x) \
+ { \
+ StringStream s(str); \
+ Handler h; \
+ Reader reader; \
+ reader.Parse(s, h); \
+ EXPECT_EQ(1u, h.step_); \
+ EXPECT_EQ(x, h.actual_); \
+ }
+
+ TEST_INTEGER(ParseUintHandler, "0", 0u);
+ TEST_INTEGER(ParseUintHandler, "123", 123u);
+ TEST_INTEGER(ParseUintHandler, "2147483648", 2147483648u); // 2^31 - 1 (cannot be stored in int)
+ TEST_INTEGER(ParseUintHandler, "4294967295", 4294967295u);
+
+ TEST_INTEGER(ParseIntHandler, "-123", -123);
+ TEST_INTEGER(ParseIntHandler, "-2147483648", static_cast<int32_t>(0x80000000)); // -2^31 (min of int)
+
+ TEST_INTEGER(ParseUint64Handler, "4294967296", RAPIDJSON_UINT64_C2(1, 0)); // 2^32 (max of unsigned + 1, force to use uint64_t)
+ TEST_INTEGER(ParseUint64Handler, "18446744073709551615", RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0xFFFFFFFF)); // 2^64 - 1 (max of uint64_t)
+
+ TEST_INTEGER(ParseInt64Handler, "-2147483649", static_cast<int64_t>(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x7FFFFFFF))); // -2^31 -1 (min of int - 1, force to use int64_t)
+ TEST_INTEGER(ParseInt64Handler, "-9223372036854775808", static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x80000000, 0x00000000))); // -2^63 (min of int64_t)
+
+ // Random test for uint32_t/int32_t
+ {
+ union {
+ uint32_t u;
+ int32_t i;
+ }u;
+ Random r;
+
+ for (unsigned i = 0; i < 100000; i++) {
+ u.u = r();
+
+ char buffer[32];
+ *internal::u32toa(u.u, buffer) = '\0';
+ TEST_INTEGER(ParseUintHandler, buffer, u.u);
+
+ if (u.i < 0) {
+ *internal::i32toa(u.i, buffer) = '\0';
+ TEST_INTEGER(ParseIntHandler, buffer, u.i);
+ }
+ }
+ }
+
+ // Random test for uint64_t/int64_t
+ {
+ union {
+ uint64_t u;
+ int64_t i;
+ }u;
+ Random r;
+
+ for (unsigned i = 0; i < 100000; i++) {
+ u.u = uint64_t(r()) << 32;
+ u.u |= r();
+
+ char buffer[32];
+ if (u.u > uint64_t(4294967295u)) {
+ *internal::u64toa(u.u, buffer) = '\0';
+ TEST_INTEGER(ParseUint64Handler, buffer, u.u);
+ }
+
+ if (u.i < -int64_t(2147483648u)) {
+ *internal::i64toa(u.i, buffer) = '\0';
+ TEST_INTEGER(ParseInt64Handler, buffer, u.i);
+ }
+ }
+ }
+#undef TEST_INTEGER
+}
+
+template<bool fullPrecision>
+static void TestParseDouble() {
+#define TEST_DOUBLE(fullPrecision, str, x) \
+ { \
+ StringStream s(str); \
+ ParseDoubleHandler h; \
+ Reader reader; \
+ ASSERT_EQ(kParseErrorNone, reader.Parse<fullPrecision ? kParseFullPrecisionFlag : 0>(s, h).Code()); \
+ EXPECT_EQ(1u, h.step_); \
+ internal::Double e(x), a(h.actual_); \
+ if (fullPrecision) { \
+ EXPECT_EQ(e.Uint64Value(), a.Uint64Value()); \
+ if (e.Uint64Value() != a.Uint64Value()) \
+ printf(" String: %s\n Actual: %.17g\nExpected: %.17g\n", str, h.actual_, x); \
+ } \
+ else { \
+ EXPECT_EQ(e.Sign(), a.Sign()); /* for 0.0 != -0.0 */ \
+ EXPECT_DOUBLE_EQ(x, h.actual_); \
+ } \
+ }
+
+ TEST_DOUBLE(fullPrecision, "0.0", 0.0);
+ TEST_DOUBLE(fullPrecision, "-0.0", -0.0); // For checking issue #289
+ TEST_DOUBLE(fullPrecision, "0e100", 0.0); // For checking issue #1249
+ TEST_DOUBLE(fullPrecision, "1.0", 1.0);
+ TEST_DOUBLE(fullPrecision, "-1.0", -1.0);
+ TEST_DOUBLE(fullPrecision, "1.5", 1.5);
+ TEST_DOUBLE(fullPrecision, "-1.5", -1.5);
+ TEST_DOUBLE(fullPrecision, "3.1416", 3.1416);
+ TEST_DOUBLE(fullPrecision, "1E10", 1E10);
+ TEST_DOUBLE(fullPrecision, "1e10", 1e10);
+ TEST_DOUBLE(fullPrecision, "1E+10", 1E+10);
+ TEST_DOUBLE(fullPrecision, "1E-10", 1E-10);
+ TEST_DOUBLE(fullPrecision, "-1E10", -1E10);
+ TEST_DOUBLE(fullPrecision, "-1e10", -1e10);
+ TEST_DOUBLE(fullPrecision, "-1E+10", -1E+10);
+ TEST_DOUBLE(fullPrecision, "-1E-10", -1E-10);
+ TEST_DOUBLE(fullPrecision, "1.234E+10", 1.234E+10);
+ TEST_DOUBLE(fullPrecision, "1.234E-10", 1.234E-10);
+ TEST_DOUBLE(fullPrecision, "1.79769e+308", 1.79769e+308);
+ TEST_DOUBLE(fullPrecision, "2.22507e-308", 2.22507e-308);
+ TEST_DOUBLE(fullPrecision, "-1.79769e+308", -1.79769e+308);
+ TEST_DOUBLE(fullPrecision, "-2.22507e-308", -2.22507e-308);
+ TEST_DOUBLE(fullPrecision, "4.9406564584124654e-324", 4.9406564584124654e-324); // minimum denormal
+ TEST_DOUBLE(fullPrecision, "2.2250738585072009e-308", 2.2250738585072009e-308); // Max subnormal double
+ TEST_DOUBLE(fullPrecision, "2.2250738585072014e-308", 2.2250738585072014e-308); // Min normal positive double
+ TEST_DOUBLE(fullPrecision, "1.7976931348623157e+308", 1.7976931348623157e+308); // Max double
+ TEST_DOUBLE(fullPrecision, "1e-10000", 0.0); // must underflow
+ TEST_DOUBLE(fullPrecision, "18446744073709551616", 18446744073709551616.0); // 2^64 (max of uint64_t + 1, force to use double)
+ TEST_DOUBLE(fullPrecision, "-9223372036854775809", -9223372036854775809.0); // -2^63 - 1(min of int64_t + 1, force to use double)
+ TEST_DOUBLE(fullPrecision, "0.9868011474609375", 0.9868011474609375); // https://github.com/Tencent/rapidjson/issues/120
+ TEST_DOUBLE(fullPrecision, "123e34", 123e34); // Fast Path Cases In Disguise
+ TEST_DOUBLE(fullPrecision, "45913141877270640000.0", 45913141877270640000.0);
+ TEST_DOUBLE(fullPrecision, "2.2250738585072011e-308", 2.2250738585072011e-308); // http://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/
+ TEST_DOUBLE(fullPrecision, "1e-00011111111111", 0.0); // Issue #313
+ TEST_DOUBLE(fullPrecision, "-1e-00011111111111", -0.0);
+ TEST_DOUBLE(fullPrecision, "1e-214748363", 0.0); // Maximum supported negative exponent
+ TEST_DOUBLE(fullPrecision, "1e-214748364", 0.0);
+ TEST_DOUBLE(fullPrecision, "1e-21474836311", 0.0);
+ TEST_DOUBLE(fullPrecision, "1.00000000001e-2147483638", 0.0);
+ TEST_DOUBLE(fullPrecision, "0.017976931348623157e+310", 1.7976931348623157e+308); // Max double in another form
+ TEST_DOUBLE(fullPrecision, "128.74836467836484838364836483643636483648e-336", 0.0); // Issue #1251
+
+ // Since
+ // abs((2^-1022 - 2^-1074) - 2.2250738585072012e-308) = 3.109754131239141401123495768877590405345064751974375599... x 10^-324
+ // abs((2^-1022) - 2.2250738585072012e-308) = 1.830902327173324040642192159804623318305533274168872044... x 10 ^ -324
+ // So 2.2250738585072012e-308 should round to 2^-1022 = 2.2250738585072014e-308
+ TEST_DOUBLE(fullPrecision, "2.2250738585072012e-308", 2.2250738585072014e-308); // http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/
+
+ // More closer to normal/subnormal boundary
+ // boundary = 2^-1022 - 2^-1075 = 2.225073858507201136057409796709131975934819546351645648... x 10^-308
+ TEST_DOUBLE(fullPrecision, "2.22507385850720113605740979670913197593481954635164564e-308", 2.2250738585072009e-308);
+ TEST_DOUBLE(fullPrecision, "2.22507385850720113605740979670913197593481954635164565e-308", 2.2250738585072014e-308);
+
+ // 1.0 is in (1.0 - 2^-54, 1.0 + 2^-53)
+ // 1.0 - 2^-54 = 0.999999999999999944488848768742172978818416595458984375
+ TEST_DOUBLE(fullPrecision, "0.999999999999999944488848768742172978818416595458984375", 1.0); // round to even
+ TEST_DOUBLE(fullPrecision, "0.999999999999999944488848768742172978818416595458984374", 0.99999999999999989); // previous double
+ TEST_DOUBLE(fullPrecision, "0.999999999999999944488848768742172978818416595458984376", 1.0); // next double
+ // 1.0 + 2^-53 = 1.00000000000000011102230246251565404236316680908203125
+ TEST_DOUBLE(fullPrecision, "1.00000000000000011102230246251565404236316680908203125", 1.0); // round to even
+ 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';
+ for (int i = 1; i < 309; i++)
+ n1e308[i] = '0';
+ n1e308[309] = '\0';
+ TEST_DOUBLE(fullPrecision, n1e308, 1E308);
+ }
+
+ // 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
+
+ // 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 = (exp << 52) | uint64_t(r() & 0x000FFFFF) << 32;
+ u |= uint64_t(r());
+ internal::Double d = internal::Double(u);
+
+ char buffer[32];
+ *internal::dtoa(d.Value(), buffer) = '\0';
+
+ StringStream s(buffer);
+ ParseDoubleHandler h;
+ ASSERT_EQ(kParseErrorNone, reader.Parse<fullPrecision ? kParseFullPrecisionFlag : 0>(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: %s\n Actual: %.17g\nExpected: %.17g\n", buffer, h.actual_, d.Value());
+ }
+ else {
+ EXPECT_EQ(d.Sign(), a.Sign()); // for 0.0 != -0.0
+ EXPECT_DOUBLE_EQ(d.Value(), h.actual_);
+ }
+ }
+ }
+ }
+
+ // Issue #340
+ TEST_DOUBLE(fullPrecision, "7.450580596923828e-9", 7.450580596923828e-9);
+ {
+ internal::Double d(1.0);
+ for (int i = 0; i < 324; i++) {
+ char buffer[32];
+ *internal::dtoa(d.Value(), buffer) = '\0';
+
+ StringStream s(buffer);
+ ParseDoubleHandler h;
+ Reader reader;
+ ASSERT_EQ(kParseErrorNone, reader.Parse<fullPrecision ? kParseFullPrecisionFlag : 0>(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: %s\n Actual: %.17g\nExpected: %.17g\n", buffer, h.actual_, d.Value());
+ }
+ else {
+ EXPECT_EQ(d.Sign(), a.Sign()); // for 0.0 != -0.0
+ EXPECT_DOUBLE_EQ(d.Value(), h.actual_);
+ }
+
+
+ d = d.Value() * 0.5;
+ }
+ }
+
+ // Issue 1249
+ TEST_DOUBLE(fullPrecision, "0e100", 0.0);
+
+ // Issue 1251
+ TEST_DOUBLE(fullPrecision, "128.74836467836484838364836483643636483648e-336", 0.0);
+
+ // Issue 1256
+ TEST_DOUBLE(fullPrecision,
+ "6223372036854775296.1701512723685473547372536854755293372036854685477"
+ "529752233737201701512337200972013723685473123372036872036854236854737"
+ "247372368372367752975258547752975254729752547372368737201701512354737"
+ "83723677529752585477247372368372368547354737253685475529752",
+ 6223372036854775808.0);
+
+#if 0
+ // Test (length + exponent) overflow
+ TEST_DOUBLE(fullPrecision, "0e+2147483647", 0.0);
+ TEST_DOUBLE(fullPrecision, "0e-2147483648", 0.0);
+ TEST_DOUBLE(fullPrecision, "1e-2147483648", 0.0);
+ TEST_DOUBLE(fullPrecision, "0e+9223372036854775807", 0.0);
+ TEST_DOUBLE(fullPrecision, "0e-9223372036854775808", 0.0);
+#endif
+
+ if (fullPrecision)
+ {
+ TEST_DOUBLE(fullPrecision, "1e-325", 0.0);
+ TEST_DOUBLE(fullPrecision, "1e-324", 0.0);
+ TEST_DOUBLE(fullPrecision, "2e-324", 0.0);
+ TEST_DOUBLE(fullPrecision, "2.4703282292062327e-324", 0.0);
+ TEST_DOUBLE(fullPrecision, "2.4703282292062328e-324", 5e-324);
+ TEST_DOUBLE(fullPrecision, "2.48e-324",5e-324);
+ TEST_DOUBLE(fullPrecision, "2.5e-324", 5e-324);
+
+ // Slightly above max-normal
+ TEST_DOUBLE(fullPrecision, "1.7976931348623158e+308", 1.7976931348623158e+308);
+
+ TEST_DOUBLE(fullPrecision,
+ "17976931348623157081452742373170435679807056752584499659891747680315726"
+ "07800285387605895586327668781715404589535143824642343213268894641827684"
+ "67546703537516986049910576551282076245490090389328944075868508455133942"
+ "30458323690322294816580855933212334827479782620414472316873817718091929"
+ "9881250404026184124858368",
+ (std::numeric_limits<double>::max)());
+
+ TEST_DOUBLE(fullPrecision,
+ "243546080556034731077856379609316893158278902575447060151047"
+ "212703405344938119816206067372775299130836050315842578309818"
+ "316450894337978612745889730079163798234256495613858256849283"
+ "467066859489192118352020514036083287319232435355752493038825"
+ "828481044358810649108367633313557305310641892225870327827273"
+ "41408256.000000",
+ 2.4354608055603473e+307);
+ // 9007199254740991 * 2^971 (max normal)
+ TEST_DOUBLE(fullPrecision,
+ "1.797693134862315708145274237317043567980705675258449965989174768031572607800285"
+ "38760589558632766878171540458953514382464234321326889464182768467546703537516986"
+ "04991057655128207624549009038932894407586850845513394230458323690322294816580855"
+ "9332123348274797826204144723168738177180919299881250404026184124858368e+308",
+ 1.797693134862315708e+308 // 0x1.fffffffffffffp1023
+ );
+#if 0
+ // TODO:
+ // Should work at least in full-precision mode...
+ TEST_DOUBLE(fullPrecision,
+ "0.00000000000000000000000000000000000000000000000000000000000"
+ "0000000000000000000000000000000000000000000000000000000000000"
+ "0000000000000000000000000000000000000000000000000000000000000"
+ "0000000000000000000000000000000000000000000000000000000000000"
+ "0000000000000000000000000000000000000000000000000000000000000"
+ "0000000000000000000024703282292062327208828439643411068618252"
+ "9901307162382212792841250337753635104375932649918180817996189"
+ "8982823477228588654633283551779698981993873980053909390631503"
+ "5659515570226392290858392449105184435931802849936536152500319"
+ "3704576782492193656236698636584807570015857692699037063119282"
+ "7955855133292783433840935197801553124659726357957462276646527"
+ "2827220056374006485499977096599470454020828166226237857393450"
+ "7363390079677619305775067401763246736009689513405355374585166"
+ "6113422376667860416215968046191446729184030053005753084904876"
+ "5391711386591646239524912623653881879636239373280423891018672"
+ "3484976682350898633885879256283027559956575244555072551893136"
+ "9083625477918694866799496832404970582102851318545139621383772"
+ "2826145437693412532098591327667236328125",
+ 0.0);
+#endif
+ // 9007199254740991 * 2^-1074 = (2^53 - 1) * 2^-1074
+ TEST_DOUBLE(fullPrecision,
+ "4.450147717014402272114819593418263951869639092703291296046852219449644444042153"
+ "89103305904781627017582829831782607924221374017287738918929105531441481564124348"
+ "67599762821265346585071045737627442980259622449029037796981144446145705102663115"
+ "10031828794952795966823603998647925096578034214163701381261333311989876551545144"
+ "03152612538132666529513060001849177663286607555958373922409899478075565940981010"
+ "21612198814605258742579179000071675999344145086087205681577915435923018910334964"
+ "86942061405218289243144579760516365090360651414037721744226256159024466852576737"
+ "24464300755133324500796506867194913776884780053099639677097589658441378944337966"
+ "21993967316936280457084866613206797017728916080020698679408551343728867675409720"
+ "757232455434770912461317493580281734466552734375e-308",
+ 4.450147717014402272e-308 // 0x1.fffffffffffffp-1022
+ );
+ // 9007199254740990 * 2^-1074
+ TEST_DOUBLE(fullPrecision,
+ "4.450147717014401778049173752171719775300846224481918930987049605124880018456471"
+ "39035755177760751831052846195619008686241717547743167145836439860405887584484471"
+ "19639655002484083577939142623582164522087943959208000909794783876158397872163051"
+ "22622675229968408654350206725478309956546318828765627255022767720818849892988457"
+ "26333908582101604036318532842699932130356061901518261174396928478121372742040102"
+ "17446565569357687263889031732270082446958029584739170416643195242132750803227473"
+ "16608838720742955671061336566907126801014814608027120593609275183716632624844904"
+ "31985250929886016737037234388448352929102742708402644340627409931664203093081360"
+ "70794835812045179006047003875039546061891526346421705014598610179523165038319441"
+ "51446491086954182492263498716056346893310546875e-308",
+ 4.450147717014401778e-308 // 0x1.ffffffffffffep-1022
+ );
+ // half way between the two numbers above.
+ // round to nearest even.
+ TEST_DOUBLE(fullPrecision,
+ "4.450147717014402025081996672794991863585242658592605113516950912287262231249312"
+ "64069530541271189424317838013700808305231545782515453032382772695923684574304409"
+ "93619708911874715081505094180604803751173783204118519353387964161152051487413083"
+ "16327252012460602310586905362063117526562176521464664318142050516404363222266800"
+ "64743260560117135282915796422274554896821334728738317548403413978098469341510556"
+ "19529382191981473003234105366170879223151087335413188049110555339027884856781219"
+ "01775450062980622457102958163711745945687733011032421168917765671370549738710820"
+ "78224775842509670618916870627821633352993761380751142008862499795052791018709663"
+ "46394401564490729731565935244123171539810221213221201847003580761626016356864581"
+ "1358486831521563686919762403704226016998291015625e-308",
+ 4.450147717014401778e-308 // 0x1.ffffffffffffep-1022
+ );
+ TEST_DOUBLE(fullPrecision,
+ "4.450147717014402025081996672794991863585242658592605113516950912287262231249312"
+ "64069530541271189424317838013700808305231545782515453032382772695923684574304409"
+ "93619708911874715081505094180604803751173783204118519353387964161152051487413083"
+ "16327252012460602310586905362063117526562176521464664318142050516404363222266800"
+ "64743260560117135282915796422274554896821334728738317548403413978098469341510556"
+ "19529382191981473003234105366170879223151087335413188049110555339027884856781219"
+ "01775450062980622457102958163711745945687733011032421168917765671370549738710820"
+ "78224775842509670618916870627821633352993761380751142008862499795052791018709663"
+ "46394401564490729731565935244123171539810221213221201847003580761626016356864581"
+ "13584868315215636869197624037042260169982910156250000000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000e-308",
+ 4.450147717014401778e-308 // 0x1.ffffffffffffep-1022
+ );
+#if 0
+ // ... round up
+ // TODO:
+ // Should work at least in full-precision mode...
+ TEST_DOUBLE(fullPrecision,
+ "4.450147717014402025081996672794991863585242658592605113516950912287262231249312"
+ "64069530541271189424317838013700808305231545782515453032382772695923684574304409"
+ "93619708911874715081505094180604803751173783204118519353387964161152051487413083"
+ "16327252012460602310586905362063117526562176521464664318142050516404363222266800"
+ "64743260560117135282915796422274554896821334728738317548403413978098469341510556"
+ "19529382191981473003234105366170879223151087335413188049110555339027884856781219"
+ "01775450062980622457102958163711745945687733011032421168917765671370549738710820"
+ "78224775842509670618916870627821633352993761380751142008862499795052791018709663"
+ "46394401564490729731565935244123171539810221213221201847003580761626016356864581"
+ "13584868315215636869197624037042260169982910156250000000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000001e-308",
+ 4.450147717014402272e-308 // 0x1.fffffffffffffp-1022
+ );
+#endif
+ // ... round down
+ TEST_DOUBLE(fullPrecision,
+ "4.450147717014402025081996672794991863585242658592605113516950912287262231249312"
+ "64069530541271189424317838013700808305231545782515453032382772695923684574304409"
+ "93619708911874715081505094180604803751173783204118519353387964161152051487413083"
+ "16327252012460602310586905362063117526562176521464664318142050516404363222266800"
+ "64743260560117135282915796422274554896821334728738317548403413978098469341510556"
+ "19529382191981473003234105366170879223151087335413188049110555339027884856781219"
+ "01775450062980622457102958163711745945687733011032421168917765671370549738710820"
+ "78224775842509670618916870627821633352993761380751142008862499795052791018709663"
+ "46394401564490729731565935244123171539810221213221201847003580761626016356864581"
+ "13584868315215636869197624037042260169982910156249999999999999999999999999999999"
+ "99999999999999999999999999999999999999999999999999999999999999999999999999999999"
+ "99999999999999999999999999999999999999999999999999999999999999999999999999999999e-308",
+ 4.450147717014401778e-308 // 0x1.ffffffffffffep-1022
+ );
+ // Slightly below half way between max-normal and infinity.
+ // Should round down.
+ TEST_DOUBLE(fullPrecision,
+ "1.797693134862315807937289714053034150799341327100378269361737789804449682927647"
+ "50946649017977587207096330286416692887910946555547851940402630657488671505820681"
+ "90890200070838367627385484581771153176447573027006985557136695962284291481986083"
+ "49364752927190741684443655107043427115596995080930428801779041744977919999999999"
+ "99999999999999999999999999999999999999999999999999999999999999999999999999999999"
+ "99999999999999999999999999999999999999999999999999999999999999999999999999999999"
+ "99999999999999999999999999999999999999999999999999999999999999999999999999999999"
+ "99999999999999999999999999999999999999999999999999999999999999999999999999999999"
+ "99999999999999999999999999999999999999999999999999999999999999999999999999999999"
+ "99999999999999999999999999999999999999999999999999999999999999999999999999999999"
+ "99999999999999999999999999999999999999999999999999999999999999999999999999999999"
+ "99999999999999999999999999999999999999999999999999999999999999999999999999999999"
+ "99999999999999999999999999999999999999999999999999999999999999999999999999999999"
+ "99999999999999999999999999999999999999999999999999999999999999999999999999999999"
+ "99999999999999999999999999999999999999999999999999999999999999999999999999999999"
+ "99999999999999999999999999999999999999999999999999999999999999999999999999999999e+308",
+ 1.797693134862315708e+308 // 0x1.fffffffffffffp1023
+ );
+ }
+
+#undef TEST_DOUBLE
+}
+
+TEST(Reader, ParseNumber_NormalPrecisionDouble) {
+ TestParseDouble<false>();
+}
+
+TEST(Reader, ParseNumber_FullPrecisionDouble) {
+ TestParseDouble<true>();
+}
+
+TEST(Reader, ParseNumber_NormalPrecisionError) {
+ static unsigned count = 1000000;
+ Random r;
+
+ double ulpSum = 0.0;
+ double ulpMax = 0.0;
+ for (unsigned i = 0; i < count; i++) {
+ internal::Double e, a;
+ do {
+ // Need to call r() in two statements for cross-platform coherent sequence.
+ uint64_t u = uint64_t(r()) << 32;
+ u |= uint64_t(r());
+ e = u;
+ } while (e.IsNan() || e.IsInf() || !e.IsNormal());
+
+ char buffer[32];
+ *internal::dtoa(e.Value(), buffer) = '\0';
+
+ StringStream s(buffer);
+ ParseDoubleHandler h;
+ Reader reader;
+ ASSERT_EQ(kParseErrorNone, reader.Parse(s, h).Code());
+ EXPECT_EQ(1u, h.step_);
+
+ a = h.actual_;
+ uint64_t bias1 = e.ToBias();
+ uint64_t bias2 = a.ToBias();
+ double ulp = static_cast<double>(bias1 >= bias2 ? bias1 - bias2 : bias2 - bias1);
+ ulpMax = (std::max)(ulpMax, ulp);
+ ulpSum += ulp;
+ }
+ printf("ULP Average = %g, Max = %g \n", ulpSum / count, ulpMax);
+}
+
+template<bool fullPrecision>
+static void TestParseNumberError() {
+#define TEST_NUMBER_ERROR(errorCode, str, errorOffset, streamPos) \
+ { \
+ char buffer[2048]; \
+ ASSERT_LT(std::strlen(str), 2048u); \
+ sprintf(buffer, "%s", str); \
+ InsituStringStream s(buffer); \
+ BaseReaderHandler<> h; \
+ Reader reader; \
+ EXPECT_FALSE(reader.Parse<fullPrecision ? kParseFullPrecisionFlag : 0>(s, h)); \
+ EXPECT_EQ(errorCode, reader.GetParseErrorCode());\
+ EXPECT_EQ(errorOffset, reader.GetErrorOffset());\
+ EXPECT_EQ(streamPos, s.Tell());\
+ }
+
+ // Number too big to be stored in double.
+ {
+ char n1e309[311]; // '1' followed by 309 '0'
+ n1e309[0] = '1';
+ for (int i = 1; i < 310; i++)
+ n1e309[i] = '0';
+ n1e309[310] = '\0';
+ TEST_NUMBER_ERROR(kParseErrorNumberTooBig, n1e309, 0u, 310u);
+ }
+ TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "1e309", 0u, 5u);
+
+ // Miss fraction part in number.
+ TEST_NUMBER_ERROR(kParseErrorNumberMissFraction, "1.", 2u, 2u);
+ TEST_NUMBER_ERROR(kParseErrorNumberMissFraction, "1.a", 2u, 2u);
+
+ // Miss exponent in number.
+ TEST_NUMBER_ERROR(kParseErrorNumberMissExponent, "1e", 2u, 2u);
+ TEST_NUMBER_ERROR(kParseErrorNumberMissExponent, "1e_", 2u, 2u);
+
+ // Issue 849
+ TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "1.8e308", 0u, 7u);
+ TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "5e308", 0u, 5u);
+ TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "1e309", 0u, 5u);
+ TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "1.0e310", 0u, 7u);
+ TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "1.00e310", 0u, 8u);
+ TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "-1.8e308", 0u, 8u);
+ TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "-1e309", 0u, 6u);
+
+ // Issue 1253
+ TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "2e308", 0u, 5u);
+
+ // Issue 1259
+ TEST_NUMBER_ERROR(kParseErrorNumberTooBig,
+ "88474320368547737236837236775298547354737253685475547552933720368546854775297525"
+ "29337203685468547770151233720097201372368547312337203687203685423685123372036872"
+ "03685473724737236837236775297525854775297525472975254737236873720170151235473783"
+ "7236737247372368772473723683723456789012E66", 0u, 283u);
+
+#if 0
+ // Test (length + exponent) overflow
+ TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "1e+2147483647", 0u, 13u);
+ TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "1e+9223372036854775807", 0u, 22u);
+ TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "1e+10000", 0u, 8u);
+ TEST_NUMBER_ERROR(kParseErrorNumberTooBig, "1e+50000", 0u, 8u);
+#endif
+
+ // 9007199254740992 * 2^971 ("infinity")
+ TEST_NUMBER_ERROR(kParseErrorNumberTooBig,
+ "1.797693134862315907729305190789024733617976978942306572734300811577326758055009"
+ "63132708477322407536021120113879871393357658789768814416622492847430639474124377"
+ "76789342486548527630221960124609411945308295208500576883815068234246288147391311"
+ "0540827237163350510684586298239947245938479716304835356329624224137216e+308", 0u, 315u);
+
+ // TODO:
+ // These tests (currently) fail in normal-precision mode
+ if (fullPrecision)
+ {
+ // Half way between max-normal and infinity
+ // Should round to infinity in nearest-even mode.
+ TEST_NUMBER_ERROR(kParseErrorNumberTooBig,
+ "1.797693134862315807937289714053034150799341327100378269361737789804449682927647"
+ "50946649017977587207096330286416692887910946555547851940402630657488671505820681"
+ "90890200070838367627385484581771153176447573027006985557136695962284291481986083"
+ "49364752927190741684443655107043427115596995080930428801779041744977920000000000"
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000e+308", 0u, 1125u);
+ // ...round up
+ TEST_NUMBER_ERROR(kParseErrorNumberTooBig,
+ "1.797693134862315807937289714053034150799341327100378269361737789804449682927647"
+ "50946649017977587207096330286416692887910946555547851940402630657488671505820681"
+ "90890200070838367627385484581771153176447573027006985557136695962284291481986083"
+ "49364752927190741684443655107043427115596995080930428801779041744977920000000000"
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000001e+308", 0u, 1205u);
+ }
+
+ TEST_NUMBER_ERROR(kParseErrorNumberTooBig,
+ "10000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "0000000000000000000000000000000000000000000000000000000000000000000001", 0u, 310u);
+
+#undef TEST_NUMBER_ERROR
+}
+
+TEST(Reader, ParseNumberError_NormalPrecisionDouble) {
+ TestParseNumberError<false>();
+}
+
+TEST(Reader, ParseNumberError_FullPrecisionDouble) {
+ TestParseNumberError<true>();
+}
+
+template <typename Encoding>
+struct ParseStringHandler : BaseReaderHandler<Encoding, ParseStringHandler<Encoding> > {
+ ParseStringHandler() : str_(0), length_(0), copy_() {}
+ ~ParseStringHandler() { EXPECT_TRUE(str_ != 0); if (copy_) free(const_cast<typename Encoding::Ch*>(str_)); }
+
+ ParseStringHandler(const ParseStringHandler&);
+ ParseStringHandler& operator=(const ParseStringHandler&);
+
+ bool Default() { ADD_FAILURE(); return false; }
+ bool String(const typename Encoding::Ch* str, size_t length, bool copy) {
+ EXPECT_EQ(0, str_);
+ if (copy) {
+ str_ = static_cast<typename Encoding::Ch*>(malloc((length + 1) * sizeof(typename Encoding::Ch)));
+ memcpy(const_cast<typename Encoding::Ch*>(str_), str, (length + 1) * sizeof(typename Encoding::Ch));
+ }
+ else
+ str_ = str;
+ length_ = length;
+ copy_ = copy;
+ return true;
+ }
+
+ const typename Encoding::Ch* str_;
+ size_t length_;
+ bool copy_;
+};
+
+TEST(Reader, ParseString) {
+#define TEST_STRING(Encoding, e, x) \
+ { \
+ Encoding::Ch* buffer = StrDup(x); \
+ GenericInsituStringStream<Encoding> is(buffer); \
+ ParseStringHandler<Encoding> h; \
+ GenericReader<Encoding, Encoding> reader; \
+ reader.Parse<kParseInsituFlag | kParseValidateEncodingFlag>(is, h); \
+ EXPECT_EQ(0, StrCmp<Encoding::Ch>(e, h.str_)); \
+ EXPECT_EQ(StrLen(e), h.length_); \
+ free(buffer); \
+ GenericStringStream<Encoding> s(x); \
+ ParseStringHandler<Encoding> h2; \
+ GenericReader<Encoding, Encoding> reader2; \
+ reader2.Parse(s, h2); \
+ EXPECT_EQ(0, StrCmp<Encoding::Ch>(e, h2.str_)); \
+ EXPECT_EQ(StrLen(e), h2.length_); \
+ }
+
+ // String constant L"\xXX" can only specify character code in bytes, which is not endianness-neutral.
+ // And old compiler does not support u"" and U"" string literal. So here specify string literal by array of Ch.
+ // In addition, GCC 4.8 generates -Wnarrowing warnings when character code >= 128 are assigned to signed integer types.
+ // Therefore, utype is added for declaring unsigned array, and then cast it to Encoding::Ch.
+#define ARRAY(...) { __VA_ARGS__ }
+#define TEST_STRINGARRAY(Encoding, utype, array, x) \
+ { \
+ static const utype ue[] = array; \
+ static const Encoding::Ch* e = reinterpret_cast<const Encoding::Ch *>(&ue[0]); \
+ TEST_STRING(Encoding, e, x); \
+ }
+
+#define TEST_STRINGARRAY2(Encoding, utype, earray, xarray) \
+ { \
+ static const utype ue[] = earray; \
+ static const utype xe[] = xarray; \
+ static const Encoding::Ch* e = reinterpret_cast<const Encoding::Ch *>(&ue[0]); \
+ static const Encoding::Ch* x = reinterpret_cast<const Encoding::Ch *>(&xe[0]); \
+ TEST_STRING(Encoding, e, x); \
+ }
+
+ TEST_STRING(UTF8<>, "", "\"\"");
+ TEST_STRING(UTF8<>, "Hello", "\"Hello\"");
+ TEST_STRING(UTF8<>, "Hello\nWorld", "\"Hello\\nWorld\"");
+ TEST_STRING(UTF8<>, "\"\\/\b\f\n\r\t", "\"\\\"\\\\/\\b\\f\\n\\r\\t\"");
+ TEST_STRING(UTF8<>, "\x24", "\"\\u0024\""); // Dollar sign U+0024
+ TEST_STRING(UTF8<>, "\xC2\xA2", "\"\\u00A2\""); // Cents sign U+00A2
+ TEST_STRING(UTF8<>, "\xE2\x82\xAC", "\"\\u20AC\""); // Euro sign U+20AC
+ TEST_STRING(UTF8<>, "\xF0\x9D\x84\x9E", "\"\\uD834\\uDD1E\""); // G clef sign U+1D11E
+
+ // UTF16
+ TEST_STRING(UTF16<>, L"", L"\"\"");
+ TEST_STRING(UTF16<>, L"Hello", L"\"Hello\"");
+ TEST_STRING(UTF16<>, L"Hello\nWorld", L"\"Hello\\nWorld\"");
+ TEST_STRING(UTF16<>, L"\"\\/\b\f\n\r\t", L"\"\\\"\\\\/\\b\\f\\n\\r\\t\"");
+ TEST_STRINGARRAY(UTF16<>, wchar_t, ARRAY(0x0024, 0x0000), L"\"\\u0024\"");
+ TEST_STRINGARRAY(UTF16<>, wchar_t, ARRAY(0x00A2, 0x0000), L"\"\\u00A2\""); // Cents sign U+00A2
+ TEST_STRINGARRAY(UTF16<>, wchar_t, ARRAY(0x20AC, 0x0000), L"\"\\u20AC\""); // Euro sign U+20AC
+ TEST_STRINGARRAY(UTF16<>, wchar_t, ARRAY(0xD834, 0xDD1E, 0x0000), L"\"\\uD834\\uDD1E\""); // G clef sign U+1D11E
+
+ // UTF32
+ TEST_STRINGARRAY2(UTF32<>, unsigned, ARRAY('\0'), ARRAY('\"', '\"', '\0'));
+ TEST_STRINGARRAY2(UTF32<>, unsigned, ARRAY('H', 'e', 'l', 'l', 'o', '\0'), ARRAY('\"', 'H', 'e', 'l', 'l', 'o', '\"', '\0'));
+ TEST_STRINGARRAY2(UTF32<>, unsigned, ARRAY('H', 'e', 'l', 'l', 'o', '\n', 'W', 'o', 'r', 'l', 'd', '\0'), ARRAY('\"', 'H', 'e', 'l', 'l', 'o', '\\', 'n', 'W', 'o', 'r', 'l', 'd', '\"', '\0'));
+ TEST_STRINGARRAY2(UTF32<>, unsigned, ARRAY('\"', '\\', '/', '\b', '\f', '\n', '\r', '\t', '\0'), ARRAY('\"', '\\', '\"', '\\', '\\', '/', '\\', 'b', '\\', 'f', '\\', 'n', '\\', 'r', '\\', 't', '\"', '\0'));
+ TEST_STRINGARRAY2(UTF32<>, unsigned, ARRAY(0x00024, 0x0000), ARRAY('\"', '\\', 'u', '0', '0', '2', '4', '\"', '\0'));
+ TEST_STRINGARRAY2(UTF32<>, unsigned, ARRAY(0x000A2, 0x0000), ARRAY('\"', '\\', 'u', '0', '0', 'A', '2', '\"', '\0')); // Cents sign U+00A2
+ TEST_STRINGARRAY2(UTF32<>, unsigned, ARRAY(0x020AC, 0x0000), ARRAY('\"', '\\', 'u', '2', '0', 'A', 'C', '\"', '\0')); // Euro sign U+20AC
+ TEST_STRINGARRAY2(UTF32<>, unsigned, ARRAY(0x1D11E, 0x0000), ARRAY('\"', '\\', 'u', 'D', '8', '3', '4', '\\', 'u', 'D', 'D', '1', 'E', '\"', '\0')); // G clef sign U+1D11E
+
+#undef TEST_STRINGARRAY
+#undef ARRAY
+#undef TEST_STRING
+
+ // Support of null character in string
+ {
+ StringStream s("\"Hello\\u0000World\"");
+ const char e[] = "Hello\0World";
+ ParseStringHandler<UTF8<> > h;
+ Reader reader;
+ reader.Parse(s, h);
+ EXPECT_EQ(0, memcmp(e, h.str_, h.length_ + 1));
+ EXPECT_EQ(11u, h.length_);
+ }
+}
+
+TEST(Reader, ParseString_Transcoding) {
+ const char* x = "\"Hello\"";
+ const wchar_t* e = L"Hello";
+ GenericStringStream<UTF8<> > is(x);
+ GenericReader<UTF8<>, UTF16<> > reader;
+ ParseStringHandler<UTF16<> > h;
+ reader.Parse(is, h);
+ EXPECT_EQ(0, StrCmp<UTF16<>::Ch>(e, h.str_));
+ EXPECT_EQ(StrLen(e), h.length_);
+}
+
+TEST(Reader, ParseString_TranscodingWithValidation) {
+ const char* x = "\"Hello\"";
+ const wchar_t* e = L"Hello";
+ GenericStringStream<UTF8<> > is(x);
+ GenericReader<UTF8<>, UTF16<> > reader;
+ ParseStringHandler<UTF16<> > h;
+ reader.Parse<kParseValidateEncodingFlag>(is, h);
+ EXPECT_EQ(0, StrCmp<UTF16<>::Ch>(e, h.str_));
+ EXPECT_EQ(StrLen(e), h.length_);
+}
+
+TEST(Reader, ParseString_NonDestructive) {
+ StringStream s("\"Hello\\nWorld\"");
+ ParseStringHandler<UTF8<> > h;
+ Reader reader;
+ reader.Parse(s, h);
+ EXPECT_EQ(0, StrCmp("Hello\nWorld", h.str_));
+ EXPECT_EQ(11u, h.length_);
+}
+
+template <typename Encoding>
+ParseErrorCode TestString(const typename Encoding::Ch* str) {
+ GenericStringStream<Encoding> s(str);
+ BaseReaderHandler<Encoding> h;
+ GenericReader<Encoding, Encoding> reader;
+ reader.template Parse<kParseValidateEncodingFlag>(s, h);
+ return reader.GetParseErrorCode();
+}
+
+TEST(Reader, ParseString_Error) {
+#define TEST_STRING_ERROR(errorCode, str, errorOffset, streamPos)\
+{\
+ GenericStringStream<UTF8<> > s(str);\
+ BaseReaderHandler<UTF8<> > h;\
+ GenericReader<UTF8<> , UTF8<> > reader;\
+ reader.Parse<kParseValidateEncodingFlag>(s, h);\
+ EXPECT_EQ(errorCode, reader.GetParseErrorCode());\
+ EXPECT_EQ(errorOffset, reader.GetErrorOffset());\
+ EXPECT_EQ(streamPos, s.Tell());\
+}
+
+#define ARRAY(...) { __VA_ARGS__ }
+#define TEST_STRINGENCODING_ERROR(Encoding, TargetEncoding, utype, array) \
+ { \
+ static const utype ue[] = array; \
+ static const Encoding::Ch* e = reinterpret_cast<const Encoding::Ch *>(&ue[0]); \
+ EXPECT_EQ(kParseErrorStringInvalidEncoding, TestString<Encoding>(e));\
+ /* decode error */\
+ GenericStringStream<Encoding> s(e);\
+ BaseReaderHandler<TargetEncoding> h;\
+ GenericReader<Encoding, TargetEncoding> reader;\
+ reader.Parse(s, h);\
+ EXPECT_EQ(kParseErrorStringInvalidEncoding, reader.GetParseErrorCode());\
+ }
+
+ // Invalid escape character in string.
+ TEST_STRING_ERROR(kParseErrorStringEscapeInvalid, "[\"\\a\"]", 2u, 3u);
+
+ // Incorrect hex digit after \\u escape in string.
+ TEST_STRING_ERROR(kParseErrorStringUnicodeEscapeInvalidHex, "[\"\\uABCG\"]", 2u, 7u);
+
+ // Quotation in \\u escape in string (Issue #288)
+ TEST_STRING_ERROR(kParseErrorStringUnicodeEscapeInvalidHex, "[\"\\uaaa\"]", 2u, 7u);
+ TEST_STRING_ERROR(kParseErrorStringUnicodeEscapeInvalidHex, "[\"\\uD800\\uFFF\"]", 2u, 13u);
+
+ // The surrogate pair in string is invalid.
+ TEST_STRING_ERROR(kParseErrorStringUnicodeSurrogateInvalid, "[\"\\uD800X\"]", 2u, 8u);
+ TEST_STRING_ERROR(kParseErrorStringUnicodeSurrogateInvalid, "[\"\\uD800\\uFFFF\"]", 2u, 14u);
+
+ // Single low surrogate pair in string is invalid.
+ TEST_STRING_ERROR(kParseErrorStringUnicodeSurrogateInvalid, "[\"\\udc4d\"]", 2u, 8u);
+
+ // Missing a closing quotation mark in string.
+ TEST_STRING_ERROR(kParseErrorStringMissQuotationMark, "[\"Test]", 7u, 7u);
+
+ // http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
+
+ // 3 Malformed sequences
+
+ // 3.1 Unexpected continuation bytes
+ {
+ char e[] = { '[', '\"', 0, '\"', ']', '\0' };
+ for (unsigned char c = 0x80u; c <= 0xBFu; c++) {
+ e[2] = static_cast<char>(c);
+ ParseErrorCode error = TestString<UTF8<> >(e);
+ EXPECT_EQ(kParseErrorStringInvalidEncoding, error);
+ if (error != kParseErrorStringInvalidEncoding)
+ std::cout << static_cast<unsigned>(c) << std::endl;
+ }
+ }
+
+ // 3.2 Lonely start characters, 3.5 Impossible bytes
+ {
+ char e[] = { '[', '\"', 0, ' ', '\"', ']', '\0' };
+ for (unsigned c = 0xC0u; c <= 0xFFu; c++) {
+ e[2] = static_cast<char>(c);
+ unsigned streamPos;
+ if (c <= 0xC1u)
+ streamPos = 3; // 0xC0 - 0xC1
+ else if (c <= 0xDFu)
+ streamPos = 4; // 0xC2 - 0xDF
+ else if (c <= 0xEFu)
+ streamPos = 5; // 0xE0 - 0xEF
+ else if (c <= 0xF4u)
+ streamPos = 6; // 0xF0 - 0xF4
+ else
+ streamPos = 3; // 0xF5 - 0xFF
+ TEST_STRING_ERROR(kParseErrorStringInvalidEncoding, e, 2u, streamPos);
+ }
+ }
+
+ // 4 Overlong sequences
+
+ // 4.1 Examples of an overlong ASCII character
+ 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<>, 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<>, 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<>, 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(0x80u), '\"', ']', '\0'));
+ TEST_STRINGENCODING_ERROR(ASCII<>, UTF8<>, char, ARRAY('[', '\"', char(0x01u), '\"', ']', '\0'));
+ TEST_STRINGENCODING_ERROR(ASCII<>, UTF8<>, char, ARRAY('[', '\"', char(0x1Cu), '\"', ']', '\0'));
+
+#undef ARRAY
+#undef TEST_STRINGARRAY_ERROR
+}
+
+template <unsigned count>
+struct ParseArrayHandler : BaseReaderHandler<UTF8<>, ParseArrayHandler<count> > {
+ ParseArrayHandler() : step_(0) {}
+
+ bool Default() { ADD_FAILURE(); return false; }
+ bool Uint(unsigned i) { EXPECT_EQ(step_, i); step_++; return true; }
+ bool StartArray() { EXPECT_EQ(0u, step_); step_++; return true; }
+ bool EndArray(SizeType) { step_++; return true; }
+
+ unsigned step_;
+};
+
+TEST(Reader, ParseEmptyArray) {
+ char *json = StrDup("[ ] ");
+ InsituStringStream s(json);
+ ParseArrayHandler<0> h;
+ Reader reader;
+ reader.Parse(s, h);
+ EXPECT_EQ(2u, h.step_);
+ free(json);
+}
+
+TEST(Reader, ParseArray) {
+ char *json = StrDup("[1, 2, 3, 4]");
+ InsituStringStream s(json);
+ ParseArrayHandler<4> h;
+ Reader reader;
+ reader.Parse(s, h);
+ EXPECT_EQ(6u, h.step_);
+ free(json);
+}
+
+TEST(Reader, ParseArray_Error) {
+#define TEST_ARRAY_ERROR(errorCode, str, errorOffset) \
+ { \
+ unsigned streamPos = errorOffset; \
+ char buffer[1001]; \
+ strncpy(buffer, str, 1000); \
+ InsituStringStream s(buffer); \
+ BaseReaderHandler<> h; \
+ GenericReader<UTF8<>, UTF8<>, CrtAllocator> reader; \
+ EXPECT_FALSE(reader.Parse(s, h)); \
+ EXPECT_EQ(errorCode, reader.GetParseErrorCode());\
+ EXPECT_EQ(errorOffset, reader.GetErrorOffset());\
+ EXPECT_EQ(streamPos, s.Tell());\
+ }
+
+ // Missing a comma or ']' after an array element.
+ TEST_ARRAY_ERROR(kParseErrorArrayMissCommaOrSquareBracket, "[1", 2u);
+ TEST_ARRAY_ERROR(kParseErrorArrayMissCommaOrSquareBracket, "[1}", 2u);
+ TEST_ARRAY_ERROR(kParseErrorArrayMissCommaOrSquareBracket, "[1 2]", 3u);
+
+ // Array cannot have a trailing comma (without kParseTrailingCommasFlag);
+ // a value must follow a comma
+ TEST_ARRAY_ERROR(kParseErrorValueInvalid, "[1,]", 3u);
+
+#undef TEST_ARRAY_ERROR
+}
+
+struct ParseObjectHandler : BaseReaderHandler<UTF8<>, ParseObjectHandler> {
+ ParseObjectHandler() : step_(0) {}
+
+ bool Default() { ADD_FAILURE(); return false; }
+ bool Null() { EXPECT_EQ(8u, step_); step_++; return true; }
+ bool Bool(bool b) {
+ switch(step_) {
+ case 4: EXPECT_TRUE(b); step_++; return true;
+ case 6: EXPECT_FALSE(b); step_++; return true;
+ default: ADD_FAILURE(); return false;
+ }
+ }
+ bool Int(int i) {
+ switch(step_) {
+ case 10: EXPECT_EQ(123, i); step_++; return true;
+ case 15: EXPECT_EQ(1, i); step_++; return true;
+ case 16: EXPECT_EQ(2, i); step_++; return true;
+ case 17: EXPECT_EQ(3, i); step_++; return true;
+ default: ADD_FAILURE(); return false;
+ }
+ }
+ bool Uint(unsigned i) { return Int(static_cast<int>(i)); }
+ bool Double(double d) { EXPECT_EQ(12u, step_); EXPECT_DOUBLE_EQ(3.1416, d); step_++; return true; }
+ bool String(const char* str, size_t, bool) {
+ switch(step_) {
+ case 1: EXPECT_STREQ("hello", str); step_++; return true;
+ case 2: EXPECT_STREQ("world", str); step_++; return true;
+ case 3: EXPECT_STREQ("t", str); step_++; return true;
+ case 5: EXPECT_STREQ("f", str); step_++; return true;
+ case 7: EXPECT_STREQ("n", str); step_++; return true;
+ case 9: EXPECT_STREQ("i", str); step_++; return true;
+ case 11: EXPECT_STREQ("pi", str); step_++; return true;
+ case 13: EXPECT_STREQ("a", str); step_++; return true;
+ default: ADD_FAILURE(); return false;
+ }
+ }
+ bool StartObject() { EXPECT_EQ(0u, step_); step_++; return true; }
+ bool EndObject(SizeType memberCount) { EXPECT_EQ(19u, step_); EXPECT_EQ(7u, memberCount); step_++; return true; }
+ bool StartArray() { EXPECT_EQ(14u, step_); step_++; return true; }
+ bool EndArray(SizeType elementCount) { EXPECT_EQ(18u, step_); EXPECT_EQ(3u, elementCount); step_++; return true; }
+
+ unsigned step_;
+};
+
+TEST(Reader, ParseObject) {
+ const char* json = "{ \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3] } ";
+
+ // Insitu
+ {
+ char* json2 = StrDup(json);
+ InsituStringStream s(json2);
+ ParseObjectHandler h;
+ Reader reader;
+ reader.Parse<kParseInsituFlag>(s, h);
+ EXPECT_EQ(20u, h.step_);
+ free(json2);
+ }
+
+ // Normal
+ {
+ StringStream s(json);
+ ParseObjectHandler h;
+ Reader reader;
+ reader.Parse(s, h);
+ EXPECT_EQ(20u, h.step_);
+ }
+}
+
+struct ParseEmptyObjectHandler : BaseReaderHandler<UTF8<>, ParseEmptyObjectHandler> {
+ ParseEmptyObjectHandler() : step_(0) {}
+
+ bool Default() { ADD_FAILURE(); return false; }
+ bool StartObject() { EXPECT_EQ(0u, step_); step_++; return true; }
+ bool EndObject(SizeType) { EXPECT_EQ(1u, step_); step_++; return true; }
+
+ unsigned step_;
+};
+
+TEST(Reader, Parse_EmptyObject) {
+ StringStream s("{ } ");
+ ParseEmptyObjectHandler h;
+ Reader reader;
+ reader.Parse(s, h);
+ EXPECT_EQ(2u, h.step_);
+}
+
+struct ParseMultipleRootHandler : BaseReaderHandler<UTF8<>, ParseMultipleRootHandler> {
+ ParseMultipleRootHandler() : step_(0) {}
+
+ bool Default() { ADD_FAILURE(); return false; }
+ bool StartObject() { EXPECT_EQ(0u, step_); step_++; return true; }
+ bool EndObject(SizeType) { EXPECT_EQ(1u, step_); step_++; return true; }
+ bool StartArray() { EXPECT_EQ(2u, step_); step_++; return true; }
+ bool EndArray(SizeType) { EXPECT_EQ(3u, step_); step_++; return true; }
+
+ unsigned step_;
+};
+
+template <unsigned parseFlags>
+void TestMultipleRoot() {
+ StringStream s("{}[] a");
+ ParseMultipleRootHandler h;
+ Reader reader;
+ EXPECT_TRUE(reader.Parse<parseFlags>(s, h));
+ EXPECT_EQ(2u, h.step_);
+ EXPECT_TRUE(reader.Parse<parseFlags>(s, h));
+ EXPECT_EQ(4u, h.step_);
+ EXPECT_EQ(' ', s.Take());
+ EXPECT_EQ('a', s.Take());
+}
+
+TEST(Reader, Parse_MultipleRoot) {
+ TestMultipleRoot<kParseStopWhenDoneFlag>();
+}
+
+TEST(Reader, ParseIterative_MultipleRoot) {
+ TestMultipleRoot<kParseIterativeFlag | kParseStopWhenDoneFlag>();
+}
+
+template <unsigned parseFlags>
+void TestInsituMultipleRoot() {
+ char* buffer = strdup("{}[] a");
+ InsituStringStream s(buffer);
+ ParseMultipleRootHandler h;
+ Reader reader;
+ EXPECT_TRUE(reader.Parse<kParseInsituFlag | parseFlags>(s, h));
+ EXPECT_EQ(2u, h.step_);
+ EXPECT_TRUE(reader.Parse<kParseInsituFlag | parseFlags>(s, h));
+ EXPECT_EQ(4u, h.step_);
+ EXPECT_EQ(' ', s.Take());
+ EXPECT_EQ('a', s.Take());
+ free(buffer);
+}
+
+TEST(Reader, ParseInsitu_MultipleRoot) {
+ TestInsituMultipleRoot<kParseStopWhenDoneFlag>();
+}
+
+TEST(Reader, ParseInsituIterative_MultipleRoot) {
+ TestInsituMultipleRoot<kParseIterativeFlag | kParseStopWhenDoneFlag>();
+}
+
+#define TEST_ERROR(errorCode, str, errorOffset) \
+ { \
+ unsigned streamPos = errorOffset; \
+ char buffer[1001]; \
+ strncpy(buffer, str, 1000); \
+ InsituStringStream s(buffer); \
+ BaseReaderHandler<> h; \
+ Reader reader; \
+ EXPECT_FALSE(reader.Parse(s, h)); \
+ EXPECT_EQ(errorCode, reader.GetParseErrorCode());\
+ EXPECT_EQ(errorOffset, reader.GetErrorOffset());\
+ EXPECT_EQ(streamPos, s.Tell());\
+ }
+
+TEST(Reader, ParseDocument_Error) {
+ // The document is empty.
+ TEST_ERROR(kParseErrorDocumentEmpty, "", 0u);
+ TEST_ERROR(kParseErrorDocumentEmpty, " ", 1u);
+ TEST_ERROR(kParseErrorDocumentEmpty, " \n", 2u);
+
+ // The document root must not follow by other values.
+ TEST_ERROR(kParseErrorDocumentRootNotSingular, "[] 0", 3u);
+ TEST_ERROR(kParseErrorDocumentRootNotSingular, "{} 0", 3u);
+ TEST_ERROR(kParseErrorDocumentRootNotSingular, "null []", 5u);
+ TEST_ERROR(kParseErrorDocumentRootNotSingular, "0 {}", 2u);
+}
+
+TEST(Reader, ParseValue_Error) {
+ // Invalid value.
+ TEST_ERROR(kParseErrorValueInvalid, "nulL", 3u);
+ TEST_ERROR(kParseErrorValueInvalid, "truE", 3u);
+ TEST_ERROR(kParseErrorValueInvalid, "falsE", 4u);
+ TEST_ERROR(kParseErrorValueInvalid, "a]", 0u);
+ TEST_ERROR(kParseErrorValueInvalid, ".1", 0u);
+}
+
+TEST(Reader, ParseObject_Error) {
+ // Missing a name for object member.
+ TEST_ERROR(kParseErrorObjectMissName, "{1}", 1u);
+ TEST_ERROR(kParseErrorObjectMissName, "{:1}", 1u);
+ TEST_ERROR(kParseErrorObjectMissName, "{null:1}", 1u);
+ TEST_ERROR(kParseErrorObjectMissName, "{true:1}", 1u);
+ TEST_ERROR(kParseErrorObjectMissName, "{false:1}", 1u);
+ TEST_ERROR(kParseErrorObjectMissName, "{1:1}", 1u);
+ TEST_ERROR(kParseErrorObjectMissName, "{[]:1}", 1u);
+ TEST_ERROR(kParseErrorObjectMissName, "{{}:1}", 1u);
+ TEST_ERROR(kParseErrorObjectMissName, "{xyz:1}", 1u);
+
+ // Missing a colon after a name of object member.
+ TEST_ERROR(kParseErrorObjectMissColon, "{\"a\" 1}", 5u);
+ TEST_ERROR(kParseErrorObjectMissColon, "{\"a\",1}", 4u);
+
+ // Must be a comma or '}' after an object member
+ TEST_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, "{\"a\":1]", 6u);
+
+ // Object cannot have a trailing comma (without kParseTrailingCommasFlag);
+ // an object member name must follow a comma
+ TEST_ERROR(kParseErrorObjectMissName, "{\"a\":1,}", 7u);
+
+ // This tests that MemoryStream is checking the length in Peek().
+ {
+ MemoryStream ms("{\"a\"", 1);
+ BaseReaderHandler<> h;
+ Reader reader;
+ EXPECT_FALSE(reader.Parse<kParseStopWhenDoneFlag>(ms, h));
+ EXPECT_EQ(kParseErrorObjectMissName, reader.GetParseErrorCode());
+ }
+}
+
+#undef TEST_ERROR
+
+TEST(Reader, SkipWhitespace) {
+ StringStream ss(" A \t\tB\n \n\nC\r\r \rD \t\n\r E");
+ const char* expected = "ABCDE";
+ for (size_t i = 0; i < 5; i++) {
+ SkipWhitespace(ss);
+ EXPECT_EQ(expected[i], ss.Take());
+ }
+}
+
+// Test implementing a stream without copy stream optimization.
+// Clone from GenericStringStream except that copy constructor is disabled.
+template <typename Encoding>
+class CustomStringStream {
+public:
+ typedef typename Encoding::Ch Ch;
+
+ CustomStringStream(const Ch *src) : src_(src), head_(src) {}
+
+ Ch Peek() const { return *src_; }
+ Ch Take() { return *src_++; }
+ size_t Tell() const { return static_cast<size_t>(src_ - head_); }
+
+ Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
+ void Put(Ch) { RAPIDJSON_ASSERT(false); }
+ void Flush() { RAPIDJSON_ASSERT(false); }
+ size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
+
+private:
+ // Prohibit copy constructor & assignment operator.
+ CustomStringStream(const CustomStringStream&);
+ CustomStringStream& operator=(const CustomStringStream&);
+
+ const Ch* src_; //!< Current read position.
+ const Ch* head_; //!< Original head of the string.
+};
+
+// If the following code is compiled, it should generate compilation error as predicted.
+// Because CustomStringStream<> is not copyable via making copy constructor private.
+#if 0
+namespace rapidjson {
+
+template <typename Encoding>
+struct StreamTraits<CustomStringStream<Encoding> > {
+ enum { copyOptimization = 1 };
+};
+
+} // namespace rapidjson
+#endif
+
+TEST(Reader, CustomStringStream) {
+ const char* json = "{ \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3] } ";
+ CustomStringStream<UTF8<char> > s(json);
+ ParseObjectHandler h;
+ Reader reader;
+ reader.Parse(s, h);
+ EXPECT_EQ(20u, h.step_);
+}
+
+#include <sstream>
+
+class IStreamWrapper {
+public:
+ typedef char Ch;
+
+ IStreamWrapper(std::istream& is) : is_(is) {}
+
+ Ch Peek() const {
+ int c = is_.peek();
+ return c == std::char_traits<char>::eof() ? '\0' : static_cast<Ch>(c);
+ }
+
+ Ch Take() {
+ int c = is_.get();
+ return c == std::char_traits<char>::eof() ? '\0' : static_cast<Ch>(c);
+ }
+
+ size_t Tell() const { return static_cast<size_t>(is_.tellg()); }
+
+ Ch* PutBegin() { assert(false); return 0; }
+ void Put(Ch) { assert(false); }
+ void Flush() { assert(false); }
+ size_t PutEnd(Ch*) { assert(false); return 0; }
+
+private:
+ IStreamWrapper(const IStreamWrapper&);
+ IStreamWrapper& operator=(const IStreamWrapper&);
+
+ std::istream& is_;
+};
+
+class WIStreamWrapper {
+public:
+ typedef wchar_t Ch;
+
+ WIStreamWrapper(std::wistream& is) : is_(is) {}
+
+ Ch Peek() const {
+ unsigned c = is_.peek();
+ return c == std::char_traits<wchar_t>::eof() ? Ch('\0') : static_cast<Ch>(c);
+ }
+
+ Ch Take() {
+ unsigned c = is_.get();
+ return c == std::char_traits<wchar_t>::eof() ? Ch('\0') : static_cast<Ch>(c);
+ }
+
+ size_t Tell() const { return static_cast<size_t>(is_.tellg()); }
+
+ Ch* PutBegin() { assert(false); return 0; }
+ void Put(Ch) { assert(false); }
+ void Flush() { assert(false); }
+ size_t PutEnd(Ch*) { assert(false); return 0; }
+
+private:
+ WIStreamWrapper(const WIStreamWrapper&);
+ WIStreamWrapper& operator=(const WIStreamWrapper&);
+
+ std::wistream& is_;
+};
+
+TEST(Reader, Parse_IStreamWrapper_StringStream) {
+ const char* json = "[1,2,3,4]";
+
+ std::stringstream ss(json);
+ IStreamWrapper is(ss);
+
+ Reader reader;
+ ParseArrayHandler<4> h;
+ reader.Parse(is, h);
+ EXPECT_FALSE(reader.HasParseError());
+}
+
+// Test iterative parsing.
+
+#define TESTERRORHANDLING(text, errorCode, offset)\
+{\
+ unsigned streamPos = offset; \
+ StringStream json(text); \
+ BaseReaderHandler<> handler; \
+ Reader reader; \
+ reader.Parse<kParseIterativeFlag>(json, handler); \
+ EXPECT_TRUE(reader.HasParseError()); \
+ EXPECT_EQ(errorCode, reader.GetParseErrorCode()); \
+ EXPECT_EQ(offset, reader.GetErrorOffset()); \
+ EXPECT_EQ(streamPos, json.Tell()); \
+}
+
+TEST(Reader, IterativeParsing_ErrorHandling) {
+ TESTERRORHANDLING("{\"a\": a}", kParseErrorValueInvalid, 6u);
+
+ TESTERRORHANDLING("", kParseErrorDocumentEmpty, 0u);
+ TESTERRORHANDLING("{}{}", kParseErrorDocumentRootNotSingular, 2u);
+
+ TESTERRORHANDLING("{1}", kParseErrorObjectMissName, 1u);
+ TESTERRORHANDLING("{\"a\", 1}", kParseErrorObjectMissColon, 4u);
+ TESTERRORHANDLING("{\"a\"}", kParseErrorObjectMissColon, 4u);
+ TESTERRORHANDLING("{\"a\": 1", kParseErrorObjectMissCommaOrCurlyBracket, 7u);
+ TESTERRORHANDLING("[1 2 3]", kParseErrorArrayMissCommaOrSquareBracket, 3u);
+ TESTERRORHANDLING("{\"a: 1", kParseErrorStringMissQuotationMark, 6u);
+ TESTERRORHANDLING("{\"a\":}", kParseErrorValueInvalid, 5u);
+ TESTERRORHANDLING("{\"a\":]", kParseErrorValueInvalid, 5u);
+ TESTERRORHANDLING("[1,2,}", kParseErrorValueInvalid, 5u);
+ TESTERRORHANDLING("[}]", kParseErrorValueInvalid, 1u);
+ TESTERRORHANDLING("[,]", kParseErrorValueInvalid, 1u);
+ TESTERRORHANDLING("[1,,]", kParseErrorValueInvalid, 3u);
+
+ // Trailing commas are not allowed without kParseTrailingCommasFlag
+ TESTERRORHANDLING("{\"a\": 1,}", kParseErrorObjectMissName, 8u);
+ TESTERRORHANDLING("[1,2,3,]", kParseErrorValueInvalid, 7u);
+
+ // Any JSON value can be a valid root element in RFC7159.
+ TESTERRORHANDLING("\"ab", kParseErrorStringMissQuotationMark, 3u);
+ 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<typename Encoding = UTF8<> >
+struct IterativeParsingReaderHandler {
+ typedef typename Encoding::Ch Ch;
+
+ const static uint32_t LOG_NULL = 0x10000000;
+ const static uint32_t LOG_BOOL = 0x20000000;
+ const static uint32_t LOG_INT = 0x30000000;
+ const static uint32_t LOG_UINT = 0x40000000;
+ const static uint32_t LOG_INT64 = 0x50000000;
+ const static uint32_t LOG_UINT64 = 0x60000000;
+ const static uint32_t LOG_DOUBLE = 0x70000000;
+ const static uint32_t LOG_STRING = 0x80000000;
+ const static uint32_t LOG_STARTOBJECT = 0x90000000;
+ const static uint32_t LOG_KEY = 0xA0000000;
+ const static uint32_t LOG_ENDOBJECT = 0xB0000000;
+ const static uint32_t LOG_STARTARRAY = 0xC0000000;
+ const static uint32_t LOG_ENDARRAY = 0xD0000000;
+
+ const static size_t LogCapacity = 256;
+ uint32_t Logs[LogCapacity];
+ size_t LogCount;
+
+ IterativeParsingReaderHandler() : LogCount(0) {
+ }
+
+ bool Null() { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_NULL; return true; }
+
+ bool Bool(bool) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_BOOL; return true; }
+
+ bool Int(int) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_INT; return true; }
+
+ bool Uint(unsigned) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_INT; return true; }
+
+ bool Int64(int64_t) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_INT64; return true; }
+
+ bool Uint64(uint64_t) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_UINT64; return true; }
+
+ bool Double(double) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_DOUBLE; return true; }
+
+ bool RawNumber(const Ch*, SizeType, bool) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_STRING; return true; }
+
+ bool String(const Ch*, SizeType, bool) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_STRING; return true; }
+
+ bool StartObject() { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_STARTOBJECT; return true; }
+
+ bool Key (const Ch*, SizeType, bool) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_KEY; return true; }
+
+ bool EndObject(SizeType c) {
+ RAPIDJSON_ASSERT(LogCount < LogCapacity);
+ RAPIDJSON_ASSERT((static_cast<uint32_t>(c) & 0xF0000000) == 0);
+ Logs[LogCount++] = LOG_ENDOBJECT | static_cast<uint32_t>(c);
+ return true;
+ }
+
+ bool StartArray() { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_STARTARRAY; return true; }
+
+ bool EndArray(SizeType c) {
+ RAPIDJSON_ASSERT(LogCount < LogCapacity);
+ RAPIDJSON_ASSERT((static_cast<uint32_t>(c) & 0xF0000000) == 0);
+ Logs[LogCount++] = LOG_ENDARRAY | static_cast<uint32_t>(c);
+ return true;
+ }
+};
+
+TEST(Reader, IterativeParsing_General) {
+ {
+ StringStream is("[1, {\"k\": [1, 2]}, null, false, true, \"string\", 1.2]");
+ Reader reader;
+ IterativeParsingReaderHandler<> handler;
+
+ ParseResult r = reader.Parse<kParseIterativeFlag>(is, handler);
+
+ EXPECT_FALSE(r.IsError());
+ EXPECT_FALSE(reader.HasParseError());
+
+ uint32_t e[] = {
+ handler.LOG_STARTARRAY,
+ handler.LOG_INT,
+ handler.LOG_STARTOBJECT,
+ handler.LOG_KEY,
+ handler.LOG_STARTARRAY,
+ handler.LOG_INT,
+ handler.LOG_INT,
+ handler.LOG_ENDARRAY | 2,
+ handler.LOG_ENDOBJECT | 1,
+ handler.LOG_NULL,
+ handler.LOG_BOOL,
+ handler.LOG_BOOL,
+ handler.LOG_STRING,
+ handler.LOG_DOUBLE,
+ handler.LOG_ENDARRAY | 7
+ };
+
+ EXPECT_EQ(sizeof(e) / sizeof(int), handler.LogCount);
+
+ for (size_t i = 0; i < handler.LogCount; ++i) {
+ EXPECT_EQ(e[i], handler.Logs[i]) << "i = " << i;
+ }
+ }
+}
+
+TEST(Reader, IterativeParsing_Count) {
+ {
+ StringStream is("[{}, {\"k\": 1}, [1], []]");
+ Reader reader;
+ IterativeParsingReaderHandler<> handler;
+
+ ParseResult r = reader.Parse<kParseIterativeFlag>(is, handler);
+
+ EXPECT_FALSE(r.IsError());
+ EXPECT_FALSE(reader.HasParseError());
+
+ uint32_t e[] = {
+ handler.LOG_STARTARRAY,
+ handler.LOG_STARTOBJECT,
+ handler.LOG_ENDOBJECT | 0,
+ handler.LOG_STARTOBJECT,
+ handler.LOG_KEY,
+ handler.LOG_INT,
+ handler.LOG_ENDOBJECT | 1,
+ handler.LOG_STARTARRAY,
+ handler.LOG_INT,
+ handler.LOG_ENDARRAY | 1,
+ handler.LOG_STARTARRAY,
+ handler.LOG_ENDARRAY | 0,
+ handler.LOG_ENDARRAY | 4
+ };
+
+ EXPECT_EQ(sizeof(e) / sizeof(int), handler.LogCount);
+
+ for (size_t i = 0; i < handler.LogCount; ++i) {
+ EXPECT_EQ(e[i], handler.Logs[i]) << "i = " << i;
+ }
+ }
+}
+
+TEST(Reader, IterativePullParsing_General) {
+ {
+ IterativeParsingReaderHandler<> handler;
+ uint32_t e[] = {
+ handler.LOG_STARTARRAY,
+ handler.LOG_INT,
+ handler.LOG_STARTOBJECT,
+ handler.LOG_KEY,
+ handler.LOG_STARTARRAY,
+ handler.LOG_INT,
+ handler.LOG_INT,
+ handler.LOG_ENDARRAY | 2,
+ handler.LOG_ENDOBJECT | 1,
+ handler.LOG_NULL,
+ handler.LOG_BOOL,
+ handler.LOG_BOOL,
+ handler.LOG_STRING,
+ handler.LOG_DOUBLE,
+ handler.LOG_ENDARRAY | 7
+ };
+
+ StringStream is("[1, {\"k\": [1, 2]}, null, false, true, \"string\", 1.2]");
+ Reader reader;
+
+ reader.IterativeParseInit();
+ while (!reader.IterativeParseComplete()) {
+ size_t oldLogCount = handler.LogCount;
+ EXPECT_TRUE(oldLogCount < sizeof(e) / sizeof(int)) << "overrun";
+
+ EXPECT_TRUE(reader.IterativeParseNext<kParseDefaultFlags>(is, handler)) << "parse fail";
+ EXPECT_EQ(handler.LogCount, oldLogCount + 1) << "handler should be invoked exactly once each time";
+ EXPECT_EQ(e[oldLogCount], handler.Logs[oldLogCount]) << "wrong event returned";
+ }
+
+ EXPECT_FALSE(reader.HasParseError());
+ EXPECT_EQ(sizeof(e) / sizeof(int), handler.LogCount) << "handler invoked wrong number of times";
+
+ // The handler should not be invoked when the JSON has been fully read, but it should not fail
+ size_t oldLogCount = handler.LogCount;
+ EXPECT_TRUE(reader.IterativeParseNext<kParseDefaultFlags>(is, handler)) << "parse-next past complete is allowed";
+ EXPECT_EQ(handler.LogCount, oldLogCount) << "parse-next past complete should not invoke handler";
+ EXPECT_FALSE(reader.HasParseError()) << "parse-next past complete should not generate parse error";
+ }
+}
+
+// Test iterative parsing on kParseErrorTermination.
+struct HandlerTerminateAtStartObject : public IterativeParsingReaderHandler<> {
+ bool StartObject() { return false; }
+};
+
+struct HandlerTerminateAtStartArray : public IterativeParsingReaderHandler<> {
+ bool StartArray() { return false; }
+};
+
+struct HandlerTerminateAtEndObject : public IterativeParsingReaderHandler<> {
+ bool EndObject(SizeType) { return false; }
+};
+
+struct HandlerTerminateAtEndArray : public IterativeParsingReaderHandler<> {
+ bool EndArray(SizeType) { return false; }
+};
+
+TEST(Reader, IterativeParsing_ShortCircuit) {
+ {
+ HandlerTerminateAtStartObject handler;
+ Reader reader;
+ StringStream is("[1, {}]");
+
+ ParseResult r = reader.Parse<kParseIterativeFlag>(is, handler);
+
+ EXPECT_TRUE(reader.HasParseError());
+ EXPECT_EQ(kParseErrorTermination, r.Code());
+ EXPECT_EQ(4u, r.Offset());
+ }
+
+ {
+ HandlerTerminateAtStartArray handler;
+ Reader reader;
+ StringStream is("{\"a\": []}");
+
+ ParseResult r = reader.Parse<kParseIterativeFlag>(is, handler);
+
+ EXPECT_TRUE(reader.HasParseError());
+ EXPECT_EQ(kParseErrorTermination, r.Code());
+ EXPECT_EQ(6u, r.Offset());
+ }
+
+ {
+ HandlerTerminateAtEndObject handler;
+ Reader reader;
+ StringStream is("[1, {}]");
+
+ ParseResult r = reader.Parse<kParseIterativeFlag>(is, handler);
+
+ EXPECT_TRUE(reader.HasParseError());
+ EXPECT_EQ(kParseErrorTermination, r.Code());
+ EXPECT_EQ(5u, r.Offset());
+ }
+
+ {
+ HandlerTerminateAtEndArray handler;
+ Reader reader;
+ StringStream is("{\"a\": []}");
+
+ ParseResult r = reader.Parse<kParseIterativeFlag>(is, handler);
+
+ EXPECT_TRUE(reader.HasParseError());
+ EXPECT_EQ(kParseErrorTermination, r.Code());
+ EXPECT_EQ(7u, r.Offset());
+ }
+}
+
+// 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 <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 RawNumber(const char*, SizeType, bool) { return e != 7; }
+ bool String(const char*, SizeType, bool) { return e != 8; }
+ bool StartObject() { return e != 9; }
+ bool Key(const char*, SizeType, bool) { return e != 10; }
+ bool EndObject(SizeType) { return e != 11; }
+ bool StartArray() { return e != 12; }
+ bool EndArray(SizeType) { return e != 13; }
+};
+
+#define TEST_TERMINATION(e, json)\
+{\
+ Reader reader;\
+ TerminateHandler<e> 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]");
+ // RawNumber() is never called
+ TEST_TERMINATION(8, "[\"a\"");
+ TEST_TERMINATION(9, "[{");
+ TEST_TERMINATION(10, "[{\"a\"");
+ TEST_TERMINATION(11, "[{}");
+ TEST_TERMINATION(11, "[{\"a\":1}"); // non-empty object
+ TEST_TERMINATION(12, "{\"a\":[");
+ TEST_TERMINATION(13, "{\"a\":[]");
+ TEST_TERMINATION(13, "{\"a\":[1]"); // non-empty array
+}
+
+TEST(Reader, ParseComments) {
+ const char* json =
+ "// Here is a one-line comment.\n"
+ "{// And here's another one\n"
+ " /*And here's an in-line one.*/\"hello\" : \"world\","
+ " \"t\" :/* And one with '*' symbol*/true ,"
+ "/* A multiline comment\n"
+ " goes here*/"
+ " \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3]"
+ "}/*And the last one to be sure */";
+
+ StringStream s(json);
+ ParseObjectHandler h;
+ Reader reader;
+ EXPECT_TRUE(reader.Parse<kParseCommentsFlag>(s, h));
+ EXPECT_EQ(20u, h.step_);
+}
+
+TEST(Reader, ParseEmptyInlineComment) {
+ const char* json = "{/**/\"hello\" : \"world\", \"t\" : true, \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3] }";
+
+ StringStream s(json);
+ ParseObjectHandler h;
+ Reader reader;
+ EXPECT_TRUE(reader.Parse<kParseCommentsFlag>(s, h));
+ EXPECT_EQ(20u, h.step_);
+}
+
+TEST(Reader, ParseEmptyOnelineComment) {
+ const char* json = "{//\n\"hello\" : \"world\", \"t\" : true, \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3] }";
+
+ StringStream s(json);
+ ParseObjectHandler h;
+ Reader reader;
+ EXPECT_TRUE(reader.Parse<kParseCommentsFlag>(s, h));
+ EXPECT_EQ(20u, h.step_);
+}
+
+TEST(Reader, ParseMultipleCommentsInARow) {
+ const char* json =
+ "{/* first comment *//* second */\n"
+ "/* third */ /*fourth*/// last one\n"
+ "\"hello\" : \"world\", \"t\" : true, \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3] }";
+
+ StringStream s(json);
+ ParseObjectHandler h;
+ Reader reader;
+ EXPECT_TRUE(reader.Parse<kParseCommentsFlag>(s, h));
+ EXPECT_EQ(20u, h.step_);
+}
+
+TEST(Reader, InlineCommentsAreDisabledByDefault) {
+ {
+ const char* json = "{/* Inline comment. */\"hello\" : \"world\", \"t\" : true, \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3] }";
+
+ StringStream s(json);
+ ParseObjectHandler h;
+ Reader reader;
+ EXPECT_FALSE(reader.Parse<kParseDefaultFlags>(s, h));
+ }
+
+ {
+ const char* json =
+ "{\"hello\" : /* Multiline comment starts here\n"
+ " continues here\n"
+ " and ends here */\"world\", \"t\" :true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3] }";
+
+ StringStream s(json);
+ ParseObjectHandler h;
+ Reader reader;
+ EXPECT_FALSE(reader.Parse<kParseDefaultFlags>(s, h));
+ }
+}
+
+TEST(Reader, OnelineCommentsAreDisabledByDefault) {
+ const char* json = "{// One-line comment\n\"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3] }";
+
+ StringStream s(json);
+ ParseObjectHandler h;
+ Reader reader;
+ EXPECT_FALSE(reader.Parse<kParseDefaultFlags>(s, h));
+}
+
+TEST(Reader, EofAfterOneLineComment) {
+ const char* json = "{\"hello\" : \"world\" // EOF is here -->\0 \n}";
+
+ StringStream s(json);
+ ParseObjectHandler h;
+ Reader reader;
+ EXPECT_FALSE(reader.Parse<kParseCommentsFlag>(s, h));
+ EXPECT_EQ(kParseErrorObjectMissCommaOrCurlyBracket, reader.GetParseErrorCode());
+}
+
+TEST(Reader, IncompleteMultilineComment) {
+ const char* json = "{\"hello\" : \"world\" /* EOF is here -->\0 */}";
+
+ StringStream s(json);
+ ParseObjectHandler h;
+ Reader reader;
+ EXPECT_FALSE(reader.Parse<kParseCommentsFlag>(s, h));
+ EXPECT_EQ(kParseErrorUnspecificSyntaxError, reader.GetParseErrorCode());
+}
+
+TEST(Reader, IncompleteMultilineComment2) {
+ const char* json = "{\"hello\" : \"world\" /* *\0 */}";
+
+ StringStream s(json);
+ ParseObjectHandler h;
+ Reader reader;
+ EXPECT_FALSE(reader.Parse<kParseCommentsFlag>(s, h));
+ EXPECT_EQ(kParseErrorUnspecificSyntaxError, reader.GetParseErrorCode());
+}
+
+TEST(Reader, UnrecognizedComment) {
+ const char* json = "{\"hello\" : \"world\" /! }";
+
+ StringStream s(json);
+ ParseObjectHandler h;
+ Reader reader;
+ EXPECT_FALSE(reader.Parse<kParseCommentsFlag>(s, h));
+ EXPECT_EQ(kParseErrorUnspecificSyntaxError, reader.GetParseErrorCode());
+}
+
+struct NumbersAsStringsHandler {
+ bool Null() { return true; }
+ bool Bool(bool) { return true; }
+ bool Int(int) { return true; }
+ bool Uint(unsigned) { return true; }
+ bool Int64(int64_t) { return true; }
+ bool Uint64(uint64_t) { return true; }
+ bool Double(double) { return true; }
+ // 'str' is not null-terminated
+ bool RawNumber(const char* str, SizeType length, bool) {
+ EXPECT_TRUE(str != 0);
+ EXPECT_TRUE(expected_len_ == length);
+ EXPECT_TRUE(strncmp(str, expected_, length) == 0);
+ return true;
+ }
+ bool String(const char*, SizeType, bool) { return true; }
+ bool StartObject() { return true; }
+ bool Key(const char*, SizeType, bool) { return true; }
+ bool EndObject(SizeType) { return true; }
+ bool StartArray() { return true; }
+ bool EndArray(SizeType) { return true; }
+
+ NumbersAsStringsHandler(const char* expected)
+ : expected_(expected)
+ , expected_len_(strlen(expected)) {}
+
+ const char* expected_;
+ size_t expected_len_;
+};
+
+TEST(Reader, NumbersAsStrings) {
+ {
+ const char* json = "{ \"pi\": 3.1416 } ";
+ StringStream s(json);
+ NumbersAsStringsHandler h("3.1416");
+ Reader reader;
+ EXPECT_TRUE(reader.Parse<kParseNumbersAsStringsFlag>(s, h));
+ }
+ {
+ char* json = StrDup("{ \"pi\": 3.1416 } ");
+ InsituStringStream s(json);
+ NumbersAsStringsHandler h("3.1416");
+ Reader reader;
+ EXPECT_TRUE(reader.Parse<kParseInsituFlag|kParseNumbersAsStringsFlag>(s, h));
+ free(json);
+ }
+ {
+ const char* json = "{ \"gigabyte\": 1.0e9 } ";
+ StringStream s(json);
+ NumbersAsStringsHandler h("1.0e9");
+ Reader reader;
+ EXPECT_TRUE(reader.Parse<kParseNumbersAsStringsFlag>(s, h));
+ }
+ {
+ char* json = StrDup("{ \"gigabyte\": 1.0e9 } ");
+ InsituStringStream s(json);
+ NumbersAsStringsHandler h("1.0e9");
+ Reader reader;
+ EXPECT_TRUE(reader.Parse<kParseInsituFlag|kParseNumbersAsStringsFlag>(s, h));
+ free(json);
+ }
+ {
+ const char* json = "{ \"pi\": 314.159e-2 } ";
+ StringStream s(json);
+ NumbersAsStringsHandler h("314.159e-2");
+ Reader reader;
+ EXPECT_TRUE(reader.Parse<kParseNumbersAsStringsFlag>(s, h));
+ }
+ {
+ char* json = StrDup("{ \"gigabyte\": 314.159e-2 } ");
+ InsituStringStream s(json);
+ NumbersAsStringsHandler h("314.159e-2");
+ Reader reader;
+ EXPECT_TRUE(reader.Parse<kParseInsituFlag|kParseNumbersAsStringsFlag>(s, h));
+ free(json);
+ }
+ {
+ const char* json = "{ \"negative\": -1.54321 } ";
+ StringStream s(json);
+ NumbersAsStringsHandler h("-1.54321");
+ Reader reader;
+ EXPECT_TRUE(reader.Parse<kParseNumbersAsStringsFlag>(s, h));
+ }
+ {
+ char* json = StrDup("{ \"negative\": -1.54321 } ");
+ InsituStringStream s(json);
+ NumbersAsStringsHandler h("-1.54321");
+ Reader reader;
+ EXPECT_TRUE(reader.Parse<kParseInsituFlag|kParseNumbersAsStringsFlag>(s, h));
+ free(json);
+ }
+ {
+ const char* json = "{ \"pi\": 314.159e-2 } ";
+ std::stringstream ss(json);
+ IStreamWrapper s(ss);
+ NumbersAsStringsHandler h("314.159e-2");
+ Reader reader;
+ EXPECT_TRUE(reader.Parse<kParseNumbersAsStringsFlag>(s, h));
+ }
+ {
+ char n1e319[321]; // '1' followed by 319 '0'
+ n1e319[0] = '1';
+ for (int i = 1; i < 320; i++)
+ n1e319[i] = '0';
+ n1e319[320] = '\0';
+ StringStream s(n1e319);
+ NumbersAsStringsHandler h(n1e319);
+ Reader reader;
+ EXPECT_TRUE(reader.Parse<kParseNumbersAsStringsFlag>(s, h));
+ }
+}
+
+struct NumbersAsStringsHandlerWChar_t {
+ bool Null() { return true; }
+ bool Bool(bool) { return true; }
+ bool Int(int) { return true; }
+ bool Uint(unsigned) { return true; }
+ bool Int64(int64_t) { return true; }
+ bool Uint64(uint64_t) { return true; }
+ bool Double(double) { return true; }
+ // 'str' is not null-terminated
+ bool RawNumber(const wchar_t* str, SizeType length, bool) {
+ EXPECT_TRUE(str != 0);
+ EXPECT_TRUE(expected_len_ == length);
+ EXPECT_TRUE(wcsncmp(str, expected_, length) == 0);
+ return true;
+ }
+ bool String(const wchar_t*, SizeType, bool) { return true; }
+ bool StartObject() { return true; }
+ bool Key(const wchar_t*, SizeType, bool) { return true; }
+ bool EndObject(SizeType) { return true; }
+ bool StartArray() { return true; }
+ bool EndArray(SizeType) { return true; }
+
+ NumbersAsStringsHandlerWChar_t(const wchar_t* expected)
+ : expected_(expected)
+ , expected_len_(wcslen(expected)) {}
+
+ const wchar_t* expected_;
+ size_t expected_len_;
+};
+
+TEST(Reader, NumbersAsStringsWChar_t) {
+ {
+ const wchar_t* json = L"{ \"pi\": 3.1416 } ";
+ GenericStringStream<UTF16<> > s(json);
+ NumbersAsStringsHandlerWChar_t h(L"3.1416");
+ GenericReader<UTF16<>, UTF16<> > reader;
+ EXPECT_TRUE(reader.Parse<kParseNumbersAsStringsFlag>(s, h));
+ }
+ {
+ wchar_t* json = StrDup(L"{ \"pi\": 3.1416 } ");
+ GenericInsituStringStream<UTF16<> > s(json);
+ NumbersAsStringsHandlerWChar_t h(L"3.1416");
+ GenericReader<UTF16<>, UTF16<> > reader;
+ EXPECT_TRUE(reader.Parse<kParseInsituFlag | kParseNumbersAsStringsFlag>(s, h));
+ free(json);
+ }
+ {
+ const wchar_t* json = L"{ \"gigabyte\": 1.0e9 } ";
+ GenericStringStream<UTF16<> > s(json);
+ NumbersAsStringsHandlerWChar_t h(L"1.0e9");
+ GenericReader<UTF16<>, UTF16<> > reader;
+ EXPECT_TRUE(reader.Parse<kParseNumbersAsStringsFlag>(s, h));
+ }
+ {
+ wchar_t* json = StrDup(L"{ \"gigabyte\": 1.0e9 } ");
+ GenericInsituStringStream<UTF16<> > s(json);
+ NumbersAsStringsHandlerWChar_t h(L"1.0e9");
+ GenericReader<UTF16<>, UTF16<> > reader;
+ EXPECT_TRUE(reader.Parse<kParseInsituFlag | kParseNumbersAsStringsFlag>(s, h));
+ free(json);
+ }
+ {
+ const wchar_t* json = L"{ \"pi\": 314.159e-2 } ";
+ GenericStringStream<UTF16<> > s(json);
+ NumbersAsStringsHandlerWChar_t h(L"314.159e-2");
+ GenericReader<UTF16<>, UTF16<> > reader;
+ EXPECT_TRUE(reader.Parse<kParseNumbersAsStringsFlag>(s, h));
+ }
+ {
+ wchar_t* json = StrDup(L"{ \"gigabyte\": 314.159e-2 } ");
+ GenericInsituStringStream<UTF16<> > s(json);
+ NumbersAsStringsHandlerWChar_t h(L"314.159e-2");
+ GenericReader<UTF16<>, UTF16<> > reader;
+ EXPECT_TRUE(reader.Parse<kParseInsituFlag | kParseNumbersAsStringsFlag>(s, h));
+ free(json);
+ }
+ {
+ const wchar_t* json = L"{ \"negative\": -1.54321 } ";
+ GenericStringStream<UTF16<> > s(json);
+ NumbersAsStringsHandlerWChar_t h(L"-1.54321");
+ GenericReader<UTF16<>, UTF16<> > reader;
+ EXPECT_TRUE(reader.Parse<kParseNumbersAsStringsFlag>(s, h));
+ }
+ {
+ wchar_t* json = StrDup(L"{ \"negative\": -1.54321 } ");
+ GenericInsituStringStream<UTF16<> > s(json);
+ NumbersAsStringsHandlerWChar_t h(L"-1.54321");
+ GenericReader<UTF16<>, UTF16<> > reader;
+ EXPECT_TRUE(reader.Parse<kParseInsituFlag | kParseNumbersAsStringsFlag>(s, h));
+ free(json);
+ }
+ {
+ const wchar_t* json = L"{ \"pi\": 314.159e-2 } ";
+ std::wstringstream ss(json);
+ WIStreamWrapper s(ss);
+ NumbersAsStringsHandlerWChar_t h(L"314.159e-2");
+ GenericReader<UTF16<>, UTF16<> > reader;
+ EXPECT_TRUE(reader.Parse<kParseNumbersAsStringsFlag>(s, h));
+ }
+ {
+ wchar_t n1e319[321]; // '1' followed by 319 '0'
+ n1e319[0] = L'1';
+ for(int i = 1; i < 320; i++)
+ n1e319[i] = L'0';
+ n1e319[320] = L'\0';
+ GenericStringStream<UTF16<> > s(n1e319);
+ NumbersAsStringsHandlerWChar_t h(n1e319);
+ GenericReader<UTF16<>, UTF16<> > reader;
+ EXPECT_TRUE(reader.Parse<kParseNumbersAsStringsFlag>(s, h));
+ }
+}
+
+template <unsigned extraFlags>
+void TestTrailingCommas() {
+ {
+ StringStream s("[1,2,3,]");
+ ParseArrayHandler<3> h;
+ Reader reader;
+ EXPECT_TRUE(reader.Parse<extraFlags|kParseTrailingCommasFlag>(s, h));
+ EXPECT_EQ(5u, h.step_);
+ }
+ {
+ const char* json = "{ \"hello\" : \"world\", \"t\" : true , \"f\" : false,"
+ "\"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3],}";
+ StringStream s(json);
+ ParseObjectHandler h;
+ Reader reader;
+ EXPECT_TRUE(reader.Parse<extraFlags|kParseTrailingCommasFlag>(s, h));
+ EXPECT_EQ(20u, h.step_);
+ }
+ {
+ // whitespace around trailing commas
+ const char* json = "{ \"hello\" : \"world\", \"t\" : true , \"f\" : false,"
+ "\"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3\n,\n]\n,\n} ";
+ StringStream s(json);
+ ParseObjectHandler h;
+ Reader reader;
+ EXPECT_TRUE(reader.Parse<extraFlags|kParseTrailingCommasFlag>(s, h));
+ EXPECT_EQ(20u, h.step_);
+ }
+ {
+ // comments around trailing commas
+ const char* json = "{ \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null,"
+ "\"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3/*test*/,/*test*/]/*test*/,/*test*/}";
+ StringStream s(json);
+ ParseObjectHandler h;
+ Reader reader;
+ EXPECT_TRUE(reader.Parse<extraFlags|kParseTrailingCommasFlag|kParseCommentsFlag>(s, h));
+ EXPECT_EQ(20u, h.step_);
+ }
+}
+
+TEST(Reader, TrailingCommas) {
+ TestTrailingCommas<kParseNoFlags>();
+}
+
+TEST(Reader, TrailingCommasIterative) {
+ TestTrailingCommas<kParseIterativeFlag>();
+}
+
+template <unsigned extraFlags>
+void TestMultipleTrailingCommaErrors() {
+ // only a single trailing comma is allowed.
+ {
+ StringStream s("[1,2,3,,]");
+ ParseArrayHandler<3> h;
+ Reader reader;
+ ParseResult r = reader.Parse<extraFlags|kParseTrailingCommasFlag>(s, h);
+ EXPECT_TRUE(reader.HasParseError());
+ EXPECT_EQ(kParseErrorValueInvalid, r.Code());
+ EXPECT_EQ(7u, r.Offset());
+ }
+ {
+ const char* json = "{ \"hello\" : \"world\", \"t\" : true , \"f\" : false,"
+ "\"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3,],,}";
+ StringStream s(json);
+ ParseObjectHandler h;
+ Reader reader;
+ ParseResult r = reader.Parse<extraFlags|kParseTrailingCommasFlag>(s, h);
+ EXPECT_TRUE(reader.HasParseError());
+ EXPECT_EQ(kParseErrorObjectMissName, r.Code());
+ EXPECT_EQ(95u, r.Offset());
+ }
+}
+
+TEST(Reader, MultipleTrailingCommaErrors) {
+ TestMultipleTrailingCommaErrors<kParseNoFlags>();
+}
+
+TEST(Reader, MultipleTrailingCommaErrorsIterative) {
+ TestMultipleTrailingCommaErrors<kParseIterativeFlag>();
+}
+
+template <unsigned extraFlags>
+void TestEmptyExceptForCommaErrors() {
+ // not allowed even with trailing commas enabled; the
+ // trailing comma must follow a value.
+ {
+ StringStream s("[,]");
+ ParseArrayHandler<3> h;
+ Reader reader;
+ ParseResult r = reader.Parse<extraFlags|kParseTrailingCommasFlag>(s, h);
+ EXPECT_TRUE(reader.HasParseError());
+ EXPECT_EQ(kParseErrorValueInvalid, r.Code());
+ EXPECT_EQ(1u, r.Offset());
+ }
+ {
+ StringStream s("{,}");
+ ParseObjectHandler h;
+ Reader reader;
+ ParseResult r = reader.Parse<extraFlags|kParseTrailingCommasFlag>(s, h);
+ EXPECT_TRUE(reader.HasParseError());
+ EXPECT_EQ(kParseErrorObjectMissName, r.Code());
+ EXPECT_EQ(1u, r.Offset());
+ }
+}
+
+TEST(Reader, EmptyExceptForCommaErrors) {
+ TestEmptyExceptForCommaErrors<kParseNoFlags>();
+}
+
+TEST(Reader, EmptyExceptForCommaErrorsIterative) {
+ TestEmptyExceptForCommaErrors<kParseIterativeFlag>();
+}
+
+template <unsigned extraFlags>
+void TestTrailingCommaHandlerTermination() {
+ {
+ HandlerTerminateAtEndArray h;
+ Reader reader;
+ StringStream s("[1,2,3,]");
+ ParseResult r = reader.Parse<extraFlags|kParseTrailingCommasFlag>(s, h);
+ EXPECT_TRUE(reader.HasParseError());
+ EXPECT_EQ(kParseErrorTermination, r.Code());
+ EXPECT_EQ(7u, r.Offset());
+ }
+ {
+ HandlerTerminateAtEndObject h;
+ Reader reader;
+ StringStream s("{\"t\": true, \"f\": false,}");
+ ParseResult r = reader.Parse<extraFlags|kParseTrailingCommasFlag>(s, h);
+ EXPECT_TRUE(reader.HasParseError());
+ EXPECT_EQ(kParseErrorTermination, r.Code());
+ EXPECT_EQ(23u, r.Offset());
+ }
+}
+
+TEST(Reader, TrailingCommaHandlerTermination) {
+ TestTrailingCommaHandlerTermination<kParseNoFlags>();
+}
+
+TEST(Reader, TrailingCommaHandlerTerminationIterative) {
+ TestTrailingCommaHandlerTermination<kParseIterativeFlag>();
+}
+
+TEST(Reader, ParseNanAndInfinity) {
+#define TEST_NAN_INF(str, x) \
+ { \
+ { \
+ StringStream s(str); \
+ ParseDoubleHandler h; \
+ Reader reader; \
+ ASSERT_EQ(kParseErrorNone, reader.Parse<kParseNanAndInfFlag>(s, h).Code()); \
+ EXPECT_EQ(1u, h.step_); \
+ internal::Double e(x), a(h.actual_); \
+ EXPECT_EQ(e.IsNan(), a.IsNan()); \
+ EXPECT_EQ(e.IsInf(), a.IsInf()); \
+ if (!e.IsNan()) \
+ EXPECT_EQ(e.Sign(), a.Sign()); \
+ } \
+ { \
+ const char* json = "{ \"naninfdouble\": " str " } "; \
+ StringStream s(json); \
+ NumbersAsStringsHandler h(str); \
+ Reader reader; \
+ EXPECT_TRUE(reader.Parse<kParseNumbersAsStringsFlag|kParseNanAndInfFlag>(s, h)); \
+ } \
+ { \
+ char* json = StrDup("{ \"naninfdouble\": " str " } "); \
+ InsituStringStream s(json); \
+ NumbersAsStringsHandler h(str); \
+ Reader reader; \
+ EXPECT_TRUE(reader.Parse<kParseInsituFlag|kParseNumbersAsStringsFlag|kParseNanAndInfFlag>(s, h)); \
+ free(json); \
+ } \
+ }
+#define TEST_NAN_INF_ERROR(errorCode, str, errorOffset) \
+ { \
+ unsigned streamPos = errorOffset; \
+ char buffer[1001]; \
+ strncpy(buffer, str, 1000); \
+ InsituStringStream s(buffer); \
+ BaseReaderHandler<> h; \
+ Reader reader; \
+ EXPECT_FALSE(reader.Parse<kParseNanAndInfFlag>(s, h)); \
+ EXPECT_EQ(errorCode, reader.GetParseErrorCode());\
+ EXPECT_EQ(errorOffset, reader.GetErrorOffset());\
+ EXPECT_EQ(streamPos, s.Tell());\
+ }
+
+ double nan = std::numeric_limits<double>::quiet_NaN();
+ double inf = std::numeric_limits<double>::infinity();
+
+ TEST_NAN_INF("NaN", nan);
+ TEST_NAN_INF("-NaN", nan);
+ TEST_NAN_INF("Inf", inf);
+ TEST_NAN_INF("Infinity", inf);
+ TEST_NAN_INF("-Inf", -inf);
+ TEST_NAN_INF("-Infinity", -inf);
+ TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "NInf", 1u);
+ TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "NaInf", 2u);
+ TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "INan", 1u);
+ TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "InNan", 2u);
+ TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "nan", 1u);
+ TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "-nan", 1u);
+ TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "NAN", 1u);
+ TEST_NAN_INF_ERROR(kParseErrorValueInvalid, "-Infinty", 6u);
+
+#undef TEST_NAN_INF_ERROR
+#undef TEST_NAN_INF
+}
+
+TEST(Reader, EscapedApostrophe) {
+ const char json[] = " { \"foo\": \"bar\\'buzz\" } ";
+
+ BaseReaderHandler<> h;
+
+ {
+ StringStream s(json);
+ Reader reader;
+ ParseResult r = reader.Parse<kParseNoFlags>(s, h);
+ EXPECT_TRUE(reader.HasParseError());
+ EXPECT_EQ(kParseErrorStringEscapeInvalid, r.Code());
+ EXPECT_EQ(14u, r.Offset());
+ }
+
+ {
+ StringStream s(json);
+ Reader reader;
+ ParseResult r = reader.Parse<kParseEscapedApostropheFlag>(s, h);
+ EXPECT_FALSE(reader.HasParseError());
+ EXPECT_EQ(kParseErrorNone, r.Code());
+ EXPECT_EQ(0u, r.Offset());
+ }
+}
+
+RAPIDJSON_DIAG_POP
diff --git a/src/s3select/rapidjson/test/unittest/regextest.cpp b/src/s3select/rapidjson/test/unittest/regextest.cpp
new file mode 100644
index 000000000..a288622bc
--- /dev/null
+++ b/src/s3select/rapidjson/test/unittest/regextest.cpp
@@ -0,0 +1,639 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#include "unittest.h"
+#include "rapidjson/internal/regex.h"
+
+using namespace rapidjson::internal;
+
+TEST(Regex, Single) {
+ Regex re("a");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("a"));
+ EXPECT_FALSE(rs.Match(""));
+ EXPECT_FALSE(rs.Match("b"));
+}
+
+TEST(Regex, Concatenation) {
+ Regex re("abc");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("abc"));
+ EXPECT_FALSE(rs.Match(""));
+ EXPECT_FALSE(rs.Match("a"));
+ EXPECT_FALSE(rs.Match("b"));
+ EXPECT_FALSE(rs.Match("ab"));
+ EXPECT_FALSE(rs.Match("abcd"));
+}
+
+TEST(Regex, Alternation1) {
+ Regex re("abab|abbb");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("abab"));
+ EXPECT_TRUE(rs.Match("abbb"));
+ EXPECT_FALSE(rs.Match(""));
+ EXPECT_FALSE(rs.Match("ab"));
+ EXPECT_FALSE(rs.Match("ababa"));
+ EXPECT_FALSE(rs.Match("abb"));
+ EXPECT_FALSE(rs.Match("abbbb"));
+}
+
+TEST(Regex, Alternation2) {
+ Regex re("a|b|c");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("a"));
+ EXPECT_TRUE(rs.Match("b"));
+ EXPECT_TRUE(rs.Match("c"));
+ EXPECT_FALSE(rs.Match(""));
+ EXPECT_FALSE(rs.Match("aa"));
+ EXPECT_FALSE(rs.Match("ab"));
+}
+
+TEST(Regex, Parenthesis1) {
+ Regex re("(ab)c");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("abc"));
+ EXPECT_FALSE(rs.Match(""));
+ EXPECT_FALSE(rs.Match("a"));
+ EXPECT_FALSE(rs.Match("b"));
+ EXPECT_FALSE(rs.Match("ab"));
+ EXPECT_FALSE(rs.Match("abcd"));
+}
+
+TEST(Regex, Parenthesis2) {
+ Regex re("a(bc)");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("abc"));
+ EXPECT_FALSE(rs.Match(""));
+ EXPECT_FALSE(rs.Match("a"));
+ EXPECT_FALSE(rs.Match("b"));
+ EXPECT_FALSE(rs.Match("ab"));
+ EXPECT_FALSE(rs.Match("abcd"));
+}
+
+TEST(Regex, Parenthesis3) {
+ Regex re("(a|b)(c|d)");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("ac"));
+ EXPECT_TRUE(rs.Match("ad"));
+ EXPECT_TRUE(rs.Match("bc"));
+ EXPECT_TRUE(rs.Match("bd"));
+ EXPECT_FALSE(rs.Match(""));
+ EXPECT_FALSE(rs.Match("ab"));
+ EXPECT_FALSE(rs.Match("cd"));
+}
+
+TEST(Regex, ZeroOrOne1) {
+ Regex re("a?");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match(""));
+ EXPECT_TRUE(rs.Match("a"));
+ EXPECT_FALSE(rs.Match("aa"));
+}
+
+TEST(Regex, ZeroOrOne2) {
+ Regex re("a?b");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("b"));
+ EXPECT_TRUE(rs.Match("ab"));
+ EXPECT_FALSE(rs.Match("a"));
+ EXPECT_FALSE(rs.Match("aa"));
+ EXPECT_FALSE(rs.Match("bb"));
+ EXPECT_FALSE(rs.Match("ba"));
+}
+
+TEST(Regex, ZeroOrOne3) {
+ Regex re("ab?");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("a"));
+ EXPECT_TRUE(rs.Match("ab"));
+ EXPECT_FALSE(rs.Match("b"));
+ EXPECT_FALSE(rs.Match("aa"));
+ EXPECT_FALSE(rs.Match("bb"));
+ EXPECT_FALSE(rs.Match("ba"));
+}
+
+TEST(Regex, ZeroOrOne4) {
+ Regex re("a?b?");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match(""));
+ EXPECT_TRUE(rs.Match("a"));
+ EXPECT_TRUE(rs.Match("b"));
+ EXPECT_TRUE(rs.Match("ab"));
+ EXPECT_FALSE(rs.Match("aa"));
+ EXPECT_FALSE(rs.Match("bb"));
+ EXPECT_FALSE(rs.Match("ba"));
+ EXPECT_FALSE(rs.Match("abc"));
+}
+
+TEST(Regex, ZeroOrOne5) {
+ Regex re("a(ab)?b");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("ab"));
+ EXPECT_TRUE(rs.Match("aabb"));
+ EXPECT_FALSE(rs.Match("aab"));
+ EXPECT_FALSE(rs.Match("abb"));
+}
+
+TEST(Regex, ZeroOrMore1) {
+ Regex re("a*");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match(""));
+ EXPECT_TRUE(rs.Match("a"));
+ EXPECT_TRUE(rs.Match("aa"));
+ EXPECT_FALSE(rs.Match("b"));
+ EXPECT_FALSE(rs.Match("ab"));
+}
+
+TEST(Regex, ZeroOrMore2) {
+ Regex re("a*b");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("b"));
+ EXPECT_TRUE(rs.Match("ab"));
+ EXPECT_TRUE(rs.Match("aab"));
+ EXPECT_FALSE(rs.Match(""));
+ EXPECT_FALSE(rs.Match("bb"));
+}
+
+TEST(Regex, ZeroOrMore3) {
+ Regex re("a*b*");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match(""));
+ EXPECT_TRUE(rs.Match("a"));
+ EXPECT_TRUE(rs.Match("aa"));
+ EXPECT_TRUE(rs.Match("b"));
+ EXPECT_TRUE(rs.Match("bb"));
+ EXPECT_TRUE(rs.Match("ab"));
+ EXPECT_TRUE(rs.Match("aabb"));
+ EXPECT_FALSE(rs.Match("ba"));
+}
+
+TEST(Regex, ZeroOrMore4) {
+ Regex re("a(ab)*b");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("ab"));
+ EXPECT_TRUE(rs.Match("aabb"));
+ EXPECT_TRUE(rs.Match("aababb"));
+ EXPECT_FALSE(rs.Match(""));
+ EXPECT_FALSE(rs.Match("aa"));
+}
+
+TEST(Regex, OneOrMore1) {
+ Regex re("a+");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("a"));
+ EXPECT_TRUE(rs.Match("aa"));
+ EXPECT_FALSE(rs.Match(""));
+ EXPECT_FALSE(rs.Match("b"));
+ EXPECT_FALSE(rs.Match("ab"));
+}
+
+TEST(Regex, OneOrMore2) {
+ Regex re("a+b");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("ab"));
+ EXPECT_TRUE(rs.Match("aab"));
+ EXPECT_FALSE(rs.Match(""));
+ EXPECT_FALSE(rs.Match("b"));
+}
+
+TEST(Regex, OneOrMore3) {
+ Regex re("a+b+");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("ab"));
+ EXPECT_TRUE(rs.Match("aab"));
+ EXPECT_TRUE(rs.Match("abb"));
+ EXPECT_TRUE(rs.Match("aabb"));
+ EXPECT_FALSE(rs.Match(""));
+ EXPECT_FALSE(rs.Match("b"));
+ EXPECT_FALSE(rs.Match("ba"));
+}
+
+TEST(Regex, OneOrMore4) {
+ Regex re("a(ab)+b");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("aabb"));
+ EXPECT_TRUE(rs.Match("aababb"));
+ EXPECT_FALSE(rs.Match(""));
+ EXPECT_FALSE(rs.Match("ab"));
+}
+
+TEST(Regex, QuantifierExact1) {
+ Regex re("ab{3}c");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("abbbc"));
+ EXPECT_FALSE(rs.Match("ac"));
+ EXPECT_FALSE(rs.Match("abc"));
+ EXPECT_FALSE(rs.Match("abbc"));
+ EXPECT_FALSE(rs.Match("abbbbc"));
+}
+
+TEST(Regex, QuantifierExact2) {
+ Regex re("a(bc){3}d");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("abcbcbcd"));
+ EXPECT_FALSE(rs.Match("ad"));
+ EXPECT_FALSE(rs.Match("abcd"));
+ EXPECT_FALSE(rs.Match("abcbcd"));
+ EXPECT_FALSE(rs.Match("abcbcbcbcd"));
+}
+
+TEST(Regex, QuantifierExact3) {
+ Regex re("a(b|c){3}d");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("abbbd"));
+ EXPECT_TRUE(rs.Match("acccd"));
+ EXPECT_TRUE(rs.Match("abcbd"));
+ EXPECT_FALSE(rs.Match("ad"));
+ EXPECT_FALSE(rs.Match("abbd"));
+ EXPECT_FALSE(rs.Match("accccd"));
+ EXPECT_FALSE(rs.Match("abbbbd"));
+}
+
+TEST(Regex, QuantifierMin1) {
+ Regex re("ab{3,}c");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("abbbc"));
+ EXPECT_TRUE(rs.Match("abbbbc"));
+ EXPECT_TRUE(rs.Match("abbbbbc"));
+ EXPECT_FALSE(rs.Match("ac"));
+ EXPECT_FALSE(rs.Match("abc"));
+ EXPECT_FALSE(rs.Match("abbc"));
+}
+
+TEST(Regex, QuantifierMin2) {
+ Regex re("a(bc){3,}d");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("abcbcbcd"));
+ EXPECT_TRUE(rs.Match("abcbcbcbcd"));
+ EXPECT_FALSE(rs.Match("ad"));
+ EXPECT_FALSE(rs.Match("abcd"));
+ EXPECT_FALSE(rs.Match("abcbcd"));
+}
+
+TEST(Regex, QuantifierMin3) {
+ Regex re("a(b|c){3,}d");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("abbbd"));
+ EXPECT_TRUE(rs.Match("acccd"));
+ EXPECT_TRUE(rs.Match("abcbd"));
+ EXPECT_TRUE(rs.Match("accccd"));
+ EXPECT_TRUE(rs.Match("abbbbd"));
+ EXPECT_FALSE(rs.Match("ad"));
+ EXPECT_FALSE(rs.Match("abbd"));
+}
+
+TEST(Regex, QuantifierMinMax1) {
+ Regex re("ab{3,5}c");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("abbbc"));
+ EXPECT_TRUE(rs.Match("abbbbc"));
+ EXPECT_TRUE(rs.Match("abbbbbc"));
+ EXPECT_FALSE(rs.Match("ac"));
+ EXPECT_FALSE(rs.Match("abc"));
+ EXPECT_FALSE(rs.Match("abbc"));
+ EXPECT_FALSE(rs.Match("abbbbbbc"));
+}
+
+TEST(Regex, QuantifierMinMax2) {
+ Regex re("a(bc){3,5}d");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("abcbcbcd"));
+ EXPECT_TRUE(rs.Match("abcbcbcbcd"));
+ EXPECT_TRUE(rs.Match("abcbcbcbcbcd"));
+ EXPECT_FALSE(rs.Match("ad"));
+ EXPECT_FALSE(rs.Match("abcd"));
+ EXPECT_FALSE(rs.Match("abcbcd"));
+ EXPECT_FALSE(rs.Match("abcbcbcbcbcbcd"));
+}
+
+TEST(Regex, QuantifierMinMax3) {
+ Regex re("a(b|c){3,5}d");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("abbbd"));
+ EXPECT_TRUE(rs.Match("acccd"));
+ EXPECT_TRUE(rs.Match("abcbd"));
+ EXPECT_TRUE(rs.Match("accccd"));
+ EXPECT_TRUE(rs.Match("abbbbd"));
+ EXPECT_TRUE(rs.Match("acccccd"));
+ EXPECT_TRUE(rs.Match("abbbbbd"));
+ EXPECT_FALSE(rs.Match("ad"));
+ EXPECT_FALSE(rs.Match("abbd"));
+ EXPECT_FALSE(rs.Match("accccccd"));
+ EXPECT_FALSE(rs.Match("abbbbbbd"));
+}
+
+// Issue538
+TEST(Regex, QuantifierMinMax4) {
+ Regex re("a(b|c){0,3}d");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("ad"));
+ EXPECT_TRUE(rs.Match("abd"));
+ EXPECT_TRUE(rs.Match("acd"));
+ EXPECT_TRUE(rs.Match("abbd"));
+ EXPECT_TRUE(rs.Match("accd"));
+ EXPECT_TRUE(rs.Match("abcd"));
+ EXPECT_TRUE(rs.Match("abbbd"));
+ EXPECT_TRUE(rs.Match("acccd"));
+ EXPECT_FALSE(rs.Match("abbbbd"));
+ EXPECT_FALSE(rs.Match("add"));
+ EXPECT_FALSE(rs.Match("accccd"));
+ EXPECT_FALSE(rs.Match("abcbcd"));
+}
+
+// Issue538
+TEST(Regex, QuantifierMinMax5) {
+ Regex re("a(b|c){0,}d");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("ad"));
+ EXPECT_TRUE(rs.Match("abd"));
+ EXPECT_TRUE(rs.Match("acd"));
+ EXPECT_TRUE(rs.Match("abbd"));
+ EXPECT_TRUE(rs.Match("accd"));
+ EXPECT_TRUE(rs.Match("abcd"));
+ EXPECT_TRUE(rs.Match("abbbd"));
+ EXPECT_TRUE(rs.Match("acccd"));
+ EXPECT_TRUE(rs.Match("abbbbd"));
+ EXPECT_TRUE(rs.Match("accccd"));
+ EXPECT_TRUE(rs.Match("abcbcd"));
+ EXPECT_FALSE(rs.Match("add"));
+ EXPECT_FALSE(rs.Match("aad"));
+}
+
+#define EURO "\xE2\x82\xAC" // "\xE2\x82\xAC" is UTF-8 rsquence of Euro sign U+20AC
+
+TEST(Regex, Unicode) {
+ Regex re("a" EURO "+b");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("a" EURO "b"));
+ EXPECT_TRUE(rs.Match("a" EURO EURO "b"));
+ EXPECT_FALSE(rs.Match("a?b"));
+ EXPECT_FALSE(rs.Match("a" EURO "\xAC" "b")); // unaware of UTF-8 will match
+}
+
+TEST(Regex, AnyCharacter) {
+ Regex re(".");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("a"));
+ EXPECT_TRUE(rs.Match("b"));
+ EXPECT_TRUE(rs.Match(EURO));
+ EXPECT_FALSE(rs.Match(""));
+ EXPECT_FALSE(rs.Match("aa"));
+}
+
+TEST(Regex, CharacterRange1) {
+ Regex re("[abc]");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("a"));
+ EXPECT_TRUE(rs.Match("b"));
+ EXPECT_TRUE(rs.Match("c"));
+ EXPECT_FALSE(rs.Match(""));
+ EXPECT_FALSE(rs.Match("`"));
+ EXPECT_FALSE(rs.Match("d"));
+ EXPECT_FALSE(rs.Match("aa"));
+}
+
+TEST(Regex, CharacterRange2) {
+ Regex re("[^abc]");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("`"));
+ EXPECT_TRUE(rs.Match("d"));
+ EXPECT_FALSE(rs.Match("a"));
+ EXPECT_FALSE(rs.Match("b"));
+ EXPECT_FALSE(rs.Match("c"));
+ EXPECT_FALSE(rs.Match(""));
+ EXPECT_FALSE(rs.Match("aa"));
+}
+
+TEST(Regex, CharacterRange3) {
+ Regex re("[a-c]");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("a"));
+ EXPECT_TRUE(rs.Match("b"));
+ EXPECT_TRUE(rs.Match("c"));
+ EXPECT_FALSE(rs.Match(""));
+ EXPECT_FALSE(rs.Match("`"));
+ EXPECT_FALSE(rs.Match("d"));
+ EXPECT_FALSE(rs.Match("aa"));
+}
+
+TEST(Regex, CharacterRange4) {
+ Regex re("[^a-c]");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("`"));
+ EXPECT_TRUE(rs.Match("d"));
+ EXPECT_FALSE(rs.Match("a"));
+ EXPECT_FALSE(rs.Match("b"));
+ EXPECT_FALSE(rs.Match("c"));
+ EXPECT_FALSE(rs.Match(""));
+ EXPECT_FALSE(rs.Match("aa"));
+}
+
+TEST(Regex, CharacterRange5) {
+ Regex re("[-]");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("-"));
+ EXPECT_FALSE(rs.Match(""));
+ EXPECT_FALSE(rs.Match("a"));
+}
+
+TEST(Regex, CharacterRange6) {
+ Regex re("[a-]");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("a"));
+ EXPECT_TRUE(rs.Match("-"));
+ EXPECT_FALSE(rs.Match(""));
+ EXPECT_FALSE(rs.Match("`"));
+ EXPECT_FALSE(rs.Match("b"));
+}
+
+TEST(Regex, CharacterRange7) {
+ Regex re("[-a]");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("a"));
+ EXPECT_TRUE(rs.Match("-"));
+ EXPECT_FALSE(rs.Match(""));
+ EXPECT_FALSE(rs.Match("`"));
+ EXPECT_FALSE(rs.Match("b"));
+}
+
+TEST(Regex, CharacterRange8) {
+ Regex re("[a-zA-Z0-9]*");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("Milo"));
+ EXPECT_TRUE(rs.Match("MT19937"));
+ EXPECT_TRUE(rs.Match("43"));
+ EXPECT_FALSE(rs.Match("a_b"));
+ EXPECT_FALSE(rs.Match("!"));
+}
+
+TEST(Regex, Search) {
+ Regex re("abc");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Search("abc"));
+ EXPECT_TRUE(rs.Search("_abc"));
+ EXPECT_TRUE(rs.Search("abc_"));
+ EXPECT_TRUE(rs.Search("_abc_"));
+ EXPECT_TRUE(rs.Search("__abc__"));
+ EXPECT_TRUE(rs.Search("abcabc"));
+ EXPECT_FALSE(rs.Search("a"));
+ EXPECT_FALSE(rs.Search("ab"));
+ EXPECT_FALSE(rs.Search("bc"));
+ EXPECT_FALSE(rs.Search("cba"));
+}
+
+TEST(Regex, Search_BeginAnchor) {
+ Regex re("^abc");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Search("abc"));
+ EXPECT_TRUE(rs.Search("abc_"));
+ EXPECT_TRUE(rs.Search("abcabc"));
+ EXPECT_FALSE(rs.Search("_abc"));
+ EXPECT_FALSE(rs.Search("_abc_"));
+ EXPECT_FALSE(rs.Search("a"));
+ EXPECT_FALSE(rs.Search("ab"));
+ EXPECT_FALSE(rs.Search("bc"));
+ EXPECT_FALSE(rs.Search("cba"));
+}
+
+TEST(Regex, Search_EndAnchor) {
+ Regex re("abc$");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Search("abc"));
+ EXPECT_TRUE(rs.Search("_abc"));
+ EXPECT_TRUE(rs.Search("abcabc"));
+ EXPECT_FALSE(rs.Search("abc_"));
+ EXPECT_FALSE(rs.Search("_abc_"));
+ EXPECT_FALSE(rs.Search("a"));
+ EXPECT_FALSE(rs.Search("ab"));
+ EXPECT_FALSE(rs.Search("bc"));
+ EXPECT_FALSE(rs.Search("cba"));
+}
+
+TEST(Regex, Search_BothAnchor) {
+ Regex re("^abc$");
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Search("abc"));
+ EXPECT_FALSE(rs.Search(""));
+ EXPECT_FALSE(rs.Search("a"));
+ EXPECT_FALSE(rs.Search("b"));
+ EXPECT_FALSE(rs.Search("ab"));
+ EXPECT_FALSE(rs.Search("abcd"));
+}
+
+TEST(Regex, Escape) {
+ const char* s = "\\^\\$\\|\\(\\)\\?\\*\\+\\.\\[\\]\\{\\}\\\\\\f\\n\\r\\t\\v[\\b][\\[][\\]]";
+ Regex re(s);
+ ASSERT_TRUE(re.IsValid());
+ RegexSearch rs(re);
+ EXPECT_TRUE(rs.Match("^$|()?*+.[]{}\\\x0C\n\r\t\x0B\b[]"));
+ EXPECT_FALSE(rs.Match(s)); // Not escaping
+}
+
+TEST(Regex, Invalid) {
+#define TEST_INVALID(s) \
+ {\
+ Regex re(s);\
+ EXPECT_FALSE(re.IsValid());\
+ }
+
+ TEST_INVALID("");
+ TEST_INVALID("a|");
+ TEST_INVALID("()");
+ TEST_INVALID("(");
+ TEST_INVALID(")");
+ TEST_INVALID("(a))");
+ TEST_INVALID("(a|)");
+ TEST_INVALID("(a||b)");
+ TEST_INVALID("(|b)");
+ TEST_INVALID("?");
+ TEST_INVALID("*");
+ TEST_INVALID("+");
+ TEST_INVALID("{");
+ TEST_INVALID("{}");
+ TEST_INVALID("a{a}");
+ TEST_INVALID("a{0}");
+ TEST_INVALID("a{-1}");
+ TEST_INVALID("a{}");
+ // TEST_INVALID("a{0,}"); // Support now
+ TEST_INVALID("a{,0}");
+ TEST_INVALID("a{1,0}");
+ TEST_INVALID("a{-1,0}");
+ TEST_INVALID("a{-1,1}");
+ TEST_INVALID("a{4294967296}"); // overflow of unsigned
+ TEST_INVALID("a{1a}");
+ TEST_INVALID("[");
+ TEST_INVALID("[]");
+ TEST_INVALID("[^]");
+ TEST_INVALID("[\\a]");
+ TEST_INVALID("\\a");
+
+#undef TEST_INVALID
+}
+
+TEST(Regex, Issue538) {
+ Regex re("^[0-9]+(\\\\.[0-9]+){0,2}");
+ EXPECT_TRUE(re.IsValid());
+}
+
+TEST(Regex, Issue583) {
+ Regex re("[0-9]{99999}");
+ ASSERT_TRUE(re.IsValid());
+}
+
+#undef EURO
diff --git a/src/s3select/rapidjson/test/unittest/schematest.cpp b/src/s3select/rapidjson/test/unittest/schematest.cpp
new file mode 100644
index 000000000..1b25e2f44
--- /dev/null
+++ b/src/s3select/rapidjson/test/unittest/schematest.cpp
@@ -0,0 +1,2952 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#define RAPIDJSON_SCHEMA_VERBOSE 0
+#define RAPIDJSON_HAS_STDSTRING 1
+
+#include "unittest.h"
+#include "rapidjson/schema.h"
+#include "rapidjson/stringbuffer.h"
+#include "rapidjson/writer.h"
+#include "rapidjson/error/error.h"
+#include "rapidjson/error/en.h"
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(variadic-macros)
+#elif defined(_MSC_VER)
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(4822) // local class member function does not have a body
+#endif
+
+using namespace rapidjson;
+
+#define TEST_HASHER(json1, json2, expected) \
+{\
+ Document d1, d2;\
+ d1.Parse(json1);\
+ ASSERT_FALSE(d1.HasParseError());\
+ d2.Parse(json2);\
+ ASSERT_FALSE(d2.HasParseError());\
+ internal::Hasher<Value, CrtAllocator> h1, h2;\
+ d1.Accept(h1);\
+ d2.Accept(h2);\
+ ASSERT_TRUE(h1.IsValid());\
+ ASSERT_TRUE(h2.IsValid());\
+ /*printf("%s: 0x%016llx\n%s: 0x%016llx\n\n", json1, h1.GetHashCode(), json2, h2.GetHashCode());*/\
+ EXPECT_TRUE(expected == (h1.GetHashCode() == h2.GetHashCode()));\
+}
+
+TEST(SchemaValidator, Hasher) {
+ TEST_HASHER("null", "null", true);
+
+ TEST_HASHER("true", "true", true);
+ TEST_HASHER("false", "false", true);
+ TEST_HASHER("true", "false", false);
+ TEST_HASHER("false", "true", false);
+ TEST_HASHER("true", "null", false);
+ TEST_HASHER("false", "null", false);
+
+ TEST_HASHER("1", "1", true);
+ TEST_HASHER("2147483648", "2147483648", true); // 2^31 can only be fit in unsigned
+ TEST_HASHER("-2147483649", "-2147483649", true); // -2^31 - 1 can only be fit in int64_t
+ TEST_HASHER("2147483648", "2147483648", true); // 2^31 can only be fit in unsigned
+ TEST_HASHER("4294967296", "4294967296", true); // 2^32 can only be fit in int64_t
+ TEST_HASHER("9223372036854775808", "9223372036854775808", true); // 2^63 can only be fit in uint64_t
+ TEST_HASHER("1.5", "1.5", true);
+ TEST_HASHER("1", "1.0", true);
+ TEST_HASHER("1", "-1", false);
+ TEST_HASHER("0.0", "-0.0", false);
+ TEST_HASHER("1", "true", false);
+ TEST_HASHER("0", "false", false);
+ TEST_HASHER("0", "null", false);
+
+ TEST_HASHER("\"\"", "\"\"", true);
+ TEST_HASHER("\"\"", "\"\\u0000\"", false);
+ TEST_HASHER("\"Hello\"", "\"Hello\"", true);
+ TEST_HASHER("\"Hello\"", "\"World\"", false);
+ TEST_HASHER("\"Hello\"", "null", false);
+ TEST_HASHER("\"Hello\\u0000\"", "\"Hello\"", false);
+ TEST_HASHER("\"\"", "null", false);
+ TEST_HASHER("\"\"", "true", false);
+ TEST_HASHER("\"\"", "false", false);
+
+ TEST_HASHER("[]", "[ ]", true);
+ TEST_HASHER("[1, true, false]", "[1, true, false]", true);
+ TEST_HASHER("[1, true, false]", "[1, true]", false);
+ TEST_HASHER("[1, 2]", "[2, 1]", false);
+ TEST_HASHER("[[1], 2]", "[[1, 2]]", false);
+ TEST_HASHER("[1, 2]", "[1, [2]]", false);
+ TEST_HASHER("[]", "null", false);
+ TEST_HASHER("[]", "true", false);
+ TEST_HASHER("[]", "false", false);
+ TEST_HASHER("[]", "0", false);
+ TEST_HASHER("[]", "0.0", false);
+ TEST_HASHER("[]", "\"\"", false);
+
+ TEST_HASHER("{}", "{ }", true);
+ TEST_HASHER("{\"a\":1}", "{\"a\":1}", true);
+ TEST_HASHER("{\"a\":1}", "{\"b\":1}", false);
+ TEST_HASHER("{\"a\":1}", "{\"a\":2}", false);
+ TEST_HASHER("{\"a\":1, \"b\":2}", "{\"b\":2, \"a\":1}", true); // Member order insensitive
+ TEST_HASHER("{}", "null", false);
+ TEST_HASHER("{}", "false", false);
+ TEST_HASHER("{}", "true", false);
+ TEST_HASHER("{}", "0", false);
+ TEST_HASHER("{}", "0.0", false);
+ TEST_HASHER("{}", "\"\"", false);
+}
+
+// Test cases following http://spacetelescope.github.io/understanding-json-schema
+
+#define VALIDATE(schema, json, expected) \
+{\
+ SchemaValidator validator(schema);\
+ Document d;\
+ /*printf("\n%s\n", json);*/\
+ d.Parse(json);\
+ EXPECT_FALSE(d.HasParseError());\
+ EXPECT_TRUE(expected == d.Accept(validator));\
+ EXPECT_TRUE(expected == validator.IsValid());\
+ ValidateErrorCode code = validator.GetInvalidSchemaCode();\
+ if (expected) {\
+ EXPECT_TRUE(code == kValidateErrorNone);\
+ EXPECT_TRUE(validator.GetInvalidSchemaKeyword() == 0);\
+ }\
+ if ((expected) && !validator.IsValid()) {\
+ StringBuffer sb;\
+ validator.GetInvalidSchemaPointer().StringifyUriFragment(sb);\
+ printf("Invalid schema: %s\n", sb.GetString());\
+ printf("Invalid keyword: %s\n", validator.GetInvalidSchemaKeyword());\
+ printf("Invalid code: %d\n", code);\
+ printf("Invalid message: %s\n", GetValidateError_En(code));\
+ sb.Clear();\
+ validator.GetInvalidDocumentPointer().StringifyUriFragment(sb);\
+ printf("Invalid document: %s\n", sb.GetString());\
+ sb.Clear();\
+ Writer<StringBuffer> w(sb);\
+ validator.GetError().Accept(w);\
+ printf("Validation error: %s\n", sb.GetString());\
+ }\
+}
+
+#define INVALIDATE(schema, json, invalidSchemaPointer, invalidSchemaKeyword, invalidDocumentPointer, error) \
+{\
+ INVALIDATE_(schema, json, invalidSchemaPointer, invalidSchemaKeyword, invalidDocumentPointer, error, kValidateDefaultFlags, SchemaValidator, Pointer) \
+}
+
+#define INVALIDATE_(schema, json, invalidSchemaPointer, invalidSchemaKeyword, invalidDocumentPointer, error, \
+ flags, SchemaValidatorType, PointerType) \
+{\
+ SchemaValidatorType validator(schema);\
+ validator.SetValidateFlags(flags);\
+ Document d;\
+ /*printf("\n%s\n", json);*/\
+ d.Parse(json);\
+ EXPECT_FALSE(d.HasParseError());\
+ d.Accept(validator);\
+ EXPECT_FALSE(validator.IsValid());\
+ ValidateErrorCode code = validator.GetInvalidSchemaCode();\
+ ASSERT_TRUE(code != kValidateErrorNone);\
+ ASSERT_TRUE(strcmp(GetValidateError_En(code), "Unknown error.") != 0);\
+ if (validator.GetInvalidSchemaPointer() != PointerType(invalidSchemaPointer)) {\
+ StringBuffer sb;\
+ validator.GetInvalidSchemaPointer().Stringify(sb);\
+ printf("GetInvalidSchemaPointer() Expected: %s Actual: %s\n", invalidSchemaPointer, sb.GetString());\
+ ADD_FAILURE();\
+ }\
+ ASSERT_TRUE(validator.GetInvalidSchemaKeyword() != 0);\
+ if (strcmp(validator.GetInvalidSchemaKeyword(), invalidSchemaKeyword) != 0) {\
+ printf("GetInvalidSchemaKeyword() Expected: %s Actual %s\n", invalidSchemaKeyword, validator.GetInvalidSchemaKeyword());\
+ ADD_FAILURE();\
+ }\
+ if (validator.GetInvalidDocumentPointer() != PointerType(invalidDocumentPointer)) {\
+ StringBuffer sb;\
+ validator.GetInvalidDocumentPointer().Stringify(sb);\
+ printf("GetInvalidDocumentPointer() Expected: %s Actual: %s\n", invalidDocumentPointer, sb.GetString());\
+ ADD_FAILURE();\
+ }\
+ Document e;\
+ e.Parse(error);\
+ if (validator.GetError() != e) {\
+ StringBuffer sb;\
+ Writer<StringBuffer> w(sb);\
+ validator.GetError().Accept(w);\
+ printf("GetError() Expected: %s Actual: %s\n", error, sb.GetString());\
+ ADD_FAILURE();\
+ }\
+}
+
+TEST(SchemaValidator, Typeless) {
+ Document sd;
+ sd.Parse("{}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "42", true);
+ VALIDATE(s, "\"I'm a string\"", true);
+ VALIDATE(s, "{ \"an\": [ \"arbitrarily\", \"nested\" ], \"data\": \"structure\" }", true);
+}
+
+TEST(SchemaValidator, MultiType) {
+ Document sd;
+ sd.Parse("{ \"type\": [\"number\", \"string\"] }");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "42", true);
+ VALIDATE(s, "\"Life, the universe, and everything\"", true);
+ INVALIDATE(s, "[\"Life\", \"the universe\", \"and everything\"]", "", "type", "",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": [\"string\", \"number\"], \"actual\": \"array\""
+ "}}");
+}
+
+TEST(SchemaValidator, Enum_Typed) {
+ Document sd;
+ sd.Parse("{ \"type\": \"string\", \"enum\" : [\"red\", \"amber\", \"green\"] }");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "\"red\"", true);
+ INVALIDATE(s, "\"blue\"", "", "enum", "",
+ "{ \"enum\": { \"errorCode\": 19, \"instanceRef\": \"#\", \"schemaRef\": \"#\" }}");
+}
+
+TEST(SchemaValidator, Enum_Typless) {
+ Document sd;
+ sd.Parse("{ \"enum\": [\"red\", \"amber\", \"green\", null, 42] }");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "\"red\"", true);
+ VALIDATE(s, "null", true);
+ VALIDATE(s, "42", true);
+ INVALIDATE(s, "0", "", "enum", "",
+ "{ \"enum\": { \"errorCode\": 19, \"instanceRef\": \"#\", \"schemaRef\": \"#\" }}");
+}
+
+TEST(SchemaValidator, Enum_InvalidType) {
+ Document sd;
+ sd.Parse("{ \"type\": \"string\", \"enum\": [\"red\", \"amber\", \"green\", null] }");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "\"red\"", true);
+ INVALIDATE(s, "null", "", "type", "",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": [\"string\"], \"actual\": \"null\""
+ "}}");
+}
+
+TEST(SchemaValidator, AllOf) {
+ {
+ Document sd;
+ sd.Parse("{\"allOf\": [{ \"type\": \"string\" }, { \"type\": \"string\", \"maxLength\": 5 }]}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "\"ok\"", true);
+ INVALIDATE(s, "\"too long\"", "", "allOf", "",
+ "{ \"allOf\": {"
+ " \"errors\": ["
+ " {},"
+ " {\"maxLength\": {\"errorCode\": 6, \"instanceRef\": \"#\", \"schemaRef\": \"#/allOf/1\", \"expected\": 5, \"actual\": \"too long\"}}"
+ " ],"
+ " \"errorCode\": 23, \"instanceRef\": \"#\", \"schemaRef\": \"#\""
+ "}}");
+ }
+ {
+ Document sd;
+ sd.Parse("{\"allOf\": [{ \"type\": \"string\" }, { \"type\": \"number\" } ] }");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "\"No way\"", false);
+ INVALIDATE(s, "-1", "", "allOf", "",
+ "{ \"allOf\": {"
+ " \"errors\": ["
+ " {\"type\": { \"instanceRef\": \"#\", \"schemaRef\": \"#/allOf/0\", \"errorCode\": 20, \"expected\": [\"string\"], \"actual\": \"integer\"}},"
+ " {}"
+ " ],"
+ " \"errorCode\": 23, \"instanceRef\": \"#\", \"schemaRef\": \"#\""
+ "}}");
+ }
+}
+
+TEST(SchemaValidator, AnyOf) {
+ Document sd;
+ sd.Parse("{\"anyOf\": [{ \"type\": \"string\" }, { \"type\": \"number\" } ] }");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "\"Yes\"", true);
+ VALIDATE(s, "42", true);
+ INVALIDATE(s, "{ \"Not a\": \"string or number\" }", "", "anyOf", "",
+ "{ \"anyOf\": {"
+ " \"errorCode\": 24,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\", "
+ " \"errors\": ["
+ " { \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#/anyOf/0\","
+ " \"expected\": [\"string\"], \"actual\": \"object\""
+ " }},"
+ " { \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#/anyOf/1\","
+ " \"expected\": [\"number\"], \"actual\": \"object\""
+ " }}"
+ " ]"
+ "}}");
+}
+
+TEST(SchemaValidator, OneOf) {
+ Document sd;
+ sd.Parse("{\"oneOf\": [{ \"type\": \"number\", \"multipleOf\": 5 }, { \"type\": \"number\", \"multipleOf\": 3 } ] }");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "10", true);
+ VALIDATE(s, "9", true);
+ INVALIDATE(s, "2", "", "oneOf", "",
+ "{ \"oneOf\": {"
+ " \"errorCode\": 21,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"errors\": ["
+ " { \"multipleOf\": {"
+ " \"errorCode\": 1,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#/oneOf/0\","
+ " \"expected\": 5, \"actual\": 2"
+ " }},"
+ " { \"multipleOf\": {"
+ " \"errorCode\": 1,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#/oneOf/1\","
+ " \"expected\": 3, \"actual\": 2"
+ " }}"
+ " ]"
+ "}}");
+ INVALIDATE(s, "15", "", "oneOf", "",
+ "{ \"oneOf\": { \"errorCode\": 22, \"instanceRef\": \"#\", \"schemaRef\": \"#\", \"errors\": [{}, {}]}}");
+}
+
+TEST(SchemaValidator, Not) {
+ Document sd;
+ sd.Parse("{\"not\":{ \"type\": \"string\"}}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "42", true);
+ VALIDATE(s, "{ \"key\": \"value\" }", true);
+ INVALIDATE(s, "\"I am a string\"", "", "not", "",
+ "{ \"not\": { \"errorCode\": 25, \"instanceRef\": \"#\", \"schemaRef\": \"#\" }}");
+}
+
+TEST(SchemaValidator, Ref) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"$schema\": \"http://json-schema.org/draft-04/schema#\","
+ ""
+ " \"definitions\": {"
+ " \"address\": {"
+ " \"type\": \"object\","
+ " \"properties\": {"
+ " \"street_address\": { \"type\": \"string\" },"
+ " \"city\": { \"type\": \"string\" },"
+ " \"state\": { \"type\": \"string\" }"
+ " },"
+ " \"required\": [\"street_address\", \"city\", \"state\"]"
+ " }"
+ " },"
+ " \"type\": \"object\","
+ " \"properties\": {"
+ " \"billing_address\": { \"$ref\": \"#/definitions/address\" },"
+ " \"shipping_address\": { \"$ref\": \"#/definitions/address\" }"
+ " }"
+ "}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "{\"shipping_address\": {\"street_address\": \"1600 Pennsylvania Avenue NW\", \"city\": \"Washington\", \"state\": \"DC\"}, \"billing_address\": {\"street_address\": \"1st Street SE\", \"city\": \"Washington\", \"state\": \"DC\"} }", true);
+}
+
+TEST(SchemaValidator, Ref_AllOf) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"$schema\": \"http://json-schema.org/draft-04/schema#\","
+ ""
+ " \"definitions\": {"
+ " \"address\": {"
+ " \"type\": \"object\","
+ " \"properties\": {"
+ " \"street_address\": { \"type\": \"string\" },"
+ " \"city\": { \"type\": \"string\" },"
+ " \"state\": { \"type\": \"string\" }"
+ " },"
+ " \"required\": [\"street_address\", \"city\", \"state\"]"
+ " }"
+ " },"
+ " \"type\": \"object\","
+ " \"properties\": {"
+ " \"billing_address\": { \"$ref\": \"#/definitions/address\" },"
+ " \"shipping_address\": {"
+ " \"allOf\": ["
+ " { \"$ref\": \"#/definitions/address\" },"
+ " { \"properties\":"
+ " { \"type\": { \"enum\": [ \"residential\", \"business\" ] } },"
+ " \"required\": [\"type\"]"
+ " }"
+ " ]"
+ " }"
+ " }"
+ "}");
+ SchemaDocument s(sd);
+
+ INVALIDATE(s, "{\"shipping_address\": {\"street_address\": \"1600 Pennsylvania Avenue NW\", \"city\": \"Washington\", \"state\": \"DC\"} }", "/properties/shipping_address", "allOf", "/shipping_address",
+ "{ \"allOf\": {"
+ " \"errors\": ["
+ " {},"
+ " {\"required\": {\"errorCode\": 15, \"instanceRef\": \"#/shipping_address\", \"schemaRef\": \"#/properties/shipping_address/allOf/1\", \"missing\": [\"type\"]}}"
+ " ],"
+ " \"errorCode\":23,\"instanceRef\":\"#/shipping_address\",\"schemaRef\":\"#/properties/shipping_address\""
+ "}}");
+ VALIDATE(s, "{\"shipping_address\": {\"street_address\": \"1600 Pennsylvania Avenue NW\", \"city\": \"Washington\", \"state\": \"DC\", \"type\": \"business\"} }", true);
+}
+
+TEST(SchemaValidator, String) {
+ Document sd;
+ sd.Parse("{\"type\":\"string\"}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "\"I'm a string\"", true);
+ INVALIDATE(s, "42", "", "type", "",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": [\"string\"], \"actual\": \"integer\""
+ "}}");
+ INVALIDATE(s, "2147483648", "", "type", "",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": [\"string\"], \"actual\": \"integer\""
+ "}}"); // 2^31 can only be fit in unsigned
+ INVALIDATE(s, "-2147483649", "", "type", "",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": [\"string\"], \"actual\": \"integer\""
+ "}}"); // -2^31 - 1 can only be fit in int64_t
+ INVALIDATE(s, "4294967296", "", "type", "",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": [\"string\"], \"actual\": \"integer\""
+ "}}"); // 2^32 can only be fit in int64_t
+ INVALIDATE(s, "3.1415926", "", "type", "",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": [\"string\"], \"actual\": \"number\""
+ "}}");
+}
+
+TEST(SchemaValidator, String_LengthRange) {
+ Document sd;
+ sd.Parse("{\"type\":\"string\",\"minLength\":2,\"maxLength\":3}");
+ SchemaDocument s(sd);
+
+ INVALIDATE(s, "\"A\"", "", "minLength", "",
+ "{ \"minLength\": {"
+ " \"errorCode\": 7,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 2, \"actual\": \"A\""
+ "}}");
+ VALIDATE(s, "\"AB\"", true);
+ VALIDATE(s, "\"ABC\"", true);
+ INVALIDATE(s, "\"ABCD\"", "", "maxLength", "",
+ "{ \"maxLength\": {"
+ " \"errorCode\": 6,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 3, \"actual\": \"ABCD\""
+ "}}");
+}
+
+#if RAPIDJSON_SCHEMA_HAS_REGEX
+TEST(SchemaValidator, String_Pattern) {
+ Document sd;
+ sd.Parse("{\"type\":\"string\",\"pattern\":\"^(\\\\([0-9]{3}\\\\))?[0-9]{3}-[0-9]{4}$\"}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "\"555-1212\"", true);
+ VALIDATE(s, "\"(888)555-1212\"", true);
+ INVALIDATE(s, "\"(888)555-1212 ext. 532\"", "", "pattern", "",
+ "{ \"pattern\": {"
+ " \"errorCode\": 8,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"actual\": \"(888)555-1212 ext. 532\""
+ "}}");
+ INVALIDATE(s, "\"(800)FLOWERS\"", "", "pattern", "",
+ "{ \"pattern\": {"
+ " \"errorCode\": 8,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"actual\": \"(800)FLOWERS\""
+ "}}");
+}
+
+TEST(SchemaValidator, String_Pattern_Invalid) {
+ Document sd;
+ sd.Parse("{\"type\":\"string\",\"pattern\":\"a{0}\"}"); // TODO: report regex is invalid somehow
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "\"\"", true);
+ VALIDATE(s, "\"a\"", true);
+ VALIDATE(s, "\"aa\"", true);
+}
+#endif
+
+TEST(SchemaValidator, Integer) {
+ Document sd;
+ sd.Parse("{\"type\":\"integer\"}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "42", true);
+ VALIDATE(s, "-1", true);
+ VALIDATE(s, "2147483648", true); // 2^31 can only be fit in unsigned
+ VALIDATE(s, "-2147483649", true); // -2^31 - 1 can only be fit in int64_t
+ VALIDATE(s, "2147483648", true); // 2^31 can only be fit in unsigned
+ VALIDATE(s, "4294967296", true); // 2^32 can only be fit in int64_t
+ INVALIDATE(s, "3.1415926", "", "type", "",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": [\"integer\"], \"actual\": \"number\""
+ "}}");
+ INVALIDATE(s, "\"42\"", "", "type", "",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": [\"integer\"], \"actual\": \"string\""
+ "}}");
+}
+
+TEST(SchemaValidator, Integer_Range) {
+ Document sd;
+ sd.Parse("{\"type\":\"integer\",\"minimum\":0,\"maximum\":100,\"exclusiveMaximum\":true}");
+ SchemaDocument s(sd);
+
+ INVALIDATE(s, "-1", "", "minimum", "",
+ "{ \"minimum\": {"
+ " \"errorCode\": 4,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 0, \"actual\": -1"
+ "}}");
+ VALIDATE(s, "0", true);
+ VALIDATE(s, "10", true);
+ VALIDATE(s, "99", true);
+ INVALIDATE(s, "100", "", "maximum", "",
+ "{ \"maximum\": {"
+ " \"errorCode\": 3,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 100, \"exclusiveMaximum\": true, \"actual\": 100"
+ "}}");
+ INVALIDATE(s, "101", "", "maximum", "",
+ "{ \"maximum\": {"
+ " \"errorCode\": 3,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 100, \"exclusiveMaximum\": true, \"actual\": 101"
+ "}}");
+}
+
+TEST(SchemaValidator, Integer_Range64Boundary) {
+ Document sd;
+ sd.Parse("{\"type\":\"integer\",\"minimum\":-9223372036854775807,\"maximum\":9223372036854775806}");
+ SchemaDocument s(sd);
+
+ INVALIDATE(s, "-9223372036854775808", "", "minimum", "",
+ "{ \"minimum\": {"
+ " \"errorCode\": 4,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": -9223372036854775807, \"actual\": -9223372036854775808"
+ "}}");
+ VALIDATE(s, "-9223372036854775807", true);
+ VALIDATE(s, "-2147483648", true); // int min
+ VALIDATE(s, "0", true);
+ VALIDATE(s, "2147483647", true); // int max
+ VALIDATE(s, "2147483648", true); // unsigned first
+ VALIDATE(s, "4294967295", true); // unsigned max
+ VALIDATE(s, "9223372036854775806", true);
+ INVALIDATE(s, "9223372036854775807", "", "maximum", "",
+ "{ \"maximum\": {"
+ " \"errorCode\": 2,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 9223372036854775806, \"actual\": 9223372036854775807"
+ "}}");
+ INVALIDATE(s, "18446744073709551615", "", "maximum", "",
+ "{ \"maximum\": {"
+ " \"errorCode\": 2,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 9223372036854775806, \"actual\": 18446744073709551615"
+ "}}"); // uint64_t max
+}
+
+TEST(SchemaValidator, Integer_RangeU64Boundary) {
+ Document sd;
+ sd.Parse("{\"type\":\"integer\",\"minimum\":9223372036854775808,\"maximum\":18446744073709551614}");
+ SchemaDocument s(sd);
+
+ INVALIDATE(s, "-9223372036854775808", "", "minimum", "",
+ "{ \"minimum\": {"
+ " \"errorCode\": 4,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 9223372036854775808, \"actual\": -9223372036854775808"
+ "}}");
+ INVALIDATE(s, "9223372036854775807", "", "minimum", "",
+ "{ \"minimum\": {"
+ " \"errorCode\": 4,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 9223372036854775808, \"actual\": 9223372036854775807"
+ "}}");
+ INVALIDATE(s, "-2147483648", "", "minimum", "",
+ "{ \"minimum\": {"
+ " \"errorCode\": 4,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 9223372036854775808, \"actual\": -2147483648"
+ "}}"); // int min
+ INVALIDATE(s, "0", "", "minimum", "",
+ "{ \"minimum\": {"
+ " \"errorCode\": 4,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 9223372036854775808, \"actual\": 0"
+ "}}");
+ INVALIDATE(s, "2147483647", "", "minimum", "",
+ "{ \"minimum\": {"
+ " \"errorCode\": 4,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 9223372036854775808, \"actual\": 2147483647"
+ "}}"); // int max
+ INVALIDATE(s, "2147483648", "", "minimum", "",
+ "{ \"minimum\": {"
+ " \"errorCode\": 4,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 9223372036854775808, \"actual\": 2147483648"
+ "}}"); // unsigned first
+ INVALIDATE(s, "4294967295", "", "minimum", "",
+ "{ \"minimum\": {"
+ " \"errorCode\": 4,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 9223372036854775808, \"actual\": 4294967295"
+ "}}"); // unsigned max
+ VALIDATE(s, "9223372036854775808", true);
+ VALIDATE(s, "18446744073709551614", true);
+ INVALIDATE(s, "18446744073709551615", "", "maximum", "",
+ "{ \"maximum\": {"
+ " \"errorCode\": 2,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 18446744073709551614, \"actual\": 18446744073709551615"
+ "}}");
+}
+
+TEST(SchemaValidator, Integer_Range64BoundaryExclusive) {
+ Document sd;
+ sd.Parse("{\"type\":\"integer\",\"minimum\":-9223372036854775808,\"maximum\":18446744073709551615,\"exclusiveMinimum\":true,\"exclusiveMaximum\":true}");
+ SchemaDocument s(sd);
+
+ INVALIDATE(s, "-9223372036854775808", "", "minimum", "",
+ "{ \"minimum\": {"
+ " \"errorCode\": 5,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": -9223372036854775808, \"exclusiveMinimum\": true, "
+ " \"actual\": -9223372036854775808"
+ "}}");
+ VALIDATE(s, "-9223372036854775807", true);
+ VALIDATE(s, "18446744073709551614", true);
+ INVALIDATE(s, "18446744073709551615", "", "maximum", "",
+ "{ \"maximum\": {"
+ " \"errorCode\": 3,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 18446744073709551615, \"exclusiveMaximum\": true, "
+ " \"actual\": 18446744073709551615"
+ "}}");
+}
+
+TEST(SchemaValidator, Integer_MultipleOf) {
+ Document sd;
+ sd.Parse("{\"type\":\"integer\",\"multipleOf\":10}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "0", true);
+ VALIDATE(s, "10", true);
+ VALIDATE(s, "-10", true);
+ VALIDATE(s, "20", true);
+ INVALIDATE(s, "23", "", "multipleOf", "",
+ "{ \"multipleOf\": {"
+ " \"errorCode\": 1,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 10, \"actual\": 23"
+ "}}");
+ INVALIDATE(s, "-23", "", "multipleOf", "",
+ "{ \"multipleOf\": {"
+ " \"errorCode\": 1,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 10, \"actual\": -23"
+ "}}");
+}
+
+TEST(SchemaValidator, Integer_MultipleOf64Boundary) {
+ Document sd;
+ sd.Parse("{\"type\":\"integer\",\"multipleOf\":18446744073709551615}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "0", true);
+ VALIDATE(s, "18446744073709551615", true);
+ INVALIDATE(s, "18446744073709551614", "", "multipleOf", "",
+ "{ \"multipleOf\": {"
+ " \"errorCode\": 1,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 18446744073709551615, \"actual\": 18446744073709551614"
+ "}}");
+}
+
+TEST(SchemaValidator, Number_Range) {
+ Document sd;
+ sd.Parse("{\"type\":\"number\",\"minimum\":0,\"maximum\":100,\"exclusiveMaximum\":true}");
+ SchemaDocument s(sd);
+
+ INVALIDATE(s, "-1", "", "minimum", "",
+ "{ \"minimum\": {"
+ " \"errorCode\": 4,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 0, \"actual\": -1"
+ "}}");
+ VALIDATE(s, "0", true);
+ VALIDATE(s, "0.1", true);
+ VALIDATE(s, "10", true);
+ VALIDATE(s, "99", true);
+ VALIDATE(s, "99.9", true);
+ INVALIDATE(s, "100", "", "maximum", "",
+ "{ \"maximum\": {"
+ " \"errorCode\": 3,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 100, \"exclusiveMaximum\": true, \"actual\": 100"
+ "}}");
+ INVALIDATE(s, "100.0", "", "maximum", "",
+ "{ \"maximum\": {"
+ " \"errorCode\": 3,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 100, \"exclusiveMaximum\": true, \"actual\": 100.0"
+ "}}");
+ INVALIDATE(s, "101.5", "", "maximum", "",
+ "{ \"maximum\": {"
+ " \"errorCode\": 3,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 100, \"exclusiveMaximum\": true, \"actual\": 101.5"
+ "}}");
+}
+
+TEST(SchemaValidator, Number_RangeInt) {
+ Document sd;
+ sd.Parse("{\"type\":\"number\",\"minimum\":-100,\"maximum\":-1,\"exclusiveMaximum\":true}");
+ SchemaDocument s(sd);
+
+ INVALIDATE(s, "-101", "", "minimum", "",
+ "{ \"minimum\": {"
+ " \"errorCode\": 4,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": -100, \"actual\": -101"
+ "}}");
+ INVALIDATE(s, "-100.1", "", "minimum", "",
+ "{ \"minimum\": {"
+ " \"errorCode\": 4,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": -100, \"actual\": -100.1"
+ "}}");
+ VALIDATE(s, "-100", true);
+ VALIDATE(s, "-2", true);
+ INVALIDATE(s, "-1", "", "maximum", "",
+ "{ \"maximum\": {"
+ " \"errorCode\": 3,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": -1, \"exclusiveMaximum\": true, \"actual\": -1"
+ "}}");
+ INVALIDATE(s, "-0.9", "", "maximum", "",
+ "{ \"maximum\": {"
+ " \"errorCode\": 3,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": -1, \"exclusiveMaximum\": true, \"actual\": -0.9"
+ "}}");
+ INVALIDATE(s, "0", "", "maximum", "",
+ "{ \"maximum\": {"
+ " \"errorCode\": 3,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": -1, \"exclusiveMaximum\": true, \"actual\": 0"
+ "}}");
+ INVALIDATE(s, "2147483647", "", "maximum", "",
+ "{ \"maximum\": {"
+ " \"errorCode\": 3,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": -1, \"exclusiveMaximum\": true, \"actual\": 2147483647"
+ "}}"); // int max
+ INVALIDATE(s, "2147483648", "", "maximum", "",
+ "{ \"maximum\": {"
+ " \"errorCode\": 3,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": -1, \"exclusiveMaximum\": true, \"actual\": 2147483648"
+ "}}"); // unsigned first
+ INVALIDATE(s, "4294967295", "", "maximum", "",
+ "{ \"maximum\": {"
+ " \"errorCode\": 3,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": -1, \"exclusiveMaximum\": true, \"actual\": 4294967295"
+ "}}"); // unsigned max
+ INVALIDATE(s, "9223372036854775808", "", "maximum", "",
+ "{ \"maximum\": {"
+ " \"errorCode\": 3,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": -1, \"exclusiveMaximum\": true, \"actual\": 9223372036854775808"
+ "}}");
+ INVALIDATE(s, "18446744073709551614", "", "maximum", "",
+ "{ \"maximum\": {"
+ " \"errorCode\": 3,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": -1, \"exclusiveMaximum\": true, \"actual\": 18446744073709551614"
+ "}}");
+ INVALIDATE(s, "18446744073709551615", "", "maximum", "",
+ "{ \"maximum\": {"
+ " \"errorCode\": 3,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": -1, \"exclusiveMaximum\": true, \"actual\": 18446744073709551615"
+ "}}");
+}
+
+TEST(SchemaValidator, Number_RangeDouble) {
+ Document sd;
+ sd.Parse("{\"type\":\"number\",\"minimum\":0.1,\"maximum\":100.1,\"exclusiveMaximum\":true}");
+ SchemaDocument s(sd);
+
+ INVALIDATE(s, "-9223372036854775808", "", "minimum", "",
+ "{ \"minimum\": {"
+ " \"errorCode\": 4,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 0.1, \"actual\": -9223372036854775808"
+ "}}");
+ INVALIDATE(s, "-2147483648", "", "minimum", "",
+ "{ \"minimum\": {"
+ " \"errorCode\": 4,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 0.1, \"actual\": -2147483648"
+ "}}"); // int min
+ INVALIDATE(s, "-1", "", "minimum", "",
+ "{ \"minimum\": {"
+ " \"errorCode\": 4,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 0.1, \"actual\": -1"
+ "}}");
+ VALIDATE(s, "0.1", true);
+ VALIDATE(s, "10", true);
+ VALIDATE(s, "99", true);
+ VALIDATE(s, "100", true);
+ INVALIDATE(s, "101", "", "maximum", "",
+ "{ \"maximum\": {"
+ " \"errorCode\": 3,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 100.1, \"exclusiveMaximum\": true, \"actual\": 101"
+ "}}");
+ INVALIDATE(s, "101.5", "", "maximum", "",
+ "{ \"maximum\": {"
+ " \"errorCode\": 3,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 100.1, \"exclusiveMaximum\": true, \"actual\": 101.5"
+ "}}");
+ INVALIDATE(s, "18446744073709551614", "", "maximum", "",
+ "{ \"maximum\": {"
+ " \"errorCode\": 3,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 100.1, \"exclusiveMaximum\": true, \"actual\": 18446744073709551614"
+ "}}");
+ INVALIDATE(s, "18446744073709551615", "", "maximum", "",
+ "{ \"maximum\": {"
+ " \"errorCode\": 3,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 100.1, \"exclusiveMaximum\": true, \"actual\": 18446744073709551615"
+ "}}");
+ INVALIDATE(s, "2147483647", "", "maximum", "",
+ "{ \"maximum\": {"
+ " \"errorCode\": 3,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 100.1, \"exclusiveMaximum\": true, \"actual\": 2147483647"
+ "}}"); // int max
+ INVALIDATE(s, "2147483648", "", "maximum", "",
+ "{ \"maximum\": {"
+ " \"errorCode\": 3,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 100.1, \"exclusiveMaximum\": true, \"actual\": 2147483648"
+ "}}"); // unsigned first
+ INVALIDATE(s, "4294967295", "", "maximum", "",
+ "{ \"maximum\": {"
+ " \"errorCode\": 3,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 100.1, \"exclusiveMaximum\": true, \"actual\": 4294967295"
+ "}}"); // unsigned max
+ INVALIDATE(s, "9223372036854775808", "", "maximum", "",
+ "{ \"maximum\": {"
+ " \"errorCode\": 3,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 100.1, \"exclusiveMaximum\": true, \"actual\": 9223372036854775808"
+ "}}");
+ INVALIDATE(s, "18446744073709551614", "", "maximum", "",
+ "{ \"maximum\": {"
+ " \"errorCode\": 3,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 100.1, \"exclusiveMaximum\": true, \"actual\": 18446744073709551614"
+ "}}");
+ INVALIDATE(s, "18446744073709551615", "", "maximum", "",
+ "{ \"maximum\": {"
+ " \"errorCode\": 3,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 100.1, \"exclusiveMaximum\": true, \"actual\": 18446744073709551615"
+ "}}");
+}
+
+TEST(SchemaValidator, Number_RangeDoubleU64Boundary) {
+ Document sd;
+ sd.Parse("{\"type\":\"number\",\"minimum\":9223372036854775808.0,\"maximum\":18446744073709550000.0}");
+ SchemaDocument s(sd);
+
+ INVALIDATE(s, "-9223372036854775808", "", "minimum", "",
+ "{ \"minimum\": {"
+ " \"errorCode\": 4,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 9223372036854775808.0, \"actual\": -9223372036854775808"
+ "}}");
+ INVALIDATE(s, "-2147483648", "", "minimum", "",
+ "{ \"minimum\": {"
+ " \"errorCode\": 4,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 9223372036854775808.0, \"actual\": -2147483648"
+ "}}"); // int min
+ INVALIDATE(s, "0", "", "minimum", "",
+ "{ \"minimum\": {"
+ " \"errorCode\": 4,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 9223372036854775808.0, \"actual\": 0"
+ "}}");
+ INVALIDATE(s, "2147483647", "", "minimum", "",
+ "{ \"minimum\": {"
+ " \"errorCode\": 4,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 9223372036854775808.0, \"actual\": 2147483647"
+ "}}"); // int max
+ INVALIDATE(s, "2147483648", "", "minimum", "",
+ "{ \"minimum\": {"
+ " \"errorCode\": 4,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 9223372036854775808.0, \"actual\": 2147483648"
+ "}}"); // unsigned first
+ INVALIDATE(s, "4294967295", "", "minimum", "",
+ "{ \"minimum\": {"
+ " \"errorCode\": 4,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 9223372036854775808.0, \"actual\": 4294967295"
+ "}}"); // unsigned max
+ VALIDATE(s, "9223372036854775808", true);
+ VALIDATE(s, "18446744073709540000", true);
+ INVALIDATE(s, "18446744073709551615", "", "maximum", "",
+ "{ \"maximum\": {"
+ " \"errorCode\": 2,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 18446744073709550000.0, \"actual\": 18446744073709551615"
+ "}}");
+}
+
+TEST(SchemaValidator, Number_MultipleOf) {
+ Document sd;
+ sd.Parse("{\"type\":\"number\",\"multipleOf\":10.0}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "0", true);
+ VALIDATE(s, "10", true);
+ VALIDATE(s, "-10", true);
+ VALIDATE(s, "20", true);
+ INVALIDATE(s, "23", "", "multipleOf", "",
+ "{ \"multipleOf\": {"
+ " \"errorCode\": 1,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 10.0, \"actual\": 23"
+ "}}");
+ INVALIDATE(s, "-2147483648", "", "multipleOf", "",
+ "{ \"multipleOf\": {"
+ " \"errorCode\": 1,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 10.0, \"actual\": -2147483648"
+ "}}"); // int min
+ VALIDATE(s, "-2147483640", true);
+ INVALIDATE(s, "2147483647", "", "multipleOf", "",
+ "{ \"multipleOf\": {"
+ " \"errorCode\": 1,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 10.0, \"actual\": 2147483647"
+ "}}"); // int max
+ INVALIDATE(s, "2147483648", "", "multipleOf", "",
+ "{ \"multipleOf\": {"
+ " \"errorCode\": 1,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 10.0, \"actual\": 2147483648"
+ "}}"); // unsigned first
+ VALIDATE(s, "2147483650", true);
+ INVALIDATE(s, "4294967295", "", "multipleOf", "",
+ "{ \"multipleOf\": {"
+ " \"errorCode\": 1,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 10.0, \"actual\": 4294967295"
+ "}}"); // unsigned max
+ VALIDATE(s, "4294967300", true);
+}
+
+TEST(SchemaValidator, Number_MultipleOfOne) {
+ Document sd;
+ sd.Parse("{\"type\":\"number\",\"multipleOf\":1}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "42", true);
+ VALIDATE(s, "42.0", true);
+ INVALIDATE(s, "3.1415926", "", "multipleOf", "",
+ "{ \"multipleOf\": {"
+ " \"errorCode\": 1,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 1, \"actual\": 3.1415926"
+ "}}");
+}
+
+TEST(SchemaValidator, Object) {
+ Document sd;
+ sd.Parse("{\"type\":\"object\"}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "{\"key\":\"value\",\"another_key\":\"another_value\"}", true);
+ VALIDATE(s, "{\"Sun\":1.9891e30,\"Jupiter\":1.8986e27,\"Saturn\":5.6846e26,\"Neptune\":10.243e25,\"Uranus\":8.6810e25,\"Earth\":5.9736e24,\"Venus\":4.8685e24,\"Mars\":6.4185e23,\"Mercury\":3.3022e23,\"Moon\":7.349e22,\"Pluto\":1.25e22}", true);
+ INVALIDATE(s, "[\"An\", \"array\", \"not\", \"an\", \"object\"]", "", "type", "",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": [\"object\"], \"actual\": \"array\""
+ "}}");
+ INVALIDATE(s, "\"Not an object\"", "", "type", "",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": [\"object\"], \"actual\": \"string\""
+ "}}");
+}
+
+TEST(SchemaValidator, Object_Properties) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"type\": \"object\","
+ " \"properties\" : {"
+ " \"number\": { \"type\": \"number\" },"
+ " \"street_name\" : { \"type\": \"string\" },"
+ " \"street_type\" : { \"type\": \"string\", \"enum\" : [\"Street\", \"Avenue\", \"Boulevard\"] }"
+ " }"
+ "}");
+
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\" }", true);
+ INVALIDATE(s, "{ \"number\": \"1600\", \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\" }", "/properties/number", "type", "/number",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#/number\", \"schemaRef\": \"#/properties/number\","
+ " \"expected\": [\"number\"], \"actual\": \"string\""
+ "}}");
+ INVALIDATE(s, "{ \"number\": \"One\", \"street_name\": \"Microsoft\", \"street_type\": \"Way\" }",
+ "/properties/number", "type", "/number",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#/number\", \"schemaRef\": \"#/properties/number\","
+ " \"expected\": [\"number\"], \"actual\": \"string\""
+ "}}"); // fail fast
+ VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\" }", true);
+ VALIDATE(s, "{}", true);
+ VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\", \"direction\": \"NW\" }", true);
+}
+
+TEST(SchemaValidator, Object_AdditionalPropertiesBoolean) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"type\": \"object\","
+ " \"properties\" : {"
+ " \"number\": { \"type\": \"number\" },"
+ " \"street_name\" : { \"type\": \"string\" },"
+ " \"street_type\" : { \"type\": \"string\","
+ " \"enum\" : [\"Street\", \"Avenue\", \"Boulevard\"]"
+ " }"
+ " },"
+ " \"additionalProperties\": false"
+ "}");
+
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\" }", true);
+ INVALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\", \"direction\": \"NW\" }", "", "additionalProperties", "/direction",
+ "{ \"additionalProperties\": {"
+ " \"errorCode\": 16,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"disallowed\": \"direction\""
+ "}}");
+}
+
+TEST(SchemaValidator, Object_AdditionalPropertiesObject) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"type\": \"object\","
+ " \"properties\" : {"
+ " \"number\": { \"type\": \"number\" },"
+ " \"street_name\" : { \"type\": \"string\" },"
+ " \"street_type\" : { \"type\": \"string\","
+ " \"enum\" : [\"Street\", \"Avenue\", \"Boulevard\"]"
+ " }"
+ " },"
+ " \"additionalProperties\": { \"type\": \"string\" }"
+ "}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\" }", true);
+ VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\", \"direction\": \"NW\" }", true);
+ INVALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\", \"office_number\": 201 }", "/additionalProperties", "type", "/office_number",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#/office_number\", \"schemaRef\": \"#/additionalProperties\","
+ " \"expected\": [\"string\"], \"actual\": \"integer\""
+ "}}");
+}
+
+TEST(SchemaValidator, Object_Required) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"type\": \"object\","
+ " \"properties\" : {"
+ " \"name\": { \"type\": \"string\" },"
+ " \"email\" : { \"type\": \"string\" },"
+ " \"address\" : { \"type\": \"string\" },"
+ " \"telephone\" : { \"type\": \"string\" }"
+ " },"
+ " \"required\":[\"name\", \"email\"]"
+ "}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "{ \"name\": \"William Shakespeare\", \"email\" : \"bill@stratford-upon-avon.co.uk\" }", true);
+ VALIDATE(s, "{ \"name\": \"William Shakespeare\", \"email\" : \"bill@stratford-upon-avon.co.uk\", \"address\" : \"Henley Street, Stratford-upon-Avon, Warwickshire, England\", \"authorship\" : \"in question\"}", true);
+ INVALIDATE(s, "{ \"name\": \"William Shakespeare\", \"address\" : \"Henley Street, Stratford-upon-Avon, Warwickshire, England\" }", "", "required", "",
+ "{ \"required\": {"
+ " \"errorCode\": 15,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"missing\": [\"email\"]"
+ "}}");
+ INVALIDATE(s, "{}", "", "required", "",
+ "{ \"required\": {"
+ " \"errorCode\": 15,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"missing\": [\"name\", \"email\"]"
+ "}}");
+}
+
+TEST(SchemaValidator, Object_Required_PassWithDefault) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"type\": \"object\","
+ " \"properties\" : {"
+ " \"name\": { \"type\": \"string\", \"default\": \"William Shakespeare\" },"
+ " \"email\" : { \"type\": \"string\", \"default\": \"\" },"
+ " \"address\" : { \"type\": \"string\" },"
+ " \"telephone\" : { \"type\": \"string\" }"
+ " },"
+ " \"required\":[\"name\", \"email\"]"
+ "}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "{ \"email\" : \"bill@stratford-upon-avon.co.uk\", \"address\" : \"Henley Street, Stratford-upon-Avon, Warwickshire, England\", \"authorship\" : \"in question\"}", true);
+ INVALIDATE(s, "{ \"name\": \"William Shakespeare\", \"address\" : \"Henley Street, Stratford-upon-Avon, Warwickshire, England\" }", "", "required", "",
+ "{ \"required\": {"
+ " \"errorCode\": 15,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"missing\": [\"email\"]"
+ "}}");
+ INVALIDATE(s, "{}", "", "required", "",
+ "{ \"required\": {"
+ " \"errorCode\": 15,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"missing\": [\"email\"]"
+ "}}");
+}
+
+TEST(SchemaValidator, Object_PropertiesRange) {
+ Document sd;
+ sd.Parse("{\"type\":\"object\", \"minProperties\":2, \"maxProperties\":3}");
+ SchemaDocument s(sd);
+
+ INVALIDATE(s, "{}", "", "minProperties", "",
+ "{ \"minProperties\": {"
+ " \"errorCode\": 14,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 2, \"actual\": 0"
+ "}}");
+ INVALIDATE(s, "{\"a\":0}", "", "minProperties", "",
+ "{ \"minProperties\": {"
+ " \"errorCode\": 14,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 2, \"actual\": 1"
+ "}}");
+ VALIDATE(s, "{\"a\":0,\"b\":1}", true);
+ VALIDATE(s, "{\"a\":0,\"b\":1,\"c\":2}", true);
+ INVALIDATE(s, "{\"a\":0,\"b\":1,\"c\":2,\"d\":3}", "", "maxProperties", "",
+ "{ \"maxProperties\": {"
+ " \"errorCode\": 13,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\", "
+ " \"expected\": 3, \"actual\": 4"
+ "}}");
+}
+
+TEST(SchemaValidator, Object_PropertyDependencies) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"type\": \"object\","
+ " \"properties\": {"
+ " \"name\": { \"type\": \"string\" },"
+ " \"credit_card\": { \"type\": \"number\" },"
+ " \"cvv_code\": { \"type\": \"number\" },"
+ " \"billing_address\": { \"type\": \"string\" }"
+ " },"
+ " \"required\": [\"name\"],"
+ " \"dependencies\": {"
+ " \"credit_card\": [\"cvv_code\", \"billing_address\"]"
+ " }"
+ "}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "{ \"name\": \"John Doe\", \"credit_card\": 5555555555555555, \"cvv_code\": 777, "
+ "\"billing_address\": \"555 Debtor's Lane\" }", true);
+ INVALIDATE(s, "{ \"name\": \"John Doe\", \"credit_card\": 5555555555555555 }", "", "dependencies", "",
+ "{ \"dependencies\": {"
+ " \"errorCode\": 18,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"errors\": {"
+ " \"credit_card\": {"
+ " \"required\": {"
+ " \"errorCode\": 15,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#/dependencies/credit_card\","
+ " \"missing\": [\"cvv_code\", \"billing_address\"]"
+ " } } }"
+ "}}");
+ VALIDATE(s, "{ \"name\": \"John Doe\"}", true);
+ VALIDATE(s, "{ \"name\": \"John Doe\", \"cvv_code\": 777, \"billing_address\": \"555 Debtor's Lane\" }", true);
+}
+
+TEST(SchemaValidator, Object_SchemaDependencies) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"type\": \"object\","
+ " \"properties\" : {"
+ " \"name\": { \"type\": \"string\" },"
+ " \"credit_card\" : { \"type\": \"number\" }"
+ " },"
+ " \"required\" : [\"name\"],"
+ " \"dependencies\" : {"
+ " \"credit_card\": {"
+ " \"properties\": {"
+ " \"billing_address\": { \"type\": \"string\" }"
+ " },"
+ " \"required\" : [\"billing_address\"]"
+ " }"
+ " }"
+ "}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "{\"name\": \"John Doe\", \"credit_card\" : 5555555555555555,\"billing_address\" : \"555 Debtor's Lane\"}", true);
+ INVALIDATE(s, "{\"name\": \"John Doe\", \"credit_card\" : 5555555555555555 }", "", "dependencies", "",
+ "{ \"dependencies\": {"
+ " \"errorCode\": 18,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"errors\": {"
+ " \"credit_card\": {"
+ " \"required\": {"
+ " \"errorCode\": 15,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#/dependencies/credit_card\","
+ " \"missing\": [\"billing_address\"]"
+ " } } }"
+ "}}");
+ VALIDATE(s, "{\"name\": \"John Doe\", \"billing_address\" : \"555 Debtor's Lane\"}", true);
+}
+
+#if RAPIDJSON_SCHEMA_HAS_REGEX
+TEST(SchemaValidator, Object_PatternProperties) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"type\": \"object\","
+ " \"patternProperties\": {"
+ " \"^S_\": { \"type\": \"string\" },"
+ " \"^I_\": { \"type\": \"integer\" }"
+ " }"
+ "}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "{ \"S_25\": \"This is a string\" }", true);
+ VALIDATE(s, "{ \"I_0\": 42 }", true);
+ INVALIDATE(s, "{ \"S_0\": 42 }", "", "patternProperties", "/S_0",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#/S_0\", \"schemaRef\": \"#/patternProperties/%5ES_\","
+ " \"expected\": [\"string\"], \"actual\": \"integer\""
+ "}}");
+ INVALIDATE(s, "{ \"I_42\": \"This is a string\" }", "", "patternProperties", "/I_42",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#/I_42\", \"schemaRef\": \"#/patternProperties/%5EI_\","
+ " \"expected\": [\"integer\"], \"actual\": \"string\""
+ "}}");
+ VALIDATE(s, "{ \"keyword\": \"value\" }", true);
+}
+
+TEST(SchemaValidator, Object_PatternProperties_ErrorConflict) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"type\": \"object\","
+ " \"patternProperties\": {"
+ " \"^I_\": { \"multipleOf\": 5 },"
+ " \"30$\": { \"multipleOf\": 6 }"
+ " }"
+ "}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "{ \"I_30\": 30 }", true);
+ INVALIDATE(s, "{ \"I_30\": 7 }", "", "patternProperties", "/I_30",
+ "{ \"multipleOf\": ["
+ " {"
+ " \"errorCode\": 1,"
+ " \"instanceRef\": \"#/I_30\", \"schemaRef\": \"#/patternProperties/%5EI_\","
+ " \"expected\": 5, \"actual\": 7"
+ " }, {"
+ " \"errorCode\": 1,"
+ " \"instanceRef\": \"#/I_30\", \"schemaRef\": \"#/patternProperties/30%24\","
+ " \"expected\": 6, \"actual\": 7"
+ " }"
+ "]}");
+}
+
+TEST(SchemaValidator, Object_Properties_PatternProperties) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"type\": \"object\","
+ " \"properties\": {"
+ " \"I_42\": { \"type\": \"integer\", \"minimum\": 73 }"
+ " },"
+ " \"patternProperties\": {"
+ " \"^I_\": { \"type\": \"integer\", \"multipleOf\": 6 }"
+ " }"
+ "}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "{ \"I_6\": 6 }", true);
+ VALIDATE(s, "{ \"I_42\": 78 }", true);
+ INVALIDATE(s, "{ \"I_42\": 42 }", "", "patternProperties", "/I_42",
+ "{ \"minimum\": {"
+ " \"errorCode\": 4,"
+ " \"instanceRef\": \"#/I_42\", \"schemaRef\": \"#/properties/I_42\","
+ " \"expected\": 73, \"actual\": 42"
+ "}}");
+ INVALIDATE(s, "{ \"I_42\": 7 }", "", "patternProperties", "/I_42",
+ "{ \"minimum\": {"
+ " \"errorCode\": 4,"
+ " \"instanceRef\": \"#/I_42\", \"schemaRef\": \"#/properties/I_42\","
+ " \"expected\": 73, \"actual\": 7"
+ " },"
+ " \"multipleOf\": {"
+ " \"errorCode\": 1,"
+ " \"instanceRef\": \"#/I_42\", \"schemaRef\": \"#/patternProperties/%5EI_\","
+ " \"expected\": 6, \"actual\": 7"
+ " }"
+ "}");
+}
+
+TEST(SchemaValidator, Object_PatternProperties_AdditionalPropertiesObject) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"type\": \"object\","
+ " \"properties\": {"
+ " \"builtin\": { \"type\": \"number\" }"
+ " },"
+ " \"patternProperties\": {"
+ " \"^S_\": { \"type\": \"string\" },"
+ " \"^I_\": { \"type\": \"integer\" }"
+ " },"
+ " \"additionalProperties\": { \"type\": \"string\" }"
+ "}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "{ \"builtin\": 42 }", true);
+ VALIDATE(s, "{ \"keyword\": \"value\" }", true);
+ INVALIDATE(s, "{ \"keyword\": 42 }", "/additionalProperties", "type", "/keyword",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#/keyword\", \"schemaRef\": \"#/additionalProperties\","
+ " \"expected\": [\"string\"], \"actual\": \"integer\""
+ "}}");
+}
+
+// Replaces test Issue285 and tests failure as well as success
+TEST(SchemaValidator, Object_PatternProperties_AdditionalPropertiesBoolean) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"type\": \"object\","
+ " \"patternProperties\": {"
+ " \"^S_\": { \"type\": \"string\" },"
+ " \"^I_\": { \"type\": \"integer\" }"
+ " },"
+ " \"additionalProperties\": false"
+ "}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "{ \"S_25\": \"This is a string\" }", true);
+ VALIDATE(s, "{ \"I_0\": 42 }", true);
+ INVALIDATE(s, "{ \"keyword\": \"value\" }", "", "additionalProperties", "/keyword",
+ "{ \"additionalProperties\": {"
+ " \"errorCode\": 16,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"disallowed\": \"keyword\""
+ "}}");
+}
+#endif
+
+TEST(SchemaValidator, Array) {
+ Document sd;
+ sd.Parse("{\"type\":\"array\"}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "[1, 2, 3, 4, 5]", true);
+ VALIDATE(s, "[3, \"different\", { \"types\" : \"of values\" }]", true);
+ INVALIDATE(s, "{\"Not\": \"an array\"}", "", "type", "",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": [\"array\"], \"actual\": \"object\""
+ "}}");
+}
+
+TEST(SchemaValidator, Array_ItemsList) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"type\": \"array\","
+ " \"items\" : {"
+ " \"type\": \"number\""
+ " }"
+ "}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "[1, 2, 3, 4, 5]", true);
+ INVALIDATE(s, "[1, 2, \"3\", 4, 5]", "/items", "type", "/2",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#/2\", \"schemaRef\": \"#/items\","
+ " \"expected\": [\"number\"], \"actual\": \"string\""
+ "}}");
+ VALIDATE(s, "[]", true);
+}
+
+TEST(SchemaValidator, Array_ItemsTuple) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"type\": \"array\","
+ " \"items\": ["
+ " {"
+ " \"type\": \"number\""
+ " },"
+ " {"
+ " \"type\": \"string\""
+ " },"
+ " {"
+ " \"type\": \"string\","
+ " \"enum\": [\"Street\", \"Avenue\", \"Boulevard\"]"
+ " },"
+ " {"
+ " \"type\": \"string\","
+ " \"enum\": [\"NW\", \"NE\", \"SW\", \"SE\"]"
+ " }"
+ " ]"
+ "}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "[1600, \"Pennsylvania\", \"Avenue\", \"NW\"]", true);
+ INVALIDATE(s, "[24, \"Sussex\", \"Drive\"]", "/items/2", "enum", "/2",
+ "{ \"enum\": { \"errorCode\": 19, \"instanceRef\": \"#/2\", \"schemaRef\": \"#/items/2\" }}");
+ INVALIDATE(s, "[\"Palais de l'Elysee\"]", "/items/0", "type", "/0",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#/0\", \"schemaRef\": \"#/items/0\","
+ " \"expected\": [\"number\"], \"actual\": \"string\""
+ "}}");
+ INVALIDATE(s, "[\"Twenty-four\", \"Sussex\", \"Drive\"]", "/items/0", "type", "/0",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#/0\", \"schemaRef\": \"#/items/0\","
+ " \"expected\": [\"number\"], \"actual\": \"string\""
+ "}}"); // fail fast
+ VALIDATE(s, "[10, \"Downing\", \"Street\"]", true);
+ VALIDATE(s, "[1600, \"Pennsylvania\", \"Avenue\", \"NW\", \"Washington\"]", true);
+}
+
+TEST(SchemaValidator, Array_AdditionalItems) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"type\": \"array\","
+ " \"items\": ["
+ " {"
+ " \"type\": \"number\""
+ " },"
+ " {"
+ " \"type\": \"string\""
+ " },"
+ " {"
+ " \"type\": \"string\","
+ " \"enum\": [\"Street\", \"Avenue\", \"Boulevard\"]"
+ " },"
+ " {"
+ " \"type\": \"string\","
+ " \"enum\": [\"NW\", \"NE\", \"SW\", \"SE\"]"
+ " }"
+ " ],"
+ " \"additionalItems\": false"
+ "}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "[1600, \"Pennsylvania\", \"Avenue\", \"NW\"]", true);
+ VALIDATE(s, "[1600, \"Pennsylvania\", \"Avenue\"]", true);
+ INVALIDATE(s, "[1600, \"Pennsylvania\", \"Avenue\", \"NW\", \"Washington\"]", "", "additionalItems", "/4",
+ "{ \"additionalItems\": {"
+ " \"errorCode\": 12,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"disallowed\": 4"
+ "}}");
+}
+
+TEST(SchemaValidator, Array_ItemsRange) {
+ Document sd;
+ sd.Parse("{\"type\": \"array\",\"minItems\": 2,\"maxItems\" : 3}");
+ SchemaDocument s(sd);
+
+ INVALIDATE(s, "[]", "", "minItems", "",
+ "{ \"minItems\": {"
+ " \"errorCode\": 10,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 2, \"actual\": 0"
+ "}}");
+ INVALIDATE(s, "[1]", "", "minItems", "",
+ "{ \"minItems\": {"
+ " \"errorCode\": 10,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 2, \"actual\": 1"
+ "}}");
+ VALIDATE(s, "[1, 2]", true);
+ VALIDATE(s, "[1, 2, 3]", true);
+ INVALIDATE(s, "[1, 2, 3, 4]", "", "maxItems", "",
+ "{ \"maxItems\": {"
+ " \"errorCode\": 9,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 3, \"actual\": 4"
+ "}}");
+}
+
+TEST(SchemaValidator, Array_UniqueItems) {
+ Document sd;
+ sd.Parse("{\"type\": \"array\", \"uniqueItems\": true}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "[1, 2, 3, 4, 5]", true);
+ INVALIDATE(s, "[1, 2, 3, 3, 4]", "", "uniqueItems", "/3",
+ "{ \"uniqueItems\": {"
+ " \"errorCode\": 11,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"duplicates\": [2, 3]"
+ "}}");
+ INVALIDATE(s, "[1, 2, 3, 3, 3]", "", "uniqueItems", "/3",
+ "{ \"uniqueItems\": {"
+ " \"errorCode\": 11,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"duplicates\": [2, 3]"
+ "}}"); // fail fast
+ VALIDATE(s, "[]", true);
+}
+
+TEST(SchemaValidator, Boolean) {
+ Document sd;
+ sd.Parse("{\"type\":\"boolean\"}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "true", true);
+ VALIDATE(s, "false", true);
+ INVALIDATE(s, "\"true\"", "", "type", "",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": [\"boolean\"], \"actual\": \"string\""
+ "}}");
+ INVALIDATE(s, "0", "", "type", "",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": [\"boolean\"], \"actual\": \"integer\""
+ "}}");
+}
+
+TEST(SchemaValidator, Null) {
+ Document sd;
+ sd.Parse("{\"type\":\"null\"}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "null", true);
+ INVALIDATE(s, "false", "", "type", "",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": [\"null\"], \"actual\": \"boolean\""
+ "}}");
+ INVALIDATE(s, "0", "", "type", "",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": [\"null\"], \"actual\": \"integer\""
+ "}}");
+ INVALIDATE(s, "\"\"", "", "type", "",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": [\"null\"], \"actual\": \"string\""
+ "}}");
+}
+
+// Additional tests
+
+TEST(SchemaValidator, ObjectInArray) {
+ Document sd;
+ sd.Parse("{\"type\":\"array\", \"items\": { \"type\":\"string\" }}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "[\"a\"]", true);
+ INVALIDATE(s, "[1]", "/items", "type", "/0",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#/0\", \"schemaRef\": \"#/items\","
+ " \"expected\": [\"string\"], \"actual\": \"integer\""
+ "}}");
+ INVALIDATE(s, "[{}]", "/items", "type", "/0",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#/0\", \"schemaRef\": \"#/items\","
+ " \"expected\": [\"string\"], \"actual\": \"object\""
+ "}}");
+}
+
+TEST(SchemaValidator, MultiTypeInObject) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"type\":\"object\","
+ " \"properties\": {"
+ " \"tel\" : {"
+ " \"type\":[\"integer\", \"string\"]"
+ " }"
+ " }"
+ "}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "{ \"tel\": 999 }", true);
+ VALIDATE(s, "{ \"tel\": \"123-456\" }", true);
+ INVALIDATE(s, "{ \"tel\": true }", "/properties/tel", "type", "/tel",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#/tel\", \"schemaRef\": \"#/properties/tel\","
+ " \"expected\": [\"string\", \"integer\"], \"actual\": \"boolean\""
+ "}}");
+}
+
+TEST(SchemaValidator, MultiTypeWithObject) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"type\": [\"object\",\"string\"],"
+ " \"properties\": {"
+ " \"tel\" : {"
+ " \"type\": \"integer\""
+ " }"
+ " }"
+ "}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "\"Hello\"", true);
+ VALIDATE(s, "{ \"tel\": 999 }", true);
+ INVALIDATE(s, "{ \"tel\": \"fail\" }", "/properties/tel", "type", "/tel",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#/tel\", \"schemaRef\": \"#/properties/tel\","
+ " \"expected\": [\"integer\"], \"actual\": \"string\""
+ "}}");
+}
+
+TEST(SchemaValidator, AllOf_Nested) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"allOf\": ["
+ " { \"type\": \"string\", \"minLength\": 2 },"
+ " { \"type\": \"string\", \"maxLength\": 5 },"
+ " { \"allOf\": [ { \"enum\" : [\"ok\", \"okay\", \"OK\", \"o\"] }, { \"enum\" : [\"ok\", \"OK\", \"o\"]} ] }"
+ " ]"
+ "}");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "\"ok\"", true);
+ VALIDATE(s, "\"OK\"", true);
+ INVALIDATE(s, "\"okay\"", "", "allOf", "",
+ "{ \"allOf\": {"
+ " \"errors\": ["
+ " {},{},"
+ " { \"allOf\": {"
+ " \"errors\": ["
+ " {},"
+ " { \"enum\": {\"errorCode\": 19, \"instanceRef\": \"#\", \"schemaRef\": \"#/allOf/2/allOf/1\" }}"
+ " ],"
+ " \"errorCode\": 23, \"instanceRef\": \"#\", \"schemaRef\": \"#/allOf/2\""
+ " }}],"
+ " \"errorCode\": 23, \"instanceRef\": \"#\", \"schemaRef\": \"#\""
+ "}}");
+ INVALIDATE(s, "\"o\"", "", "allOf", "",
+ "{ \"allOf\": {"
+ " \"errors\": ["
+ " { \"minLength\": {\"actual\": \"o\", \"expected\": 2, \"errorCode\": 7, \"instanceRef\": \"#\", \"schemaRef\": \"#/allOf/0\" }},"
+ " {},{}"
+ " ],"
+ " \"errorCode\": 23, \"instanceRef\": \"#\", \"schemaRef\": \"#\""
+ "}}");
+ INVALIDATE(s, "\"n\"", "", "allOf", "",
+ "{ \"allOf\": {"
+ " \"errors\": ["
+ " { \"minLength\": {\"actual\": \"n\", \"expected\": 2, \"errorCode\": 7, \"instanceRef\": \"#\", \"schemaRef\": \"#/allOf/0\" }},"
+ " {},"
+ " { \"allOf\": {"
+ " \"errors\": ["
+ " { \"enum\": {\"errorCode\": 19 ,\"instanceRef\": \"#\", \"schemaRef\": \"#/allOf/2/allOf/0\"}},"
+ " { \"enum\": {\"errorCode\": 19, \"instanceRef\": \"#\", \"schemaRef\": \"#/allOf/2/allOf/1\"}}"
+ " ],"
+ " \"errorCode\": 23, \"instanceRef\": \"#\", \"schemaRef\": \"#/allOf/2\""
+ " }}"
+ " ],"
+ " \"errorCode\":23,\"instanceRef\":\"#\",\"schemaRef\":\"#\""
+ "}}");
+ INVALIDATE(s, "\"too long\"", "", "allOf", "",
+ "{ \"allOf\": {"
+ " \"errors\": ["
+ " {},"
+ " { \"maxLength\": {\"actual\": \"too long\", \"expected\": 5, \"errorCode\": 6, \"instanceRef\": \"#\", \"schemaRef\": \"#/allOf/1\" }},"
+ " { \"allOf\": {"
+ " \"errors\": ["
+ " { \"enum\": {\"errorCode\": 19 ,\"instanceRef\": \"#\", \"schemaRef\": \"#/allOf/2/allOf/0\"}},"
+ " { \"enum\": {\"errorCode\": 19, \"instanceRef\": \"#\", \"schemaRef\": \"#/allOf/2/allOf/1\"}}"
+ " ],"
+ " \"errorCode\": 23, \"instanceRef\": \"#\", \"schemaRef\": \"#/allOf/2\""
+ " }}"
+ " ],"
+ " \"errorCode\":23,\"instanceRef\":\"#\",\"schemaRef\":\"#\""
+ "}}");
+ INVALIDATE(s, "123", "", "allOf", "",
+ "{ \"allOf\": {"
+ " \"errors\": ["
+ " {\"type\": {\"expected\": [\"string\"], \"actual\": \"integer\", \"errorCode\": 20, \"instanceRef\": \"#\", \"schemaRef\": \"#/allOf/0\"}},"
+ " {\"type\": {\"expected\": [\"string\"], \"actual\": \"integer\", \"errorCode\": 20, \"instanceRef\": \"#\", \"schemaRef\": \"#/allOf/1\"}},"
+ " { \"allOf\": {"
+ " \"errors\": ["
+ " { \"enum\": {\"errorCode\": 19 ,\"instanceRef\": \"#\", \"schemaRef\": \"#/allOf/2/allOf/0\"}},"
+ " { \"enum\": {\"errorCode\": 19, \"instanceRef\": \"#\", \"schemaRef\": \"#/allOf/2/allOf/1\"}}"
+ " ],"
+ " \"errorCode\": 23, \"instanceRef\": \"#\", \"schemaRef\": \"#/allOf/2\""
+ " }}"
+ " ],"
+ " \"errorCode\":23,\"instanceRef\":\"#\",\"schemaRef\":\"#\""
+ "}}");
+}
+
+TEST(SchemaValidator, EscapedPointer) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"type\": \"object\","
+ " \"properties\": {"
+ " \"~/\": { \"type\": \"number\" }"
+ " }"
+ "}");
+ SchemaDocument s(sd);
+ INVALIDATE(s, "{\"~/\":true}", "/properties/~0~1", "type", "/~0~1",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#/~0~1\", \"schemaRef\": \"#/properties/~0~1\","
+ " \"expected\": [\"number\"], \"actual\": \"boolean\""
+ "}}");
+}
+
+TEST(SchemaValidator, SchemaPointer) {
+ Document sd;
+ sd.Parse(
+ "{"
+ " \"swagger\": \"2.0\","
+ " \"paths\": {"
+ " \"/some/path\": {"
+ " \"post\": {"
+ " \"parameters\": ["
+ " {"
+ " \"in\": \"body\","
+ " \"name\": \"body\","
+ " \"schema\": {"
+ " \"properties\": {"
+ " \"a\": {"
+ " \"$ref\": \"#/definitions/Prop_a\""
+ " },"
+ " \"b\": {"
+ " \"type\": \"integer\""
+ " }"
+ " },"
+ " \"type\": \"object\""
+ " }"
+ " }"
+ " ],"
+ " \"responses\": {"
+ " \"200\": {"
+ " \"schema\": {"
+ " \"$ref\": \"#/definitions/Resp_200\""
+ " }"
+ " }"
+ " }"
+ " }"
+ " }"
+ " },"
+ " \"definitions\": {"
+ " \"Prop_a\": {"
+ " \"properties\": {"
+ " \"c\": {"
+ " \"enum\": ["
+ " \"C1\","
+ " \"C2\","
+ " \"C3\""
+ " ],"
+ " \"type\": \"string\""
+ " },"
+ " \"d\": {"
+ " \"$ref\": \"#/definitions/Prop_d\""
+ " },"
+ " \"s\": {"
+ " \"type\": \"string\""
+ " }"
+ " },"
+ " \"required\": [\"c\"],"
+ " \"type\": \"object\""
+ " },"
+ " \"Prop_d\": {"
+ " \"properties\": {"
+ " \"a\": {"
+ " \"$ref\": \"#/definitions/Prop_a\""
+ " },"
+ " \"c\": {"
+ " \"$ref\": \"#/definitions/Prop_a/properties/c\""
+ " }"
+ " },"
+ " \"type\": \"object\""
+ " },"
+ " \"Resp_200\": {"
+ " \"properties\": {"
+ " \"e\": {"
+ " \"type\": \"string\""
+ " },"
+ " \"f\": {"
+ " \"type\": \"boolean\""
+ " },"
+ " \"cyclic_source\": {"
+ " \"$ref\": \"#/definitions/Resp_200/properties/cyclic_target\""
+ " },"
+ " \"cyclic_target\": {"
+ " \"$ref\": \"#/definitions/Resp_200/properties/cyclic_source\""
+ " }"
+ " },"
+ " \"type\": \"object\""
+ " }"
+ " }"
+ "}");
+ SchemaDocument s1(sd, NULL, 0, NULL, NULL, Pointer("#/paths/~1some~1path/post/parameters/0/schema"));
+ VALIDATE(s1,
+ "{"
+ " \"a\": {"
+ " \"c\": \"C1\","
+ " \"d\": {"
+ " \"a\": {"
+ " \"c\": \"C2\""
+ " },"
+ " \"c\": \"C3\""
+ " }"
+ " },"
+ " \"b\": 123"
+ "}",
+ true);
+ INVALIDATE(s1,
+ "{"
+ " \"a\": {"
+ " \"c\": \"C1\","
+ " \"d\": {"
+ " \"a\": {"
+ " \"c\": \"C2\""
+ " },"
+ " \"c\": \"C3\""
+ " }"
+ " },"
+ " \"b\": \"should be an int\""
+ "}",
+ "#/paths/~1some~1path/post/parameters/0/schema/properties/b", "type", "#/b",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\":\"#/b\","
+ " \"schemaRef\":\"#/paths/~1some~1path/post/parameters/0/schema/properties/b\","
+ " \"expected\": [\"integer\"], \"actual\":\"string\""
+ "}}");
+ INVALIDATE(s1,
+ "{"
+ " \"a\": {"
+ " \"c\": \"C1\","
+ " \"d\": {"
+ " \"a\": {"
+ " \"c\": \"should be within enum\""
+ " },"
+ " \"c\": \"C3\""
+ " }"
+ " },"
+ " \"b\": 123"
+ "}",
+ "#/definitions/Prop_a/properties/c", "enum", "#/a/d/a/c",
+ "{ \"enum\": {"
+ " \"errorCode\": 19,"
+ " \"instanceRef\":\"#/a/d/a/c\","
+ " \"schemaRef\":\"#/definitions/Prop_a/properties/c\""
+ "}}");
+ INVALIDATE(s1,
+ "{"
+ " \"a\": {"
+ " \"c\": \"C1\","
+ " \"d\": {"
+ " \"a\": {"
+ " \"s\": \"required 'c' is missing\""
+ " }"
+ " }"
+ " },"
+ " \"b\": 123"
+ "}",
+ "#/definitions/Prop_a", "required", "#/a/d/a",
+ "{ \"required\": {"
+ " \"errorCode\": 15,"
+ " \"missing\":[\"c\"],"
+ " \"instanceRef\":\"#/a/d/a\","
+ " \"schemaRef\":\"#/definitions/Prop_a\""
+ "}}");
+ SchemaDocument s2(sd, NULL, 0, NULL, NULL, Pointer("#/paths/~1some~1path/post/responses/200/schema"));
+ VALIDATE(s2,
+ "{ \"e\": \"some string\", \"f\": false }",
+ true);
+ INVALIDATE(s2,
+ "{ \"e\": true, \"f\": false }",
+ "#/definitions/Resp_200/properties/e", "type", "#/e",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\":\"#/e\","
+ " \"schemaRef\":\"#/definitions/Resp_200/properties/e\","
+ " \"expected\": [\"string\"], \"actual\":\"boolean\""
+ "}}");
+ INVALIDATE(s2,
+ "{ \"e\": \"some string\", \"f\": 123 }",
+ "#/definitions/Resp_200/properties/f", "type", "#/f",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\":\"#/f\","
+ " \"schemaRef\":\"#/definitions/Resp_200/properties/f\","
+ " \"expected\": [\"boolean\"], \"actual\":\"integer\""
+ "}}");
+}
+
+template <typename Allocator>
+static char* ReadFile(const char* filename, Allocator& allocator) {
+ const char *paths[] = {
+ "",
+ "bin/",
+ "../bin/",
+ "../../bin/",
+ "../../../bin/"
+ };
+ char buffer[1024];
+ FILE *fp = 0;
+ for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) {
+ sprintf(buffer, "%s%s", paths[i], filename);
+ fp = fopen(buffer, "rb");
+ if (fp)
+ break;
+ }
+
+ if (!fp)
+ return 0;
+
+ fseek(fp, 0, SEEK_END);
+ size_t length = static_cast<size_t>(ftell(fp));
+ fseek(fp, 0, SEEK_SET);
+ char* json = reinterpret_cast<char*>(allocator.Malloc(length + 1));
+ size_t readLength = fread(json, 1, length, fp);
+ json[readLength] = '\0';
+ fclose(fp);
+ return json;
+}
+
+TEST(SchemaValidator, ValidateMetaSchema) {
+ CrtAllocator allocator;
+ char* json = ReadFile("draft-04/schema", allocator);
+ Document d;
+ d.Parse(json);
+ ASSERT_FALSE(d.HasParseError());
+ SchemaDocument sd(d);
+ SchemaValidator validator(sd);
+ d.Accept(validator);
+ if (!validator.IsValid()) {
+ StringBuffer sb;
+ validator.GetInvalidSchemaPointer().StringifyUriFragment(sb);
+ printf("Invalid schema: %s\n", sb.GetString());
+ printf("Invalid keyword: %s\n", validator.GetInvalidSchemaKeyword());
+ printf("Invalid code: %d\n", validator.GetInvalidSchemaCode());
+ printf("Invalid message: %s\n", GetValidateError_En(validator.GetInvalidSchemaCode()));
+ sb.Clear();
+ validator.GetInvalidDocumentPointer().StringifyUriFragment(sb);
+ printf("Invalid document: %s\n", sb.GetString());
+ sb.Clear();
+ Writer<StringBuffer> w(sb);
+ validator.GetError().Accept(w);
+ printf("Validation error: %s\n", sb.GetString());
+ ADD_FAILURE();
+ }
+ CrtAllocator::Free(json);
+}
+
+TEST(SchemaValidator, ValidateMetaSchema_UTF16) {
+ typedef GenericDocument<UTF16<> > D;
+ typedef GenericSchemaDocument<D::ValueType> SD;
+ typedef GenericSchemaValidator<SD> SV;
+
+ CrtAllocator allocator;
+ char* json = ReadFile("draft-04/schema", allocator);
+
+ D d;
+ StringStream ss(json);
+ d.ParseStream<0, UTF8<> >(ss);
+ ASSERT_FALSE(d.HasParseError());
+ SD sd(d);
+ SV validator(sd);
+ d.Accept(validator);
+ if (!validator.IsValid()) {
+ GenericStringBuffer<UTF16<> > sb;
+ validator.GetInvalidSchemaPointer().StringifyUriFragment(sb);
+ wprintf(L"Invalid schema: %ls\n", sb.GetString());
+ wprintf(L"Invalid keyword: %ls\n", validator.GetInvalidSchemaKeyword());
+ sb.Clear();
+ validator.GetInvalidDocumentPointer().StringifyUriFragment(sb);
+ wprintf(L"Invalid document: %ls\n", sb.GetString());
+ sb.Clear();
+ Writer<GenericStringBuffer<UTF16<> >, UTF16<> > w(sb);
+ validator.GetError().Accept(w);
+ printf("Validation error: %ls\n", sb.GetString());
+ ADD_FAILURE();
+ }
+ CrtAllocator::Free(json);
+}
+
+template <typename SchemaDocumentType = SchemaDocument>
+class RemoteSchemaDocumentProvider : public IGenericRemoteSchemaDocumentProvider<SchemaDocumentType> {
+public:
+ RemoteSchemaDocumentProvider() :
+ documentAllocator_(documentBuffer_, sizeof(documentBuffer_)),
+ schemaAllocator_(schemaBuffer_, sizeof(schemaBuffer_))
+ {
+ const char* filenames[kCount] = {
+ "jsonschema/remotes/integer.json",
+ "jsonschema/remotes/subSchemas.json",
+ "jsonschema/remotes/folder/folderInteger.json",
+ "draft-04/schema",
+ "unittestschema/address.json"
+ };
+ const char* uris[kCount] = {
+ "http://localhost:1234/integer.json",
+ "http://localhost:1234/subSchemas.json",
+ "http://localhost:1234/folder/folderInteger.json",
+ "http://json-schema.org/draft-04/schema",
+ "http://localhost:1234/address.json"
+ };
+
+ for (size_t i = 0; i < kCount; i++) {
+ sd_[i] = 0;
+
+ char jsonBuffer[8192];
+ MemoryPoolAllocator<> jsonAllocator(jsonBuffer, sizeof(jsonBuffer));
+ char* json = ReadFile(filenames[i], jsonAllocator);
+ if (!json) {
+ printf("json remote file %s not found", filenames[i]);
+ ADD_FAILURE();
+ }
+ else {
+ char stackBuffer[4096];
+ MemoryPoolAllocator<> stackAllocator(stackBuffer, sizeof(stackBuffer));
+ DocumentType d(&documentAllocator_, 1024, &stackAllocator);
+ d.Parse(json);
+ sd_[i] = new SchemaDocumentType(d, uris[i], static_cast<SizeType>(strlen(uris[i])), 0, &schemaAllocator_);
+ MemoryPoolAllocator<>::Free(json);
+ }
+ };
+ }
+
+ ~RemoteSchemaDocumentProvider() {
+ for (size_t i = 0; i < kCount; i++)
+ delete sd_[i];
+ }
+
+ virtual const SchemaDocumentType* GetRemoteDocument(const char* uri, SizeType length) {
+ for (size_t i = 0; i < kCount; i++)
+ if (typename SchemaDocumentType::SValue(uri, length) == sd_[i]->GetURI())
+ return sd_[i];
+ return 0;
+ }
+
+private:
+ typedef GenericDocument<typename SchemaDocumentType::EncodingType, MemoryPoolAllocator<>, MemoryPoolAllocator<> > DocumentType;
+
+ RemoteSchemaDocumentProvider(const RemoteSchemaDocumentProvider&);
+ RemoteSchemaDocumentProvider& operator=(const RemoteSchemaDocumentProvider&);
+
+ static const size_t kCount = 5;
+ SchemaDocumentType* sd_[kCount];
+ typename DocumentType::AllocatorType documentAllocator_;
+ typename SchemaDocumentType::AllocatorType schemaAllocator_;
+ char documentBuffer_[16384];
+ char schemaBuffer_[128u * 1024];
+};
+
+TEST(SchemaValidator, TestSuite) {
+ const char* filenames[] = {
+ "additionalItems.json",
+ "additionalProperties.json",
+ "allOf.json",
+ "anyOf.json",
+ "default.json",
+ "definitions.json",
+ "dependencies.json",
+ "enum.json",
+ "items.json",
+ "maximum.json",
+ "maxItems.json",
+ "maxLength.json",
+ "maxProperties.json",
+ "minimum.json",
+ "minItems.json",
+ "minLength.json",
+ "minProperties.json",
+ "multipleOf.json",
+ "not.json",
+ "oneOf.json",
+ "pattern.json",
+ "patternProperties.json",
+ "properties.json",
+ "ref.json",
+ "refRemote.json",
+ "required.json",
+ "type.json",
+ "uniqueItems.json"
+ };
+
+ const char* onlyRunDescription = 0;
+ //const char* onlyRunDescription = "a string is a string";
+
+ unsigned testCount = 0;
+ unsigned passCount = 0;
+
+ typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
+ RemoteSchemaDocumentProvider<SchemaDocumentType> provider;
+
+ char jsonBuffer[65536];
+ char documentBuffer[65536];
+ char documentStackBuffer[65536];
+ char schemaBuffer[65536];
+ char validatorBuffer[65536];
+ MemoryPoolAllocator<> jsonAllocator(jsonBuffer, sizeof(jsonBuffer));
+ MemoryPoolAllocator<> documentAllocator(documentBuffer, sizeof(documentBuffer));
+ MemoryPoolAllocator<> documentStackAllocator(documentStackBuffer, sizeof(documentStackBuffer));
+ MemoryPoolAllocator<> schemaAllocator(schemaBuffer, sizeof(schemaBuffer));
+ MemoryPoolAllocator<> validatorAllocator(validatorBuffer, sizeof(validatorBuffer));
+
+ for (size_t i = 0; i < sizeof(filenames) / sizeof(filenames[0]); i++) {
+ char filename[FILENAME_MAX];
+ sprintf(filename, "jsonschema/tests/draft4/%s", filenames[i]);
+ char* json = ReadFile(filename, jsonAllocator);
+ if (!json) {
+ printf("json test suite file %s not found", filename);
+ ADD_FAILURE();
+ }
+ else {
+ //printf("\njson test suite file %s parsed ok\n", filename);
+ GenericDocument<UTF8<>, MemoryPoolAllocator<>, MemoryPoolAllocator<> > d(&documentAllocator, 1024, &documentStackAllocator);
+ d.Parse(json);
+ if (d.HasParseError()) {
+ printf("json test suite file %s has parse error", filename);
+ ADD_FAILURE();
+ }
+ else {
+ for (Value::ConstValueIterator schemaItr = d.Begin(); schemaItr != d.End(); ++schemaItr) {
+ {
+ const char* description1 = (*schemaItr)["description"].GetString();
+ //printf("\ncompiling schema for json test %s \n", description1);
+ SchemaDocumentType schema((*schemaItr)["schema"], filenames[i], static_cast<SizeType>(strlen(filenames[i])), &provider, &schemaAllocator);
+ GenericSchemaValidator<SchemaDocumentType, BaseReaderHandler<UTF8<> >, MemoryPoolAllocator<> > validator(schema, &validatorAllocator);
+ const Value& tests = (*schemaItr)["tests"];
+ for (Value::ConstValueIterator testItr = tests.Begin(); testItr != tests.End(); ++testItr) {
+ const char* description2 = (*testItr)["description"].GetString();
+ //printf("running json test %s \n", description2);
+ if (!onlyRunDescription || strcmp(description2, onlyRunDescription) == 0) {
+ const Value& data = (*testItr)["data"];
+ bool expected = (*testItr)["valid"].GetBool();
+ testCount++;
+ validator.Reset();
+ data.Accept(validator);
+ bool actual = validator.IsValid();
+ if (expected != actual)
+ printf("Fail: %30s \"%s\" \"%s\"\n", filename, description1, description2);
+ else {
+ //printf("Passed: %30s \"%s\" \"%s\"\n", filename, description1, description2);
+ passCount++;
+ }
+ }
+ }
+ //printf("%zu %zu %zu\n", documentAllocator.Size(), schemaAllocator.Size(), validatorAllocator.Size());
+ }
+ schemaAllocator.Clear();
+ validatorAllocator.Clear();
+ }
+ }
+ }
+ documentAllocator.Clear();
+ MemoryPoolAllocator<>::Free(json);
+ jsonAllocator.Clear();
+ }
+ printf("%d / %d passed (%2d%%)\n", passCount, testCount, passCount * 100 / testCount);
+ if (passCount != testCount)
+ ADD_FAILURE();
+}
+
+TEST(SchemaValidatingReader, Simple) {
+ Document sd;
+ sd.Parse("{ \"type\": \"string\", \"enum\" : [\"red\", \"amber\", \"green\"] }");
+ SchemaDocument s(sd);
+
+ Document d;
+ StringStream ss("\"red\"");
+ SchemaValidatingReader<kParseDefaultFlags, StringStream, UTF8<> > reader(ss, s);
+ d.Populate(reader);
+ EXPECT_TRUE(reader.GetParseResult());
+ EXPECT_TRUE(reader.IsValid());
+ EXPECT_TRUE(d.IsString());
+ EXPECT_STREQ("red", d.GetString());
+}
+
+TEST(SchemaValidatingReader, Invalid) {
+ Document sd;
+ sd.Parse("{\"type\":\"string\",\"minLength\":2,\"maxLength\":3}");
+ SchemaDocument s(sd);
+
+ Document d;
+ StringStream ss("\"ABCD\"");
+ SchemaValidatingReader<kParseDefaultFlags, StringStream, UTF8<> > reader(ss, s);
+ d.Populate(reader);
+ EXPECT_FALSE(reader.GetParseResult());
+ EXPECT_FALSE(reader.IsValid());
+ EXPECT_EQ(kParseErrorTermination, reader.GetParseResult().Code());
+ EXPECT_STREQ("maxLength", reader.GetInvalidSchemaKeyword());
+ EXPECT_TRUE(reader.GetInvalidSchemaCode() == kValidateErrorMaxLength);
+ EXPECT_TRUE(reader.GetInvalidSchemaPointer() == SchemaDocument::PointerType(""));
+ EXPECT_TRUE(reader.GetInvalidDocumentPointer() == SchemaDocument::PointerType(""));
+ EXPECT_TRUE(d.IsNull());
+ Document e;
+ e.Parse(
+ "{ \"maxLength\": {"
+ " \"errorCode\": 6,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 3, \"actual\": \"ABCD\""
+ "}}");
+ if (e != reader.GetError()) {
+ ADD_FAILURE();
+ }
+}
+
+TEST(SchemaValidatingWriter, Simple) {
+ Document sd;
+ sd.Parse("{\"type\":\"string\",\"minLength\":2,\"maxLength\":3}");
+ SchemaDocument s(sd);
+
+ Document d;
+ StringBuffer sb;
+ Writer<StringBuffer> writer(sb);
+ GenericSchemaValidator<SchemaDocument, Writer<StringBuffer> > validator(s, writer);
+
+ d.Parse("\"red\"");
+ EXPECT_TRUE(d.Accept(validator));
+ EXPECT_TRUE(validator.IsValid());
+ EXPECT_STREQ("\"red\"", sb.GetString());
+
+ sb.Clear();
+ validator.Reset();
+ d.Parse("\"ABCD\"");
+ EXPECT_FALSE(d.Accept(validator));
+ EXPECT_FALSE(validator.IsValid());
+ EXPECT_TRUE(validator.GetInvalidSchemaPointer() == SchemaDocument::PointerType(""));
+ EXPECT_TRUE(validator.GetInvalidDocumentPointer() == SchemaDocument::PointerType(""));
+ EXPECT_TRUE(validator.GetInvalidSchemaCode() == kValidateErrorMaxLength);
+ Document e;
+ e.Parse(
+ "{ \"maxLength\": {"
+" \"errorCode\": 6,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": 3, \"actual\": \"ABCD\""
+ "}}");
+ EXPECT_EQ(e, validator.GetError());
+}
+
+TEST(Schema, Issue848) {
+ rapidjson::Document d;
+ rapidjson::SchemaDocument s(d);
+ rapidjson::GenericSchemaValidator<rapidjson::SchemaDocument, rapidjson::Document> v(s);
+}
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+
+static SchemaDocument ReturnSchemaDocument() {
+ Document sd;
+ sd.Parse("{ \"type\": [\"number\", \"string\"] }");
+ SchemaDocument s(sd);
+ return s;
+}
+
+TEST(Schema, Issue552) {
+ SchemaDocument s = ReturnSchemaDocument();
+ VALIDATE(s, "42", true);
+ VALIDATE(s, "\"Life, the universe, and everything\"", true);
+ INVALIDATE(s, "[\"Life\", \"the universe\", \"and everything\"]", "", "type", "",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"expected\": [\"string\", \"number\"], \"actual\": \"array\""
+ "}}");
+}
+
+#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
+
+TEST(SchemaValidator, Issue608) {
+ Document sd;
+ sd.Parse("{\"required\": [\"a\", \"b\"] }");
+ SchemaDocument s(sd);
+
+ VALIDATE(s, "{\"a\" : null, \"b\": null}", true);
+ INVALIDATE(s, "{\"a\" : null, \"a\" : null}", "", "required", "",
+ "{ \"required\": {"
+ " \"errorCode\": 15,"
+ " \"instanceRef\": \"#\", \"schemaRef\": \"#\","
+ " \"missing\": [\"b\"]"
+ "}}");
+}
+
+// Fail to resolve $ref in allOf causes crash in SchemaValidator::StartObject()
+TEST(SchemaValidator, Issue728_AllOfRef) {
+ Document sd;
+ sd.Parse("{\"allOf\": [{\"$ref\": \"#/abc\"}]}");
+ SchemaDocument s(sd);
+ VALIDATE(s, "{\"key1\": \"abc\", \"key2\": \"def\"}", true);
+}
+
+TEST(SchemaValidator, Issue1017_allOfHandler) {
+ Document sd;
+ sd.Parse("{\"allOf\": [{\"type\": \"object\",\"properties\": {\"cyanArray2\": {\"type\": \"array\",\"items\": { \"type\": \"string\" }}}},{\"type\": \"object\",\"properties\": {\"blackArray\": {\"type\": \"array\",\"items\": { \"type\": \"string\" }}},\"required\": [ \"blackArray\" ]}]}");
+ SchemaDocument s(sd);
+ StringBuffer sb;
+ Writer<StringBuffer> writer(sb);
+ GenericSchemaValidator<SchemaDocument, Writer<StringBuffer> > validator(s, writer);
+ EXPECT_TRUE(validator.StartObject());
+ EXPECT_TRUE(validator.Key("cyanArray2", 10, false));
+ EXPECT_TRUE(validator.StartArray());
+ EXPECT_TRUE(validator.EndArray(0));
+ EXPECT_TRUE(validator.Key("blackArray", 10, false));
+ EXPECT_TRUE(validator.StartArray());
+ EXPECT_TRUE(validator.EndArray(0));
+ EXPECT_TRUE(validator.EndObject(0));
+ EXPECT_TRUE(validator.IsValid());
+ EXPECT_STREQ("{\"cyanArray2\":[],\"blackArray\":[]}", sb.GetString());
+}
+
+TEST(SchemaValidator, Ref_remote) {
+ typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
+ RemoteSchemaDocumentProvider<SchemaDocumentType> provider;
+ Document sd;
+ sd.Parse("{\"$ref\": \"http://localhost:1234/subSchemas.json#/integer\"}");
+ SchemaDocumentType s(sd, 0, 0, &provider);
+ typedef GenericSchemaValidator<SchemaDocumentType, BaseReaderHandler<UTF8<> >, MemoryPoolAllocator<> > SchemaValidatorType;
+ typedef GenericPointer<Value, MemoryPoolAllocator<> > PointerType;
+ INVALIDATE_(s, "null", "/integer", "type", "",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#\","
+ " \"schemaRef\": \"http://localhost:1234/subSchemas.json#/integer\","
+ " \"expected\": [\"integer\"], \"actual\": \"null\""
+ "}}",
+ kValidateDefaultFlags, SchemaValidatorType, PointerType);
+}
+
+// Merge with id where $ref is full URI
+TEST(SchemaValidator, Ref_remote_change_resolution_scope_uri) {
+ typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
+ RemoteSchemaDocumentProvider<SchemaDocumentType> provider;
+ Document sd;
+ sd.Parse("{\"id\": \"http://ignore/blah#/ref\", \"type\": \"object\", \"properties\": {\"myInt\": {\"$ref\": \"http://localhost:1234/subSchemas.json#/integer\"}}}");
+ SchemaDocumentType s(sd, 0, 0, &provider);
+ typedef GenericSchemaValidator<SchemaDocumentType, BaseReaderHandler<UTF8<> >, MemoryPoolAllocator<> > SchemaValidatorType;
+ typedef GenericPointer<Value, MemoryPoolAllocator<> > PointerType;
+ INVALIDATE_(s, "{\"myInt\": null}", "/integer", "type", "/myInt",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#/myInt\","
+ " \"schemaRef\": \"http://localhost:1234/subSchemas.json#/integer\","
+ " \"expected\": [\"integer\"], \"actual\": \"null\""
+ "}}",
+ kValidateDefaultFlags, SchemaValidatorType, PointerType);
+}
+
+// Merge with id where $ref is a relative path
+TEST(SchemaValidator, Ref_remote_change_resolution_scope_relative_path) {
+ typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
+ RemoteSchemaDocumentProvider<SchemaDocumentType> provider;
+ Document sd;
+ sd.Parse("{\"id\": \"http://localhost:1234/\", \"type\": \"object\", \"properties\": {\"myInt\": {\"$ref\": \"subSchemas.json#/integer\"}}}");
+ SchemaDocumentType s(sd, 0, 0, &provider);
+ typedef GenericSchemaValidator<SchemaDocumentType, BaseReaderHandler<UTF8<> >, MemoryPoolAllocator<> > SchemaValidatorType;
+ typedef GenericPointer<Value, MemoryPoolAllocator<> > PointerType;
+ INVALIDATE_(s, "{\"myInt\": null}", "/integer", "type", "/myInt",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#/myInt\","
+ " \"schemaRef\": \"http://localhost:1234/subSchemas.json#/integer\","
+ " \"expected\": [\"integer\"], \"actual\": \"null\""
+ "}}",
+ kValidateDefaultFlags, SchemaValidatorType, PointerType);
+}
+
+// Merge with id where $ref is an absolute path
+TEST(SchemaValidator, Ref_remote_change_resolution_scope_absolute_path) {
+ typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
+ RemoteSchemaDocumentProvider<SchemaDocumentType> provider;
+ Document sd;
+ sd.Parse("{\"id\": \"http://localhost:1234/xxxx\", \"type\": \"object\", \"properties\": {\"myInt\": {\"$ref\": \"/subSchemas.json#/integer\"}}}");
+ SchemaDocumentType s(sd, 0, 0, &provider);
+ typedef GenericSchemaValidator<SchemaDocumentType, BaseReaderHandler<UTF8<> >, MemoryPoolAllocator<> > SchemaValidatorType;
+ typedef GenericPointer<Value, MemoryPoolAllocator<> > PointerType;
+ INVALIDATE_(s, "{\"myInt\": null}", "/integer", "type", "/myInt",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#/myInt\","
+ " \"schemaRef\": \"http://localhost:1234/subSchemas.json#/integer\","
+ " \"expected\": [\"integer\"], \"actual\": \"null\""
+ "}}",
+ kValidateDefaultFlags, SchemaValidatorType, PointerType);
+}
+
+// Merge with id where $ref is an absolute path, and the document has a base URI
+TEST(SchemaValidator, Ref_remote_change_resolution_scope_absolute_path_document) {
+ typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
+ RemoteSchemaDocumentProvider<SchemaDocumentType> provider;
+ Document sd;
+ sd.Parse("{\"type\": \"object\", \"properties\": {\"myInt\": {\"$ref\": \"/subSchemas.json#/integer\"}}}");
+ SchemaDocumentType s(sd, "http://localhost:1234/xxxx", 26, &provider);
+ typedef GenericSchemaValidator<SchemaDocumentType, BaseReaderHandler<UTF8<> >, MemoryPoolAllocator<> > SchemaValidatorType;
+ typedef GenericPointer<Value, MemoryPoolAllocator<> > PointerType;
+ INVALIDATE_(s, "{\"myInt\": null}", "/integer", "type", "/myInt",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#/myInt\","
+ " \"schemaRef\": \"http://localhost:1234/subSchemas.json#/integer\","
+ " \"expected\": [\"integer\"], \"actual\": \"null\""
+ "}}",
+ kValidateDefaultFlags, SchemaValidatorType, PointerType);
+}
+
+// $ref is a non-JSON pointer fragment and there a matching id
+TEST(SchemaValidator, Ref_internal_id_1) {
+ typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
+ Document sd;
+ sd.Parse("{\"type\": \"object\", \"properties\": {\"myInt1\": {\"$ref\": \"#myId\"}, \"myStr\": {\"type\": \"string\", \"id\": \"#myStrId\"}, \"myInt2\": {\"type\": \"integer\", \"id\": \"#myId\"}}}");
+ SchemaDocumentType s(sd);
+ typedef GenericSchemaValidator<SchemaDocumentType, BaseReaderHandler<UTF8<> >, MemoryPoolAllocator<> > SchemaValidatorType;
+ typedef GenericPointer<Value, MemoryPoolAllocator<> > PointerType;
+ INVALIDATE_(s, "{\"myInt1\": null}", "/properties/myInt2", "type", "/myInt1",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#/myInt1\","
+ " \"schemaRef\": \"#/properties/myInt2\","
+ " \"expected\": [\"integer\"], \"actual\": \"null\""
+ "}}",
+ kValidateDefaultFlags, SchemaValidatorType, PointerType);
+}
+
+// $ref is a non-JSON pointer fragment and there are two matching ids so we take the first
+TEST(SchemaValidator, Ref_internal_id_2) {
+ typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
+ Document sd;
+ sd.Parse("{\"type\": \"object\", \"properties\": {\"myInt1\": {\"$ref\": \"#myId\"}, \"myInt2\": {\"type\": \"integer\", \"id\": \"#myId\"}, \"myStr\": {\"type\": \"string\", \"id\": \"#myId\"}}}");
+ SchemaDocumentType s(sd);
+ typedef GenericSchemaValidator<SchemaDocumentType, BaseReaderHandler<UTF8<> >, MemoryPoolAllocator<> > SchemaValidatorType;
+ typedef GenericPointer<Value, MemoryPoolAllocator<> > PointerType;
+ INVALIDATE_(s, "{\"myInt1\": null}", "/properties/myInt2", "type", "/myInt1",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#/myInt1\","
+ " \"schemaRef\": \"#/properties/myInt2\","
+ " \"expected\": [\"integer\"], \"actual\": \"null\""
+ "}}",
+ kValidateDefaultFlags, SchemaValidatorType, PointerType);
+}
+
+// $ref is a non-JSON pointer fragment and there is a matching id within array
+TEST(SchemaValidator, Ref_internal_id_in_array) {
+ typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
+ Document sd;
+ sd.Parse("{\"type\": \"object\", \"properties\": {\"myInt1\": {\"$ref\": \"#myId\"}, \"myInt2\": {\"anyOf\": [{\"type\": \"string\", \"id\": \"#myStrId\"}, {\"type\": \"integer\", \"id\": \"#myId\"}]}}}");
+ SchemaDocumentType s(sd);
+ typedef GenericSchemaValidator<SchemaDocumentType, BaseReaderHandler<UTF8<> >, MemoryPoolAllocator<> > SchemaValidatorType;
+ typedef GenericPointer<Value, MemoryPoolAllocator<> > PointerType;
+ INVALIDATE_(s, "{\"myInt1\": null}", "/properties/myInt2/anyOf/1", "type", "/myInt1",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#/myInt1\","
+ " \"schemaRef\": \"#/properties/myInt2/anyOf/1\","
+ " \"expected\": [\"integer\"], \"actual\": \"null\""
+ "}}",
+ kValidateDefaultFlags, SchemaValidatorType, PointerType);
+}
+
+// $ref is a non-JSON pointer fragment and there is a matching id, and the schema is embedded in the document
+TEST(SchemaValidator, Ref_internal_id_and_schema_pointer) {
+ typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
+ Document sd;
+ sd.Parse("{ \"schema\": {\"type\": \"object\", \"properties\": {\"myInt1\": {\"$ref\": \"#myId\"}, \"myInt2\": {\"anyOf\": [{\"type\": \"integer\", \"id\": \"#myId\"}]}}}}");
+ typedef GenericPointer<Value, MemoryPoolAllocator<> > PointerType;
+ SchemaDocumentType s(sd, 0, 0, 0, 0, PointerType("/schema"));
+ typedef GenericSchemaValidator<SchemaDocumentType, BaseReaderHandler<UTF8<> >, MemoryPoolAllocator<> > SchemaValidatorType;
+ INVALIDATE_(s, "{\"myInt1\": null}", "/schema/properties/myInt2/anyOf/0", "type", "/myInt1",
+ "{ \"type\": {"
+ " \"errorCode\": 20,"
+ " \"instanceRef\": \"#/myInt1\","
+ " \"schemaRef\": \"#/schema/properties/myInt2/anyOf/0\","
+ " \"expected\": [\"integer\"], \"actual\": \"null\""
+ "}}",
+ kValidateDefaultFlags, SchemaValidatorType, PointerType);
+}
+
+// Test that $refs are correctly resolved when intermediate multiple ids are present
+// Includes $ref to a part of the document with a different in-scope id, which also contains $ref..
+TEST(SchemaValidator, Ref_internal_multiple_ids) {
+ typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
+ //RemoteSchemaDocumentProvider<SchemaDocumentType> provider;
+ CrtAllocator allocator;
+ char* schema = ReadFile("unittestschema/idandref.json", allocator);
+ Document sd;
+ sd.Parse(schema);
+ ASSERT_FALSE(sd.HasParseError());
+ SchemaDocumentType s(sd, "http://xyz", 10/*, &provider*/);
+ typedef GenericSchemaValidator<SchemaDocumentType, BaseReaderHandler<UTF8<> >, MemoryPoolAllocator<> > SchemaValidatorType;
+ typedef GenericPointer<Value, MemoryPoolAllocator<> > PointerType;
+ INVALIDATE_(s, "{\"PA1\": \"s\", \"PA2\": \"t\", \"PA3\": \"r\", \"PX1\": 1, \"PX2Y\": 2, \"PX3Z\": 3, \"PX4\": 4, \"PX5\": 5, \"PX6\": 6, \"PX7W\": 7, \"PX8N\": { \"NX\": 8}}", "#", "errors", "#",
+ "{ \"type\": ["
+ " {\"errorCode\": 20, \"instanceRef\": \"#/PA1\", \"schemaRef\": \"http://xyz#/definitions/A\", \"expected\": [\"integer\"], \"actual\": \"string\"},"
+ " {\"errorCode\": 20, \"instanceRef\": \"#/PA2\", \"schemaRef\": \"http://xyz#/definitions/A\", \"expected\": [\"integer\"], \"actual\": \"string\"},"
+ " {\"errorCode\": 20, \"instanceRef\": \"#/PA3\", \"schemaRef\": \"http://xyz#/definitions/A\", \"expected\": [\"integer\"], \"actual\": \"string\"},"
+ " {\"errorCode\": 20, \"instanceRef\": \"#/PX1\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"},"
+ " {\"errorCode\": 20, \"instanceRef\": \"#/PX2Y\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"},"
+ " {\"errorCode\": 20, \"instanceRef\": \"#/PX3Z\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"},"
+ " {\"errorCode\": 20, \"instanceRef\": \"#/PX4\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"},"
+ " {\"errorCode\": 20, \"instanceRef\": \"#/PX5\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"},"
+ " {\"errorCode\": 20, \"instanceRef\": \"#/PX6\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"},"
+ " {\"errorCode\": 20, \"instanceRef\": \"#/PX7W\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"},"
+ " {\"errorCode\": 20, \"instanceRef\": \"#/PX8N/NX\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"}"
+ "]}",
+ kValidateDefaultFlags | kValidateContinueOnErrorFlag, SchemaValidatorType, PointerType);
+ CrtAllocator::Free(schema);
+}
+
+TEST(SchemaValidator, Ref_remote_issue1210) {
+ class SchemaDocumentProvider : public IRemoteSchemaDocumentProvider {
+ SchemaDocument** collection;
+
+ // Dummy private copy constructor & assignment operator.
+ // Function bodies added so that they compile in MSVC 2019.
+ SchemaDocumentProvider(const SchemaDocumentProvider&) : collection(NULL) {
+ }
+ SchemaDocumentProvider& operator=(const SchemaDocumentProvider&) {
+ return *this;
+ }
+
+ public:
+ SchemaDocumentProvider(SchemaDocument** collection) : collection(collection) { }
+ virtual const SchemaDocument* GetRemoteDocument(const char* uri, SizeType length) {
+ int i = 0;
+ while (collection[i] && SchemaDocument::SValue(uri, length) != collection[i]->GetURI()) ++i;
+ return collection[i];
+ }
+ };
+ SchemaDocument* collection[] = { 0, 0, 0 };
+ SchemaDocumentProvider provider(collection);
+
+ Document x, y, z;
+ x.Parse("{\"properties\":{\"country\":{\"$ref\":\"y.json#/definitions/country_remote\"}},\"type\":\"object\"}");
+ y.Parse("{\"definitions\":{\"country_remote\":{\"$ref\":\"z.json#/definitions/country_list\"}}}");
+ z.Parse("{\"definitions\":{\"country_list\":{\"enum\":[\"US\"]}}}");
+
+ SchemaDocument sz(z, "z.json", 6, &provider);
+ collection[0] = &sz;
+ SchemaDocument sy(y, "y.json", 6, &provider);
+ collection[1] = &sy;
+ SchemaDocument sx(x, "x.json", 6, &provider);
+
+ VALIDATE(sx, "{\"country\":\"UK\"}", false);
+ VALIDATE(sx, "{\"country\":\"US\"}", true);
+}
+
+// Test that when kValidateContinueOnErrorFlag is set, all errors are reported.
+TEST(SchemaValidator, ContinueOnErrors) {
+ CrtAllocator allocator;
+ char* schema = ReadFile("unittestschema/address.json", allocator);
+ Document sd;
+ sd.Parse(schema);
+ ASSERT_FALSE(sd.HasParseError());
+ SchemaDocument s(sd);
+ VALIDATE(s, "{\"version\": 1.0, \"address\": {\"number\": 24, \"street1\": \"The Woodlands\", \"street3\": \"Ham\", \"city\": \"Romsey\", \"area\": \"Kent\", \"country\": \"UK\", \"postcode\": \"SO51 0GP\"}, \"phones\": [\"0111-222333\", \"0777-666888\"], \"names\": [\"Fred\", \"Bloggs\"]}", true);
+ INVALIDATE_(s, "{\"version\": 1.01, \"address\": {\"number\": 0, \"street2\": false, \"street3\": \"Ham\", \"city\": \"RomseyTownFC\", \"area\": \"BC\", \"country\": \"USA\", \"postcode\": \"999ABC\"}, \"phones\": [], \"planet\": \"Earth\", \"extra\": {\"S_xxx\": 123}}", "#", "errors", "#",
+ "{ \"multipleOf\": {"
+ " \"errorCode\": 1, \"instanceRef\": \"#/version\", \"schemaRef\": \"#/definitions/decimal_type\", \"expected\": 1.0, \"actual\": 1.01"
+ " },"
+ " \"minimum\": {"
+ " \"errorCode\": 5, \"instanceRef\": \"#/address/number\", \"schemaRef\": \"#/definitions/positiveInt_type\", \"expected\": 0, \"actual\": 0, \"exclusiveMinimum\": true"
+ " },"
+ " \"type\": ["
+ " {\"expected\": [\"null\", \"string\"], \"actual\": \"boolean\", \"errorCode\": 20, \"instanceRef\": \"#/address/street2\", \"schemaRef\": \"#/definitions/address_type/properties/street2\"},"
+ " {\"expected\": [\"string\"], \"actual\": \"integer\", \"errorCode\": 20, \"instanceRef\": \"#/extra/S_xxx\", \"schemaRef\": \"#/properties/extra/patternProperties/%5ES_\"}"
+ " ],"
+ " \"maxLength\": {"
+ " \"actual\": \"RomseyTownFC\", \"expected\": 10, \"errorCode\": 6, \"instanceRef\": \"#/address/city\", \"schemaRef\": \"#/definitions/address_type/properties/city\""
+ " },"
+ " \"anyOf\": {"
+ " \"errors\":["
+ " {\"pattern\": {\"actual\": \"999ABC\", \"errorCode\": 8, \"instanceRef\": \"#/address/postcode\", \"schemaRef\": \"#/definitions/address_type/properties/postcode/anyOf/0\"}},"
+ " {\"pattern\": {\"actual\": \"999ABC\", \"errorCode\": 8, \"instanceRef\": \"#/address/postcode\", \"schemaRef\": \"#/definitions/address_type/properties/postcode/anyOf/1\"}}"
+ " ],"
+ " \"errorCode\": 24, \"instanceRef\": \"#/address/postcode\", \"schemaRef\": \"#/definitions/address_type/properties/postcode\""
+ " },"
+ " \"allOf\": {"
+ " \"errors\":["
+ " {\"enum\":{\"errorCode\":19,\"instanceRef\":\"#/address/country\",\"schemaRef\":\"#/definitions/country_type\"}}"
+ " ],"
+ " \"errorCode\":23,\"instanceRef\":\"#/address/country\",\"schemaRef\":\"#/definitions/address_type/properties/country\""
+ " },"
+ " \"minItems\": {"
+ " \"actual\": 0, \"expected\": 1, \"errorCode\": 10, \"instanceRef\": \"#/phones\", \"schemaRef\": \"#/properties/phones\""
+ " },"
+ " \"additionalProperties\": {"
+ " \"disallowed\": \"planet\", \"errorCode\": 16, \"instanceRef\": \"#\", \"schemaRef\": \"#\""
+ " },"
+ " \"required\": {"
+ " \"missing\": [\"street1\"], \"errorCode\": 15, \"instanceRef\": \"#/address\", \"schemaRef\": \"#/definitions/address_type\""
+ " }"
+ "}",
+ kValidateDefaultFlags | kValidateContinueOnErrorFlag, SchemaValidator, Pointer);
+ INVALIDATE_(s, "{\"address\": {\"number\": 200, \"street1\": {}, \"street3\": null, \"city\": \"Rom\", \"area\": \"Dorset\", \"postcode\": \"SO51 0GP\"}, \"phones\": [\"0111-222333\", \"0777-666888\", \"0777-666888\"], \"names\": [\"Fred\", \"S\", \"M\", \"Bloggs\"]}", "#", "errors", "#",
+ "{ \"maximum\": {"
+ " \"errorCode\": 3, \"instanceRef\": \"#/address/number\", \"schemaRef\": \"#/definitions/positiveInt_type\", \"expected\": 100, \"actual\": 200, \"exclusiveMaximum\": true"
+ " },"
+ " \"type\": {"
+ " \"expected\": [\"string\"], \"actual\": \"object\", \"errorCode\": 20, \"instanceRef\": \"#/address/street1\", \"schemaRef\": \"#/definitions/address_type/properties/street1\""
+ " },"
+ " \"not\": {"
+ " \"errorCode\": 25, \"instanceRef\": \"#/address/street3\", \"schemaRef\": \"#/definitions/address_type/properties/street3\""
+ " },"
+ " \"minLength\": {"
+ " \"actual\": \"Rom\", \"expected\": 4, \"errorCode\": 7, \"instanceRef\": \"#/address/city\", \"schemaRef\": \"#/definitions/address_type/properties/city\""
+ " },"
+ " \"maxItems\": {"
+ " \"actual\": 3, \"expected\": 2, \"errorCode\": 9, \"instanceRef\": \"#/phones\", \"schemaRef\": \"#/properties/phones\""
+ " },"
+ " \"uniqueItems\": {"
+ " \"duplicates\": [1, 2], \"errorCode\": 11, \"instanceRef\": \"#/phones\", \"schemaRef\": \"#/properties/phones\""
+ " },"
+ " \"minProperties\": {\"actual\": 6, \"expected\": 7, \"errorCode\": 14, \"instanceRef\": \"#/address\", \"schemaRef\": \"#/definitions/address_type\""
+ " },"
+ " \"additionalItems\": ["
+ " {\"disallowed\": 2, \"errorCode\": 12, \"instanceRef\": \"#/names\", \"schemaRef\": \"#/properties/names\"},"
+ " {\"disallowed\": 3, \"errorCode\": 12, \"instanceRef\": \"#/names\", \"schemaRef\": \"#/properties/names\"}"
+ " ],"
+ " \"dependencies\": {"
+ " \"errors\": {"
+ " \"address\": {\"required\": {\"missing\": [\"version\"], \"errorCode\": 15, \"instanceRef\": \"#\", \"schemaRef\": \"#/dependencies/address\"}},"
+ " \"names\": {\"required\": {\"missing\": [\"version\"], \"errorCode\": 15, \"instanceRef\": \"#\", \"schemaRef\": \"#/dependencies/names\"}}"
+ " },"
+ " \"errorCode\": 18, \"instanceRef\": \"#\", \"schemaRef\": \"#\""
+ " },"
+ " \"oneOf\": {"
+ " \"errors\": ["
+ " {\"enum\": {\"errorCode\": 19, \"instanceRef\": \"#/address/area\", \"schemaRef\": \"#/definitions/county_type\"}},"
+ " {\"enum\": {\"errorCode\": 19, \"instanceRef\": \"#/address/area\", \"schemaRef\": \"#/definitions/province_type\"}}"
+ " ],"
+ " \"errorCode\": 21, \"instanceRef\": \"#/address/area\", \"schemaRef\": \"#/definitions/address_type/properties/area\""
+ " }"
+ "}",
+ kValidateDefaultFlags | kValidateContinueOnErrorFlag, SchemaValidator, Pointer);
+
+ CrtAllocator::Free(schema);
+}
+
+// Test that when kValidateContinueOnErrorFlag is set, it is not propagated to oneOf sub-validator so we only get the first error.
+TEST(SchemaValidator, ContinueOnErrors_OneOf) {
+ typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
+ RemoteSchemaDocumentProvider<SchemaDocumentType> provider;
+ CrtAllocator allocator;
+ char* schema = ReadFile("unittestschema/oneOf_address.json", allocator);
+ Document sd;
+ sd.Parse(schema);
+ ASSERT_FALSE(sd.HasParseError());
+ SchemaDocumentType s(sd, 0, 0, &provider);
+ typedef GenericSchemaValidator<SchemaDocumentType, BaseReaderHandler<UTF8<> >, MemoryPoolAllocator<> > SchemaValidatorType;
+ typedef GenericPointer<Value, MemoryPoolAllocator<> > PointerType;
+ INVALIDATE_(s, "{\"version\": 1.01, \"address\": {\"number\": 0, \"street2\": false, \"street3\": \"Ham\", \"city\": \"RomseyTownFC\", \"area\": \"BC\", \"country\": \"USA\", \"postcode\": \"999ABC\"}, \"phones\": [], \"planet\": \"Earth\", \"extra\": {\"S_xxx\": 123}}", "#", "errors", "#",
+ "{ \"oneOf\": {"
+ " \"errors\": [{"
+ " \"multipleOf\": {"
+ " \"errorCode\": 1, \"instanceRef\": \"#/version\", \"schemaRef\": \"http://localhost:1234/address.json#/definitions/decimal_type\", \"expected\": 1.0, \"actual\": 1.01"
+ " }"
+ " }],"
+ " \"errorCode\": 21, \"instanceRef\": \"#\", \"schemaRef\": \"#\""
+ " }"
+ "}",
+ kValidateDefaultFlags | kValidateContinueOnErrorFlag, SchemaValidatorType, PointerType);
+ CrtAllocator::Free(schema);
+}
+
+// Test that when kValidateContinueOnErrorFlag is set, it is not propagated to allOf sub-validator so we only get the first error.
+TEST(SchemaValidator, ContinueOnErrors_AllOf) {
+ typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
+ RemoteSchemaDocumentProvider<SchemaDocumentType> provider;
+ CrtAllocator allocator;
+ char* schema = ReadFile("unittestschema/allOf_address.json", allocator);
+ Document sd;
+ sd.Parse(schema);
+ ASSERT_FALSE(sd.HasParseError());
+ SchemaDocumentType s(sd, 0, 0, &provider);
+ typedef GenericSchemaValidator<SchemaDocumentType, BaseReaderHandler<UTF8<> >, MemoryPoolAllocator<> > SchemaValidatorType;
+ typedef GenericPointer<Value, MemoryPoolAllocator<> > PointerType;
+ INVALIDATE_(s, "{\"version\": 1.01, \"address\": {\"number\": 0, \"street2\": false, \"street3\": \"Ham\", \"city\": \"RomseyTownFC\", \"area\": \"BC\", \"country\": \"USA\", \"postcode\": \"999ABC\"}, \"phones\": [], \"planet\": \"Earth\", \"extra\": {\"S_xxx\": 123}}", "#", "errors", "#",
+ "{ \"allOf\": {"
+ " \"errors\": [{"
+ " \"multipleOf\": {"
+ " \"errorCode\": 1, \"instanceRef\": \"#/version\", \"schemaRef\": \"http://localhost:1234/address.json#/definitions/decimal_type\", \"expected\": 1.0, \"actual\": 1.01"
+ " }"
+ " }],"
+ " \"errorCode\": 23, \"instanceRef\": \"#\", \"schemaRef\": \"#\""
+ " }"
+ "}",
+ kValidateDefaultFlags | kValidateContinueOnErrorFlag, SchemaValidatorType, PointerType);
+ CrtAllocator::Free(schema);
+}
+
+// Test that when kValidateContinueOnErrorFlag is set, it is not propagated to anyOf sub-validator so we only get the first error.
+TEST(SchemaValidator, ContinueOnErrors_AnyOf) {
+ typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
+ RemoteSchemaDocumentProvider<SchemaDocumentType> provider;
+ CrtAllocator allocator;
+ char* schema = ReadFile("unittestschema/anyOf_address.json", allocator);
+ Document sd;
+ sd.Parse(schema);
+ ASSERT_FALSE(sd.HasParseError());
+ SchemaDocumentType s(sd, 0, 0, &provider);
+ typedef GenericSchemaValidator<SchemaDocumentType, BaseReaderHandler<UTF8<> >, MemoryPoolAllocator<> > SchemaValidatorType;
+ typedef GenericPointer<Value, MemoryPoolAllocator<> > PointerType;
+ INVALIDATE_(s, "{\"version\": 1.01, \"address\": {\"number\": 0, \"street2\": false, \"street3\": \"Ham\", \"city\": \"RomseyTownFC\", \"area\": \"BC\", \"country\": \"USA\", \"postcode\": \"999ABC\"}, \"phones\": [], \"planet\": \"Earth\", \"extra\": {\"S_xxx\": 123}}", "#", "errors", "#",
+ "{ \"anyOf\": {"
+ " \"errors\": [{"
+ " \"multipleOf\": {"
+ " \"errorCode\": 1, \"instanceRef\": \"#/version\", \"schemaRef\": \"http://localhost:1234/address.json#/definitions/decimal_type\", \"expected\": 1.0, \"actual\": 1.01"
+ " }"
+ " }],"
+ " \"errorCode\": 24, \"instanceRef\": \"#\", \"schemaRef\": \"#\""
+ " }"
+ "}",
+ kValidateDefaultFlags | kValidateContinueOnErrorFlag, SchemaValidatorType, PointerType);
+
+ CrtAllocator::Free(schema);
+}
+
+// Test that when kValidateContinueOnErrorFlag is set, arrays with uniqueItems:true are correctly processed when an item is invalid.
+// This tests that we don't blow up if a hasher does not get created.
+TEST(SchemaValidator, ContinueOnErrors_UniqueItems) {
+ CrtAllocator allocator;
+ char* schema = ReadFile("unittestschema/address.json", allocator);
+ Document sd;
+ sd.Parse(schema);
+ ASSERT_FALSE(sd.HasParseError());
+ SchemaDocument s(sd);
+ VALIDATE(s, "{\"phones\":[\"12-34\",\"56-78\"]}", true);
+ INVALIDATE_(s, "{\"phones\":[\"12-34\",\"12-34\"]}", "#", "errors", "#",
+ "{\"uniqueItems\": {\"duplicates\": [0,1], \"errorCode\": 11, \"instanceRef\": \"#/phones\", \"schemaRef\": \"#/properties/phones\"}}",
+ kValidateDefaultFlags | kValidateContinueOnErrorFlag, SchemaValidator, Pointer);
+ INVALIDATE_(s, "{\"phones\":[\"ab-34\",\"cd-78\"]}", "#", "errors", "#",
+ "{\"pattern\": ["
+ " {\"actual\": \"ab-34\", \"errorCode\": 8, \"instanceRef\": \"#/phones/0\", \"schemaRef\": \"#/definitions/phone_type\"},"
+ " {\"actual\": \"cd-78\", \"errorCode\": 8, \"instanceRef\": \"#/phones/1\", \"schemaRef\": \"#/definitions/phone_type\"}"
+ "]}",
+ kValidateDefaultFlags | kValidateContinueOnErrorFlag, SchemaValidator, Pointer);
+ CrtAllocator::Free(schema);
+}
+
+// Test that when kValidateContinueOnErrorFlag is set, an enum field is correctly processed when it has an invalid value.
+// This tests that we don't blow up if a hasher does not get created.
+TEST(SchemaValidator, ContinueOnErrors_Enum) {
+ CrtAllocator allocator;
+ char* schema = ReadFile("unittestschema/address.json", allocator);
+ Document sd;
+ sd.Parse(schema);
+ ASSERT_FALSE(sd.HasParseError());
+ SchemaDocument s(sd);
+ VALIDATE(s, "{\"gender\":\"M\"}", true);
+ INVALIDATE_(s, "{\"gender\":\"X\"}", "#", "errors", "#",
+ "{\"enum\": {\"errorCode\": 19, \"instanceRef\": \"#/gender\", \"schemaRef\": \"#/properties/gender\"}}",
+ kValidateDefaultFlags | kValidateContinueOnErrorFlag, SchemaValidator, Pointer);
+ INVALIDATE_(s, "{\"gender\":1}", "#", "errors", "#",
+ "{\"type\": {\"expected\":[\"string\"], \"actual\": \"integer\", \"errorCode\": 20, \"instanceRef\": \"#/gender\", \"schemaRef\": \"#/properties/gender\"}}",
+ kValidateDefaultFlags | kValidateContinueOnErrorFlag, SchemaValidator, Pointer);
+ CrtAllocator::Free(schema);
+}
+
+// Test that when kValidateContinueOnErrorFlag is set, an array appearing for an object property is handled
+// This tests that we don't blow up when there is a type mismatch.
+TEST(SchemaValidator, ContinueOnErrors_RogueArray) {
+ CrtAllocator allocator;
+ char* schema = ReadFile("unittestschema/address.json", allocator);
+ Document sd;
+ sd.Parse(schema);
+ ASSERT_FALSE(sd.HasParseError());
+ SchemaDocument s(sd);
+ INVALIDATE_(s, "{\"address\":[{\"number\": 0}]}", "#", "errors", "#",
+ "{\"type\": {\"expected\":[\"object\"], \"actual\": \"array\", \"errorCode\": 20, \"instanceRef\": \"#/address\", \"schemaRef\": \"#/definitions/address_type\"},"
+ " \"dependencies\": {"
+ " \"errors\": {"
+ " \"address\": {\"required\": {\"missing\": [\"version\"], \"errorCode\": 15, \"instanceRef\": \"#\", \"schemaRef\": \"#/dependencies/address\"}}"
+ " },\"errorCode\": 18, \"instanceRef\": \"#\", \"schemaRef\": \"#\"}}",
+ kValidateDefaultFlags | kValidateContinueOnErrorFlag, SchemaValidator, Pointer);
+ CrtAllocator::Free(schema);
+}
+
+// Test that when kValidateContinueOnErrorFlag is set, an object appearing for an array property is handled
+// This tests that we don't blow up when there is a type mismatch.
+TEST(SchemaValidator, ContinueOnErrors_RogueObject) {
+ CrtAllocator allocator;
+ char* schema = ReadFile("unittestschema/address.json", allocator);
+ Document sd;
+ sd.Parse(schema);
+ ASSERT_FALSE(sd.HasParseError());
+ SchemaDocument s(sd);
+ INVALIDATE_(s, "{\"phones\":{\"number\": 0}}", "#", "errors", "#",
+ "{\"type\": {\"expected\":[\"array\"], \"actual\": \"object\", \"errorCode\": 20, \"instanceRef\": \"#/phones\", \"schemaRef\": \"#/properties/phones\"}}",
+ kValidateDefaultFlags | kValidateContinueOnErrorFlag, SchemaValidator, Pointer);
+ CrtAllocator::Free(schema);
+}
+
+// Test that when kValidateContinueOnErrorFlag is set, a string appearing for an array or object property is handled
+// This tests that we don't blow up when there is a type mismatch.
+TEST(SchemaValidator, ContinueOnErrors_RogueString) {
+ CrtAllocator allocator;
+ char* schema = ReadFile("unittestschema/address.json", allocator);
+ Document sd;
+ sd.Parse(schema);
+ ASSERT_FALSE(sd.HasParseError());
+ SchemaDocument s(sd);
+ INVALIDATE_(s, "{\"address\":\"number\"}", "#", "errors", "#",
+ "{\"type\": {\"expected\":[\"object\"], \"actual\": \"string\", \"errorCode\": 20, \"instanceRef\": \"#/address\", \"schemaRef\": \"#/definitions/address_type\"},"
+ " \"dependencies\": {"
+ " \"errors\": {"
+ " \"address\": {\"required\": {\"missing\": [\"version\"], \"errorCode\": 15, \"instanceRef\": \"#\", \"schemaRef\": \"#/dependencies/address\"}}"
+ " },\"errorCode\": 18, \"instanceRef\": \"#\", \"schemaRef\": \"#\"}}",
+ kValidateDefaultFlags | kValidateContinueOnErrorFlag, SchemaValidator, Pointer);
+ INVALIDATE_(s, "{\"phones\":\"number\"}", "#", "errors", "#",
+ "{\"type\": {\"expected\":[\"array\"], \"actual\": \"string\", \"errorCode\": 20, \"instanceRef\": \"#/phones\", \"schemaRef\": \"#/properties/phones\"}}",
+ kValidateDefaultFlags | kValidateContinueOnErrorFlag, SchemaValidator, Pointer);
+ CrtAllocator::Free(schema);
+}
+
+// Test that when kValidateContinueOnErrorFlag is set, an incorrect simple type with a sub-schema is handled correctly.
+// This tests that we don't blow up when there is a type mismatch but there is a sub-schema present
+TEST(SchemaValidator, ContinueOnErrors_Issue2) {
+ Document sd;
+ sd.Parse("{\"type\":\"string\", \"anyOf\":[{\"maxLength\":2}]}");
+ ASSERT_FALSE(sd.HasParseError());
+ SchemaDocument s(sd);
+ VALIDATE(s, "\"AB\"", true);
+ INVALIDATE_(s, "\"ABC\"", "#", "errors", "#",
+ "{ \"anyOf\": {"
+ " \"errors\": [{"
+ " \"maxLength\": {"
+ " \"errorCode\": 6, \"instanceRef\": \"#\", \"schemaRef\": \"#/anyOf/0\", \"expected\": 2, \"actual\": \"ABC\""
+ " }"
+ " }],"
+ " \"errorCode\": 24, \"instanceRef\": \"#\", \"schemaRef\": \"#\""
+ " }"
+ "}",
+ kValidateDefaultFlags | kValidateContinueOnErrorFlag, SchemaValidator, Pointer);
+ // Invalid type
+ INVALIDATE_(s, "333", "#", "errors", "#",
+ "{ \"type\": {"
+ " \"errorCode\": 20, \"instanceRef\": \"#\", \"schemaRef\": \"#\", \"expected\": [\"string\"], \"actual\": \"integer\""
+ " }"
+ "}",
+ kValidateDefaultFlags | kValidateContinueOnErrorFlag, SchemaValidator, Pointer);
+}
+
+TEST(SchemaValidator, Schema_UnknownError) {
+ ASSERT_TRUE(SchemaValidator::SchemaType::GetValidateErrorKeyword(kValidateErrors).GetString() == std::string("null"));
+}
+
+#if defined(_MSC_VER) || defined(__clang__)
+RAPIDJSON_DIAG_POP
+#endif
diff --git a/src/s3select/rapidjson/test/unittest/simdtest.cpp b/src/s3select/rapidjson/test/unittest/simdtest.cpp
new file mode 100644
index 000000000..649505fab
--- /dev/null
+++ b/src/s3select/rapidjson/test/unittest/simdtest.cpp
@@ -0,0 +1,219 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+// 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
+#elif defined(__ARM_NEON)
+# define RAPIDJSON_NEON
+#endif
+
+#define RAPIDJSON_NAMESPACE rapidjson_simd
+
+#include "unittest.h"
+
+#include "rapidjson/reader.h"
+#include "rapidjson/writer.h"
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(effc++)
+#endif
+
+using namespace rapidjson_simd;
+
+#ifdef RAPIDJSON_SSE2
+#define SIMD_SUFFIX(name) name##_SSE2
+#elif defined(RAPIDJSON_SSE42)
+#define SIMD_SUFFIX(name) name##_SSE42
+#elif defined(RAPIDJSON_NEON)
+#define SIMD_SUFFIX(name) name##_NEON
+#else
+#define SIMD_SUFFIX(name) name
+#endif
+
+template <typename StreamType>
+void TestSkipWhitespace() {
+ for (size_t 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>();
+}
+
+TEST(SIMD, SIMD_SUFFIX(SkipWhitespace_EncodedMemoryStream)) {
+ for (size_t step = 1; step < 32; step++) {
+ char buffer[1024];
+ 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';
+
+ MemoryStream ms(buffer, 1024);
+ EncodedInputStream<UTF8<>, MemoryStream> s(ms);
+ size_t i = 0;
+ for (;;) {
+ SkipWhitespace(s);
+ if (s.Peek() == '\0')
+ break;
+ //EXPECT_EQ(i, s.Tell());
+ EXPECT_EQ('X', s.Take());
+ i += step;
+ }
+ }
+}
+
+struct ScanCopyUnescapedStringHandler : BaseReaderHandler<UTF8<>, ScanCopyUnescapedStringHandler> {
+ bool String(const char* str, size_t length, bool) {
+ memcpy(buffer, str, length + 1);
+ return true;
+ }
+ char buffer[1024 + 5 + 32];
+};
+
+template <unsigned parseFlags, typename StreamType>
+void TestScanCopyUnescapedString() {
+ char buffer[1024u + 5 + 32];
+ char backup[1024u + 5 + 32];
+
+ // Test "ABCDABCD...\\"
+ for (size_t offset = 0; offset < 32; offset++) {
+ for (size_t step = 0; step < 1024; step++) {
+ char* json = buffer + offset;
+ char *p = json;
+ *p++ = '\"';
+ for (size_t i = 0; i < step; i++)
+ *p++ = "ABCD"[i % 4];
+ *p++ = '\\';
+ *p++ = '\\';
+ *p++ = '\"';
+ *p++ = '\0';
+ strcpy(backup, json); // insitu parsing will overwrite buffer, so need to backup first
+
+ StreamType s(json);
+ Reader reader;
+ ScanCopyUnescapedStringHandler h;
+ reader.Parse<parseFlags>(s, h);
+ EXPECT_TRUE(memcmp(h.buffer, backup + 1, step) == 0);
+ EXPECT_EQ('\\', h.buffer[step]); // escaped
+ EXPECT_EQ('\0', h.buffer[step + 1]);
+ }
+ }
+
+ // Test "\\ABCDABCD..."
+ for (size_t offset = 0; offset < 32; offset++) {
+ for (size_t step = 0; step < 1024; step++) {
+ char* json = buffer + offset;
+ char *p = json;
+ *p++ = '\"';
+ *p++ = '\\';
+ *p++ = '\\';
+ for (size_t i = 0; i < step; i++)
+ *p++ = "ABCD"[i % 4];
+ *p++ = '\"';
+ *p++ = '\0';
+ strcpy(backup, json); // insitu parsing will overwrite buffer, so need to backup first
+
+ StreamType s(json);
+ Reader reader;
+ ScanCopyUnescapedStringHandler h;
+ reader.Parse<parseFlags>(s, h);
+ EXPECT_TRUE(memcmp(h.buffer + 1, backup + 3, step) == 0);
+ EXPECT_EQ('\\', h.buffer[0]); // escaped
+ EXPECT_EQ('\0', h.buffer[step + 1]);
+ }
+ }
+}
+
+TEST(SIMD, SIMD_SUFFIX(ScanCopyUnescapedString)) {
+ TestScanCopyUnescapedString<kParseDefaultFlags, StringStream>();
+ TestScanCopyUnescapedString<kParseInsituFlag, InsituStringStream>();
+}
+
+TEST(SIMD, SIMD_SUFFIX(ScanWriteUnescapedString)) {
+ char buffer[2048 + 1 + 32];
+ for (size_t offset = 0; offset < 32; offset++) {
+ for (size_t step = 0; step < 1024; step++) {
+ char* s = buffer + offset;
+ char* p = s;
+ for (size_t i = 0; i < step; i++)
+ *p++ = "ABCD"[i % 4];
+ char escape = "\0\n\\\""[step % 4];
+ *p++ = escape;
+ for (size_t i = 0; i < step; i++)
+ *p++ = "ABCD"[i % 4];
+
+ StringBuffer sb;
+ Writer<StringBuffer> writer(sb);
+ writer.String(s, SizeType(step * 2 + 1));
+ const char* q = sb.GetString();
+ EXPECT_EQ('\"', *q++);
+ for (size_t i = 0; i < step; i++)
+ EXPECT_EQ("ABCD"[i % 4], *q++);
+ if (escape == '\0') {
+ EXPECT_EQ('\\', *q++);
+ EXPECT_EQ('u', *q++);
+ EXPECT_EQ('0', *q++);
+ EXPECT_EQ('0', *q++);
+ EXPECT_EQ('0', *q++);
+ EXPECT_EQ('0', *q++);
+ }
+ else if (escape == '\n') {
+ EXPECT_EQ('\\', *q++);
+ EXPECT_EQ('n', *q++);
+ }
+ else if (escape == '\\') {
+ EXPECT_EQ('\\', *q++);
+ EXPECT_EQ('\\', *q++);
+ }
+ else if (escape == '\"') {
+ EXPECT_EQ('\\', *q++);
+ EXPECT_EQ('\"', *q++);
+ }
+ for (size_t i = 0; i < step; i++)
+ EXPECT_EQ("ABCD"[i % 4], *q++);
+ EXPECT_EQ('\"', *q++);
+ EXPECT_EQ('\0', *q++);
+ }
+ }
+}
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_POP
+#endif
diff --git a/src/s3select/rapidjson/test/unittest/strfunctest.cpp b/src/s3select/rapidjson/test/unittest/strfunctest.cpp
new file mode 100644
index 000000000..411269396
--- /dev/null
+++ b/src/s3select/rapidjson/test/unittest/strfunctest.cpp
@@ -0,0 +1,30 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#include "unittest.h"
+#include "rapidjson/internal/strfunc.h"
+
+using namespace rapidjson;
+using namespace rapidjson::internal;
+
+TEST(StrFunc, CountStringCodePoint) {
+ SizeType count;
+ EXPECT_TRUE(CountStringCodePoint<UTF8<> >("", 0, &count));
+ EXPECT_EQ(0u, count);
+ EXPECT_TRUE(CountStringCodePoint<UTF8<> >("Hello", 5, &count));
+ EXPECT_EQ(5u, count);
+ EXPECT_TRUE(CountStringCodePoint<UTF8<> >("\xC2\xA2\xE2\x82\xAC\xF0\x9D\x84\x9E", 9, &count)); // cents euro G-clef
+ EXPECT_EQ(3u, count);
+ EXPECT_FALSE(CountStringCodePoint<UTF8<> >("\xC2\xA2\xE2\x82\xAC\xF0\x9D\x84\x9E\x80", 10, &count));
+}
diff --git a/src/s3select/rapidjson/test/unittest/stringbuffertest.cpp b/src/s3select/rapidjson/test/unittest/stringbuffertest.cpp
new file mode 100644
index 000000000..eaa29e715
--- /dev/null
+++ b/src/s3select/rapidjson/test/unittest/stringbuffertest.cpp
@@ -0,0 +1,192 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#include "unittest.h"
+#include "rapidjson/stringbuffer.h"
+#include "rapidjson/writer.h"
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(c++98-compat)
+#endif
+
+using namespace rapidjson;
+
+TEST(StringBuffer, InitialSize) {
+ StringBuffer buffer;
+ EXPECT_EQ(0u, buffer.GetSize());
+ EXPECT_EQ(0u, buffer.GetLength());
+ EXPECT_STREQ("", buffer.GetString());
+}
+
+TEST(StringBuffer, Put) {
+ StringBuffer buffer;
+ buffer.Put('A');
+
+ EXPECT_EQ(1u, buffer.GetSize());
+ EXPECT_EQ(1u, buffer.GetLength());
+ EXPECT_STREQ("A", buffer.GetString());
+}
+
+TEST(StringBuffer, PutN_Issue672) {
+ GenericStringBuffer<UTF8<>, MemoryPoolAllocator<> > buffer;
+ EXPECT_EQ(0u, buffer.GetSize());
+ EXPECT_EQ(0u, buffer.GetLength());
+ rapidjson::PutN(buffer, ' ', 1);
+ EXPECT_EQ(1u, buffer.GetSize());
+ EXPECT_EQ(1u, buffer.GetLength());
+}
+
+TEST(StringBuffer, Clear) {
+ StringBuffer buffer;
+ buffer.Put('A');
+ buffer.Put('B');
+ buffer.Put('C');
+ buffer.Clear();
+
+ EXPECT_EQ(0u, buffer.GetSize());
+ EXPECT_EQ(0u, buffer.GetLength());
+ EXPECT_STREQ("", buffer.GetString());
+}
+
+TEST(StringBuffer, Push) {
+ StringBuffer buffer;
+ buffer.Push(5);
+
+ EXPECT_EQ(5u, buffer.GetSize());
+ EXPECT_EQ(5u, buffer.GetLength());
+
+ // Causes sudden expansion to make the stack's capacity equal to size
+ buffer.Push(65536u);
+ EXPECT_EQ(5u + 65536u, buffer.GetSize());
+}
+
+TEST(StringBuffer, Pop) {
+ StringBuffer buffer;
+ buffer.Put('A');
+ buffer.Put('B');
+ buffer.Put('C');
+ buffer.Put('D');
+ buffer.Put('E');
+ buffer.Pop(3);
+
+ EXPECT_EQ(2u, buffer.GetSize());
+ EXPECT_EQ(2u, buffer.GetLength());
+ EXPECT_STREQ("AB", buffer.GetString());
+}
+
+TEST(StringBuffer, GetLength_Issue744) {
+ GenericStringBuffer<UTF16<wchar_t> > buffer;
+ buffer.Put('A');
+ buffer.Put('B');
+ buffer.Put('C');
+ EXPECT_EQ(3u * sizeof(wchar_t), buffer.GetSize());
+ EXPECT_EQ(3u, buffer.GetLength());
+}
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+
+#if 0 // Many old compiler does not support these. Turn it off temporaily.
+
+#include <type_traits>
+
+TEST(StringBuffer, Traits) {
+ static_assert( std::is_constructible<StringBuffer>::value, "");
+ static_assert( std::is_default_constructible<StringBuffer>::value, "");
+#ifndef _MSC_VER
+ static_assert(!std::is_copy_constructible<StringBuffer>::value, "");
+#endif
+ static_assert( std::is_move_constructible<StringBuffer>::value, "");
+
+ static_assert(!std::is_nothrow_constructible<StringBuffer>::value, "");
+ static_assert(!std::is_nothrow_default_constructible<StringBuffer>::value, "");
+
+#if !defined(_MSC_VER) || _MSC_VER >= 1800
+ static_assert(!std::is_nothrow_copy_constructible<StringBuffer>::value, "");
+ static_assert(!std::is_nothrow_move_constructible<StringBuffer>::value, "");
+#endif
+
+ static_assert( std::is_assignable<StringBuffer,StringBuffer>::value, "");
+#ifndef _MSC_VER
+ static_assert(!std::is_copy_assignable<StringBuffer>::value, "");
+#endif
+ static_assert( std::is_move_assignable<StringBuffer>::value, "");
+
+#if !defined(_MSC_VER) || _MSC_VER >= 1800
+ static_assert(!std::is_nothrow_assignable<StringBuffer, StringBuffer>::value, "");
+#endif
+
+ static_assert(!std::is_nothrow_copy_assignable<StringBuffer>::value, "");
+ static_assert(!std::is_nothrow_move_assignable<StringBuffer>::value, "");
+
+ static_assert( std::is_destructible<StringBuffer>::value, "");
+#ifndef _MSC_VER
+ static_assert(std::is_nothrow_destructible<StringBuffer>::value, "");
+#endif
+}
+
+#endif
+
+TEST(StringBuffer, MoveConstructor) {
+ StringBuffer x;
+ x.Put('A');
+ x.Put('B');
+ x.Put('C');
+ x.Put('D');
+
+ EXPECT_EQ(4u, x.GetSize());
+ EXPECT_EQ(4u, x.GetLength());
+ EXPECT_STREQ("ABCD", x.GetString());
+
+ // StringBuffer y(x); // does not compile (!is_copy_constructible)
+ StringBuffer y(std::move(x));
+ EXPECT_EQ(0u, x.GetSize());
+ EXPECT_EQ(0u, x.GetLength());
+ EXPECT_EQ(4u, y.GetSize());
+ EXPECT_EQ(4u, y.GetLength());
+ EXPECT_STREQ("ABCD", y.GetString());
+
+ // StringBuffer z = y; // does not compile (!is_copy_assignable)
+ StringBuffer z = std::move(y);
+ EXPECT_EQ(0u, y.GetSize());
+ EXPECT_EQ(0u, y.GetLength());
+ EXPECT_EQ(4u, z.GetSize());
+ EXPECT_EQ(4u, z.GetLength());
+ EXPECT_STREQ("ABCD", z.GetString());
+}
+
+TEST(StringBuffer, MoveAssignment) {
+ StringBuffer x;
+ x.Put('A');
+ x.Put('B');
+ x.Put('C');
+ x.Put('D');
+
+ EXPECT_EQ(4u, x.GetSize());
+ EXPECT_EQ(4u, x.GetLength());
+ EXPECT_STREQ("ABCD", x.GetString());
+
+ StringBuffer y;
+ // y = x; // does not compile (!is_copy_assignable)
+ y = std::move(x);
+ EXPECT_EQ(0u, x.GetSize());
+ EXPECT_EQ(4u, y.GetLength());
+ EXPECT_STREQ("ABCD", y.GetString());
+}
+
+#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
diff --git a/src/s3select/rapidjson/test/unittest/strtodtest.cpp b/src/s3select/rapidjson/test/unittest/strtodtest.cpp
new file mode 100644
index 000000000..66167a4a3
--- /dev/null
+++ b/src/s3select/rapidjson/test/unittest/strtodtest.cpp
@@ -0,0 +1,132 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#include "unittest.h"
+
+#include "rapidjson/internal/strtod.h"
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(unreachable-code)
+#endif
+
+#define BIGINTEGER_LITERAL(s) BigInteger(s, sizeof(s) - 1)
+
+using namespace rapidjson::internal;
+
+TEST(Strtod, CheckApproximationCase) {
+ static const int kSignificandSize = 52;
+ static const int kExponentBias = 0x3FF;
+ static const uint64_t kExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000);
+ static const uint64_t kSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF);
+ static const uint64_t kHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000);
+
+ // http://www.exploringbinary.com/using-integers-to-check-a-floating-point-approximation/
+ // Let b = 0x1.465a72e467d88p-149
+ // = 5741268244528520 x 2^-201
+ union {
+ double d;
+ uint64_t u;
+ }u;
+ u.u = 0x465a72e467d88 | ((static_cast<uint64_t>(-149 + kExponentBias)) << kSignificandSize);
+ const double b = u.d;
+ const uint64_t bInt = (u.u & kSignificandMask) | kHiddenBit;
+ const int bExp = static_cast<int>(((u.u & kExponentMask) >> kSignificandSize) - kExponentBias - kSignificandSize);
+ EXPECT_DOUBLE_EQ(1.7864e-45, b);
+ EXPECT_EQ(RAPIDJSON_UINT64_C2(0x001465a7, 0x2e467d88), bInt);
+ EXPECT_EQ(-201, bExp);
+
+ // Let d = 17864 x 10-49
+ const char dInt[] = "17864";
+ const int dExp = -49;
+
+ // Let h = 2^(bExp-1)
+ const int hExp = bExp - 1;
+ EXPECT_EQ(-202, hExp);
+
+ int dS_Exp2 = 0;
+ int dS_Exp5 = 0;
+ int bS_Exp2 = 0;
+ int bS_Exp5 = 0;
+ int hS_Exp2 = 0;
+ int hS_Exp5 = 0;
+
+ // Adjust for decimal exponent
+ if (dExp >= 0) {
+ dS_Exp2 += dExp;
+ dS_Exp5 += dExp;
+ }
+ else {
+ bS_Exp2 -= dExp;
+ bS_Exp5 -= dExp;
+ hS_Exp2 -= dExp;
+ hS_Exp5 -= dExp;
+ }
+
+ // Adjust for binary exponent
+ if (bExp >= 0)
+ bS_Exp2 += bExp;
+ else {
+ dS_Exp2 -= bExp;
+ hS_Exp2 -= bExp;
+ }
+
+ // Adjust for half ulp exponent
+ if (hExp >= 0)
+ hS_Exp2 += hExp;
+ else {
+ dS_Exp2 -= hExp;
+ bS_Exp2 -= hExp;
+ }
+
+ // Remove common power of two factor from all three scaled values
+ int common_Exp2 = (std::min)(dS_Exp2, (std::min)(bS_Exp2, hS_Exp2));
+ dS_Exp2 -= common_Exp2;
+ bS_Exp2 -= common_Exp2;
+ hS_Exp2 -= common_Exp2;
+
+ EXPECT_EQ(153, dS_Exp2);
+ EXPECT_EQ(0, dS_Exp5);
+ EXPECT_EQ(1, bS_Exp2);
+ EXPECT_EQ(49, bS_Exp5);
+ EXPECT_EQ(0, hS_Exp2);
+ EXPECT_EQ(49, hS_Exp5);
+
+ BigInteger dS = BIGINTEGER_LITERAL(dInt);
+ dS.MultiplyPow5(static_cast<unsigned>(dS_Exp5)) <<= static_cast<size_t>(dS_Exp2);
+
+ BigInteger bS(bInt);
+ bS.MultiplyPow5(static_cast<unsigned>(bS_Exp5)) <<= static_cast<size_t>(bS_Exp2);
+
+ BigInteger hS(1);
+ hS.MultiplyPow5(static_cast<unsigned>(hS_Exp5)) <<= static_cast<size_t>(hS_Exp2);
+
+ EXPECT_TRUE(BIGINTEGER_LITERAL("203970822259994138521801764465966248930731085529088") == dS);
+ EXPECT_TRUE(BIGINTEGER_LITERAL("203970822259994122305215569213032722473144531250000") == bS);
+ EXPECT_TRUE(BIGINTEGER_LITERAL("17763568394002504646778106689453125") == hS);
+
+ EXPECT_EQ(1, dS.Compare(bS));
+
+ BigInteger delta(0);
+ EXPECT_FALSE(dS.Difference(bS, &delta));
+ EXPECT_TRUE(BIGINTEGER_LITERAL("16216586195252933526457586554279088") == delta);
+ EXPECT_TRUE(bS.Difference(dS, &delta));
+ EXPECT_TRUE(BIGINTEGER_LITERAL("16216586195252933526457586554279088") == delta);
+
+ EXPECT_EQ(-1, delta.Compare(hS));
+}
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
diff --git a/src/s3select/rapidjson/test/unittest/unittest.cpp b/src/s3select/rapidjson/test/unittest/unittest.cpp
new file mode 100644
index 000000000..879976a78
--- /dev/null
+++ b/src/s3select/rapidjson/test/unittest/unittest.cpp
@@ -0,0 +1,51 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#include "unittest.h"
+#include "rapidjson/rapidjson.h"
+
+#ifdef __clang__
+#pragma GCC diagnostic push
+#if __has_warning("-Wdeprecated")
+#pragma GCC diagnostic ignored "-Wdeprecated"
+#endif
+#endif
+
+AssertException::~AssertException() throw() {}
+
+#ifdef __clang__
+#pragma GCC diagnostic pop
+#endif
+
+int main(int argc, char **argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ std::cout << "RapidJSON v" << RAPIDJSON_VERSION_STRING << std::endl;
+
+#ifdef _MSC_VER
+ _CrtMemState memoryState = { 0 };
+ (void)memoryState;
+ _CrtMemCheckpoint(&memoryState);
+ //_CrtSetBreakAlloc(X);
+ //void *testWhetherMemoryLeakDetectionWorks = malloc(1);
+#endif
+
+ int ret = RUN_ALL_TESTS();
+
+#ifdef _MSC_VER
+ // Current gtest constantly leak 2 blocks at exit
+ _CrtMemDumpAllObjectsSince(&memoryState);
+#endif
+ return ret;
+}
diff --git a/src/s3select/rapidjson/test/unittest/unittest.h b/src/s3select/rapidjson/test/unittest/unittest.h
new file mode 100644
index 000000000..0e64d3970
--- /dev/null
+++ b/src/s3select/rapidjson/test/unittest/unittest.h
@@ -0,0 +1,143 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef UNITTEST_H_
+#define UNITTEST_H_
+
+// gtest indirectly included inttypes.h, without __STDC_CONSTANT_MACROS.
+#ifndef __STDC_CONSTANT_MACROS
+#ifdef __clang__
+#pragma GCC diagnostic push
+#if __has_warning("-Wreserved-id-macro")
+#pragma GCC diagnostic ignored "-Wreserved-id-macro"
+#endif
+#endif
+
+# define __STDC_CONSTANT_MACROS 1 // required by C++ standard
+
+#ifdef __clang__
+#pragma GCC diagnostic pop
+#endif
+#endif
+
+#ifdef _MSC_VER
+#define _CRTDBG_MAP_ALLOC
+#include <crtdbg.h>
+#pragma warning(disable : 4996) // 'function': was declared deprecated
+#endif
+
+#if defined(__clang__) || defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2))
+#if defined(__clang__) || (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
+#pragma GCC diagnostic push
+#endif
+#pragma GCC diagnostic ignored "-Weffc++"
+#endif
+
+#include "gtest/gtest.h"
+#include <stdexcept>
+
+#if defined(__clang__) || defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
+#pragma GCC diagnostic pop
+#endif
+
+#ifdef __clang__
+// All TEST() macro generated this warning, disable globally
+#pragma GCC diagnostic ignored "-Wglobal-constructors"
+#endif
+
+template <typename Ch>
+inline unsigned StrLen(const Ch* s) {
+ const Ch* p = s;
+ while (*p) p++;
+ return unsigned(p - s);
+}
+
+template<typename Ch>
+inline int StrCmp(const Ch* s1, const Ch* s2) {
+ while(*s1 && (*s1 == *s2)) { s1++; s2++; }
+ return static_cast<unsigned>(*s1) < static_cast<unsigned>(*s2) ? -1 : static_cast<unsigned>(*s1) > static_cast<unsigned>(*s2);
+}
+
+template <typename Ch>
+inline Ch* StrDup(const Ch* str) {
+ size_t bufferSize = sizeof(Ch) * (StrLen(str) + 1);
+ Ch* buffer = static_cast<Ch*>(malloc(bufferSize));
+ memcpy(buffer, str, bufferSize);
+ return buffer;
+}
+
+inline FILE* TempFile(char *filename) {
+#if defined(__WIN32__) || defined(_MSC_VER)
+ filename = tmpnam(filename);
+
+ // For Visual Studio, tmpnam() adds a backslash in front. Remove it.
+ if (filename[0] == '\\')
+ for (int i = 0; filename[i] != '\0'; i++)
+ filename[i] = filename[i + 1];
+
+ return fopen(filename, "wb");
+#else
+ strcpy(filename, "/tmp/fileXXXXXX");
+ int fd = mkstemp(filename);
+ return fdopen(fd, "w");
+#endif
+}
+
+// Use exception for catching assert
+#ifdef _MSC_VER
+#pragma warning(disable : 4127)
+#endif
+
+#ifdef __clang__
+#pragma GCC diagnostic push
+#if __has_warning("-Wdeprecated")
+#pragma GCC diagnostic ignored "-Wdeprecated"
+#endif
+#endif
+
+class AssertException : public std::logic_error {
+public:
+ AssertException(const char* w) : std::logic_error(w) {}
+ AssertException(const AssertException& rhs) : std::logic_error(rhs) {}
+ virtual ~AssertException() throw();
+};
+
+#ifdef __clang__
+#pragma GCC diagnostic pop
+#endif
+
+// Not using noexcept for testing RAPIDJSON_ASSERT()
+#define RAPIDJSON_HAS_CXX11_NOEXCEPT 0
+
+#ifndef RAPIDJSON_ASSERT
+#define RAPIDJSON_ASSERT(x) (!(x) ? throw AssertException(RAPIDJSON_STRINGIFY(x)) : (void)0u)
+#ifndef RAPIDJSON_ASSERT_THROWS
+#define RAPIDJSON_ASSERT_THROWS
+#endif
+#endif
+
+class Random {
+public:
+ Random(unsigned seed = 0) : mSeed(seed) {}
+
+ unsigned operator()() {
+ mSeed = 214013 * mSeed + 2531011;
+ return mSeed;
+ }
+
+private:
+ unsigned mSeed;
+};
+
+#endif // UNITTEST_H_
diff --git a/src/s3select/rapidjson/test/unittest/uritest.cpp b/src/s3select/rapidjson/test/unittest/uritest.cpp
new file mode 100644
index 000000000..5506aa1e8
--- /dev/null
+++ b/src/s3select/rapidjson/test/unittest/uritest.cpp
@@ -0,0 +1,718 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// (C) Copyright IBM Corporation 2021
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#define RAPIDJSON_SCHEMA_VERBOSE 0
+#define RAPIDJSON_HAS_STDSTRING 1
+
+#include "unittest.h"
+#include "rapidjson/document.h"
+#include "rapidjson/uri.h"
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(variadic-macros)
+#elif defined(_MSC_VER)
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(4822) // local class member function does not have a body
+#endif
+
+using namespace rapidjson;
+
+TEST(Uri, DefaultConstructor) {
+ typedef GenericUri<Value> UriType;
+ UriType u;
+ EXPECT_TRUE(u.GetSchemeString() == 0);
+ EXPECT_TRUE(u.GetAuthString() == 0);
+ EXPECT_TRUE(u.GetPathString() == 0);
+ EXPECT_TRUE(u.GetBaseString() == 0);
+ EXPECT_TRUE(u.GetQueryString() == 0);
+ EXPECT_TRUE(u.GetFragString() == 0);
+ EXPECT_TRUE(u.GetString() == 0);
+ EXPECT_TRUE(u.GetSchemeStringLength() == 0);
+ EXPECT_TRUE(u.GetAuthStringLength() == 0);
+ EXPECT_TRUE(u.GetPathStringLength() == 0);
+ EXPECT_TRUE(u.GetBaseStringLength() == 0);
+ EXPECT_TRUE(u.GetQueryStringLength() == 0);
+ EXPECT_TRUE(u.GetFragStringLength() == 0);
+ EXPECT_TRUE(u.GetStringLength() == 0);
+}
+
+
+TEST(Uri, Parse) {
+ typedef GenericUri<Value, MemoryPoolAllocator<> > UriType;
+ MemoryPoolAllocator<CrtAllocator> allocator;
+ Value v;
+ Value w;
+
+ v.SetString("http://auth/path/xxx?query#frag", allocator);
+ UriType u = UriType(v, &allocator);
+ EXPECT_TRUE(StrCmp(u.GetSchemeString(), "http:") == 0);
+ EXPECT_TRUE(StrCmp(u.GetAuthString(), "//auth") == 0);
+ EXPECT_TRUE(StrCmp(u.GetPathString(), "/path/xxx") == 0);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), "http://auth/path/xxx?query") == 0);
+ EXPECT_TRUE(StrCmp(u.GetQueryString(), "?query") == 0);
+ EXPECT_TRUE(StrCmp(u.GetFragString(), "#frag") == 0);
+ u.Get(w, allocator);
+ EXPECT_TRUE(*w.GetString() == *v.GetString());
+
+#if RAPIDJSON_HAS_STDSTRING
+ typedef std::basic_string<Value::Ch> String;
+ String str = "http://auth/path/xxx?query#frag";
+ const UriType uri = UriType(str);
+ EXPECT_TRUE(UriType::GetScheme(uri) == "http:");
+ EXPECT_TRUE(UriType::GetAuth(uri) == "//auth");
+ EXPECT_TRUE(UriType::GetPath(uri) == "/path/xxx");
+ EXPECT_TRUE(UriType::GetBase(uri) == "http://auth/path/xxx?query");
+ EXPECT_TRUE(UriType::GetQuery(uri) == "?query");
+ EXPECT_TRUE(UriType::GetFrag(uri) == "#frag");
+ EXPECT_TRUE(UriType::Get(uri) == str);
+#endif
+
+ v.SetString("urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f", allocator);
+ u = UriType(v);
+ EXPECT_TRUE(StrCmp(u.GetSchemeString(), "urn:") == 0);
+ EXPECT_TRUE(u.GetAuthStringLength() == 0);
+ EXPECT_TRUE(StrCmp(u.GetPathString(), "uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0);
+ EXPECT_TRUE(u.GetQueryStringLength() == 0);
+ EXPECT_TRUE(u.GetFragStringLength() == 0);
+ u.Get(w, allocator);
+ EXPECT_TRUE(*w.GetString() == *v.GetString());
+
+ v.SetString("", allocator);
+ u = UriType(v);
+ EXPECT_TRUE(u.GetSchemeStringLength() == 0);
+ EXPECT_TRUE(u.GetAuthStringLength() == 0);
+ EXPECT_TRUE(u.GetPathStringLength() == 0);
+ EXPECT_TRUE(u.GetBaseStringLength() == 0);
+ EXPECT_TRUE(u.GetQueryStringLength() == 0);
+ EXPECT_TRUE(u.GetFragStringLength() == 0);
+
+ v.SetString("http://auth/", allocator);
+ u = UriType(v);
+ EXPECT_TRUE(StrCmp(u.GetSchemeString(), "http:") == 0);
+ EXPECT_TRUE(StrCmp(u.GetAuthString(), "//auth") == 0);
+ EXPECT_TRUE(StrCmp(u.GetPathString(), "/") == 0);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), "http://auth/") == 0);
+ EXPECT_TRUE(u.GetQueryStringLength() == 0);
+ EXPECT_TRUE(u.GetFragStringLength() == 0);
+
+ u = UriType("/path/sub");
+ EXPECT_TRUE(u.GetSchemeStringLength() == 0);
+ EXPECT_TRUE(u.GetAuthStringLength() == 0);
+ EXPECT_TRUE(StrCmp(u.GetPathString(), "/path/sub") == 0);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), "/path/sub") == 0);
+ EXPECT_TRUE(u.GetQueryStringLength() == 0);
+ EXPECT_TRUE(u.GetFragStringLength() == 0);
+
+ // absolute path gets normalized
+ u = UriType("/path/../sub/");
+ EXPECT_TRUE(u.GetSchemeStringLength() == 0);
+ EXPECT_TRUE(u.GetAuthStringLength() == 0);
+ EXPECT_TRUE(StrCmp(u.GetPathString(), "/sub/") == 0);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), "/sub/") == 0);
+ EXPECT_TRUE(u.GetQueryStringLength() == 0);
+ EXPECT_TRUE(u.GetFragStringLength() == 0);
+
+ // relative path does not
+ u = UriType("path/../sub");
+ EXPECT_TRUE(u.GetSchemeStringLength() == 0);
+ EXPECT_TRUE(u.GetAuthStringLength() == 0);
+ EXPECT_TRUE(StrCmp(u.GetPathString(), "path/../sub") == 0);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), "path/../sub") == 0);
+ EXPECT_TRUE(u.GetQueryStringLength() == 0);
+ EXPECT_TRUE(u.GetFragStringLength() == 0);
+
+ u = UriType("http://auth#frag/stuff");
+ EXPECT_TRUE(StrCmp(u.GetSchemeString(), "http:") == 0);
+ EXPECT_TRUE(StrCmp(u.GetAuthString(), "//auth") == 0);
+ EXPECT_TRUE(u.GetPathStringLength() == 0);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), "http://auth") == 0);
+ EXPECT_TRUE(u.GetQueryStringLength() == 0);
+ EXPECT_TRUE(StrCmp(u.GetFragString(), "#frag/stuff") == 0);
+ EXPECT_TRUE(StrCmp(u.GetString(), "http://auth#frag/stuff") == 0);
+
+ const Value::Ch c[] = { '#', 'f', 'r', 'a', 'g', '/', 's', 't', 'u', 'f', 'f', '\0'};
+ SizeType len = internal::StrLen<Value::Ch>(c);
+ u = UriType(c, len);
+ EXPECT_TRUE(StrCmp(u.GetString(), "#frag/stuff") == 0);
+ EXPECT_TRUE(u.GetStringLength() == len);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), "") == 0);
+ EXPECT_TRUE(u.GetBaseStringLength() == 0);
+ EXPECT_TRUE(StrCmp(u.GetFragString(), "#frag/stuff") == 0);
+ EXPECT_TRUE(u.GetFragStringLength() == len);
+
+ u = UriType(c);
+ EXPECT_TRUE(StrCmp(u.GetString(), "#frag/stuff") == 0);
+ EXPECT_TRUE(u.GetStringLength() == len);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), "") == 0);
+ EXPECT_TRUE(u.GetBaseStringLength() == 0);
+ EXPECT_TRUE(StrCmp(u.GetFragString(), "#frag/stuff") == 0);
+ EXPECT_TRUE(u.GetFragStringLength() == len);
+
+ // Incomplete auth treated as path
+ str = "http:/";
+ const UriType u2 = UriType(str);
+ EXPECT_TRUE(StrCmp(u2.GetSchemeString(), "http:") == 0);
+ EXPECT_TRUE(u2.GetAuthStringLength() == 0);
+ EXPECT_TRUE(StrCmp(u2.GetPathString(), "/") == 0);
+ EXPECT_TRUE(StrCmp(u2.GetBaseString(), "http:/") == 0);
+}
+
+TEST(Uri, Parse_UTF16) {
+ typedef GenericValue<UTF16<> > Value16;
+ typedef GenericUri<Value16, MemoryPoolAllocator<> > UriType;
+ MemoryPoolAllocator<CrtAllocator> allocator;
+ Value16 v;
+ Value16 w;
+
+ v.SetString(L"http://auth/path/xxx?query#frag", allocator);
+ UriType u = UriType(v, &allocator);
+ EXPECT_TRUE(StrCmp(u.GetSchemeString(), L"http:") == 0);
+ EXPECT_TRUE(StrCmp(u.GetAuthString(), L"//auth") == 0);
+ EXPECT_TRUE(StrCmp(u.GetPathString(), L"/path/xxx") == 0);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), L"http://auth/path/xxx?query") == 0);
+ EXPECT_TRUE(StrCmp(u.GetQueryString(), L"?query") == 0);
+ EXPECT_TRUE(StrCmp(u.GetFragString(), L"#frag") == 0);
+ u.Get(w, allocator);
+ EXPECT_TRUE(*w.GetString() == *v.GetString());
+
+#if RAPIDJSON_HAS_STDSTRING
+ typedef std::basic_string<Value16::Ch> String;
+ String str = L"http://auth/path/xxx?query#frag";
+ const UriType uri = UriType(str);
+ EXPECT_TRUE(UriType::GetScheme(uri) == L"http:");
+ EXPECT_TRUE(UriType::GetAuth(uri) == L"//auth");
+ EXPECT_TRUE(UriType::GetPath(uri) == L"/path/xxx");
+ EXPECT_TRUE(UriType::GetBase(uri) == L"http://auth/path/xxx?query");
+ EXPECT_TRUE(UriType::GetQuery(uri) == L"?query");
+ EXPECT_TRUE(UriType::GetFrag(uri) == L"#frag");
+ EXPECT_TRUE(UriType::Get(uri) == str);
+#endif
+
+ v.SetString(L"urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f", allocator);
+ u = UriType(v);
+ EXPECT_TRUE(StrCmp(u.GetSchemeString(), L"urn:") == 0);
+ EXPECT_TRUE(u.GetAuthStringLength() == 0);
+ EXPECT_TRUE(StrCmp(u.GetPathString(), L"uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), L"urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0);
+ EXPECT_TRUE(u.GetQueryStringLength() == 0);
+ EXPECT_TRUE(u.GetFragStringLength() == 0);
+ u.Get(w, allocator);
+ EXPECT_TRUE(*w.GetString() == *v.GetString());
+
+ v.SetString(L"", allocator);
+ u = UriType(v);
+ EXPECT_TRUE(u.GetSchemeStringLength() == 0);
+ EXPECT_TRUE(u.GetAuthStringLength() == 0);
+ EXPECT_TRUE(u.GetPathStringLength() == 0);
+ EXPECT_TRUE(u.GetBaseStringLength() == 0);
+ EXPECT_TRUE(u.GetQueryStringLength() == 0);
+ EXPECT_TRUE(u.GetFragStringLength() == 0);
+
+ v.SetString(L"http://auth/", allocator);
+ u = UriType(v);
+ EXPECT_TRUE(StrCmp(u.GetSchemeString(), L"http:") == 0);
+ EXPECT_TRUE(StrCmp(u.GetAuthString(), L"//auth") == 0);
+ EXPECT_TRUE(StrCmp(u.GetPathString(), L"/") == 0);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), L"http://auth/") == 0);
+ EXPECT_TRUE(u.GetQueryStringLength() == 0);
+ EXPECT_TRUE(u.GetFragStringLength() == 0);
+
+ u = UriType(L"/path/sub");
+ EXPECT_TRUE(u.GetSchemeStringLength() == 0);
+ EXPECT_TRUE(u.GetAuthStringLength() == 0);
+ EXPECT_TRUE(StrCmp(u.GetPathString(), L"/path/sub") == 0);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), L"/path/sub") == 0);
+ EXPECT_TRUE(u.GetQueryStringLength() == 0);
+ EXPECT_TRUE(u.GetFragStringLength() == 0);
+
+ // absolute path gets normalized
+ u = UriType(L"/path/../sub/");
+ EXPECT_TRUE(u.GetSchemeStringLength() == 0);
+ EXPECT_TRUE(u.GetAuthStringLength() == 0);
+ EXPECT_TRUE(StrCmp(u.GetPathString(), L"/sub/") == 0);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), L"/sub/") == 0);
+ EXPECT_TRUE(u.GetQueryStringLength() == 0);
+ EXPECT_TRUE(u.GetFragStringLength() == 0);
+
+ // relative path does not
+ u = UriType(L"path/../sub");
+ EXPECT_TRUE(u.GetSchemeStringLength() == 0);
+ EXPECT_TRUE(u.GetAuthStringLength() == 0);
+ EXPECT_TRUE(StrCmp(u.GetPathString(), L"path/../sub") == 0);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), L"path/../sub") == 0);
+ EXPECT_TRUE(u.GetQueryStringLength() == 0);
+ EXPECT_TRUE(u.GetFragStringLength() == 0);
+
+ u = UriType(L"http://auth#frag/stuff");
+ EXPECT_TRUE(StrCmp(u.GetSchemeString(), L"http:") == 0);
+ EXPECT_TRUE(StrCmp(u.GetAuthString(), L"//auth") == 0);
+ EXPECT_TRUE(u.GetPathStringLength() == 0);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), L"http://auth") == 0);
+ EXPECT_TRUE(u.GetQueryStringLength() == 0);
+ EXPECT_TRUE(StrCmp(u.GetFragString(), L"#frag/stuff") == 0);
+ EXPECT_TRUE(StrCmp(u.GetString(), L"http://auth#frag/stuff") == 0);
+
+ const Value16::Ch c[] = { '#', 'f', 'r', 'a', 'g', '/', 's', 't', 'u', 'f', 'f', '\0'};
+ SizeType len = internal::StrLen<Value16::Ch>(c);
+ u = UriType(c, len);
+ EXPECT_TRUE(StrCmp(u.GetString(), L"#frag/stuff") == 0);
+ EXPECT_TRUE(u.GetStringLength() == len);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), L"") == 0);
+ EXPECT_TRUE(u.GetBaseStringLength() == 0);
+ EXPECT_TRUE(StrCmp(u.GetFragString(), L"#frag/stuff") == 0);
+ EXPECT_TRUE(u.GetFragStringLength() == len);
+
+ u = UriType(c);
+ EXPECT_TRUE(StrCmp(u.GetString(), L"#frag/stuff") == 0);
+ EXPECT_TRUE(u.GetStringLength() == len);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), L"") == 0);
+ EXPECT_TRUE(u.GetBaseStringLength() == 0);
+ EXPECT_TRUE(StrCmp(u.GetFragString(), L"#frag/stuff") == 0);
+ EXPECT_TRUE(u.GetFragStringLength() == len);
+
+ // Incomplete auth treated as path
+ u = UriType(L"http:/");
+ EXPECT_TRUE(StrCmp(u.GetSchemeString(), L"http:") == 0);
+ EXPECT_TRUE(u.GetAuthStringLength() == 0);
+ EXPECT_TRUE(StrCmp(u.GetPathString(), L"/") == 0);
+ EXPECT_TRUE(StrCmp(u.GetBaseString(), L"http:/") == 0);
+}
+
+TEST(Uri, CopyConstructor) {
+ typedef GenericUri<Value> UriType;
+ CrtAllocator allocator;
+
+ UriType u("http://auth/path/xxx?query#frag", &allocator);
+ UriType u2(u);
+ EXPECT_TRUE(u == u2);
+ EXPECT_NE(&u.GetAllocator(), &u2.GetAllocator());
+}
+
+TEST(Uri, Assignment) {
+ typedef GenericUri<Value> UriType;
+ CrtAllocator allocator;
+
+ UriType u("http://auth/path/xxx?query#frag", &allocator);
+ UriType u2;
+ u2 = u;
+ EXPECT_TRUE(u == u2);
+ EXPECT_NE(&u.GetAllocator(), &u2.GetAllocator());
+}
+
+TEST(Uri, Resolve) {
+ typedef GenericUri<Value> UriType;
+ CrtAllocator allocator;
+
+ // ref is full uri
+ UriType base = UriType("http://auth/path/#frag");
+ UriType ref = UriType("http://newauth/newpath#newfrag");
+ UriType res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://newauth/newpath#newfrag") == 0);
+
+ base = UriType("/path/#frag", &allocator);
+ ref = UriType("http://newauth/newpath#newfrag", &allocator);
+ res = ref.Resolve(base, &allocator);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://newauth/newpath#newfrag") == 0);
+
+ // ref is alternate uri
+ base = UriType("http://auth/path/#frag");
+ ref = UriType("urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0);
+
+ // ref is absolute path
+ base = UriType("http://auth/path/#");
+ ref = UriType("/newpath#newfrag");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://auth/newpath#newfrag") == 0);
+
+ // ref is relative path
+ base = UriType("http://auth/path/file.json#frag");
+ ref = UriType("newfile.json#");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://auth/path/newfile.json#") == 0);
+
+ base = UriType("http://auth/path/file.json#frag/stuff");
+ ref = UriType("newfile.json#newfrag/newstuff");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://auth/path/newfile.json#newfrag/newstuff") == 0);
+
+ base = UriType("file.json", &allocator);
+ ref = UriType("newfile.json", &base.GetAllocator());
+ res = ref.Resolve(base, &ref.GetAllocator());
+ EXPECT_TRUE(StrCmp(res.GetString(), "newfile.json") == 0);
+
+ base = UriType("file.json", &allocator);
+ ref = UriType("./newfile.json", &allocator);
+ res = ref.Resolve(base, &allocator);
+ EXPECT_TRUE(StrCmp(res.GetString(), "newfile.json") == 0);
+
+ base = UriType("file.json");
+ ref = UriType("parent/../newfile.json");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "newfile.json") == 0);
+
+ base = UriType("file.json");
+ ref = UriType("parent/./newfile.json");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "parent/newfile.json") == 0);
+
+ base = UriType("file.json");
+ ref = UriType("../../parent/.././newfile.json");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "newfile.json") == 0);
+
+ // This adds a joining slash so resolved length is base length + ref length + 1
+ base = UriType("http://auth");
+ ref = UriType("newfile.json");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://auth/newfile.json") == 0);
+
+ // ref is fragment
+ base = UriType("#frag/stuff");
+ ref = UriType("#newfrag/newstuff");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "#newfrag/newstuff") == 0);
+
+ // test ref fragment always wins
+ base = UriType("/path#frag");
+ ref = UriType("");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "/path") == 0);
+
+ // Examples from RFC3896
+ base = UriType("http://a/b/c/d;p?q");
+ ref = UriType("g:h");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "g:h") == 0);
+ ref = UriType("g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g") == 0);
+ ref = UriType("./g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g") == 0);
+ ref = UriType("g/");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g/") == 0);
+ ref = UriType("/g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0);
+ ref = UriType("//g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://g") == 0);
+ ref = UriType("?y");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/d;p?y") == 0);
+ ref = UriType("g?y");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g?y") == 0);
+ ref = UriType("#s");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/d;p?q#s") == 0);
+ ref = UriType("g#s");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g#s") == 0);
+ ref = UriType("g?y#s");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g?y#s") == 0);
+ ref = UriType(";x");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/;x") == 0);
+ ref = UriType("g;x");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g;x") == 0);
+ ref = UriType("g;x?y#s");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g;x?y#s") == 0);
+ ref = UriType("");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/d;p?q") == 0);
+ ref = UriType(".");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/") == 0);
+ ref = UriType("./");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/") == 0);
+ ref = UriType("..");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/") == 0);
+ ref = UriType("../");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/") == 0);
+ ref = UriType("../g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/g") == 0);
+ ref = UriType("../..");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/") == 0);
+ ref = UriType("../../");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/") == 0);
+ ref = UriType("../../g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0);
+ ref = UriType("../../../g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0);
+ ref = UriType("../../../../g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0);
+ ref = UriType("/./g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0);
+ ref = UriType("/../g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0);
+ ref = UriType("g.");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g.") == 0);
+ ref = UriType(".g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/.g") == 0);
+ ref = UriType("g..");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g..") == 0);
+ ref = UriType("..g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/..g") == 0);
+ ref = UriType("g#s/../x");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g#s/../x") == 0);
+}
+
+TEST(Uri, Resolve_UTF16) {
+ typedef GenericValue<UTF16<> > Value16;
+ typedef GenericUri<Value16> UriType;
+ CrtAllocator allocator;
+
+ // ref is full uri
+ UriType base = UriType(L"http://auth/path/#frag");
+ UriType ref = UriType(L"http://newauth/newpath#newfrag");
+ UriType res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://newauth/newpath#newfrag") == 0);
+
+ base = UriType(L"/path/#frag");
+ ref = UriType(L"http://newauth/newpath#newfrag");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://newauth/newpath#newfrag") == 0);
+
+ // ref is alternate uri
+ base = UriType(L"http://auth/path/#frag");
+ ref = UriType(L"urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0);
+
+ // ref is absolute path
+ base = UriType(L"http://auth/path/#");
+ ref = UriType(L"/newpath#newfrag");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://auth/newpath#newfrag") == 0);
+
+ // ref is relative path
+ base = UriType(L"http://auth/path/file.json#frag");
+ ref = UriType(L"newfile.json#");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://auth/path/newfile.json#") == 0);
+
+ base = UriType(L"http://auth/path/file.json#frag/stuff");
+ ref = UriType(L"newfile.json#newfrag/newstuff");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://auth/path/newfile.json#newfrag/newstuff") == 0);
+
+ base = UriType(L"file.json", &allocator);
+ ref = UriType(L"newfile.json", &base.GetAllocator());
+ res = ref.Resolve(base, &ref.GetAllocator());
+ EXPECT_TRUE(StrCmp(res.GetString(), L"newfile.json") == 0);
+
+ base = UriType(L"file.json", &allocator);
+ ref = UriType(L"./newfile.json", &allocator);
+ res = ref.Resolve(base, &allocator);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"newfile.json") == 0);
+
+ base = UriType(L"file.json");
+ ref = UriType(L"parent/../newfile.json");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"newfile.json") == 0);
+
+ base = UriType(L"file.json");
+ ref = UriType(L"parent/./newfile.json");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"parent/newfile.json") == 0);
+
+ base = UriType(L"file.json");
+ ref = UriType(L"../../parent/.././newfile.json");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"newfile.json") == 0);
+
+ // This adds a joining slash so resolved length is base length + ref length + 1
+ base = UriType(L"http://auth");
+ ref = UriType(L"newfile.json");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://auth/newfile.json") == 0);
+
+ // ref is fragment
+ base = UriType(L"#frag/stuff");
+ ref = UriType(L"#newfrag/newstuff");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"#newfrag/newstuff") == 0);
+
+ // test ref fragment always wins
+ base = UriType(L"/path#frag");
+ ref = UriType(L"");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"/path") == 0);
+
+ // Examples from RFC3896
+ base = UriType(L"http://a/b/c/d;p?q");
+ ref = UriType(L"g:h");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"g:h") == 0);
+ ref = UriType(L"g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g") == 0);
+ ref = UriType(L"./g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g") == 0);
+ ref = UriType(L"g/");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g/") == 0);
+ ref = UriType(L"/g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0);
+ ref = UriType(L"//g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://g") == 0);
+ ref = UriType(L"?y");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/d;p?y") == 0);
+ ref = UriType(L"g?y");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g?y") == 0);
+ ref = UriType(L"#s");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/d;p?q#s") == 0);
+ ref = UriType(L"g#s");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g#s") == 0);
+ ref = UriType(L"g?y#s");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g?y#s") == 0);
+ ref = UriType(L";x");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/;x") == 0);
+ ref = UriType(L"g;x");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g;x") == 0);
+ ref = UriType(L"g;x?y#s");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g;x?y#s") == 0);
+ ref = UriType(L"");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/d;p?q") == 0);
+ ref = UriType(L".");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/") == 0);
+ ref = UriType(L"./");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/") == 0);
+ ref = UriType(L"..");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/") == 0);
+ ref = UriType(L"../");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/") == 0);
+ ref = UriType(L"../g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/g") == 0);
+ ref = UriType(L"../..");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/") == 0);
+ ref = UriType(L"../../");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/") == 0);
+ ref = UriType(L"../../g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0);
+ ref = UriType(L"../../../g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0);
+ ref = UriType(L"../../../../g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0);
+ ref = UriType(L"/./g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0);
+ ref = UriType(L"/../g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0);
+ ref = UriType(L"g.");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g.") == 0);
+ ref = UriType(L".g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/.g") == 0);
+ ref = UriType(L"g..");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g..") == 0);
+ ref = UriType(L"..g");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/..g") == 0);
+ ref = UriType(L"g#s/../x");
+ res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g#s/../x") == 0);
+}
+
+TEST(Uri, Equals) {
+ typedef GenericUri<Value> UriType;
+
+ UriType a = UriType("http://a/a#a");
+ UriType b = UriType("http://a/a#b");
+ UriType c = a;
+
+ EXPECT_TRUE(a == a);
+ EXPECT_TRUE(a == c);
+ EXPECT_TRUE(a != b);
+}
+
+TEST(Uri, Match) {
+ typedef GenericUri<Value> UriType;
+
+ UriType a = UriType("http://a/a#a");
+ UriType b = UriType("http://a/a#b");
+ UriType c = a;
+ UriType d;
+
+ EXPECT_TRUE(a.Match(a));
+ EXPECT_TRUE(a.Match(c));
+ EXPECT_FALSE(a.Match(b));
+ EXPECT_FALSE(a.Match(b, true));
+ EXPECT_TRUE(a.Match(b, false)); // Base Uri same
+ EXPECT_FALSE(a.Match(d));
+ EXPECT_FALSE(d.Match(a));
+}
+
+TEST(Uri, Issue1899) {
+ typedef GenericUri<Value, MemoryPoolAllocator<> > UriType;
+
+ UriType base = UriType("http://auth/path/#frag");
+ UriType ref = UriType("http://newauth/newpath#newfrag");
+ UriType res = ref.Resolve(base);
+ EXPECT_TRUE(StrCmp(res.GetString(), "http://newauth/newpath#newfrag") == 0);
+}
+
+#if defined(_MSC_VER) || defined(__clang__)
+RAPIDJSON_DIAG_POP
+#endif
diff --git a/src/s3select/rapidjson/test/unittest/valuetest.cpp b/src/s3select/rapidjson/test/unittest/valuetest.cpp
new file mode 100644
index 000000000..0a6b325f4
--- /dev/null
+++ b/src/s3select/rapidjson/test/unittest/valuetest.cpp
@@ -0,0 +1,1861 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#include "unittest.h"
+#include "rapidjson/document.h"
+#include <algorithm>
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(c++98-compat)
+#endif
+
+using namespace rapidjson;
+
+TEST(Value, Size) {
+ if (sizeof(SizeType) == 4) {
+#if RAPIDJSON_48BITPOINTER_OPTIMIZATION
+ EXPECT_EQ(16u, sizeof(Value));
+#elif RAPIDJSON_64BIT
+ EXPECT_EQ(24u, sizeof(Value));
+#else
+ EXPECT_EQ(16u, sizeof(Value));
+#endif
+ }
+}
+
+TEST(Value, DefaultConstructor) {
+ Value x;
+ EXPECT_EQ(kNullType, x.GetType());
+ EXPECT_TRUE(x.IsNull());
+
+ //std::cout << "sizeof(Value): " << sizeof(x) << std::endl;
+}
+
+// Should not pass compilation
+//TEST(Value, copy_constructor) {
+// Value x(1234);
+// Value y = x;
+//}
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+
+#if 0 // Many old compiler does not support these. Turn it off temporaily.
+
+#include <type_traits>
+
+TEST(Value, Traits) {
+ typedef GenericValue<UTF8<>, CrtAllocator> Value;
+ static_assert(std::is_constructible<Value>::value, "");
+ static_assert(std::is_default_constructible<Value>::value, "");
+#ifndef _MSC_VER
+ static_assert(!std::is_copy_constructible<Value>::value, "");
+#endif
+ static_assert(std::is_move_constructible<Value>::value, "");
+
+#ifndef _MSC_VER
+ static_assert(std::is_nothrow_constructible<Value>::value, "");
+ static_assert(std::is_nothrow_default_constructible<Value>::value, "");
+ static_assert(!std::is_nothrow_copy_constructible<Value>::value, "");
+ static_assert(std::is_nothrow_move_constructible<Value>::value, "");
+#endif
+
+ static_assert(std::is_assignable<Value,Value>::value, "");
+#ifndef _MSC_VER
+ static_assert(!std::is_copy_assignable<Value>::value, "");
+#endif
+ static_assert(std::is_move_assignable<Value>::value, "");
+
+#ifndef _MSC_VER
+ static_assert(std::is_nothrow_assignable<Value, Value>::value, "");
+#endif
+ static_assert(!std::is_nothrow_copy_assignable<Value>::value, "");
+#ifndef _MSC_VER
+ static_assert(std::is_nothrow_move_assignable<Value>::value, "");
+#endif
+
+ static_assert(std::is_destructible<Value>::value, "");
+#ifndef _MSC_VER
+ static_assert(std::is_nothrow_destructible<Value>::value, "");
+#endif
+}
+
+#endif
+
+TEST(Value, MoveConstructor) {
+ typedef GenericValue<UTF8<>, CrtAllocator> V;
+ V::AllocatorType allocator;
+
+ V x((V(kArrayType)));
+ x.Reserve(4u, allocator);
+ x.PushBack(1, allocator).PushBack(2, allocator).PushBack(3, allocator).PushBack(4, allocator);
+ EXPECT_TRUE(x.IsArray());
+ EXPECT_EQ(4u, x.Size());
+
+ // Value y(x); // does not compile (!is_copy_constructible)
+ V y(std::move(x));
+ EXPECT_TRUE(x.IsNull());
+ EXPECT_TRUE(y.IsArray());
+ EXPECT_EQ(4u, y.Size());
+
+ // Value z = y; // does not compile (!is_copy_assignable)
+ V z = std::move(y);
+ EXPECT_TRUE(y.IsNull());
+ EXPECT_TRUE(z.IsArray());
+ EXPECT_EQ(4u, z.Size());
+}
+
+#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
+
+TEST(Value, AssignmentOperator) {
+ Value x(1234);
+ Value y;
+ y = x;
+ EXPECT_TRUE(x.IsNull()); // move semantic
+ EXPECT_EQ(1234, y.GetInt());
+
+ y = 5678;
+ EXPECT_TRUE(y.IsInt());
+ EXPECT_EQ(5678, y.GetInt());
+
+ x = "Hello";
+ EXPECT_TRUE(x.IsString());
+ EXPECT_STREQ(x.GetString(),"Hello");
+
+ y = StringRef(x.GetString(),x.GetStringLength());
+ EXPECT_TRUE(y.IsString());
+ EXPECT_EQ(y.GetString(),x.GetString());
+ EXPECT_EQ(y.GetStringLength(),x.GetStringLength());
+
+ static char mstr[] = "mutable";
+ // y = mstr; // should not compile
+ y = StringRef(mstr);
+ EXPECT_TRUE(y.IsString());
+ EXPECT_EQ(y.GetString(),mstr);
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ // C++11 move assignment
+ x = Value("World");
+ EXPECT_TRUE(x.IsString());
+ EXPECT_STREQ("World", x.GetString());
+
+ x = std::move(y);
+ EXPECT_TRUE(y.IsNull());
+ EXPECT_TRUE(x.IsString());
+ EXPECT_EQ(x.GetString(), mstr);
+
+ y = std::move(Value().SetInt(1234));
+ EXPECT_TRUE(y.IsInt());
+ EXPECT_EQ(1234, y);
+#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
+}
+
+template <typename A, typename B>
+void TestEqual(const A& a, const B& b) {
+ EXPECT_TRUE (a == b);
+ EXPECT_FALSE(a != b);
+ EXPECT_TRUE (b == a);
+ EXPECT_FALSE(b != a);
+}
+
+template <typename A, typename B>
+void TestUnequal(const A& a, const B& b) {
+ EXPECT_FALSE(a == b);
+ EXPECT_TRUE (a != b);
+ EXPECT_FALSE(b == a);
+ EXPECT_TRUE (b != a);
+}
+
+TEST(Value, EqualtoOperator) {
+ Value::AllocatorType allocator;
+ Value x(kObjectType);
+ x.AddMember("hello", "world", allocator)
+ .AddMember("t", Value(true).Move(), allocator)
+ .AddMember("f", Value(false).Move(), allocator)
+ .AddMember("n", Value(kNullType).Move(), allocator)
+ .AddMember("i", 123, allocator)
+ .AddMember("pi", 3.14, allocator)
+ .AddMember("a", Value(kArrayType).Move().PushBack(1, allocator).PushBack(2, allocator).PushBack(3, allocator), allocator);
+
+ // Test templated operator==() and operator!=()
+ TestEqual(x["hello"], "world");
+ const char* cc = "world";
+ TestEqual(x["hello"], cc);
+ char* c = strdup("world");
+ TestEqual(x["hello"], c);
+ free(c);
+
+ TestEqual(x["t"], true);
+ TestEqual(x["f"], false);
+ TestEqual(x["i"], 123);
+ TestEqual(x["pi"], 3.14);
+
+ // Test operator==() (including different allocators)
+ CrtAllocator crtAllocator;
+ GenericValue<UTF8<>, CrtAllocator> y;
+ GenericDocument<UTF8<>, CrtAllocator> z(&crtAllocator);
+ y.CopyFrom(x, crtAllocator);
+ z.CopyFrom(y, z.GetAllocator());
+ TestEqual(x, y);
+ TestEqual(y, z);
+ TestEqual(z, x);
+
+ // Swapping member order should be fine.
+ EXPECT_TRUE(y.RemoveMember("t"));
+ TestUnequal(x, y);
+ TestUnequal(z, y);
+ EXPECT_TRUE(z.RemoveMember("t"));
+ TestUnequal(x, z);
+ TestEqual(y, z);
+ 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));
+ TestUnequal(x, y);
+}
+
+template <typename Value>
+void TestCopyFrom() {
+ typename Value::AllocatorType a;
+ Value v1(1234);
+ Value v2(v1, a); // deep copy constructor
+ EXPECT_TRUE(v1.GetType() == v2.GetType());
+ EXPECT_EQ(v1.GetInt(), v2.GetInt());
+
+ v1.SetString("foo");
+ v2.CopyFrom(v1, a);
+ EXPECT_TRUE(v1.GetType() == v2.GetType());
+ 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());
+ EXPECT_EQ(v1.Size(), v2.Size());
+
+ v1.PushBack(Value().SetString("foo", a), a); // push string copy
+ EXPECT_TRUE(v1.Size() != v2.Size());
+ v2.CopyFrom(v1, a);
+ EXPECT_TRUE(v1.Size() == v2.Size());
+ EXPECT_STREQ(v1[1].GetString(), v2[1].GetString());
+ EXPECT_NE(v1[1].GetString(), v2[1].GetString()); // string got copied
+}
+
+TEST(Value, CopyFrom) {
+ TestCopyFrom<Value>();
+ TestCopyFrom<GenericValue<UTF8<>, CrtAllocator> >();
+}
+
+TEST(Value, Swap) {
+ Value v1(1234);
+ Value v2(kObjectType);
+
+ EXPECT_EQ(&v1, &v1.Swap(v2));
+ EXPECT_TRUE(v1.IsObject());
+ EXPECT_TRUE(v2.IsInt());
+ EXPECT_EQ(1234, v2.GetInt());
+
+ // testing std::swap compatibility
+ using std::swap;
+ swap(v1, v2);
+ EXPECT_TRUE(v1.IsInt());
+ EXPECT_TRUE(v2.IsObject());
+}
+
+TEST(Value, Null) {
+ // Default constructor
+ Value x;
+ EXPECT_EQ(kNullType, x.GetType());
+ EXPECT_TRUE(x.IsNull());
+
+ EXPECT_FALSE(x.IsTrue());
+ EXPECT_FALSE(x.IsFalse());
+ EXPECT_FALSE(x.IsNumber());
+ EXPECT_FALSE(x.IsString());
+ EXPECT_FALSE(x.IsObject());
+ EXPECT_FALSE(x.IsArray());
+
+ // Constructor with type
+ Value y(kNullType);
+ EXPECT_TRUE(y.IsNull());
+
+ // SetNull();
+ Value z(true);
+ z.SetNull();
+ EXPECT_TRUE(z.IsNull());
+}
+
+TEST(Value, True) {
+ // Constructor with bool
+ Value x(true);
+ EXPECT_EQ(kTrueType, x.GetType());
+ EXPECT_TRUE(x.GetBool());
+ EXPECT_TRUE(x.IsBool());
+ EXPECT_TRUE(x.IsTrue());
+
+ EXPECT_FALSE(x.IsNull());
+ EXPECT_FALSE(x.IsFalse());
+ EXPECT_FALSE(x.IsNumber());
+ EXPECT_FALSE(x.IsString());
+ EXPECT_FALSE(x.IsObject());
+ EXPECT_FALSE(x.IsArray());
+
+ // Constructor with type
+ Value y(kTrueType);
+ EXPECT_TRUE(y.IsTrue());
+
+ // SetBool()
+ Value z;
+ z.SetBool(true);
+ EXPECT_TRUE(z.IsTrue());
+
+ // Templated functions
+ EXPECT_TRUE(z.Is<bool>());
+ EXPECT_TRUE(z.Get<bool>());
+ EXPECT_FALSE(z.Set<bool>(false).Get<bool>());
+ EXPECT_TRUE(z.Set(true).Get<bool>());
+}
+
+TEST(Value, False) {
+ // Constructor with bool
+ Value x(false);
+ EXPECT_EQ(kFalseType, x.GetType());
+ EXPECT_TRUE(x.IsBool());
+ EXPECT_TRUE(x.IsFalse());
+
+ EXPECT_FALSE(x.IsNull());
+ EXPECT_FALSE(x.IsTrue());
+ EXPECT_FALSE(x.GetBool());
+ //EXPECT_FALSE((bool)x);
+ EXPECT_FALSE(x.IsNumber());
+ EXPECT_FALSE(x.IsString());
+ EXPECT_FALSE(x.IsObject());
+ EXPECT_FALSE(x.IsArray());
+
+ // Constructor with type
+ Value y(kFalseType);
+ EXPECT_TRUE(y.IsFalse());
+
+ // SetBool()
+ Value z;
+ z.SetBool(false);
+ EXPECT_TRUE(z.IsFalse());
+}
+
+TEST(Value, Int) {
+ // Constructor with int
+ Value x(1234);
+ EXPECT_EQ(kNumberType, x.GetType());
+ EXPECT_EQ(1234, x.GetInt());
+ EXPECT_EQ(1234u, x.GetUint());
+ EXPECT_EQ(1234, x.GetInt64());
+ EXPECT_EQ(1234u, x.GetUint64());
+ EXPECT_NEAR(1234.0, x.GetDouble(), 0.0);
+ //EXPECT_EQ(1234, (int)x);
+ //EXPECT_EQ(1234, (unsigned)x);
+ //EXPECT_EQ(1234, (int64_t)x);
+ //EXPECT_EQ(1234, (uint64_t)x);
+ //EXPECT_EQ(1234, (double)x);
+ EXPECT_TRUE(x.IsNumber());
+ EXPECT_TRUE(x.IsInt());
+ EXPECT_TRUE(x.IsUint());
+ EXPECT_TRUE(x.IsInt64());
+ EXPECT_TRUE(x.IsUint64());
+
+ EXPECT_FALSE(x.IsDouble());
+ EXPECT_FALSE(x.IsFloat());
+ EXPECT_FALSE(x.IsNull());
+ EXPECT_FALSE(x.IsBool());
+ EXPECT_FALSE(x.IsFalse());
+ EXPECT_FALSE(x.IsTrue());
+ EXPECT_FALSE(x.IsString());
+ EXPECT_FALSE(x.IsObject());
+ EXPECT_FALSE(x.IsArray());
+
+ Value nx(-1234);
+ EXPECT_EQ(-1234, nx.GetInt());
+ EXPECT_EQ(-1234, nx.GetInt64());
+ EXPECT_TRUE(nx.IsInt());
+ EXPECT_TRUE(nx.IsInt64());
+ EXPECT_FALSE(nx.IsUint());
+ EXPECT_FALSE(nx.IsUint64());
+
+ // Constructor with type
+ Value y(kNumberType);
+ EXPECT_TRUE(y.IsNumber());
+ EXPECT_TRUE(y.IsInt());
+ EXPECT_EQ(0, y.GetInt());
+
+ // SetInt()
+ Value z;
+ z.SetInt(1234);
+ EXPECT_EQ(1234, z.GetInt());
+
+ // operator=(int)
+ z = 5678;
+ EXPECT_EQ(5678, z.GetInt());
+
+ // Templated functions
+ EXPECT_TRUE(z.Is<int>());
+ EXPECT_EQ(5678, z.Get<int>());
+ EXPECT_EQ(5679, z.Set(5679).Get<int>());
+ EXPECT_EQ(5680, z.Set<int>(5680).Get<int>());
+
+#ifdef _MSC_VER
+ // long as int on MSC platforms
+ RAPIDJSON_STATIC_ASSERT(sizeof(long) == sizeof(int));
+ z.SetInt(2222);
+ EXPECT_TRUE(z.Is<long>());
+ EXPECT_EQ(2222l, z.Get<long>());
+ EXPECT_EQ(3333l, z.Set(3333l).Get<long>());
+ EXPECT_EQ(4444l, z.Set<long>(4444l).Get<long>());
+ EXPECT_TRUE(z.IsInt());
+#endif
+}
+
+TEST(Value, Uint) {
+ // Constructor with int
+ Value x(1234u);
+ EXPECT_EQ(kNumberType, x.GetType());
+ EXPECT_EQ(1234, x.GetInt());
+ EXPECT_EQ(1234u, x.GetUint());
+ EXPECT_EQ(1234, x.GetInt64());
+ EXPECT_EQ(1234u, x.GetUint64());
+ EXPECT_TRUE(x.IsNumber());
+ EXPECT_TRUE(x.IsInt());
+ EXPECT_TRUE(x.IsUint());
+ EXPECT_TRUE(x.IsInt64());
+ EXPECT_TRUE(x.IsUint64());
+ EXPECT_NEAR(1234.0, x.GetDouble(), 0.0); // Number can always be cast as double but !IsDouble().
+
+ EXPECT_FALSE(x.IsDouble());
+ EXPECT_FALSE(x.IsFloat());
+ EXPECT_FALSE(x.IsNull());
+ EXPECT_FALSE(x.IsBool());
+ EXPECT_FALSE(x.IsFalse());
+ EXPECT_FALSE(x.IsTrue());
+ EXPECT_FALSE(x.IsString());
+ EXPECT_FALSE(x.IsObject());
+ EXPECT_FALSE(x.IsArray());
+
+ // SetUint()
+ Value z;
+ z.SetUint(1234);
+ EXPECT_EQ(1234u, z.GetUint());
+
+ // operator=(unsigned)
+ z = 5678u;
+ EXPECT_EQ(5678u, z.GetUint());
+
+ z = 2147483648u; // 2^31, cannot cast as int
+ EXPECT_EQ(2147483648u, z.GetUint());
+ EXPECT_FALSE(z.IsInt());
+ EXPECT_TRUE(z.IsInt64()); // Issue 41: Incorrect parsing of unsigned int number types
+
+ // Templated functions
+ EXPECT_TRUE(z.Is<unsigned>());
+ EXPECT_EQ(2147483648u, z.Get<unsigned>());
+ EXPECT_EQ(2147483649u, z.Set(2147483649u).Get<unsigned>());
+ EXPECT_EQ(2147483650u, z.Set<unsigned>(2147483650u).Get<unsigned>());
+
+#ifdef _MSC_VER
+ // unsigned long as unsigned on MSC platforms
+ RAPIDJSON_STATIC_ASSERT(sizeof(unsigned long) == sizeof(unsigned));
+ z.SetUint(2222);
+ EXPECT_TRUE(z.Is<unsigned long>());
+ EXPECT_EQ(2222ul, z.Get<unsigned long>());
+ EXPECT_EQ(3333ul, z.Set(3333ul).Get<unsigned long>());
+ EXPECT_EQ(4444ul, z.Set<unsigned long>(4444ul).Get<unsigned long>());
+ EXPECT_TRUE(x.IsUint());
+#endif
+}
+
+TEST(Value, Int64) {
+ // Constructor with int
+ Value x(int64_t(1234));
+ EXPECT_EQ(kNumberType, x.GetType());
+ EXPECT_EQ(1234, x.GetInt());
+ EXPECT_EQ(1234u, x.GetUint());
+ EXPECT_EQ(1234, x.GetInt64());
+ EXPECT_EQ(1234u, x.GetUint64());
+ EXPECT_TRUE(x.IsNumber());
+ EXPECT_TRUE(x.IsInt());
+ EXPECT_TRUE(x.IsUint());
+ EXPECT_TRUE(x.IsInt64());
+ EXPECT_TRUE(x.IsUint64());
+
+ EXPECT_FALSE(x.IsDouble());
+ EXPECT_FALSE(x.IsFloat());
+ EXPECT_FALSE(x.IsNull());
+ EXPECT_FALSE(x.IsBool());
+ EXPECT_FALSE(x.IsFalse());
+ EXPECT_FALSE(x.IsTrue());
+ EXPECT_FALSE(x.IsString());
+ EXPECT_FALSE(x.IsObject());
+ EXPECT_FALSE(x.IsArray());
+
+ Value nx(int64_t(-1234));
+ EXPECT_EQ(-1234, nx.GetInt());
+ EXPECT_EQ(-1234, nx.GetInt64());
+ EXPECT_TRUE(nx.IsInt());
+ EXPECT_TRUE(nx.IsInt64());
+ EXPECT_FALSE(nx.IsUint());
+ EXPECT_FALSE(nx.IsUint64());
+
+ // SetInt64()
+ Value z;
+ z.SetInt64(1234);
+ EXPECT_EQ(1234, z.GetInt64());
+
+ z.SetInt64(2147483648u); // 2^31, cannot cast as int
+ EXPECT_FALSE(z.IsInt());
+ EXPECT_TRUE(z.IsUint());
+ EXPECT_NEAR(2147483648.0, z.GetDouble(), 0.0);
+
+ z.SetInt64(int64_t(4294967295u) + 1); // 2^32, cannot cast as uint
+ EXPECT_FALSE(z.IsInt());
+ EXPECT_FALSE(z.IsUint());
+ EXPECT_NEAR(4294967296.0, z.GetDouble(), 0.0);
+
+ z.SetInt64(-int64_t(2147483648u) - 1); // -2^31-1, cannot cast as int
+ EXPECT_FALSE(z.IsInt());
+ EXPECT_NEAR(-2147483649.0, z.GetDouble(), 0.0);
+
+ int64_t i = static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x80000000, 00000000));
+ z.SetInt64(i);
+ EXPECT_DOUBLE_EQ(-9223372036854775808.0, z.GetDouble());
+
+ // Templated functions
+ EXPECT_TRUE(z.Is<int64_t>());
+ EXPECT_EQ(i, z.Get<int64_t>());
+#if 0 // signed integer underflow is undefined behaviour
+ EXPECT_EQ(i - 1, z.Set(i - 1).Get<int64_t>());
+ EXPECT_EQ(i - 2, z.Set<int64_t>(i - 2).Get<int64_t>());
+#endif
+}
+
+TEST(Value, Uint64) {
+ // Constructor with int
+ Value x(uint64_t(1234));
+ EXPECT_EQ(kNumberType, x.GetType());
+ EXPECT_EQ(1234, x.GetInt());
+ EXPECT_EQ(1234u, x.GetUint());
+ EXPECT_EQ(1234, x.GetInt64());
+ EXPECT_EQ(1234u, x.GetUint64());
+ EXPECT_TRUE(x.IsNumber());
+ EXPECT_TRUE(x.IsInt());
+ EXPECT_TRUE(x.IsUint());
+ EXPECT_TRUE(x.IsInt64());
+ EXPECT_TRUE(x.IsUint64());
+
+ EXPECT_FALSE(x.IsDouble());
+ EXPECT_FALSE(x.IsFloat());
+ EXPECT_FALSE(x.IsNull());
+ EXPECT_FALSE(x.IsBool());
+ EXPECT_FALSE(x.IsFalse());
+ EXPECT_FALSE(x.IsTrue());
+ EXPECT_FALSE(x.IsString());
+ EXPECT_FALSE(x.IsObject());
+ EXPECT_FALSE(x.IsArray());
+
+ // SetUint64()
+ Value z;
+ z.SetUint64(1234);
+ EXPECT_EQ(1234u, z.GetUint64());
+
+ z.SetUint64(uint64_t(2147483648u)); // 2^31, cannot cast as int
+ EXPECT_FALSE(z.IsInt());
+ EXPECT_TRUE(z.IsUint());
+ EXPECT_TRUE(z.IsInt64());
+
+ z.SetUint64(uint64_t(4294967295u) + 1); // 2^32, cannot cast as uint
+ EXPECT_FALSE(z.IsInt());
+ EXPECT_FALSE(z.IsUint());
+ EXPECT_TRUE(z.IsInt64());
+
+ uint64_t u = RAPIDJSON_UINT64_C2(0x80000000, 0x00000000);
+ z.SetUint64(u); // 2^63 cannot cast as int64
+ EXPECT_FALSE(z.IsInt64());
+ EXPECT_EQ(u, z.GetUint64()); // Issue 48
+ EXPECT_DOUBLE_EQ(9223372036854775808.0, z.GetDouble());
+
+ // Templated functions
+ EXPECT_TRUE(z.Is<uint64_t>());
+ EXPECT_EQ(u, z.Get<uint64_t>());
+ EXPECT_EQ(u + 1, z.Set(u + 1).Get<uint64_t>());
+ EXPECT_EQ(u + 2, z.Set<uint64_t>(u + 2).Get<uint64_t>());
+}
+
+TEST(Value, Double) {
+ // Constructor with double
+ Value x(12.34);
+ EXPECT_EQ(kNumberType, x.GetType());
+ EXPECT_NEAR(12.34, x.GetDouble(), 0.0);
+ EXPECT_TRUE(x.IsNumber());
+ EXPECT_TRUE(x.IsDouble());
+
+ EXPECT_FALSE(x.IsInt());
+ EXPECT_FALSE(x.IsNull());
+ EXPECT_FALSE(x.IsBool());
+ EXPECT_FALSE(x.IsFalse());
+ EXPECT_FALSE(x.IsTrue());
+ EXPECT_FALSE(x.IsString());
+ EXPECT_FALSE(x.IsObject());
+ EXPECT_FALSE(x.IsArray());
+
+ // SetDouble()
+ Value z;
+ z.SetDouble(12.34);
+ EXPECT_NEAR(12.34, z.GetDouble(), 0.0);
+
+ z = 56.78;
+ EXPECT_NEAR(56.78, z.GetDouble(), 0.0);
+
+ // Templated functions
+ EXPECT_TRUE(z.Is<double>());
+ EXPECT_EQ(56.78, z.Get<double>());
+ EXPECT_EQ(57.78, z.Set(57.78).Get<double>());
+ EXPECT_EQ(58.78, z.Set<double>(58.78).Get<double>());
+}
+
+TEST(Value, Float) {
+ // Constructor with double
+ Value x(12.34f);
+ EXPECT_EQ(kNumberType, x.GetType());
+ EXPECT_NEAR(12.34f, x.GetFloat(), 0.0);
+ EXPECT_TRUE(x.IsNumber());
+ EXPECT_TRUE(x.IsDouble());
+ EXPECT_TRUE(x.IsFloat());
+
+ EXPECT_FALSE(x.IsInt());
+ EXPECT_FALSE(x.IsNull());
+ EXPECT_FALSE(x.IsBool());
+ EXPECT_FALSE(x.IsFalse());
+ EXPECT_FALSE(x.IsTrue());
+ EXPECT_FALSE(x.IsString());
+ EXPECT_FALSE(x.IsObject());
+ EXPECT_FALSE(x.IsArray());
+
+ // SetFloat()
+ Value z;
+ z.SetFloat(12.34f);
+ EXPECT_NEAR(12.34f, z.GetFloat(), 0.0f);
+
+ // Issue 573
+ z.SetInt(0);
+ EXPECT_EQ(0.0f, z.GetFloat());
+
+ z = 56.78f;
+ EXPECT_NEAR(56.78f, z.GetFloat(), 0.0f);
+
+ // Templated functions
+ EXPECT_TRUE(z.Is<float>());
+ EXPECT_EQ(56.78f, z.Get<float>());
+ EXPECT_EQ(57.78f, z.Set(57.78f).Get<float>());
+ EXPECT_EQ(58.78f, z.Set<float>(58.78f).Get<float>());
+}
+
+TEST(Value, IsLosslessDouble) {
+ EXPECT_TRUE(Value(0.0).IsLosslessDouble());
+ EXPECT_TRUE(Value(12.34).IsLosslessDouble());
+ EXPECT_TRUE(Value(-123).IsLosslessDouble());
+ EXPECT_TRUE(Value(2147483648u).IsLosslessDouble());
+ EXPECT_TRUE(Value(-static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x40000000, 0x00000000))).IsLosslessDouble());
+#if !(defined(_MSC_VER) && _MSC_VER < 1800) // VC2010 has problem
+ EXPECT_TRUE(Value(RAPIDJSON_UINT64_C2(0xA0000000, 0x00000000)).IsLosslessDouble());
+#endif
+
+ EXPECT_FALSE(Value(static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x7FFFFFFF, 0xFFFFFFFF))).IsLosslessDouble()); // INT64_MAX
+ EXPECT_FALSE(Value(-static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x7FFFFFFF, 0xFFFFFFFF))).IsLosslessDouble()); // -INT64_MAX
+ EXPECT_TRUE(Value(-static_cast<int64_t>(RAPIDJSON_UINT64_C2(0x7FFFFFFF, 0xFFFFFFFF)) - 1).IsLosslessDouble()); // INT64_MIN
+ EXPECT_FALSE(Value(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0xFFFFFFFF)).IsLosslessDouble()); // UINT64_MAX
+
+ EXPECT_TRUE(Value(3.4028234e38f).IsLosslessDouble()); // FLT_MAX
+ EXPECT_TRUE(Value(-3.4028234e38f).IsLosslessDouble()); // -FLT_MAX
+ EXPECT_TRUE(Value(1.17549435e-38f).IsLosslessDouble()); // FLT_MIN
+ EXPECT_TRUE(Value(-1.17549435e-38f).IsLosslessDouble()); // -FLT_MIN
+ EXPECT_TRUE(Value(1.7976931348623157e+308).IsLosslessDouble()); // DBL_MAX
+ EXPECT_TRUE(Value(-1.7976931348623157e+308).IsLosslessDouble()); // -DBL_MAX
+ EXPECT_TRUE(Value(2.2250738585072014e-308).IsLosslessDouble()); // DBL_MIN
+ EXPECT_TRUE(Value(-2.2250738585072014e-308).IsLosslessDouble()); // -DBL_MIN
+}
+
+TEST(Value, IsLosslessFloat) {
+ EXPECT_TRUE(Value(12.25).IsLosslessFloat());
+ EXPECT_TRUE(Value(-123).IsLosslessFloat());
+ EXPECT_TRUE(Value(2147483648u).IsLosslessFloat());
+ EXPECT_TRUE(Value(3.4028234e38f).IsLosslessFloat());
+ EXPECT_TRUE(Value(-3.4028234e38f).IsLosslessFloat());
+ EXPECT_FALSE(Value(3.4028235e38).IsLosslessFloat());
+ EXPECT_FALSE(Value(0.3).IsLosslessFloat());
+}
+
+TEST(Value, String) {
+ // Construction with const string
+ Value x("Hello", 5); // literal
+ EXPECT_EQ(kStringType, x.GetType());
+ EXPECT_TRUE(x.IsString());
+ EXPECT_STREQ("Hello", x.GetString());
+ EXPECT_EQ(5u, x.GetStringLength());
+
+ EXPECT_FALSE(x.IsNumber());
+ EXPECT_FALSE(x.IsNull());
+ EXPECT_FALSE(x.IsBool());
+ EXPECT_FALSE(x.IsFalse());
+ EXPECT_FALSE(x.IsTrue());
+ EXPECT_FALSE(x.IsObject());
+ EXPECT_FALSE(x.IsArray());
+
+ static const char cstr[] = "World"; // const array
+ Value(cstr).Swap(x);
+ EXPECT_TRUE(x.IsString());
+ EXPECT_EQ(x.GetString(), cstr);
+ EXPECT_EQ(x.GetStringLength(), sizeof(cstr)-1);
+
+ static char mstr[] = "Howdy"; // non-const array
+ // Value(mstr).Swap(x); // should not compile
+ Value(StringRef(mstr)).Swap(x);
+ EXPECT_TRUE(x.IsString());
+ EXPECT_EQ(x.GetString(), mstr);
+ EXPECT_EQ(x.GetStringLength(), sizeof(mstr)-1);
+ strncpy(mstr,"Hello", sizeof(mstr));
+ EXPECT_STREQ(x.GetString(), "Hello");
+
+ const char* pstr = cstr;
+ //Value(pstr).Swap(x); // should not compile
+ Value(StringRef(pstr)).Swap(x);
+ EXPECT_TRUE(x.IsString());
+ EXPECT_EQ(x.GetString(), cstr);
+ EXPECT_EQ(x.GetStringLength(), sizeof(cstr)-1);
+
+ char* mpstr = mstr;
+ Value(StringRef(mpstr,sizeof(mstr)-1)).Swap(x);
+ EXPECT_TRUE(x.IsString());
+ EXPECT_EQ(x.GetString(), mstr);
+ EXPECT_EQ(x.GetStringLength(), 5u);
+ EXPECT_STREQ(x.GetString(), "Hello");
+
+ // Constructor with copy string
+ MemoryPoolAllocator<> allocator;
+ Value c(x.GetString(), x.GetStringLength(), allocator);
+ EXPECT_NE(x.GetString(), c.GetString());
+ EXPECT_EQ(x.GetStringLength(), c.GetStringLength());
+ EXPECT_STREQ(x.GetString(), c.GetString());
+ //x.SetString("World");
+ x.SetString("World", 5);
+ EXPECT_STREQ("Hello", c.GetString());
+ EXPECT_EQ(5u, c.GetStringLength());
+
+ // Constructor with type
+ Value y(kStringType);
+ EXPECT_TRUE(y.IsString());
+ EXPECT_STREQ("", y.GetString()); // Empty string should be "" instead of 0 (issue 226)
+ EXPECT_EQ(0u, y.GetStringLength());
+
+ // SetConsttring()
+ Value z;
+ z.SetString("Hello");
+ EXPECT_TRUE(x.IsString());
+ z.SetString("Hello", 5);
+ EXPECT_STREQ("Hello", z.GetString());
+ EXPECT_STREQ("Hello", z.GetString());
+ EXPECT_EQ(5u, z.GetStringLength());
+
+ z.SetString("Hello");
+ EXPECT_TRUE(z.IsString());
+ EXPECT_STREQ("Hello", z.GetString());
+
+ //z.SetString(mstr); // should not compile
+ //z.SetString(pstr); // should not compile
+ z.SetString(StringRef(mstr));
+ EXPECT_TRUE(z.IsString());
+ EXPECT_STREQ(z.GetString(), mstr);
+
+ z.SetString(cstr);
+ EXPECT_TRUE(z.IsString());
+ EXPECT_EQ(cstr, z.GetString());
+
+ z = cstr;
+ EXPECT_TRUE(z.IsString());
+ EXPECT_EQ(cstr, z.GetString());
+
+ // SetString()
+ char s[] = "World";
+ Value w;
+ w.SetString(s, static_cast<SizeType>(strlen(s)), allocator);
+ s[0] = '\0';
+ EXPECT_STREQ("World", w.GetString());
+ EXPECT_EQ(5u, w.GetStringLength());
+
+ // templated functions
+ EXPECT_TRUE(z.Is<const char*>());
+ EXPECT_STREQ(cstr, z.Get<const char*>());
+ EXPECT_STREQ("Apple", z.Set<const char*>("Apple").Get<const char*>());
+
+#if RAPIDJSON_HAS_STDSTRING
+ {
+ std::string str = "Hello World";
+ str[5] = '\0';
+ EXPECT_STREQ(str.data(),"Hello"); // embedded '\0'
+ EXPECT_EQ(str.size(), 11u);
+
+ // no copy
+ Value vs0(StringRef(str));
+ EXPECT_TRUE(vs0.IsString());
+ EXPECT_EQ(vs0.GetString(), str.data());
+ EXPECT_EQ(vs0.GetStringLength(), str.size());
+ TestEqual(vs0, str);
+
+ // do copy
+ Value vs1(str, allocator);
+ EXPECT_TRUE(vs1.IsString());
+ EXPECT_NE(vs1.GetString(), str.data());
+ EXPECT_NE(vs1.GetString(), str); // not equal due to embedded '\0'
+ EXPECT_EQ(vs1.GetStringLength(), str.size());
+ TestEqual(vs1, str);
+
+ // SetString
+ str = "World";
+ vs0.SetNull().SetString(str, allocator);
+ EXPECT_TRUE(vs0.IsString());
+ EXPECT_STREQ(vs0.GetString(), str.c_str());
+ EXPECT_EQ(vs0.GetStringLength(), str.size());
+ TestEqual(str, vs0);
+ TestUnequal(str, vs1);
+
+ // vs1 = str; // should not compile
+ vs1 = StringRef(str);
+ TestEqual(str, vs1);
+ TestEqual(vs0, vs1);
+
+ // Templated function.
+ EXPECT_TRUE(vs0.Is<std::string>());
+ EXPECT_EQ(str, vs0.Get<std::string>());
+ vs0.Set<std::string>(std::string("Apple"), allocator);
+ EXPECT_EQ(std::string("Apple"), vs0.Get<std::string>());
+ vs0.Set(std::string("Orange"), allocator);
+ EXPECT_EQ(std::string("Orange"), vs0.Get<std::string>());
+ }
+#endif // RAPIDJSON_HAS_STDSTRING
+}
+
+// Issue 226: Value of string type should not point to NULL
+TEST(Value, SetStringNull) {
+
+ MemoryPoolAllocator<> allocator;
+ const char* nullPtr = 0;
+ {
+ // Construction with string type creates empty string
+ Value v(kStringType);
+ EXPECT_NE(v.GetString(), nullPtr); // non-null string returned
+ EXPECT_EQ(v.GetStringLength(), 0u);
+
+ // Construction from/setting to null without length not allowed
+ EXPECT_THROW(Value(StringRef(nullPtr)), AssertException);
+ EXPECT_THROW(Value(StringRef(nullPtr), allocator), AssertException);
+ EXPECT_THROW(v.SetString(nullPtr, allocator), AssertException);
+
+ // Non-empty length with null string is not allowed
+ EXPECT_THROW(v.SetString(nullPtr, 17u), AssertException);
+ EXPECT_THROW(v.SetString(nullPtr, 42u, allocator), AssertException);
+
+ // Setting to null string with empty length is allowed
+ v.SetString(nullPtr, 0u);
+ EXPECT_NE(v.GetString(), nullPtr); // non-null string returned
+ EXPECT_EQ(v.GetStringLength(), 0u);
+
+ v.SetNull();
+ v.SetString(nullPtr, 0u, allocator);
+ EXPECT_NE(v.GetString(), nullPtr); // non-null string returned
+ EXPECT_EQ(v.GetStringLength(), 0u);
+ }
+ // Construction with null string and empty length is allowed
+ {
+ Value v(nullPtr,0u);
+ EXPECT_NE(v.GetString(), nullPtr); // non-null string returned
+ EXPECT_EQ(v.GetStringLength(), 0u);
+ }
+ {
+ Value v(nullPtr, 0u, allocator);
+ EXPECT_NE(v.GetString(), nullPtr); // non-null string returned
+ EXPECT_EQ(v.GetStringLength(), 0u);
+ }
+}
+
+template <typename T, typename Allocator>
+static void TestArray(T& x, Allocator& allocator) {
+ const T& y = x;
+
+ // PushBack()
+ Value v;
+ x.PushBack(v, allocator);
+ v.SetBool(true);
+ x.PushBack(v, allocator);
+ v.SetBool(false);
+ x.PushBack(v, allocator);
+ v.SetInt(123);
+ x.PushBack(v, allocator);
+ //x.PushBack((const char*)"foo", allocator); // should not compile
+ x.PushBack("foo", allocator);
+
+ EXPECT_FALSE(x.Empty());
+ EXPECT_EQ(5u, x.Size());
+ EXPECT_FALSE(y.Empty());
+ EXPECT_EQ(5u, y.Size());
+ EXPECT_TRUE(x[SizeType(0)].IsNull());
+ EXPECT_TRUE(x[1].IsTrue());
+ EXPECT_TRUE(x[2].IsFalse());
+ EXPECT_TRUE(x[3].IsInt());
+ EXPECT_EQ(123, x[3].GetInt());
+ EXPECT_TRUE(y[SizeType(0)].IsNull());
+ EXPECT_TRUE(y[1].IsTrue());
+ EXPECT_TRUE(y[2].IsFalse());
+ EXPECT_TRUE(y[3].IsInt());
+ EXPECT_EQ(123, y[3].GetInt());
+ EXPECT_TRUE(y[4].IsString());
+ EXPECT_STREQ("foo", y[4].GetString());
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ // PushBack(GenericValue&&, Allocator&);
+ {
+ Value y2(kArrayType);
+ y2.PushBack(Value(true), allocator);
+ y2.PushBack(std::move(Value(kArrayType).PushBack(Value(1), allocator).PushBack("foo", allocator)), allocator);
+ EXPECT_EQ(2u, y2.Size());
+ EXPECT_TRUE(y2[0].IsTrue());
+ EXPECT_TRUE(y2[1].IsArray());
+ EXPECT_EQ(2u, y2[1].Size());
+ EXPECT_TRUE(y2[1][0].IsInt());
+ EXPECT_TRUE(y2[1][1].IsString());
+ }
+#endif
+
+ // iterator
+ typename T::ValueIterator itr = x.Begin();
+ EXPECT_TRUE(itr != x.End());
+ EXPECT_TRUE(itr->IsNull());
+ ++itr;
+ EXPECT_TRUE(itr != x.End());
+ EXPECT_TRUE(itr->IsTrue());
+ ++itr;
+ EXPECT_TRUE(itr != x.End());
+ EXPECT_TRUE(itr->IsFalse());
+ ++itr;
+ EXPECT_TRUE(itr != x.End());
+ EXPECT_TRUE(itr->IsInt());
+ EXPECT_EQ(123, itr->GetInt());
+ ++itr;
+ EXPECT_TRUE(itr != x.End());
+ EXPECT_TRUE(itr->IsString());
+ EXPECT_STREQ("foo", itr->GetString());
+
+ // const iterator
+ typename T::ConstValueIterator citr = y.Begin();
+ EXPECT_TRUE(citr != y.End());
+ EXPECT_TRUE(citr->IsNull());
+ ++citr;
+ EXPECT_TRUE(citr != y.End());
+ EXPECT_TRUE(citr->IsTrue());
+ ++citr;
+ EXPECT_TRUE(citr != y.End());
+ EXPECT_TRUE(citr->IsFalse());
+ ++citr;
+ EXPECT_TRUE(citr != y.End());
+ EXPECT_TRUE(citr->IsInt());
+ EXPECT_EQ(123, citr->GetInt());
+ ++citr;
+ EXPECT_TRUE(citr != y.End());
+ EXPECT_TRUE(citr->IsString());
+ EXPECT_STREQ("foo", citr->GetString());
+
+ // PopBack()
+ x.PopBack();
+ EXPECT_EQ(4u, x.Size());
+ EXPECT_TRUE(y[SizeType(0)].IsNull());
+ EXPECT_TRUE(y[1].IsTrue());
+ EXPECT_TRUE(y[2].IsFalse());
+ EXPECT_TRUE(y[3].IsInt());
+
+ // Clear()
+ x.Clear();
+ EXPECT_TRUE(x.Empty());
+ EXPECT_EQ(0u, x.Size());
+ EXPECT_TRUE(y.Empty());
+ EXPECT_EQ(0u, y.Size());
+
+ // Erase(ValueIterator)
+
+ // Use array of array to ensure removed elements' destructor is called.
+ // [[0],[1],[2],...]
+ for (int i = 0; i < 10; i++)
+ x.PushBack(Value(kArrayType).PushBack(i, allocator).Move(), allocator);
+
+ // Erase the first
+ itr = x.Erase(x.Begin());
+ EXPECT_EQ(x.Begin(), itr);
+ EXPECT_EQ(9u, x.Size());
+ for (int i = 0; i < 9; i++)
+ EXPECT_EQ(i + 1, x[static_cast<SizeType>(i)][0].GetInt());
+
+ // Ease the last
+ itr = x.Erase(x.End() - 1);
+ EXPECT_EQ(x.End(), itr);
+ EXPECT_EQ(8u, x.Size());
+ for (int i = 0; i < 8; i++)
+ EXPECT_EQ(i + 1, x[static_cast<SizeType>(i)][0].GetInt());
+
+ // Erase the middle
+ itr = x.Erase(x.Begin() + 4);
+ EXPECT_EQ(x.Begin() + 4, itr);
+ EXPECT_EQ(7u, x.Size());
+ for (int i = 0; i < 4; i++)
+ EXPECT_EQ(i + 1, x[static_cast<SizeType>(i)][0].GetInt());
+ for (int i = 4; i < 7; i++)
+ EXPECT_EQ(i + 2, x[static_cast<SizeType>(i)][0].GetInt());
+
+ // Erase(ValueIterator, ValueIterator)
+ // Exhaustive test with all 0 <= first < n, first <= last <= n cases
+ const unsigned n = 10;
+ for (unsigned first = 0; first < n; first++) {
+ for (unsigned last = first; last <= n; last++) {
+ x.Clear();
+ for (unsigned i = 0; i < n; i++)
+ x.PushBack(Value(kArrayType).PushBack(i, allocator).Move(), allocator);
+
+ itr = x.Erase(x.Begin() + first, x.Begin() + last);
+ if (last == n)
+ EXPECT_EQ(x.End(), itr);
+ else
+ EXPECT_EQ(x.Begin() + first, itr);
+
+ size_t removeCount = last - first;
+ EXPECT_EQ(n - removeCount, x.Size());
+ for (unsigned i = 0; i < first; i++)
+ EXPECT_EQ(i, x[i][0].GetUint());
+ for (unsigned i = first; i < n - removeCount; i++)
+ EXPECT_EQ(i + removeCount, x[static_cast<SizeType>(i)][0].GetUint());
+ }
+ }
+}
+
+TEST(Value, Array) {
+ Value::AllocatorType allocator;
+ Value x(kArrayType);
+ const Value& y = x;
+
+ EXPECT_EQ(kArrayType, x.GetType());
+ EXPECT_TRUE(x.IsArray());
+ EXPECT_TRUE(x.Empty());
+ EXPECT_EQ(0u, x.Size());
+ EXPECT_TRUE(y.IsArray());
+ EXPECT_TRUE(y.Empty());
+ EXPECT_EQ(0u, y.Size());
+
+ EXPECT_FALSE(x.IsNull());
+ EXPECT_FALSE(x.IsBool());
+ EXPECT_FALSE(x.IsFalse());
+ EXPECT_FALSE(x.IsTrue());
+ EXPECT_FALSE(x.IsString());
+ EXPECT_FALSE(x.IsObject());
+
+ TestArray(x, allocator);
+
+ // Working in gcc without C++11, but VS2013 cannot compile. To be diagnosed.
+ // http://en.wikipedia.org/wiki/Erase-remove_idiom
+ x.Clear();
+ for (int i = 0; i < 10; i++)
+ if (i % 2 == 0)
+ x.PushBack(i, allocator);
+ else
+ x.PushBack(Value(kNullType).Move(), allocator);
+
+ const Value null(kNullType);
+ x.Erase(std::remove(x.Begin(), x.End(), null), x.End());
+ EXPECT_EQ(5u, x.Size());
+ for (int i = 0; i < 5; i++)
+ EXPECT_EQ(i * 2, x[static_cast<SizeType>(i)]);
+
+ // SetArray()
+ Value z;
+ z.SetArray();
+ EXPECT_TRUE(z.IsArray());
+ EXPECT_TRUE(z.Empty());
+
+ // PR #1503: assign from inner Value
+ {
+ CrtAllocator a; // Free() is not a noop
+ GenericValue<UTF8<>, CrtAllocator> nullValue;
+ GenericValue<UTF8<>, CrtAllocator> arrayValue(kArrayType);
+ arrayValue.PushBack(nullValue, a);
+ arrayValue = arrayValue[0]; // shouldn't crash (use after free)
+ EXPECT_TRUE(arrayValue.IsNull());
+ }
+}
+
+TEST(Value, ArrayHelper) {
+ Value::AllocatorType allocator;
+ {
+ Value x(kArrayType);
+ Value::Array a = x.GetArray();
+ TestArray(a, allocator);
+ }
+
+ {
+ Value x(kArrayType);
+ Value::Array a = x.GetArray();
+ a.PushBack(1, allocator);
+
+ Value::Array a2(a); // copy constructor
+ EXPECT_EQ(1u, a2.Size());
+
+ Value::Array a3 = a;
+ EXPECT_EQ(1u, a3.Size());
+
+ Value::ConstArray y = static_cast<const Value&>(x).GetArray();
+ (void)y;
+ // y.PushBack(1, allocator); // should not compile
+
+ // Templated functions
+ x.Clear();
+ EXPECT_TRUE(x.Is<Value::Array>());
+ EXPECT_TRUE(x.Is<Value::ConstArray>());
+ a.PushBack(1, allocator);
+ EXPECT_EQ(1, x.Get<Value::Array>()[0].GetInt());
+ EXPECT_EQ(1, x.Get<Value::ConstArray>()[0].GetInt());
+
+ Value x2;
+ x2.Set<Value::Array>(a);
+ EXPECT_TRUE(x.IsArray()); // IsArray() is invariant after moving.
+ EXPECT_EQ(1, x2.Get<Value::Array>()[0].GetInt());
+ }
+
+ {
+ Value y(kArrayType);
+ y.PushBack(123, allocator);
+
+ Value x(y.GetArray()); // Construct value form array.
+ EXPECT_TRUE(x.IsArray());
+ EXPECT_EQ(123, x[0].GetInt());
+ EXPECT_TRUE(y.IsArray()); // Invariant
+ EXPECT_TRUE(y.Empty());
+ }
+
+ {
+ Value x(kArrayType);
+ Value y(kArrayType);
+ y.PushBack(123, allocator);
+ x.PushBack(y.GetArray(), allocator); // Implicit constructor to convert Array to GenericValue
+
+ EXPECT_EQ(1u, x.Size());
+ EXPECT_EQ(123, x[0][0].GetInt());
+ EXPECT_TRUE(y.IsArray());
+ EXPECT_TRUE(y.Empty());
+ }
+}
+
+#if RAPIDJSON_HAS_CXX11_RANGE_FOR
+TEST(Value, ArrayHelperRangeFor) {
+ Value::AllocatorType allocator;
+ Value x(kArrayType);
+
+ for (int i = 0; i < 10; i++)
+ x.PushBack(i, allocator);
+
+ {
+ int i = 0;
+ for (auto& v : x.GetArray()) {
+ EXPECT_EQ(i, v.GetInt());
+ i++;
+ }
+ EXPECT_EQ(i, 10);
+ }
+ {
+ int i = 0;
+ for (const auto& v : const_cast<const Value&>(x).GetArray()) {
+ EXPECT_EQ(i, v.GetInt());
+ i++;
+ }
+ EXPECT_EQ(i, 10);
+ }
+
+ // Array a = x.GetArray();
+ // Array ca = const_cast<const Value&>(x).GetArray();
+}
+#endif
+
+template <typename T, typename Allocator>
+static void TestObject(T& x, Allocator& allocator) {
+ const T& y = x; // const version
+
+ // AddMember()
+ x.AddMember("A", "Apple", allocator);
+ EXPECT_FALSE(x.ObjectEmpty());
+ EXPECT_EQ(1u, x.MemberCount());
+
+ Value value("Banana", 6);
+ x.AddMember("B", "Banana", allocator);
+ EXPECT_EQ(2u, x.MemberCount());
+
+ // AddMember<T>(StringRefType, T, Allocator)
+ {
+ Value o(kObjectType);
+ o.AddMember("true", true, allocator);
+ o.AddMember("false", false, allocator);
+ o.AddMember("int", -1, allocator);
+ o.AddMember("uint", 1u, allocator);
+ o.AddMember("int64", int64_t(-4294967296), allocator);
+ o.AddMember("uint64", uint64_t(4294967296), allocator);
+ o.AddMember("double", 3.14, allocator);
+ o.AddMember("string", "Jelly", allocator);
+
+ EXPECT_TRUE(o["true"].GetBool());
+ EXPECT_FALSE(o["false"].GetBool());
+ EXPECT_EQ(-1, o["int"].GetInt());
+ EXPECT_EQ(1u, o["uint"].GetUint());
+ EXPECT_EQ(int64_t(-4294967296), o["int64"].GetInt64());
+ EXPECT_EQ(uint64_t(4294967296), o["uint64"].GetUint64());
+ EXPECT_STREQ("Jelly",o["string"].GetString());
+ EXPECT_EQ(8u, o.MemberCount());
+ }
+
+ // AddMember<T>(Value&, T, Allocator)
+ {
+ Value o(kObjectType);
+
+ Value n("s");
+ o.AddMember(n, "string", allocator);
+ EXPECT_EQ(1u, o.MemberCount());
+
+ Value count("#");
+ o.AddMember(count, o.MemberCount(), allocator);
+ EXPECT_EQ(2u, o.MemberCount());
+ }
+
+#if RAPIDJSON_HAS_STDSTRING
+ {
+ // AddMember(StringRefType, const std::string&, Allocator)
+ Value o(kObjectType);
+ o.AddMember("b", std::string("Banana"), allocator);
+ EXPECT_STREQ("Banana", o["b"].GetString());
+
+ // RemoveMember(const std::string&)
+ o.RemoveMember(std::string("b"));
+ EXPECT_TRUE(o.ObjectEmpty());
+ }
+#endif
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ // AddMember(GenericValue&&, ...) variants
+ {
+ Value o(kObjectType);
+ o.AddMember(Value("true"), Value(true), allocator);
+ o.AddMember(Value("false"), Value(false).Move(), allocator); // value is lvalue ref
+ o.AddMember(Value("int").Move(), Value(-1), allocator); // name is lvalue ref
+ o.AddMember("uint", std::move(Value().SetUint(1u)), allocator); // name is literal, value is rvalue
+ EXPECT_TRUE(o["true"].GetBool());
+ EXPECT_FALSE(o["false"].GetBool());
+ EXPECT_EQ(-1, o["int"].GetInt());
+ EXPECT_EQ(1u, o["uint"].GetUint());
+ EXPECT_EQ(4u, o.MemberCount());
+ }
+#endif
+
+ // Tests a member with null character
+ Value name;
+ const Value C0D("C\0D", 3);
+ name.SetString(C0D.GetString(), 3);
+ value.SetString("CherryD", 7);
+ x.AddMember(name, value, allocator);
+
+ // HasMember()
+ EXPECT_TRUE(x.HasMember("A"));
+ EXPECT_TRUE(x.HasMember("B"));
+ EXPECT_TRUE(y.HasMember("A"));
+ EXPECT_TRUE(y.HasMember("B"));
+
+#if RAPIDJSON_HAS_STDSTRING
+ EXPECT_TRUE(x.HasMember(std::string("A")));
+#endif
+
+ name.SetString("C\0D");
+ EXPECT_TRUE(x.HasMember(name));
+ EXPECT_TRUE(y.HasMember(name));
+
+ GenericValue<UTF8<>, CrtAllocator> othername("A");
+ EXPECT_TRUE(x.HasMember(othername));
+ EXPECT_TRUE(y.HasMember(othername));
+ othername.SetString("C\0D");
+ EXPECT_TRUE(x.HasMember(othername));
+ EXPECT_TRUE(y.HasMember(othername));
+
+ // operator[]
+ EXPECT_STREQ("Apple", x["A"].GetString());
+ 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());
+ EXPECT_STREQ("Banana", y["B"].GetString());
+ EXPECT_STREQ("CherryD", y[C0D].GetString());
+
+#if RAPIDJSON_HAS_STDSTRING
+ EXPECT_STREQ("Apple", x["A"].GetString());
+ EXPECT_STREQ("Apple", y[std::string("A")].GetString());
+#endif
+
+ // member iterator
+ Value::MemberIterator itr = x.MemberBegin();
+ EXPECT_TRUE(itr != x.MemberEnd());
+ EXPECT_STREQ("A", itr->name.GetString());
+ EXPECT_STREQ("Apple", itr->value.GetString());
+ ++itr;
+ EXPECT_TRUE(itr != x.MemberEnd());
+ EXPECT_STREQ("B", itr->name.GetString());
+ EXPECT_STREQ("Banana", itr->value.GetString());
+ ++itr;
+ EXPECT_TRUE(itr != x.MemberEnd());
+ EXPECT_TRUE(memcmp(itr->name.GetString(), "C\0D", 4) == 0);
+ EXPECT_STREQ("CherryD", itr->value.GetString());
+ ++itr;
+ EXPECT_FALSE(itr != x.MemberEnd());
+
+ // const member iterator
+ Value::ConstMemberIterator citr = y.MemberBegin();
+ EXPECT_TRUE(citr != y.MemberEnd());
+ EXPECT_STREQ("A", citr->name.GetString());
+ EXPECT_STREQ("Apple", citr->value.GetString());
+ ++citr;
+ EXPECT_TRUE(citr != y.MemberEnd());
+ EXPECT_STREQ("B", citr->name.GetString());
+ EXPECT_STREQ("Banana", citr->value.GetString());
+ ++citr;
+ EXPECT_TRUE(citr != y.MemberEnd());
+ EXPECT_TRUE(memcmp(citr->name.GetString(), "C\0D", 4) == 0);
+ EXPECT_STREQ("CherryD", citr->value.GetString());
+ ++citr;
+ EXPECT_FALSE(citr != y.MemberEnd());
+
+ // member iterator conversions/relations
+ itr = x.MemberBegin();
+ citr = x.MemberBegin(); // const conversion
+ TestEqual(itr, citr);
+ EXPECT_TRUE(itr < x.MemberEnd());
+ EXPECT_FALSE(itr > y.MemberEnd());
+ EXPECT_TRUE(citr < x.MemberEnd());
+ EXPECT_FALSE(citr > y.MemberEnd());
+ ++citr;
+ TestUnequal(itr, citr);
+ EXPECT_FALSE(itr < itr);
+ EXPECT_TRUE(itr < citr);
+ EXPECT_FALSE(itr > itr);
+ EXPECT_TRUE(citr > itr);
+ EXPECT_EQ(1, citr - x.MemberBegin());
+ EXPECT_EQ(0, itr - y.MemberBegin());
+ itr += citr - x.MemberBegin();
+ EXPECT_EQ(1, itr - y.MemberBegin());
+ TestEqual(citr, itr);
+ EXPECT_TRUE(itr <= citr);
+ EXPECT_TRUE(citr <= itr);
+ itr++;
+ EXPECT_TRUE(itr >= citr);
+ EXPECT_FALSE(citr >= itr);
+
+ // RemoveMember()
+ EXPECT_TRUE(x.RemoveMember("A"));
+ EXPECT_FALSE(x.HasMember("A"));
+
+ EXPECT_TRUE(x.RemoveMember("B"));
+ EXPECT_FALSE(x.HasMember("B"));
+
+ EXPECT_FALSE(x.RemoveMember("nonexist"));
+
+ EXPECT_TRUE(x.RemoveMember(othername));
+ EXPECT_FALSE(x.HasMember(name));
+
+ EXPECT_TRUE(x.MemberBegin() == x.MemberEnd());
+
+ // EraseMember(ConstMemberIterator)
+
+ // Use array members to ensure removed elements' destructor is called.
+ // { "a": [0], "b": [1],[2],...]
+ const char keys[][2] = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j" };
+ for (int i = 0; i < 10; i++)
+ x.AddMember(keys[i], Value(kArrayType).PushBack(i, allocator), allocator);
+
+ // MemberCount, iterator difference
+ EXPECT_EQ(x.MemberCount(), SizeType(x.MemberEnd() - x.MemberBegin()));
+
+ // Erase the first
+ itr = x.EraseMember(x.MemberBegin());
+ EXPECT_FALSE(x.HasMember(keys[0]));
+ EXPECT_EQ(x.MemberBegin(), itr);
+ EXPECT_EQ(9u, x.MemberCount());
+ for (; itr != x.MemberEnd(); ++itr) {
+ size_t i = static_cast<size_t>((itr - x.MemberBegin())) + 1;
+ EXPECT_STREQ(itr->name.GetString(), keys[i]);
+ EXPECT_EQ(static_cast<int>(i), itr->value[0].GetInt());
+ }
+
+ // Erase the last
+ itr = x.EraseMember(x.MemberEnd() - 1);
+ EXPECT_FALSE(x.HasMember(keys[9]));
+ EXPECT_EQ(x.MemberEnd(), itr);
+ EXPECT_EQ(8u, x.MemberCount());
+ for (; itr != x.MemberEnd(); ++itr) {
+ size_t i = static_cast<size_t>(itr - x.MemberBegin()) + 1;
+ EXPECT_STREQ(itr->name.GetString(), keys[i]);
+ EXPECT_EQ(static_cast<int>(i), itr->value[0].GetInt());
+ }
+
+ // Erase the middle
+ itr = x.EraseMember(x.MemberBegin() + 4);
+ EXPECT_FALSE(x.HasMember(keys[5]));
+ EXPECT_EQ(x.MemberBegin() + 4, itr);
+ EXPECT_EQ(7u, x.MemberCount());
+ for (; itr != x.MemberEnd(); ++itr) {
+ size_t i = static_cast<size_t>(itr - x.MemberBegin());
+ i += (i < 4) ? 1 : 2;
+ EXPECT_STREQ(itr->name.GetString(), keys[i]);
+ EXPECT_EQ(static_cast<int>(i), itr->value[0].GetInt());
+ }
+
+ // EraseMember(ConstMemberIterator, ConstMemberIterator)
+ // Exhaustive test with all 0 <= first < n, first <= last <= n cases
+ const unsigned n = 10;
+ for (unsigned first = 0; first < n; first++) {
+ for (unsigned last = first; last <= n; last++) {
+ x.RemoveAllMembers();
+ for (unsigned i = 0; i < n; i++)
+ x.AddMember(keys[i], Value(kArrayType).PushBack(i, allocator), allocator);
+
+ itr = x.EraseMember(x.MemberBegin() + static_cast<int>(first), x.MemberBegin() + static_cast<int>(last));
+ if (last == n)
+ EXPECT_EQ(x.MemberEnd(), itr);
+ else
+ EXPECT_EQ(x.MemberBegin() + static_cast<int>(first), itr);
+
+ size_t removeCount = last - first;
+ EXPECT_EQ(n - removeCount, x.MemberCount());
+ for (unsigned i = 0; i < first; i++)
+ EXPECT_EQ(i, x[keys[i]][0].GetUint());
+ for (unsigned i = first; i < n - removeCount; i++)
+ EXPECT_EQ(i + removeCount, x[keys[i+removeCount]][0].GetUint());
+ }
+ }
+
+ // RemoveAllMembers()
+ x.RemoveAllMembers();
+ EXPECT_TRUE(x.ObjectEmpty());
+ EXPECT_EQ(0u, x.MemberCount());
+}
+
+TEST(Value, Object) {
+ Value::AllocatorType allocator;
+ Value x(kObjectType);
+ const Value& y = x; // const version
+
+ EXPECT_EQ(kObjectType, x.GetType());
+ EXPECT_TRUE(x.IsObject());
+ EXPECT_TRUE(x.ObjectEmpty());
+ EXPECT_EQ(0u, x.MemberCount());
+ EXPECT_EQ(kObjectType, y.GetType());
+ EXPECT_TRUE(y.IsObject());
+ EXPECT_TRUE(y.ObjectEmpty());
+ EXPECT_EQ(0u, y.MemberCount());
+
+ TestObject(x, allocator);
+
+ // SetObject()
+ Value z;
+ z.SetObject();
+ EXPECT_TRUE(z.IsObject());
+}
+
+TEST(Value, ObjectHelper) {
+ Value::AllocatorType allocator;
+ {
+ Value x(kObjectType);
+ Value::Object o = x.GetObject();
+ TestObject(o, allocator);
+ }
+
+ {
+ Value x(kObjectType);
+ Value::Object o = x.GetObject();
+ o.AddMember("1", 1, allocator);
+
+ Value::Object o2(o); // copy constructor
+ EXPECT_EQ(1u, o2.MemberCount());
+
+ Value::Object o3 = o;
+ EXPECT_EQ(1u, o3.MemberCount());
+
+ Value::ConstObject y = static_cast<const Value&>(x).GetObject();
+ (void)y;
+ // y.AddMember("1", 1, allocator); // should not compile
+
+ // Templated functions
+ x.RemoveAllMembers();
+ EXPECT_TRUE(x.Is<Value::Object>());
+ EXPECT_TRUE(x.Is<Value::ConstObject>());
+ o.AddMember("1", 1, allocator);
+ EXPECT_EQ(1, x.Get<Value::Object>()["1"].GetInt());
+ EXPECT_EQ(1, x.Get<Value::ConstObject>()["1"].GetInt());
+
+ Value x2;
+ x2.Set<Value::Object>(o);
+ EXPECT_TRUE(x.IsObject()); // IsObject() is invariant after moving
+ EXPECT_EQ(1, x2.Get<Value::Object>()["1"].GetInt());
+ }
+
+ {
+ Value x(kObjectType);
+ x.AddMember("a", "apple", allocator);
+ Value y(x.GetObject());
+ EXPECT_STREQ("apple", y["a"].GetString());
+ EXPECT_TRUE(x.IsObject()); // Invariant
+ }
+
+ {
+ Value x(kObjectType);
+ x.AddMember("a", "apple", allocator);
+ Value y(kObjectType);
+ y.AddMember("fruits", x.GetObject(), allocator);
+ EXPECT_STREQ("apple", y["fruits"]["a"].GetString());
+ EXPECT_TRUE(x.IsObject()); // Invariant
+ }
+}
+
+#if RAPIDJSON_HAS_CXX11_RANGE_FOR
+TEST(Value, ObjectHelperRangeFor) {
+ Value::AllocatorType allocator;
+ Value x(kObjectType);
+
+ for (int i = 0; i < 10; i++) {
+ char name[10];
+ Value n(name, static_cast<SizeType>(sprintf(name, "%d", i)), allocator);
+ x.AddMember(n, i, allocator);
+ }
+
+ {
+ int i = 0;
+ for (auto& m : x.GetObject()) {
+ char name[10];
+ sprintf(name, "%d", i);
+ EXPECT_STREQ(name, m.name.GetString());
+ EXPECT_EQ(i, m.value.GetInt());
+ i++;
+ }
+ EXPECT_EQ(i, 10);
+ }
+ {
+ int i = 0;
+ for (const auto& m : const_cast<const Value&>(x).GetObject()) {
+ char name[10];
+ sprintf(name, "%d", i);
+ EXPECT_STREQ(name, m.name.GetString());
+ EXPECT_EQ(i, m.value.GetInt());
+ i++;
+ }
+ EXPECT_EQ(i, 10);
+ }
+
+ // Object a = x.GetObject();
+ // Object ca = const_cast<const Value&>(x).GetObject();
+}
+#endif
+
+TEST(Value, EraseMember_String) {
+ Value::AllocatorType allocator;
+ Value x(kObjectType);
+ x.AddMember("A", "Apple", allocator);
+ x.AddMember("B", "Banana", allocator);
+
+ EXPECT_TRUE(x.EraseMember("B"));
+ EXPECT_FALSE(x.HasMember("B"));
+
+ EXPECT_FALSE(x.EraseMember("nonexist"));
+
+ GenericValue<UTF8<>, CrtAllocator> othername("A");
+ EXPECT_TRUE(x.EraseMember(othername));
+ EXPECT_FALSE(x.HasMember("A"));
+
+ EXPECT_TRUE(x.MemberBegin() == x.MemberEnd());
+}
+
+TEST(Value, BigNestedArray) {
+ MemoryPoolAllocator<> allocator;
+ Value x(kArrayType);
+ static const SizeType n = 200;
+
+ for (SizeType i = 0; i < n; i++) {
+ Value y(kArrayType);
+ for (SizeType j = 0; j < n; j++) {
+ Value number(static_cast<int>(i * n + j));
+ y.PushBack(number, allocator);
+ }
+ x.PushBack(y, allocator);
+ }
+
+ for (SizeType i = 0; i < n; i++)
+ for (SizeType j = 0; j < n; j++) {
+ EXPECT_TRUE(x[i][j].IsInt());
+ EXPECT_EQ(static_cast<int>(i * n + j), x[i][j].GetInt());
+ }
+}
+
+TEST(Value, BigNestedObject) {
+ MemoryPoolAllocator<> allocator;
+ Value x(kObjectType);
+ static const SizeType n = 200;
+
+ for (SizeType i = 0; i < n; i++) {
+ char name1[10];
+ sprintf(name1, "%d", i);
+
+ // Value name(name1); // should not compile
+ Value name(name1, static_cast<SizeType>(strlen(name1)), allocator);
+ Value object(kObjectType);
+
+ for (SizeType j = 0; j < n; j++) {
+ char name2[10];
+ sprintf(name2, "%d", j);
+
+ Value name3(name2, static_cast<SizeType>(strlen(name2)), allocator);
+ Value number(static_cast<int>(i * n + j));
+ object.AddMember(name3, number, allocator);
+ }
+
+ // x.AddMember(name1, object, allocator); // should not compile
+ x.AddMember(name, object, allocator);
+ }
+
+ for (SizeType i = 0; i < n; i++) {
+ char name1[10];
+ sprintf(name1, "%d", i);
+
+ for (SizeType j = 0; j < n; j++) {
+ char name2[10];
+ sprintf(name2, "%d", j);
+ x[name1];
+ EXPECT_EQ(static_cast<int>(i * n + j), x[name1][name2].GetInt());
+ }
+ }
+}
+
+// Issue 18: Error removing last element of object
+// http://code.google.com/p/rapidjson/issues/detail?id=18
+TEST(Value, RemoveLastElement) {
+ rapidjson::Document doc;
+ rapidjson::Document::AllocatorType& allocator = doc.GetAllocator();
+ rapidjson::Value objVal(rapidjson::kObjectType);
+ objVal.AddMember("var1", 123, allocator);
+ objVal.AddMember("var2", "444", allocator);
+ objVal.AddMember("var3", 555, allocator);
+ EXPECT_TRUE(objVal.HasMember("var3"));
+ objVal.RemoveMember("var3"); // Assertion here in r61
+ EXPECT_FALSE(objVal.HasMember("var3"));
+}
+
+// Issue 38: Segmentation fault with CrtAllocator
+TEST(Document, CrtAllocator) {
+ typedef GenericValue<UTF8<>, CrtAllocator> V;
+
+ V::AllocatorType allocator;
+ V o(kObjectType);
+ o.AddMember("x", 1, allocator); // Should not call destructor on uninitialized name/value of newly allocated members.
+
+ V a(kArrayType);
+ a.PushBack(1, allocator); // Should not call destructor on uninitialized Value of newly allocated elements.
+}
+
+static void TestShortStringOptimization(const char* str) {
+ const rapidjson::SizeType len = static_cast<rapidjson::SizeType>(strlen(str));
+
+ rapidjson::Document doc;
+ rapidjson::Value val;
+ val.SetString(str, len, doc.GetAllocator());
+
+ EXPECT_EQ(val.GetStringLength(), len);
+ EXPECT_STREQ(val.GetString(), str);
+}
+
+TEST(Value, AllocateShortString) {
+ TestShortStringOptimization(""); // edge case: empty string
+ TestShortStringOptimization("12345678"); // regular case for short strings: 8 chars
+ TestShortStringOptimization("12345678901"); // edge case: 11 chars in 32-bit mode (=> short string)
+ TestShortStringOptimization("123456789012"); // edge case: 12 chars in 32-bit mode (=> regular string)
+ 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 RawNumber(const char*, SizeType, bool) { return e != 7; }
+ bool String(const char*, SizeType, bool) { return e != 8; }
+ bool StartObject() { return e != 9; }
+ bool Key(const char*, SizeType, bool) { return e != 10; }
+ bool EndObject(SizeType) { return e != 11; }
+ bool StartArray() { return e != 12; }
+ bool EndArray(SizeType) { return e != 13; }
+};
+
+#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]");
+ // RawNumber() is never called
+ TEST_TERMINATION(8, "[\"a\"]");
+ TEST_TERMINATION(9, "[{}]");
+ TEST_TERMINATION(10, "[{\"a\":1}]");
+ TEST_TERMINATION(11, "[{}]");
+ TEST_TERMINATION(12, "{\"a\":[]}");
+ TEST_TERMINATION(13, "{\"a\":[]}");
+}
+
+struct ValueIntComparer {
+ bool operator()(const Value& lhs, const Value& rhs) const {
+ return lhs.GetInt() < rhs.GetInt();
+ }
+};
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+TEST(Value, Sorting) {
+ Value::AllocatorType allocator;
+ Value a(kArrayType);
+ a.PushBack(5, allocator);
+ a.PushBack(1, allocator);
+ a.PushBack(3, allocator);
+ std::sort(a.Begin(), a.End(), ValueIntComparer());
+ EXPECT_EQ(1, a[0].GetInt());
+ EXPECT_EQ(3, a[1].GetInt());
+ EXPECT_EQ(5, a[2].GetInt());
+}
+#endif
+
+// http://stackoverflow.com/questions/35222230/
+
+static void MergeDuplicateKey(Value& v, Value::AllocatorType& a) {
+ if (v.IsObject()) {
+ // Convert all key:value into key:[value]
+ for (Value::MemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr)
+ itr->value = Value(kArrayType).Move().PushBack(itr->value, a);
+
+ // Merge arrays if key is duplicated
+ for (Value::MemberIterator itr = v.MemberBegin(); itr != v.MemberEnd();) {
+ Value::MemberIterator itr2 = v.FindMember(itr->name);
+ if (itr != itr2) {
+ itr2->value.PushBack(itr->value[0], a);
+ itr = v.EraseMember(itr);
+ }
+ else
+ ++itr;
+ }
+
+ // Convert key:[values] back to key:value if there is only one value
+ for (Value::MemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr) {
+ if (itr->value.Size() == 1)
+ itr->value = itr->value[0];
+ MergeDuplicateKey(itr->value, a); // Recursion on the value
+ }
+ }
+ else if (v.IsArray())
+ for (Value::ValueIterator itr = v.Begin(); itr != v.End(); ++itr)
+ MergeDuplicateKey(*itr, a);
+}
+
+TEST(Value, MergeDuplicateKey) {
+ Document d;
+ d.Parse(
+ "{"
+ " \"key1\": {"
+ " \"a\": \"asdf\","
+ " \"b\": \"foo\","
+ " \"b\": \"bar\","
+ " \"c\": \"fdas\""
+ " }"
+ "}");
+
+ Document d2;
+ d2.Parse(
+ "{"
+ " \"key1\": {"
+ " \"a\": \"asdf\","
+ " \"b\": ["
+ " \"foo\","
+ " \"bar\""
+ " ],"
+ " \"c\": \"fdas\""
+ " }"
+ "}");
+
+ EXPECT_NE(d2, d);
+ MergeDuplicateKey(d, d.GetAllocator());
+ EXPECT_EQ(d2, d);
+}
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
diff --git a/src/s3select/rapidjson/test/unittest/writertest.cpp b/src/s3select/rapidjson/test/unittest/writertest.cpp
new file mode 100644
index 000000000..ac9ad899e
--- /dev/null
+++ b/src/s3select/rapidjson/test/unittest/writertest.cpp
@@ -0,0 +1,598 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#include "unittest.h"
+
+#include "rapidjson/document.h"
+#include "rapidjson/reader.h"
+#include "rapidjson/writer.h"
+#include "rapidjson/stringbuffer.h"
+#include "rapidjson/memorybuffer.h"
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(c++98-compat)
+#endif
+
+using namespace rapidjson;
+
+TEST(Writer, Compact) {
+ StringStream s("{ \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3] } ");
+ StringBuffer buffer;
+ Writer<StringBuffer> writer(buffer);
+ buffer.ShrinkToFit();
+ Reader reader;
+ reader.Parse<0>(s, writer);
+ EXPECT_STREQ("{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3]}", buffer.GetString());
+ EXPECT_EQ(77u, buffer.GetSize());
+ EXPECT_TRUE(writer.IsComplete());
+}
+
+// json -> parse -> writer -> json
+#define TEST_ROUNDTRIP(json) \
+ { \
+ StringStream s(json); \
+ StringBuffer buffer; \
+ Writer<StringBuffer> writer(buffer); \
+ Reader reader; \
+ reader.Parse<kParseFullPrecisionFlag>(s, writer); \
+ EXPECT_STREQ(json, buffer.GetString()); \
+ EXPECT_TRUE(writer.IsComplete()); \
+ }
+
+TEST(Writer, Root) {
+ TEST_ROUNDTRIP("null");
+ TEST_ROUNDTRIP("true");
+ TEST_ROUNDTRIP("false");
+ TEST_ROUNDTRIP("0");
+ TEST_ROUNDTRIP("\"foo\"");
+ TEST_ROUNDTRIP("[]");
+ TEST_ROUNDTRIP("{}");
+}
+
+TEST(Writer, Int) {
+ TEST_ROUNDTRIP("[-1]");
+ TEST_ROUNDTRIP("[-123]");
+ TEST_ROUNDTRIP("[-2147483648]");
+}
+
+TEST(Writer, UInt) {
+ TEST_ROUNDTRIP("[0]");
+ TEST_ROUNDTRIP("[1]");
+ TEST_ROUNDTRIP("[123]");
+ TEST_ROUNDTRIP("[2147483647]");
+ TEST_ROUNDTRIP("[4294967295]");
+}
+
+TEST(Writer, Int64) {
+ TEST_ROUNDTRIP("[-1234567890123456789]");
+ TEST_ROUNDTRIP("[-9223372036854775808]");
+}
+
+TEST(Writer, Uint64) {
+ TEST_ROUNDTRIP("[1234567890123456789]");
+ TEST_ROUNDTRIP("[9223372036854775807]");
+}
+
+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, Issue_889) {
+ char buf[100] = "Hello";
+
+ StringBuffer buffer;
+ Writer<StringBuffer> writer(buffer);
+ writer.StartArray();
+ writer.String(buf);
+ writer.EndArray();
+
+ EXPECT_STREQ("[\"Hello\"]", buffer.GetString());
+ EXPECT_TRUE(writer.IsComplete()); \
+}
+
+TEST(Writer, ScanWriteUnescapedString) {
+ const char json[] = "[\" \\\"0123456789ABCDEF\"]";
+ // ^ scanning stops here.
+ char buffer2[sizeof(json) + 32];
+
+ // Use different offset to test different alignments
+ for (int i = 0; i < 32; i++) {
+ char* p = buffer2 + i;
+ memcpy(p, json, sizeof(json));
+ TEST_ROUNDTRIP(p);
+ }
+}
+
+TEST(Writer, Double) {
+ TEST_ROUNDTRIP("[1.2345,1.2345678,0.123456789012,1234567.8]");
+ 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
+
+}
+
+// UTF8 -> TargetEncoding -> UTF8
+template <typename TargetEncoding>
+void TestTranscode(const char* json) {
+ StringStream s(json);
+ GenericStringBuffer<TargetEncoding> buffer;
+ Writer<GenericStringBuffer<TargetEncoding>, UTF8<>, TargetEncoding> writer(buffer);
+ Reader reader;
+ reader.Parse(s, writer);
+
+ StringBuffer buffer2;
+ Writer<StringBuffer> writer2(buffer2);
+ GenericReader<TargetEncoding, UTF8<> > reader2;
+ GenericStringStream<TargetEncoding> s2(buffer.GetString());
+ reader2.Parse(s2, writer2);
+
+ EXPECT_STREQ(json, buffer2.GetString());
+}
+
+TEST(Writer, Transcode) {
+ const char json[] = "{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3],\"dollar\":\"\x24\",\"cents\":\"\xC2\xA2\",\"euro\":\"\xE2\x82\xAC\",\"gclef\":\"\xF0\x9D\x84\x9E\"}";
+
+ // UTF8 -> UTF16 -> UTF8
+ TestTranscode<UTF8<> >(json);
+
+ // UTF8 -> ASCII -> UTF8
+ TestTranscode<ASCII<> >(json);
+
+ // UTF8 -> UTF16 -> UTF8
+ TestTranscode<UTF16<> >(json);
+
+ // UTF8 -> UTF32 -> UTF8
+ TestTranscode<UTF32<> >(json);
+
+ // UTF8 -> AutoUTF -> UTF8
+ UTFType types[] = { kUTF8, kUTF16LE , kUTF16BE, kUTF32LE , kUTF32BE };
+ for (size_t i = 0; i < 5; i++) {
+ StringStream s(json);
+ MemoryBuffer buffer;
+ AutoUTFOutputStream<unsigned, MemoryBuffer> os(buffer, types[i], true);
+ Writer<AutoUTFOutputStream<unsigned, MemoryBuffer>, UTF8<>, AutoUTF<unsigned> > writer(os);
+ Reader reader;
+ reader.Parse(s, writer);
+
+ StringBuffer buffer2;
+ Writer<StringBuffer> writer2(buffer2);
+ GenericReader<AutoUTF<unsigned>, UTF8<> > reader2;
+ MemoryStream s2(buffer.GetBuffer(), buffer.GetSize());
+ AutoUTFInputStream<unsigned, MemoryStream> is(s2);
+ reader2.Parse(is, writer2);
+
+ EXPECT_STREQ(json, buffer2.GetString());
+ }
+
+}
+
+#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_;
+};
+
+TEST(Writer, OStreamWrapper) {
+ 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);
+
+ Writer<OStreamWrapper> writer(os);
+
+ Reader reader;
+ 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],\"u64\":1234567890123456789,\"i64\":-1234567890123456789}", actual.c_str());
+}
+
+TEST(Writer, AssertRootMayBeAnyValue) {
+#define T(x)\
+ {\
+ StringBuffer buffer;\
+ Writer<StringBuffer> writer(buffer);\
+ EXPECT_TRUE(x);\
+ }
+ T(writer.Bool(false));
+ T(writer.Bool(true));
+ T(writer.Null());
+ T(writer.Int(0));
+ T(writer.Uint(0));
+ T(writer.Int64(0));
+ T(writer.Uint64(0));
+ T(writer.Double(0));
+ T(writer.String("foo"));
+#undef T
+}
+
+TEST(Writer, AssertIncorrectObjectLevel) {
+ StringBuffer buffer;
+ Writer<StringBuffer> writer(buffer);
+ writer.StartObject();
+ writer.EndObject();
+ ASSERT_THROW(writer.EndObject(), AssertException);
+}
+
+TEST(Writer, AssertIncorrectArrayLevel) {
+ StringBuffer buffer;
+ Writer<StringBuffer> writer(buffer);
+ writer.StartArray();
+ writer.EndArray();
+ ASSERT_THROW(writer.EndArray(), AssertException);
+}
+
+TEST(Writer, AssertIncorrectEndObject) {
+ StringBuffer buffer;
+ Writer<StringBuffer> writer(buffer);
+ writer.StartObject();
+ ASSERT_THROW(writer.EndArray(), AssertException);
+}
+
+TEST(Writer, AssertIncorrectEndArray) {
+ StringBuffer buffer;
+ Writer<StringBuffer> writer(buffer);
+ writer.StartObject();
+ ASSERT_THROW(writer.EndArray(), AssertException);
+}
+
+TEST(Writer, AssertObjectKeyNotString) {
+#define T(x)\
+ {\
+ StringBuffer buffer;\
+ Writer<StringBuffer> writer(buffer);\
+ writer.StartObject();\
+ ASSERT_THROW(x, AssertException); \
+ }
+ T(writer.Bool(false));
+ T(writer.Bool(true));
+ T(writer.Null());
+ T(writer.Int(0));
+ T(writer.Uint(0));
+ T(writer.Int64(0));
+ T(writer.Uint64(0));
+ T(writer.Double(0));
+ T(writer.StartObject());
+ T(writer.StartArray());
+#undef T
+}
+
+TEST(Writer, AssertMultipleRoot) {
+ StringBuffer buffer;
+ Writer<StringBuffer> writer(buffer);
+
+ writer.StartObject();
+ writer.EndObject();
+ ASSERT_THROW(writer.StartObject(), AssertException);
+
+ writer.Reset(buffer);
+ writer.Null();
+ ASSERT_THROW(writer.Int(0), AssertException);
+
+ writer.Reset(buffer);
+ writer.String("foo");
+ ASSERT_THROW(writer.StartArray(), AssertException);
+
+ writer.Reset(buffer);
+ writer.StartArray();
+ writer.EndArray();
+ //ASSERT_THROW(writer.Double(3.14), AssertException);
+}
+
+TEST(Writer, RootObjectIsComplete) {
+ StringBuffer buffer;
+ Writer<StringBuffer> writer(buffer);
+ EXPECT_FALSE(writer.IsComplete());
+ writer.StartObject();
+ EXPECT_FALSE(writer.IsComplete());
+ writer.String("foo");
+ EXPECT_FALSE(writer.IsComplete());
+ writer.Int(1);
+ EXPECT_FALSE(writer.IsComplete());
+ writer.EndObject();
+ EXPECT_TRUE(writer.IsComplete());
+}
+
+TEST(Writer, RootArrayIsComplete) {
+ StringBuffer buffer;
+ Writer<StringBuffer> writer(buffer);
+ EXPECT_FALSE(writer.IsComplete());
+ writer.StartArray();
+ EXPECT_FALSE(writer.IsComplete());
+ writer.String("foo");
+ EXPECT_FALSE(writer.IsComplete());
+ writer.Int(1);
+ EXPECT_FALSE(writer.IsComplete());
+ writer.EndArray();
+ EXPECT_TRUE(writer.IsComplete());
+}
+
+TEST(Writer, RootValueIsComplete) {
+#define T(x)\
+ {\
+ StringBuffer buffer;\
+ Writer<StringBuffer> writer(buffer);\
+ EXPECT_FALSE(writer.IsComplete()); \
+ x; \
+ EXPECT_TRUE(writer.IsComplete()); \
+ }
+ T(writer.Null());
+ T(writer.Bool(true));
+ T(writer.Bool(false));
+ T(writer.Int(0));
+ T(writer.Uint(0));
+ T(writer.Int64(0));
+ T(writer.Uint64(0));
+ T(writer.Double(0));
+ 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, ValidateEncoding) {
+ {
+ StringBuffer buffer;
+ Writer<StringBuffer, UTF8<>, UTF8<>, CrtAllocator, kWriteValidateEncodingFlag> writer(buffer);
+ writer.StartArray();
+ EXPECT_TRUE(writer.String("\x24")); // Dollar sign U+0024
+ EXPECT_TRUE(writer.String("\xC2\xA2")); // Cents sign U+00A2
+ EXPECT_TRUE(writer.String("\xE2\x82\xAC")); // Euro sign U+20AC
+ EXPECT_TRUE(writer.String("\xF0\x9D\x84\x9E")); // G clef sign U+1D11E
+ EXPECT_TRUE(writer.String("\x01")); // SOH control U+0001
+ EXPECT_TRUE(writer.String("\x1B")); // Escape control U+001B
+ writer.EndArray();
+ EXPECT_STREQ("[\"\x24\",\"\xC2\xA2\",\"\xE2\x82\xAC\",\"\xF0\x9D\x84\x9E\",\"\\u0001\",\"\\u001B\"]", buffer.GetString());
+ }
+
+ // Fail in decoding invalid UTF-8 sequence http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
+ {
+ StringBuffer buffer;
+ Writer<StringBuffer, UTF8<>, UTF8<>, CrtAllocator, kWriteValidateEncodingFlag> writer(buffer);
+ writer.StartArray();
+ EXPECT_FALSE(writer.String("\xfe"));
+ EXPECT_FALSE(writer.String("\xff"));
+ EXPECT_FALSE(writer.String("\xfe\xfe\xff\xff"));
+ writer.EndArray();
+ }
+}
+
+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());
+ }
+
+ // { 'a' }
+ {
+ StringBuffer buffer;
+ Writer<StringBuffer> writer(buffer);
+ writer.StartObject();
+ writer.Key("a");
+ EXPECT_THROW(writer.EndObject(), AssertException);
+ EXPECT_FALSE(writer.IsComplete());
+ }
+
+ // { 'a':'b','c' }
+ {
+ StringBuffer buffer;
+ Writer<StringBuffer> writer(buffer);
+ writer.StartObject();
+ writer.Key("a");
+ writer.String("b");
+ writer.Key("c");
+ EXPECT_THROW(writer.EndObject(), AssertException);
+ EXPECT_FALSE(writer.IsComplete());
+ }
+}
+
+TEST(Writer, NaN) {
+ double nan = std::numeric_limits<double>::quiet_NaN();
+
+ EXPECT_TRUE(internal::Double(nan).IsNan());
+ StringBuffer buffer;
+ {
+ Writer<StringBuffer> writer(buffer);
+ EXPECT_FALSE(writer.Double(nan));
+ }
+ {
+ Writer<StringBuffer, UTF8<>, UTF8<>, CrtAllocator, kWriteNanAndInfFlag> writer(buffer);
+ EXPECT_TRUE(writer.Double(nan));
+ EXPECT_STREQ("NaN", buffer.GetString());
+ }
+ GenericStringBuffer<UTF16<> > buffer2;
+ Writer<GenericStringBuffer<UTF16<> > > writer2(buffer2);
+ EXPECT_FALSE(writer2.Double(nan));
+}
+
+TEST(Writer, Inf) {
+ double inf = std::numeric_limits<double>::infinity();
+
+ EXPECT_TRUE(internal::Double(inf).IsInf());
+ StringBuffer buffer;
+ {
+ Writer<StringBuffer> writer(buffer);
+ EXPECT_FALSE(writer.Double(inf));
+ }
+ {
+ Writer<StringBuffer> writer(buffer);
+ EXPECT_FALSE(writer.Double(-inf));
+ }
+ {
+ Writer<StringBuffer, UTF8<>, UTF8<>, CrtAllocator, kWriteNanAndInfFlag> writer(buffer);
+ EXPECT_TRUE(writer.Double(inf));
+ }
+ {
+ Writer<StringBuffer, UTF8<>, UTF8<>, CrtAllocator, kWriteNanAndInfFlag> writer(buffer);
+ EXPECT_TRUE(writer.Double(-inf));
+ }
+ EXPECT_STREQ("Infinity-Infinity", buffer.GetString());
+}
+
+TEST(Writer, RawValue) {
+ StringBuffer buffer;
+ Writer<StringBuffer> writer(buffer);
+ writer.StartObject();
+ writer.Key("a");
+ writer.Int(1);
+ writer.Key("raw");
+ const char json[] = "[\"Hello\\nWorld\", 123.456]";
+ writer.RawValue(json, strlen(json), kArrayType);
+ writer.EndObject();
+ EXPECT_TRUE(writer.IsComplete());
+ EXPECT_STREQ("{\"a\":1,\"raw\":[\"Hello\\nWorld\", 123.456]}", buffer.GetString());
+}
+
+TEST(Write, RawValue_Issue1152) {
+ {
+ GenericStringBuffer<UTF32<> > sb;
+ Writer<GenericStringBuffer<UTF32<> >, UTF8<>, UTF32<> > writer(sb);
+ writer.RawValue("null", 4, kNullType);
+ EXPECT_TRUE(writer.IsComplete());
+ const unsigned *out = sb.GetString();
+ EXPECT_EQ(static_cast<unsigned>('n'), out[0]);
+ EXPECT_EQ(static_cast<unsigned>('u'), out[1]);
+ EXPECT_EQ(static_cast<unsigned>('l'), out[2]);
+ EXPECT_EQ(static_cast<unsigned>('l'), out[3]);
+ EXPECT_EQ(static_cast<unsigned>(0 ), out[4]);
+ }
+
+ {
+ GenericStringBuffer<UTF8<> > sb;
+ Writer<GenericStringBuffer<UTF8<> >, UTF16<>, UTF8<> > writer(sb);
+ writer.RawValue(L"null", 4, kNullType);
+ EXPECT_TRUE(writer.IsComplete());
+ EXPECT_STREQ("null", sb.GetString());
+ }
+
+ {
+ // Fail in transcoding
+ GenericStringBuffer<UTF16<> > buffer;
+ Writer<GenericStringBuffer<UTF16<> >, UTF8<>, UTF16<> > writer(buffer);
+ EXPECT_FALSE(writer.RawValue("\"\xfe\"", 3, kStringType));
+ }
+
+ {
+ // Fail in encoding validation
+ StringBuffer buffer;
+ Writer<StringBuffer, UTF8<>, UTF8<>, CrtAllocator, kWriteValidateEncodingFlag> writer(buffer);
+ EXPECT_FALSE(writer.RawValue("\"\xfe\"", 3, kStringType));
+ }
+}
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+static Writer<StringBuffer> WriterGen(StringBuffer &target) {
+ Writer<StringBuffer> writer(target);
+ writer.StartObject();
+ writer.Key("a");
+ writer.Int(1);
+ return writer;
+}
+
+TEST(Writer, MoveCtor) {
+ StringBuffer buffer;
+ Writer<StringBuffer> writer(WriterGen(buffer));
+ writer.EndObject();
+ EXPECT_TRUE(writer.IsComplete());
+ EXPECT_STREQ("{\"a\":1}", buffer.GetString());
+}
+#endif
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif