From 752afa7b796389590c44798d4254c03941c857f1 Mon Sep 17 00:00:00 2001 From: miloyip Date: Mon, 13 Apr 2015 10:58:05 +0800 Subject: [PATCH 01/64] Add prettywritertest --- test/unittest/CMakeLists.txt | 1 + test/unittest/prettywritertest.cpp | 73 ++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 test/unittest/prettywritertest.cpp diff --git a/test/unittest/CMakeLists.txt b/test/unittest/CMakeLists.txt index 326e6de..3c49651 100644 --- a/test/unittest/CMakeLists.txt +++ b/test/unittest/CMakeLists.txt @@ -6,6 +6,7 @@ set(UNITTEST_SOURCES filestreamtest.cpp jsoncheckertest.cpp namespacetest.cpp + prettywritertest.cpp readertest.cpp stringbuffertest.cpp strtodtest.cpp diff --git a/test/unittest/prettywritertest.cpp b/test/unittest/prettywritertest.cpp new file mode 100644 index 0000000..c37f76e --- /dev/null +++ b/test/unittest/prettywritertest.cpp @@ -0,0 +1,73 @@ +// Copyright (C) 2011 Milo Yip +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include "unittest.h" +#include "rapidjson/reader.h" +#include "rapidjson/prettywriter.h" +#include "rapidjson/stringbuffer.h" + +using namespace rapidjson; + +static const char kJson[] = "{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3]}"; + +TEST(PrettyWriter, Basic) { + StringBuffer buffer; + PrettyWriter writer(buffer); + Reader reader; + reader.Parse(StringStream(kJson), writer); + EXPECT_STREQ( + "{\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" + " ]\n" + "}", + buffer.GetString()); +} + +TEST(PrettyWriter, SetIndent) { + StringBuffer buffer; + PrettyWriter writer(buffer); + writer.SetIndent('\t', 1); + Reader reader; + reader.Parse(StringStream(kJson), 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]\n" + "}", + buffer.GetString()); +} From a0a6d737fc3241db5429467267f2b037d9b18f18 Mon Sep 17 00:00:00 2001 From: miloyip Date: Mon, 13 Apr 2015 11:13:03 +0800 Subject: [PATCH 02/64] Fix gcc compilation --- test/unittest/prettywritertest.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/unittest/prettywritertest.cpp b/test/unittest/prettywritertest.cpp index c37f76e..9c22bd3 100644 --- a/test/unittest/prettywritertest.cpp +++ b/test/unittest/prettywritertest.cpp @@ -31,7 +31,8 @@ TEST(PrettyWriter, Basic) { StringBuffer buffer; PrettyWriter writer(buffer); Reader reader; - reader.Parse(StringStream(kJson), writer); + StringStream s(kJson); + reader.Parse(s, writer); EXPECT_STREQ( "{\n" " \"hello\": \"world\",\n" @@ -54,7 +55,8 @@ TEST(PrettyWriter, SetIndent) { PrettyWriter writer(buffer); writer.SetIndent('\t', 1); Reader reader; - reader.Parse(StringStream(kJson), writer); + StringStream s(kJson); + reader.Parse(s, writer); EXPECT_STREQ( "{\n" "\t\"hello\": \"world\",\n" From 6a622aa0d095998bf6619b613643cb240c5336e3 Mon Sep 17 00:00:00 2001 From: miloyip Date: Mon, 13 Apr 2015 13:08:29 +0800 Subject: [PATCH 03/64] Try disabling inline for coverage --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c64d62b..dba5f8d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,7 +21,7 @@ before_install: - sudo apt-get install -qq cmake valgrind - sudo apt-get --no-install-recommends install doxygen # Don't install LaTeX stuffs - if [ "$ARCH" = "x86" ]; then sudo apt-get install -qq g++-multilib libc6-dbg:i386; fi - - if [ "$CC" = "gcc" ] && [ "$CONF" = "debug" ]; then sudo pip install cpp-coveralls; export GCOV_FLAGS='--coverage'; fi + - if [ "$CC" = "gcc" ] && [ "$CONF" = "debug" ]; then sudo pip install cpp-coveralls; export GCOV_FLAGS='--coverage -O0 -fno-default-inline -fno-inline'; fi install: true From fddffbe82b9b76c5919852144f84d0828c15d835 Mon Sep 17 00:00:00 2001 From: miloyip Date: Mon, 13 Apr 2015 13:21:45 +0800 Subject: [PATCH 04/64] Revert "Try disabling inline for coverage" This reverts commit 6a622aa0d095998bf6619b613643cb240c5336e3. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index dba5f8d..c64d62b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,7 +21,7 @@ before_install: - sudo apt-get install -qq cmake valgrind - sudo apt-get --no-install-recommends install doxygen # Don't install LaTeX stuffs - if [ "$ARCH" = "x86" ]; then sudo apt-get install -qq g++-multilib libc6-dbg:i386; fi - - if [ "$CC" = "gcc" ] && [ "$CONF" = "debug" ]; then sudo pip install cpp-coveralls; export GCOV_FLAGS='--coverage -O0 -fno-default-inline -fno-inline'; fi + - if [ "$CC" = "gcc" ] && [ "$CONF" = "debug" ]; then sudo pip install cpp-coveralls; export GCOV_FLAGS='--coverage'; fi install: true From 79433827e8744b857128fb3c3684e97abe7a3d11 Mon Sep 17 00:00:00 2001 From: miloyip Date: Mon, 13 Apr 2015 13:41:56 +0800 Subject: [PATCH 05/64] Add Tests for WriteUInt64(), WriteInt64() of generic stream --- test/unittest/prettywritertest.cpp | 10 +++++++--- test/unittest/writertest.cpp | 4 ++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/test/unittest/prettywritertest.cpp b/test/unittest/prettywritertest.cpp index 9c22bd3..9be4f03 100644 --- a/test/unittest/prettywritertest.cpp +++ b/test/unittest/prettywritertest.cpp @@ -25,7 +25,7 @@ using namespace rapidjson; -static const char kJson[] = "{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3]}"; +static const char kJson[] = "{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3],\"u64\":1234567890123456789,\"i64\":-1234567890123456789}"; TEST(PrettyWriter, Basic) { StringBuffer buffer; @@ -45,7 +45,9 @@ TEST(PrettyWriter, Basic) { " 1,\n" " 2,\n" " 3\n" - " ]\n" + " ],\n" + " \"u64\": 1234567890123456789,\n" + " \"i64\": -1234567890123456789\n" "}", buffer.GetString()); } @@ -69,7 +71,9 @@ TEST(PrettyWriter, SetIndent) { "\t\t1,\n" "\t\t2,\n" "\t\t3\n" - "\t]\n" + "\t],\n" + "\t\"u64\": 1234567890123456789,\n" + "\t\"i64\": -1234567890123456789\n" "}", buffer.GetString()); } diff --git a/test/unittest/writertest.cpp b/test/unittest/writertest.cpp index 642161a..54ade34 100644 --- a/test/unittest/writertest.cpp +++ b/test/unittest/writertest.cpp @@ -152,7 +152,7 @@ private: }; TEST(Writer, OStreamWrapper) { - StringStream s("{ \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3] } "); + StringStream s("{ \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3], \"u64\": 1234567890123456789, \"i64\":-1234567890123456789 } "); std::stringstream ss; OStreamWrapper os(ss); @@ -163,7 +163,7 @@ TEST(Writer, OStreamWrapper) { reader.Parse<0>(s, writer); std::string actual = ss.str(); - EXPECT_STREQ("{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3]}", actual.c_str()); + EXPECT_STREQ("{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3],\"u64\":1234567890123456789,\"i64\":-1234567890123456789}", actual.c_str()); } TEST(Writer, AssertRootMayBeAnyValue) { From 127ce7175ab43bf83c334f2e3589f8bbadeee7c5 Mon Sep 17 00:00:00 2001 From: miloyip Date: Mon, 13 Apr 2015 14:05:43 +0800 Subject: [PATCH 06/64] Add a missing error handling for Writer, and add tests for invalid encoding. --- include/rapidjson/writer.h | 3 ++- test/unittest/writertest.cpp | 29 +++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/include/rapidjson/writer.h b/include/rapidjson/writer.h index 6030fe0..5a4d156 100644 --- a/include/rapidjson/writer.h +++ b/include/rapidjson/writer.h @@ -303,7 +303,8 @@ protected: } } else - Transcoder::Transcode(is, *os_); + if (!Transcoder::Transcode(is, *os_)) + return false; } os_->Put('\"'); return true; diff --git a/test/unittest/writertest.cpp b/test/unittest/writertest.cpp index 54ade34..2a0f484 100644 --- a/test/unittest/writertest.cpp +++ b/test/unittest/writertest.cpp @@ -306,3 +306,32 @@ TEST(Writer, RootValueIsComplete) { T(writer.String("")); #undef T } + +TEST(Writer, InvalidEncoding) { + // Fail in decoding invalid UTF-8 sequence http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt + { + GenericStringBuffer > buffer; + Writer >, UTF8<>, UTF16<> > writer(buffer); + writer.StartArray(); + EXPECT_FALSE(writer.String("\xfe")); + EXPECT_FALSE(writer.String("\xff")); + EXPECT_FALSE(writer.String("\xfe\xfe\xff\xff")); + writer.EndArray(); + } + + // Fail in encoding + { + StringBuffer buffer; + Writer > writer(buffer); + static const UTF32<>::Ch s[] = { 0x110000, 0 }; // Out of U+0000 to U+10FFFF + EXPECT_FALSE(writer.String(s)); + } + + // Fail in unicode escaping in ASCII output + { + StringBuffer buffer; + Writer, ASCII<> > writer(buffer); + static const UTF32<>::Ch s[] = { 0x110000, 0 }; // Out of U+0000 to U+10FFFF + EXPECT_FALSE(writer.String(s)); + } +} \ No newline at end of file From 3d82781a7539a14fe1f04d3d94f9e37eafb1a115 Mon Sep 17 00:00:00 2001 From: miloyip Date: Mon, 13 Apr 2015 14:17:21 +0800 Subject: [PATCH 07/64] Improve PrettyWriter coverage --- test/unittest/prettywritertest.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/unittest/prettywritertest.cpp b/test/unittest/prettywritertest.cpp index 9be4f03..d0abf33 100644 --- a/test/unittest/prettywritertest.cpp +++ b/test/unittest/prettywritertest.cpp @@ -25,7 +25,7 @@ using namespace rapidjson; -static const char kJson[] = "{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3],\"u64\":1234567890123456789,\"i64\":-1234567890123456789}"; +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}"; TEST(PrettyWriter, Basic) { StringBuffer buffer; @@ -44,7 +44,8 @@ TEST(PrettyWriter, Basic) { " \"a\": [\n" " 1,\n" " 2,\n" - " 3\n" + " 3,\n" + " -1\n" " ],\n" " \"u64\": 1234567890123456789,\n" " \"i64\": -1234567890123456789\n" @@ -70,7 +71,8 @@ TEST(PrettyWriter, SetIndent) { "\t\"a\": [\n" "\t\t1,\n" "\t\t2,\n" - "\t\t3\n" + "\t\t3,\n" + "\t\t-1\n" "\t],\n" "\t\"u64\": 1234567890123456789,\n" "\t\"i64\": -1234567890123456789\n" From 3c028685dfdb23ba3a8c28defa857640b9c93ed1 Mon Sep 17 00:00:00 2001 From: miloyip Date: Mon, 13 Apr 2015 14:25:05 +0800 Subject: [PATCH 08/64] Add tests for Writer API for RAPIDJSON_HAS_STDSTRING --- include/rapidjson/prettywriter.h | 2 +- include/rapidjson/writer.h | 2 +- test/unittest/prettywritertest.cpp | 11 +++++++++++ test/unittest/writertest.cpp | 9 +++++++++ 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/include/rapidjson/prettywriter.h b/include/rapidjson/prettywriter.h index 90d1983..14b8477 100644 --- a/include/rapidjson/prettywriter.h +++ b/include/rapidjson/prettywriter.h @@ -78,7 +78,7 @@ public: #if RAPIDJSON_HAS_STDSTRING bool String(const std::basic_string& str) { - return String(str.data(), SizeType(str.size())); + return String(str.data(), SizeType(str.size())); } #endif diff --git a/include/rapidjson/writer.h b/include/rapidjson/writer.h index 5a4d156..bf17f3a 100644 --- a/include/rapidjson/writer.h +++ b/include/rapidjson/writer.h @@ -127,7 +127,7 @@ public: #if RAPIDJSON_HAS_STDSTRING bool String(const std::basic_string& str) { - return String(str.data(), SizeType(str.size())); + return String(str.data(), SizeType(str.size())); } #endif diff --git a/test/unittest/prettywritertest.cpp b/test/unittest/prettywritertest.cpp index d0abf33..fcb1121 100644 --- a/test/unittest/prettywritertest.cpp +++ b/test/unittest/prettywritertest.cpp @@ -79,3 +79,14 @@ TEST(PrettyWriter, SetIndent) { "}", buffer.GetString()); } + +#if RAPIDJSON_HAS_STDSTRING +TEST(PrettyWriter, String_STDSTRING) { + StringBuffer buffer; + PrettyWriter writer(buffer); + EXPECT_TRUE(writer.StartArray()); + EXPECT_TRUE(writer.String(std::string("Hello\n"))); + EXPECT_TRUE(writer.EndArray()); + EXPECT_STREQ("[\n \"Hello\\n\"\n]", buffer.GetString()); +} +#endif diff --git a/test/unittest/writertest.cpp b/test/unittest/writertest.cpp index 2a0f484..7b9fa9a 100644 --- a/test/unittest/writertest.cpp +++ b/test/unittest/writertest.cpp @@ -89,6 +89,15 @@ TEST(Writer, String) { TEST_ROUNDTRIP("[\"Hello\"]"); TEST_ROUNDTRIP("[\"Hello\\u0000World\"]"); TEST_ROUNDTRIP("[\"\\\"\\\\/\\b\\f\\n\\r\\t\"]"); + +#if RAPIDJSON_HAS_STDSTRING + { + StringBuffer buffer; + Writer writer(buffer); + writer.String(std::string("Hello\n")); + EXPECT_STREQ("\"Hello\\n\"", buffer.GetString()); + } +#endif } TEST(Writer, Double) { From 18a8891f0d8a233d3ae4827b238631e920594f96 Mon Sep 17 00:00:00 2001 From: miloyip Date: Mon, 13 Apr 2015 14:50:08 +0800 Subject: [PATCH 09/64] Improve coverage for Writer and PrettyWriter --- include/rapidjson/prettywriter.h | 10 ++++++---- include/rapidjson/writer.h | 5 ++--- test/unittest/prettywritertest.cpp | 9 +++++++++ test/unittest/writertest.cpp | 31 +++++++++++++++++++++++++++++- 4 files changed, 47 insertions(+), 8 deletions(-) diff --git a/include/rapidjson/prettywriter.h b/include/rapidjson/prettywriter.h index 14b8477..fff8886 100644 --- a/include/rapidjson/prettywriter.h +++ b/include/rapidjson/prettywriter.h @@ -100,8 +100,9 @@ public: Base::os_->Put('\n'); WriteIndent(); } - if (!Base::WriteEndObject()) - return false; + bool ret = Base::WriteEndObject(); + (void)ret; + RAPIDJSON_ASSERT(ret == true); if (Base::level_stack_.Empty()) // end of json text Base::os_->Flush(); return true; @@ -123,8 +124,9 @@ public: Base::os_->Put('\n'); WriteIndent(); } - if (!Base::WriteEndArray()) - return false; + bool ret = Base::WriteEndArray(); + (void)ret; + RAPIDJSON_ASSERT(ret == true); if (Base::level_stack_.Empty()) // end of json text Base::os_->Flush(); return true; diff --git a/include/rapidjson/writer.h b/include/rapidjson/writer.h index bf17f3a..40cdb35 100644 --- a/include/rapidjson/writer.h +++ b/include/rapidjson/writer.h @@ -272,7 +272,8 @@ protected: os_->Put(hexDigits[(codepoint >> 4) & 15]); os_->Put(hexDigits[(codepoint ) & 15]); } - else if (codepoint >= 0x010000 && codepoint <= 0x10FFFF) { + else { + RAPIDJSON_ASSERT(codepoint >= 0x010000 && codepoint <= 0x10FFFF); // Surrogate pair unsigned s = codepoint - 0x010000; unsigned lead = (s >> 10) + 0xD800; @@ -288,8 +289,6 @@ protected: os_->Put(hexDigits[(trail >> 4) & 15]); os_->Put(hexDigits[(trail ) & 15]); } - else - return false; // invalid code point } else if ((sizeof(Ch) == 1 || (unsigned)c < 256) && escape[(unsigned char)c]) { is.Take(); diff --git a/test/unittest/prettywritertest.cpp b/test/unittest/prettywritertest.cpp index fcb1121..3b2355f 100644 --- a/test/unittest/prettywritertest.cpp +++ b/test/unittest/prettywritertest.cpp @@ -80,6 +80,15 @@ TEST(PrettyWriter, SetIndent) { buffer.GetString()); } +TEST(PrettyWriter, String) { + StringBuffer buffer; + PrettyWriter writer(buffer); + EXPECT_TRUE(writer.StartArray()); + EXPECT_TRUE(writer.String("Hello\n")); + EXPECT_TRUE(writer.EndArray()); + EXPECT_STREQ("[\n \"Hello\\n\"\n]", buffer.GetString()); +} + #if RAPIDJSON_HAS_STDSTRING TEST(PrettyWriter, String_STDSTRING) { StringBuffer buffer; diff --git a/test/unittest/writertest.cpp b/test/unittest/writertest.cpp index 7b9fa9a..d8193ca 100644 --- a/test/unittest/writertest.cpp +++ b/test/unittest/writertest.cpp @@ -343,4 +343,33 @@ TEST(Writer, InvalidEncoding) { static const UTF32<>::Ch s[] = { 0x110000, 0 }; // Out of U+0000 to U+10FFFF EXPECT_FALSE(writer.String(s)); } -} \ No newline at end of file +} + +TEST(Writer, InvalidEventSequence) { + // {] + { + StringBuffer buffer; + Writer writer(buffer); + writer.StartObject(); + EXPECT_THROW(writer.EndArray(), AssertException); + EXPECT_FALSE(writer.IsComplete()); + } + + // [} + { + StringBuffer buffer; + Writer writer(buffer); + writer.StartArray(); + EXPECT_THROW(writer.EndObject(), AssertException); + EXPECT_FALSE(writer.IsComplete()); + } + + // { 1: + { + StringBuffer buffer; + Writer writer(buffer); + writer.StartObject(); + EXPECT_THROW(writer.Int(1), AssertException); + EXPECT_FALSE(writer.IsComplete()); + } +} From e9b92256a263a6ddb2636ee1f725cf8a09ca5900 Mon Sep 17 00:00:00 2001 From: miloyip Date: Mon, 13 Apr 2015 15:11:32 +0800 Subject: [PATCH 10/64] Add itoatest --- include/rapidjson/internal/itoa.h | 2 + test/unittest/CMakeLists.txt | 1 + test/unittest/itoatest.cpp | 155 ++++++++++++++++++++++++++++++ 3 files changed, 158 insertions(+) create mode 100644 test/unittest/itoatest.cpp diff --git a/include/rapidjson/internal/itoa.h b/include/rapidjson/internal/itoa.h index 9ecf630..01a4e7e 100644 --- a/include/rapidjson/internal/itoa.h +++ b/include/rapidjson/internal/itoa.h @@ -15,6 +15,8 @@ #ifndef RAPIDJSON_ITOA_ #define RAPIDJSON_ITOA_ +#include "../rapidjson.h" + RAPIDJSON_NAMESPACE_BEGIN namespace internal { diff --git a/test/unittest/CMakeLists.txt b/test/unittest/CMakeLists.txt index 3c49651..76b598f 100644 --- a/test/unittest/CMakeLists.txt +++ b/test/unittest/CMakeLists.txt @@ -4,6 +4,7 @@ set(UNITTEST_SOURCES encodedstreamtest.cpp encodingstest.cpp filestreamtest.cpp + itoatest.cpp jsoncheckertest.cpp namespacetest.cpp prettywritertest.cpp diff --git a/test/unittest/itoatest.cpp b/test/unittest/itoatest.cpp new file mode 100644 index 0000000..4ad6d67 --- /dev/null +++ b/test/unittest/itoatest.cpp @@ -0,0 +1,155 @@ +// Copyright (C) 2011 Milo Yip +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include "unittest.h" +#include "rapidjson/internal/itoa.h" + +using namespace rapidjson::internal; + +template +struct Traits { +}; + +template <> +struct Traits { + enum { kBufferSize = 11 }; + enum { kMaxDigit = 10 }; + static uint32_t Negate(uint32_t x) { return x; }; +}; + +template <> +struct Traits { + enum { kBufferSize = 12 }; + enum { kMaxDigit = 10 }; + static int32_t Negate(int32_t x) { return -x; }; +}; + +template <> +struct Traits { + enum { kBufferSize = 21 }; + enum { kMaxDigit = 20 }; + static uint64_t Negate(uint64_t x) { return x; }; +}; + +template <> +struct Traits { + enum { kBufferSize = 22 }; + enum { kMaxDigit = 20 }; + static int64_t Negate(int64_t x) { return -x; }; +}; + +template +static void VerifyValue(T value, void(*f)(T, char*), char* (*g)(T, char*)) { + char buffer1[Traits::kBufferSize]; + char buffer2[Traits::kBufferSize]; + + f(value, buffer1); + *g(value, buffer2) = '\0'; + + + EXPECT_STREQ(buffer1, buffer2); +} + +template +static void Verify(void(*f)(T, char*), char* (*g)(T, char*)) { + // Boundary cases + VerifyValue(0, f, g); + VerifyValue(std::numeric_limits::min(), f, g); + VerifyValue(std::numeric_limits::max(), f, g); + + // 2^n - 1, 2^n, 10^n - 1, 10^n until overflow + for (uint32_t power = 2; power <= 10; power += 8) { + T i = 1, last; + do { + VerifyValue(i - 1, f, g); + VerifyValue(i, f, g); + if (std::numeric_limits::min() < 0) { + VerifyValue(Traits::Negate(i), f, g); + VerifyValue(Traits::Negate(i + 1), f, g); + } + last = i; + i *= power; + } while (last < i); + } +} + +static void u32toa_naive(uint32_t value, char* buffer) { + char temp[10]; + char *p = temp; + do { + *p++ = char(value % 10) + '0'; + value /= 10; + } while (value > 0); + + do { + *buffer++ = *--p; + } while (p != temp); + + *buffer = '\0'; +} + +static void i32toa_naive(int32_t value, char* buffer) { + uint32_t u = static_cast(value); + if (value < 0) { + *buffer++ = '-'; + u = ~u + 1; + } + u32toa_naive(u, buffer); +} + +static void u64toa_naive(uint64_t value, char* buffer) { + char temp[20]; + char *p = temp; + do { + *p++ = char(value % 10) + '0'; + value /= 10; + } while (value > 0); + + do { + *buffer++ = *--p; + } while (p != temp); + + *buffer = '\0'; +} + +static void i64toa_naive(int64_t value, char* buffer) { + uint64_t u = static_cast(value); + if (value < 0) { + *buffer++ = '-'; + u = ~u + 1; + } + u64toa_naive(u, buffer); +} + +TEST(itoa, u32toa) { + Verify(u32toa_naive, u32toa); +} + +TEST(itoa, i32toa) { + Verify(i32toa_naive, i32toa); +} + +TEST(itoa, u64toa) { + Verify(u64toa_naive, u64toa); +} + +TEST(itoa, i64toa) { + Verify(i64toa_naive, i64toa); +} From 0b793ea58a947fb6ea3b5b518399d8561a0eef33 Mon Sep 17 00:00:00 2001 From: miloyip Date: Mon, 13 Apr 2015 15:18:26 +0800 Subject: [PATCH 11/64] Add test for covering PutN() generic version --- test/unittest/prettywritertest.cpp | 57 ++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/test/unittest/prettywritertest.cpp b/test/unittest/prettywritertest.cpp index 3b2355f..b18ad3f 100644 --- a/test/unittest/prettywritertest.cpp +++ b/test/unittest/prettywritertest.cpp @@ -99,3 +99,60 @@ TEST(PrettyWriter, String_STDSTRING) { EXPECT_STREQ("[\n \"Hello\\n\"\n]", buffer.GetString()); } #endif + +#include + +class OStreamWrapper { +public: + typedef char Ch; + + OStreamWrapper(std::ostream& os) : os_(os) {} + + Ch Peek() const { assert(false); return '\0'; } + Ch Take() { assert(false); return '\0'; } + size_t Tell() const { return 0; } + + Ch* PutBegin() { assert(false); return 0; } + void Put(Ch c) { os_.put(c); } + void Flush() { os_.flush(); } + size_t PutEnd(Ch*) { assert(false); return 0; } + +private: + OStreamWrapper(const OStreamWrapper&); + OStreamWrapper& operator=(const OStreamWrapper&); + + std::ostream& os_; +}; + +// For covering PutN() generic version +TEST(PrettyWriter, OStreamWrapper) { + StringStream s(kJson); + + std::stringstream ss; + OStreamWrapper os(ss); + + PrettyWriter writer(os); + + Reader reader; + reader.Parse(s, writer); + + std::string actual = ss.str(); + EXPECT_STREQ( + "{\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" + "}", + actual.c_str()); +} From affd2736c33a82ebbf2e1d6fd3f311355aea6686 Mon Sep 17 00:00:00 2001 From: miloyip Date: Mon, 13 Apr 2015 15:22:26 +0800 Subject: [PATCH 12/64] Fix gcc warning for itoatest --- test/unittest/itoatest.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/unittest/itoatest.cpp b/test/unittest/itoatest.cpp index 4ad6d67..31a008c 100644 --- a/test/unittest/itoatest.cpp +++ b/test/unittest/itoatest.cpp @@ -21,6 +21,11 @@ #include "unittest.h" #include "rapidjson/internal/itoa.h" +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(type-limits) +#endif + using namespace rapidjson::internal; template @@ -153,3 +158,7 @@ TEST(itoa, u64toa) { TEST(itoa, i64toa) { Verify(i64toa_naive, i64toa); } + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif From bcd879653fd0044931db5387dca2ac22005e0101 Mon Sep 17 00:00:00 2001 From: miloyip Date: Mon, 13 Apr 2015 16:13:39 +0800 Subject: [PATCH 13/64] Enable SIMD macros in unit tests --- test/unittest/unittest.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/unittest/unittest.h b/test/unittest/unittest.h index 800fbab..87bb083 100644 --- a/test/unittest/unittest.h +++ b/test/unittest/unittest.h @@ -40,6 +40,14 @@ #pragma GCC diagnostic ignored "-Weffc++" #endif +// __SSE2__ and __SSE4_2__ are recognized by gcc, clang, and the Intel compiler. +// We use -march=native with gmake to enable -msse2 and -msse4.2, if supported. +#if defined(__SSE4_2__) +# define RAPIDJSON_SSE42 +#elif defined(__SSE2__) +# define RAPIDJSON_SSE2 +#endif + #include "gtest/gtest.h" #include From 23809ddef6735a7195a67f7253c049feecf1ffe2 Mon Sep 17 00:00:00 2001 From: miloyip Date: Mon, 13 Apr 2015 16:27:49 +0800 Subject: [PATCH 14/64] Revert "Enable SIMD macros in unit tests" This reverts commit bcd879653fd0044931db5387dca2ac22005e0101. --- test/unittest/unittest.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/test/unittest/unittest.h b/test/unittest/unittest.h index 87bb083..800fbab 100644 --- a/test/unittest/unittest.h +++ b/test/unittest/unittest.h @@ -40,14 +40,6 @@ #pragma GCC diagnostic ignored "-Weffc++" #endif -// __SSE2__ and __SSE4_2__ are recognized by gcc, clang, and the Intel compiler. -// We use -march=native with gmake to enable -msse2 and -msse4.2, if supported. -#if defined(__SSE4_2__) -# define RAPIDJSON_SSE42 -#elif defined(__SSE2__) -# define RAPIDJSON_SSE2 -#endif - #include "gtest/gtest.h" #include From 7e1a6a1bb2da4f6aee77423b19517fdcc15e71ef Mon Sep 17 00:00:00 2001 From: miloyip Date: Mon, 13 Apr 2015 16:45:00 +0800 Subject: [PATCH 15/64] Add Reader kParseErrorTermination coverage --- test/unittest/readertest.cpp | 50 ++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/test/unittest/readertest.cpp b/test/unittest/readertest.cpp index 8c8c63c..25a7261 100644 --- a/test/unittest/readertest.cpp +++ b/test/unittest/readertest.cpp @@ -1177,6 +1177,56 @@ TEST(Reader, IterativeParsing_ShortCircuit) { } } +// For covering BaseReaderHandler default functions +TEST(Reader, BaseReaderHandler_Default) { + BaseReaderHandler<> h; + Reader reader; + StringStream is("[null, true, -1, 1, -1234567890123456789, 1234567890123456789, 3.14, \"s\", { \"a\" : 1 }]"); + EXPECT_TRUE(reader.Parse(is, h)); +} + +template +struct TerminateHandler { + bool Null() { return e != 0; } + bool Bool(bool) { return e != 1; } + bool Int(int) { return e != 2; } + bool Uint(unsigned) { return e != 3; } + bool Int64(int64_t) { return e != 4; } + bool Uint64(uint64_t) { return e != 5; } + bool Double(double) { return e != 6; } + bool String(const char*, SizeType, bool) { return e != 7; } + bool StartObject() { return e != 8; } + bool Key(const char* str, SizeType length, bool copy) { return e != 9; } + bool EndObject(SizeType memberCount) { return e != 10; } + bool StartArray() { return e != 11; } + bool EndArray(SizeType elementCount) { return e != 12; } +}; + +#define TEST_TERMINATION(e, json)\ +{\ + Reader reader;\ + TerminateHandler h;\ + StringStream is(json);\ + EXPECT_FALSE(reader.Parse(is, h));\ + EXPECT_EQ(kParseErrorTermination, reader.GetParseErrorCode());\ +} + +TEST(Reader, ParseTerminationByHandler) { + TEST_TERMINATION(0, "[null"); + TEST_TERMINATION(1, "[true"); + TEST_TERMINATION(2, "[-1"); + TEST_TERMINATION(3, "[1"); + TEST_TERMINATION(4, "[-1234567890123456789"); + TEST_TERMINATION(5, "[1234567890123456789"); + TEST_TERMINATION(6, "[0.5]"); + TEST_TERMINATION(7, "[\"a\""); + TEST_TERMINATION(8, "[{"); + TEST_TERMINATION(9, "[{\"a\""); + TEST_TERMINATION(10, "[{}"); + TEST_TERMINATION(11, "{\"a\":["); + TEST_TERMINATION(12, "{\"a\":[]"); +} + #ifdef __GNUC__ RAPIDJSON_DIAG_POP #endif From 331009a321e4064afe99023add0aa7afb70f0abe Mon Sep 17 00:00:00 2001 From: miloyip Date: Mon, 13 Apr 2015 16:49:10 +0800 Subject: [PATCH 16/64] Fix gcc warning --- test/unittest/readertest.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/unittest/readertest.cpp b/test/unittest/readertest.cpp index 25a7261..a808d90 100644 --- a/test/unittest/readertest.cpp +++ b/test/unittest/readertest.cpp @@ -1196,10 +1196,10 @@ struct TerminateHandler { bool Double(double) { return e != 6; } bool String(const char*, SizeType, bool) { return e != 7; } bool StartObject() { return e != 8; } - bool Key(const char* str, SizeType length, bool copy) { return e != 9; } - bool EndObject(SizeType memberCount) { return e != 10; } + bool Key(const char*, SizeType, bool) { return e != 9; } + bool EndObject(SizeType) { return e != 10; } bool StartArray() { return e != 11; } - bool EndArray(SizeType elementCount) { return e != 12; } + bool EndArray(SizeType) { return e != 12; } }; #define TEST_TERMINATION(e, json)\ From 074554965dfca1d6d5e64e2fc871f37a57d9ffb7 Mon Sep 17 00:00:00 2001 From: miloyip Date: Mon, 13 Apr 2015 17:07:15 +0800 Subject: [PATCH 17/64] Improve Reading kParseErrorTermination coverage --- test/unittest/readertest.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/unittest/readertest.cpp b/test/unittest/readertest.cpp index a808d90..1461353 100644 --- a/test/unittest/readertest.cpp +++ b/test/unittest/readertest.cpp @@ -1214,6 +1214,7 @@ struct TerminateHandler { 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"); @@ -1223,8 +1224,10 @@ TEST(Reader, ParseTerminationByHandler) { TEST_TERMINATION(8, "[{"); TEST_TERMINATION(9, "[{\"a\""); TEST_TERMINATION(10, "[{}"); + TEST_TERMINATION(10, "[{\"a\":1}"); // non-empty object TEST_TERMINATION(11, "{\"a\":["); TEST_TERMINATION(12, "{\"a\":[]"); + TEST_TERMINATION(12, "{\"a\":[1]"); // non-empty array } #ifdef __GNUC__ From d439f989bfe20d188e10a5b039437306db10b4f1 Mon Sep 17 00:00:00 2001 From: miloyip Date: Mon, 13 Apr 2015 18:10:07 +0800 Subject: [PATCH 18/64] Add valuetest coverage --- test/unittest/valuetest.cpp | 86 +++++++++++++++++++++++++++++++++---- 1 file changed, 78 insertions(+), 8 deletions(-) diff --git a/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp index 846b1c8..26fde3b 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -203,12 +203,28 @@ TEST(Value, EqualtoOperator) { EXPECT_TRUE(z.RemoveMember("t")); TestUnequal(x, z); TestEqual(y, z); - y.AddMember("t", true, crtAllocator); - z.AddMember("t", true, z.GetAllocator()); + y.AddMember("t", false, crtAllocator); + z.AddMember("t", false, z.GetAllocator()); + TestUnequal(x, y); + TestUnequal(z, x); + y["t"] = true; + z["t"] = true; TestEqual(x, y); TestEqual(y, z); TestEqual(z, x); + // Swapping element order is not OK + x["a"][0].Swap(x["a"][1]); + TestUnequal(x, y); + x["a"][0].Swap(x["a"][1]); + TestEqual(x, y); + + // Array of different size + x["a"].PushBack(4, allocator); + TestUnequal(x, y); + x["a"].PopBack(); + TestEqual(x, y); + // Issue #129: compare Uint64 x.SetUint64(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0xFFFFFFF0)); y.SetUint64(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0xFFFFFFFF)); @@ -462,10 +478,19 @@ TEST(Value, Int64) { z.SetInt64(2147483648LL); // 2^31, cannot cast as int EXPECT_FALSE(z.IsInt()); EXPECT_TRUE(z.IsUint()); + EXPECT_NEAR(2147483648.0, z.GetDouble(), 0.0); z.SetInt64(4294967296LL); // 2^32, cannot cast as uint EXPECT_FALSE(z.IsInt()); EXPECT_FALSE(z.IsUint()); + EXPECT_NEAR(4294967296.0, z.GetDouble(), 0.0); + + z.SetInt64(-2147483649LL); // -2^31-1, cannot cast as int + EXPECT_FALSE(z.IsInt()); + EXPECT_NEAR(-2147483649.0, z.GetDouble(), 0.0); + + z.SetInt64(-9223372036854775808LL); + EXPECT_DOUBLE_EQ(-9223372036854775808.0, z.GetDouble()); } TEST(Value, Uint64) { @@ -508,9 +533,8 @@ TEST(Value, Uint64) { z.SetUint64(9223372036854775808uLL); // 2^63 cannot cast as int64 EXPECT_FALSE(z.IsInt64()); - - // Issue 48 - EXPECT_EQ(9223372036854775808uLL, z.GetUint64()); + EXPECT_EQ(9223372036854775808uLL, z.GetUint64()); // Issue 48 + EXPECT_DOUBLE_EQ(9223372036854775808.0, z.GetDouble()); } TEST(Value, Double) { @@ -977,6 +1001,7 @@ TEST(Value, Object) { EXPECT_STREQ("Banana", x["B"].GetString()); EXPECT_STREQ("CherryD", x[C0D].GetString()); EXPECT_STREQ("CherryD", x[othername].GetString()); + EXPECT_THROW(x["nonexist"], AssertException); // const operator[] EXPECT_STREQ("Apple", y["A"].GetString()); @@ -1041,13 +1066,15 @@ TEST(Value, Object) { EXPECT_FALSE(citr >= itr); // RemoveMember() - x.RemoveMember("A"); + EXPECT_TRUE(x.RemoveMember("A")); EXPECT_FALSE(x.HasMember("A")); - x.RemoveMember("B"); + EXPECT_TRUE(x.RemoveMember("B")); EXPECT_FALSE(x.HasMember("B")); - x.RemoveMember(othername); + EXPECT_FALSE(x.RemoveMember("nonexist")); + + EXPECT_TRUE(x.RemoveMember(othername)); EXPECT_FALSE(x.HasMember(name)); EXPECT_TRUE(x.MemberBegin() == x.MemberEnd()); @@ -1237,3 +1264,46 @@ TEST(Value, AllocateShortString) { TestShortStringOptimization("123456789012345"); // edge case: 15 chars in 64-bit mode (=> short string) TestShortStringOptimization("1234567890123456"); // edge case: 16 chars in 64-bit mode (=> regular string) } + +template +struct TerminateHandler { + bool Null() { return e != 0; } + bool Bool(bool) { return e != 1; } + bool Int(int) { return e != 2; } + bool Uint(unsigned) { return e != 3; } + bool Int64(int64_t) { return e != 4; } + bool Uint64(uint64_t) { return e != 5; } + bool Double(double) { return e != 6; } + bool String(const char*, SizeType, bool) { return e != 7; } + bool StartObject() { return e != 8; } + bool Key(const char*, SizeType, bool) { return e != 9; } + bool EndObject(SizeType) { return e != 10; } + bool StartArray() { return e != 11; } + bool EndArray(SizeType) { return e != 12; } +}; + +#define TEST_TERMINATION(e, json)\ +{\ + Document d; \ + EXPECT_FALSE(d.Parse(json).HasParseError()); \ + Reader reader; \ + TerminateHandler h;\ + EXPECT_FALSE(d.Accept(h));\ +} + +TEST(Value, AcceptTerminationByHandler) { + TEST_TERMINATION(0, "[null]"); + TEST_TERMINATION(1, "[true]"); + TEST_TERMINATION(1, "[false]"); + TEST_TERMINATION(2, "[-1]"); + TEST_TERMINATION(3, "[2147483648]"); + TEST_TERMINATION(4, "[-1234567890123456789]"); + TEST_TERMINATION(5, "[9223372036854775808]"); + TEST_TERMINATION(6, "[0.5]"); + TEST_TERMINATION(7, "[\"a\"]"); + TEST_TERMINATION(8, "[{}]"); + TEST_TERMINATION(9, "[{\"a\":1}]"); + TEST_TERMINATION(10, "[{}]"); + TEST_TERMINATION(11, "{\"a\":[]}"); + TEST_TERMINATION(12, "{\"a\":[]}"); +} From 59ffb9e5f0b5aae07c6d4a507a02cd1a0a7114a3 Mon Sep 17 00:00:00 2001 From: miloyip Date: Mon, 13 Apr 2015 18:32:11 +0800 Subject: [PATCH 19/64] Try to fix a gcc/clang error --- test/unittest/valuetest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp index 26fde3b..bb4cc04 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -489,7 +489,7 @@ TEST(Value, Int64) { EXPECT_FALSE(z.IsInt()); EXPECT_NEAR(-2147483649.0, z.GetDouble(), 0.0); - z.SetInt64(-9223372036854775808LL); + z.SetInt64(static_cast(RAPIDJSON_UINT64_C2(0x80000000, 00000000))); EXPECT_DOUBLE_EQ(-9223372036854775808.0, z.GetDouble()); } From 3bc95ecd7c8e2cc8aa1c053dc97294fa5e807587 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Mon, 13 Apr 2015 22:04:00 +0800 Subject: [PATCH 20/64] Add coverage for Document::ParseXXX() --- test/unittest/documenttest.cpp | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/test/unittest/documenttest.cpp b/test/unittest/documenttest.cpp index 12ea751..e185efb 100644 --- a/test/unittest/documenttest.cpp +++ b/test/unittest/documenttest.cpp @@ -28,13 +28,9 @@ using namespace rapidjson; -template -void ParseTest() { - typedef GenericDocument, Allocator, StackAllocator> DocumentType; +template +void ParseCheck(DocumentType& doc) { typedef typename DocumentType::ValueType ValueType; - DocumentType doc; - - doc.Parse(" { \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3, 4] } "); EXPECT_TRUE(doc.IsObject()); @@ -73,6 +69,28 @@ void ParseTest() { EXPECT_EQ(i + 1, a[i].GetUint()); } +template +void ParseTest() { + typedef GenericDocument, Allocator, StackAllocator> DocumentType; + DocumentType doc; + + const char* json = " { \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3, 4] } "; + + EXPECT_FALSE(doc.Parse(json).HasParseError()); + ParseCheck(doc); + + doc.SetNull(); + StringStream s(json); + EXPECT_FALSE(doc.ParseStream<0>( s).HasParseError()); + ParseCheck(doc); + + doc.SetNull(); + char *buffer = strdup(json); + EXPECT_FALSE(doc.ParseInsitu(buffer).HasParseError()); + ParseCheck(doc); + free(buffer); +} + TEST(Document, Parse) { ParseTest, CrtAllocator>(); ParseTest, MemoryPoolAllocator<> >(); From 985971a563b35c28d6085554c110d28ccffcdf4d Mon Sep 17 00:00:00 2001 From: miloyip Date: Mon, 13 Apr 2015 22:46:09 +0800 Subject: [PATCH 21/64] Fix gcc/clang compilation --- test/unittest/documenttest.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/unittest/documenttest.cpp b/test/unittest/documenttest.cpp index e185efb..7adf9b9 100644 --- a/test/unittest/documenttest.cpp +++ b/test/unittest/documenttest.cpp @@ -32,6 +32,8 @@ template void ParseCheck(DocumentType& doc) { typedef typename DocumentType::ValueType ValueType; + EXPECT_FALSE(doc.HasParseError()); + EXPECT_TRUE(doc.IsObject()); EXPECT_TRUE(doc.HasMember("hello")); @@ -76,17 +78,17 @@ void ParseTest() { const char* json = " { \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3, 4] } "; - EXPECT_FALSE(doc.Parse(json).HasParseError()); + doc.Parse(json); ParseCheck(doc); doc.SetNull(); StringStream s(json); - EXPECT_FALSE(doc.ParseStream<0>( s).HasParseError()); + doc.template ParseStream<0>(s); ParseCheck(doc); doc.SetNull(); char *buffer = strdup(json); - EXPECT_FALSE(doc.ParseInsitu(buffer).HasParseError()); + doc.ParseInsitu(buffer); ParseCheck(doc); free(buffer); } From 04011cdae2889c2154373220a39686343998ae9e Mon Sep 17 00:00:00 2001 From: miloyip Date: Mon, 13 Apr 2015 22:46:27 +0800 Subject: [PATCH 22/64] Adjust spaces --- include/rapidjson/document.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 204e3bd..e436b8a 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -1753,7 +1753,7 @@ public: */ template GenericDocument& ParseStream(InputStream& is) { - return ParseStream(is); + return ParseStream(is); } //! Parse JSON text from an input stream (with \ref kParseDefaultFlags) From 4bcedab513123c7620eb30f53f314852eba5ba10 Mon Sep 17 00:00:00 2001 From: miloyip Date: Mon, 13 Apr 2015 23:03:00 +0800 Subject: [PATCH 23/64] Try to improve coverage of encodings --- test/unittest/readertest.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/unittest/readertest.cpp b/test/unittest/readertest.cpp index 1461353..c5a8a5a 100644 --- a/test/unittest/readertest.cpp +++ b/test/unittest/readertest.cpp @@ -487,6 +487,17 @@ TEST(Reader, ParseString_Transcoding) { EXPECT_EQ(StrLen(e), h.length_); } +TEST(Reader, ParseString_TranscodingWithValidation) { + const char* x = "\"Hello\""; + const wchar_t* e = L"Hello"; + GenericStringStream > is(x); + GenericReader, UTF16<> > reader; + ParseStringHandler > h; + reader.Parse(is, h); + EXPECT_EQ(0, StrCmp::Ch>(e, h.str_)); + EXPECT_EQ(StrLen(e), h.length_); +} + TEST(Reader, ParseString_NonDestructive) { StringStream s("\"Hello\\nWorld\""); ParseStringHandler > h; From 933c4a6cb1ea91063b5c9b23918eea516c37c40f Mon Sep 17 00:00:00 2001 From: miloyip Date: Mon, 13 Apr 2015 23:12:57 +0800 Subject: [PATCH 24/64] Improve Value deep-clone coverage --- test/unittest/valuetest.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp index bb4cc04..fa9547b 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -245,6 +245,13 @@ void TestCopyFrom() { EXPECT_STREQ(v1.GetString(), v2.GetString()); EXPECT_EQ(v1.GetString(), v2.GetString()); // string NOT copied + v1.SetString("bar", a); // copy string + v2.CopyFrom(v1, a); + EXPECT_TRUE(v1.GetType() == v2.GetType()); + EXPECT_STREQ(v1.GetString(), v2.GetString()); + EXPECT_NE(v1.GetString(), v2.GetString()); // string copied + + v1.SetArray().PushBack(1234, a); v2.CopyFrom(v1, a); EXPECT_TRUE(v2.IsArray()); From f51c0141c57b37b81351f75321932b1c19fc56e2 Mon Sep 17 00:00:00 2001 From: miloyip Date: Tue, 14 Apr 2015 00:39:54 +0800 Subject: [PATCH 25/64] Improve coverage of encodings --- test/unittest/readertest.cpp | 67 ++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 25 deletions(-) diff --git a/test/unittest/readertest.cpp b/test/unittest/readertest.cpp index c5a8a5a..f5f6cce 100644 --- a/test/unittest/readertest.cpp +++ b/test/unittest/readertest.cpp @@ -507,24 +507,31 @@ TEST(Reader, ParseString_NonDestructive) { EXPECT_EQ(11u, h.length_); } -ParseErrorCode TestString(const char* str) { - StringStream s(str); - BaseReaderHandler<> h; - Reader reader; - reader.Parse(s, h); +template +ParseErrorCode TestString(const typename Encoding::Ch* str) { + GenericStringStream s(str); + BaseReaderHandler h; + GenericReader reader; + reader.template Parse(s, h); return reader.GetParseErrorCode(); } TEST(Reader, ParseString_Error) { #define TEST_STRING_ERROR(errorCode, str)\ - EXPECT_EQ(errorCode, TestString(str)) + EXPECT_EQ(errorCode, TestString >(str)) #define ARRAY(...) { __VA_ARGS__ } -#define TEST_STRINGENCODING_ERROR(Encoding, utype, array) \ +#define TEST_STRINGENCODING_ERROR(Encoding, TargetEncoding, utype, array) \ { \ static const utype ue[] = array; \ static const Encoding::Ch* e = reinterpret_cast(&ue[0]); \ - EXPECT_EQ(kParseErrorStringInvalidEncoding, TestString(e));\ + EXPECT_EQ(kParseErrorStringInvalidEncoding, TestString(e));\ + /* decode error */\ + GenericStringStream s(e);\ + BaseReaderHandler h;\ + GenericReader reader;\ + reader.Parse(s, h);\ + EXPECT_EQ(kParseErrorStringInvalidEncoding, reader.GetParseErrorCode());\ } // Invalid escape character in string. @@ -553,7 +560,7 @@ TEST(Reader, ParseString_Error) { char e[] = { '[', '\"', 0, '\"', ']', '\0' }; for (unsigned char c = 0x80u; c <= 0xBFu; c++) { e[2] = c; - ParseErrorCode error = TestString(e); + ParseErrorCode error = TestString >(e); EXPECT_EQ(kParseErrorStringInvalidEncoding, error); if (error != kParseErrorStringInvalidEncoding) std::cout << (unsigned)(unsigned char)c << std::endl; @@ -572,30 +579,40 @@ TEST(Reader, ParseString_Error) { // 4 Overlong sequences // 4.1 Examples of an overlong ASCII character - TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xC0u, 0xAFu, '\"', ']', '\0')); - TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xE0u, 0x80u, 0xAFu, '\"', ']', '\0')); - TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xF0u, 0x80u, 0x80u, 0xAFu, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xC0u, 0xAFu, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xE0u, 0x80u, 0xAFu, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xF0u, 0x80u, 0x80u, 0xAFu, '\"', ']', '\0')); // 4.2 Maximum overlong sequences - TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xC1u, 0xBFu, '\"', ']', '\0')); - TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xE0u, 0x9Fu, 0xBFu, '\"', ']', '\0')); - TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xF0u, 0x8Fu, 0xBFu, 0xBFu, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xC1u, 0xBFu, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xE0u, 0x9Fu, 0xBFu, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xF0u, 0x8Fu, 0xBFu, 0xBFu, '\"', ']', '\0')); // 4.3 Overlong representation of the NUL character - TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xC0u, 0x80u, '\"', ']', '\0')); - TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xE0u, 0x80u, 0x80u, '\"', ']', '\0')); - TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xF0u, 0x80u, 0x80u, 0x80u, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xC0u, 0x80u, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xE0u, 0x80u, 0x80u, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xF0u, 0x80u, 0x80u, 0x80u, '\"', ']', '\0')); // 5 Illegal code positions // 5.1 Single UTF-16 surrogates - TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xA0u, 0x80u, '\"', ']', '\0')); - TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xADu, 0xBFu, '\"', ']', '\0')); - TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xAEu, 0x80u, '\"', ']', '\0')); - TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xAFu, 0xBFu, '\"', ']', '\0')); - TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xB0u, 0x80u, '\"', ']', '\0')); - TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xBEu, 0x80u, '\"', ']', '\0')); - TEST_STRINGENCODING_ERROR(UTF8<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xBFu, 0xBFu, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xA0u, 0x80u, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xADu, 0xBFu, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xAEu, 0x80u, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xAFu, 0xBFu, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xB0u, 0x80u, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xBEu, 0x80u, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF8<>, UTF16<>, unsigned char, ARRAY('[', '\"', 0xEDu, 0xBFu, 0xBFu, '\"', ']', '\0')); + + // Malform UTF-16 sequences + TEST_STRINGENCODING_ERROR(UTF16<>, UTF8<>, wchar_t, ARRAY('[', '\"', 0xDC00, 0xDC00, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(UTF16<>, UTF8<>, wchar_t, ARRAY('[', '\"', 0xD800, 0xD800, '\"', ']', '\0')); + + // Malform UTF-32 sequence + TEST_STRINGENCODING_ERROR(UTF32<>, UTF8<>, unsigned, ARRAY('[', '\"', 0x110000, '\"', ']', '\0')); + + // Malform ASCII sequence + TEST_STRINGENCODING_ERROR(ASCII<>, UTF8<>, char, ARRAY('[', '\"', 0x80, '\"', ']', '\0')); #undef ARRAY #undef TEST_STRINGARRAY_ERROR From 5dde9ae45e79d9a27c4d89225990001afc6f9ebc Mon Sep 17 00:00:00 2001 From: miloyip Date: Tue, 14 Apr 2015 09:49:55 +0800 Subject: [PATCH 26/64] Cover MemoryPoolAllocator::Capacity() --- test/unittest/documenttest.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/unittest/documenttest.cpp b/test/unittest/documenttest.cpp index 7adf9b9..6e8370e 100644 --- a/test/unittest/documenttest.cpp +++ b/test/unittest/documenttest.cpp @@ -243,6 +243,10 @@ TEST(Document, UserBuffer) { EXPECT_FALSE(doc.HasParseError()); EXPECT_LE(valueAllocator.Size(), sizeof(valueBuffer)); EXPECT_LE(parseAllocator.Size(), sizeof(parseBuffer)); + + // Cover MemoryPool::Capacity() + EXPECT_LE(valueAllocator.Size(), valueAllocator.Capacity()); + EXPECT_LE(parseAllocator.Size(), parseAllocator.Capacity()); } // Issue 226: Value of string type should not point to NULL From bff588e6650d687a04472a306d880618a88e31d4 Mon Sep 17 00:00:00 2001 From: miloyip Date: Tue, 14 Apr 2015 09:50:22 +0800 Subject: [PATCH 27/64] Typo --- test/unittest/documenttest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unittest/documenttest.cpp b/test/unittest/documenttest.cpp index 6e8370e..bf6f3a2 100644 --- a/test/unittest/documenttest.cpp +++ b/test/unittest/documenttest.cpp @@ -244,7 +244,7 @@ TEST(Document, UserBuffer) { EXPECT_LE(valueAllocator.Size(), sizeof(valueBuffer)); EXPECT_LE(parseAllocator.Size(), sizeof(parseBuffer)); - // Cover MemoryPool::Capacity() + // Cover MemoryPoolAllocator::Capacity() EXPECT_LE(valueAllocator.Size(), valueAllocator.Capacity()); EXPECT_LE(parseAllocator.Size(), parseAllocator.Capacity()); } From 056125db82bdd0f15d43b0e7cfbe95ad14f128aa Mon Sep 17 00:00:00 2001 From: miloyip Date: Tue, 14 Apr 2015 10:00:53 +0800 Subject: [PATCH 28/64] Improve coverage of Stack --- test/unittest/stringbuffertest.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/unittest/stringbuffertest.cpp b/test/unittest/stringbuffertest.cpp index 71e0caf..e6ebbcf 100644 --- a/test/unittest/stringbuffertest.cpp +++ b/test/unittest/stringbuffertest.cpp @@ -54,6 +54,9 @@ TEST(StringBuffer, Push) { buffer.Push(5); EXPECT_EQ(5u, buffer.GetSize()); + + buffer.Push(65536u); + EXPECT_EQ(5u + 65536u, buffer.GetSize()); } TEST(StringBuffer, Pop) { From c0ba5cffcdc39ab0207f62f3a96047a2077224ca Mon Sep 17 00:00:00 2001 From: miloyip Date: Tue, 14 Apr 2015 10:01:22 +0800 Subject: [PATCH 29/64] Add comment --- test/unittest/stringbuffertest.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/unittest/stringbuffertest.cpp b/test/unittest/stringbuffertest.cpp index e6ebbcf..7cfde4f 100644 --- a/test/unittest/stringbuffertest.cpp +++ b/test/unittest/stringbuffertest.cpp @@ -55,6 +55,7 @@ TEST(StringBuffer, Push) { EXPECT_EQ(5u, buffer.GetSize()); + // Causes sudden expansion to make the stack's capacity equal to size buffer.Push(65536u); EXPECT_EQ(5u + 65536u, buffer.GetSize()); } From 3f7a3bcc042feb667732667a07807259921e009c Mon Sep 17 00:00:00 2001 From: miloyip Date: Tue, 14 Apr 2015 10:19:05 +0800 Subject: [PATCH 30/64] Add separate allocators test --- test/unittest/CMakeLists.txt | 1 + test/unittest/allocatorstest.cpp | 64 ++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 test/unittest/allocatorstest.cpp diff --git a/test/unittest/CMakeLists.txt b/test/unittest/CMakeLists.txt index 76b598f..52d5acd 100644 --- a/test/unittest/CMakeLists.txt +++ b/test/unittest/CMakeLists.txt @@ -1,4 +1,5 @@ set(UNITTEST_SOURCES + allocatorstest.cpp bigintegertest.cpp documenttest.cpp encodedstreamtest.cpp diff --git a/test/unittest/allocatorstest.cpp b/test/unittest/allocatorstest.cpp new file mode 100644 index 0000000..2cf9a2e --- /dev/null +++ b/test/unittest/allocatorstest.cpp @@ -0,0 +1,64 @@ +// Copyright (C) 2011 Milo Yip +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include "unittest.h" + +#include "rapidjson/allocators.h" + +using namespace rapidjson; + +template +void TestAllocator(Allocator& a) { + uint8_t* p = (uint8_t*)a.Malloc(100); + EXPECT_TRUE(p != 0); + for (size_t i = 0; i < 100; i++) + p[i] = (uint8_t)i; + + // Expand + uint8_t* q = (uint8_t*)a.Realloc(p, 100, 200); + EXPECT_TRUE(q != 0); + for (size_t i = 0; i < 100; i++) + EXPECT_EQ(i, q[i]); + for (size_t i = 100; i < 200; i++) + q[i] = (uint8_t)i; + + // Shrink + uint8_t *r = (uint8_t*)a.Realloc(q, 200, 150); + EXPECT_TRUE(r != 0); + for (size_t i = 0; i < 150; i++) + EXPECT_EQ(i, r[i]); + + Allocator::Free(r); +} + +TEST(Allocator, CrtAllocator) { + CrtAllocator a; + TestAllocator(a); +} + +TEST(Allocator, MemoryPoolAllocator) { + MemoryPoolAllocator<> a; + TestAllocator(a); + + for (int i = 0; i < 1000; i++) { + EXPECT_TRUE(a.Malloc(i) != 0); + EXPECT_LE(a.Size(), a.Capacity()); + } +} From 3675b33a2a77a16160e42783ec1199444cca0bf5 Mon Sep 17 00:00:00 2001 From: miloyip Date: Tue, 14 Apr 2015 10:35:45 +0800 Subject: [PATCH 31/64] Search more paths for different build situations --- test/unittest/documenttest.cpp | 19 ++++++++----- test/unittest/encodedstreamtest.cpp | 19 ++++++++----- test/unittest/filestreamtest.cpp | 18 ++++++++++--- test/unittest/jsoncheckertest.cpp | 41 ++++++++++++++++------------- 4 files changed, 64 insertions(+), 33 deletions(-) diff --git a/test/unittest/documenttest.cpp b/test/unittest/documenttest.cpp index bf6f3a2..dd4fb13 100644 --- a/test/unittest/documenttest.cpp +++ b/test/unittest/documenttest.cpp @@ -101,14 +101,21 @@ TEST(Document, Parse) { } static FILE* OpenEncodedFile(const char* filename) { + const char *paths[] = { + "encodings/%s", + "bin/encodings/%s", + "../bin/encodings/%s", + "../../bin/encodings/%s", + "../../../bin/encodings/%s" + }; char buffer[1024]; - sprintf(buffer, "encodings/%s", filename); - FILE *fp = fopen(buffer, "rb"); - if (!fp) { - sprintf(buffer, "../../bin/encodings/%s", filename); - fp = fopen(buffer, "rb"); + for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) { + sprintf(buffer, paths[i], filename); + FILE *fp = fopen(buffer, "rb"); + if (fp) + return fp; } - return fp; + return 0; } TEST(Document, ParseStream_EncodedInputStream) { diff --git a/test/unittest/encodedstreamtest.cpp b/test/unittest/encodedstreamtest.cpp index 3580608..c2920a0 100644 --- a/test/unittest/encodedstreamtest.cpp +++ b/test/unittest/encodedstreamtest.cpp @@ -47,14 +47,21 @@ private: protected: static FILE* Open(const char* filename) { + const char *paths[] = { + "encodings/%s", + "bin/encodings/%s", + "../bin/encodings/%s", + "../../bin/encodings/%s", + "../../../bin/encodings/%s" + }; char buffer[1024]; - sprintf(buffer, "encodings/%s", filename); - FILE *fp = fopen(buffer, "rb"); - if (!fp) { - sprintf(buffer, "../../bin/encodings/%s", filename); - fp = fopen(buffer, "rb"); + for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) { + sprintf(buffer, paths[i], filename); + FILE *fp = fopen(buffer, "rb"); + if (fp) + return fp; } - return fp; + return 0; } static char *ReadFile(const char* filename, bool appendPath, size_t* outLength) { diff --git a/test/unittest/filestreamtest.cpp b/test/unittest/filestreamtest.cpp index a176332..5b3df53 100644 --- a/test/unittest/filestreamtest.cpp +++ b/test/unittest/filestreamtest.cpp @@ -31,9 +31,21 @@ public: FileStreamTest() : filename_(), json_(), length_() {} virtual void SetUp() { - FILE *fp = fopen(filename_ = "data/sample.json", "rb"); - if (!fp) - fp = fopen(filename_ = "../../bin/data/sample.json", "rb"); + const char *paths[] = { + "data/sample.json", + "bin/data/sample.json", + "../bin/data/sample.json", + "../../bin/data/sample.json", + "../../../bin/data/sample.json" + }; + FILE* fp = 0; + for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) { + fp = fopen(paths[i], "rb"); + if (fp) { + filename_ = paths[i]; + break; + } + } ASSERT_TRUE(fp != 0); fseek(fp, 0, SEEK_END); diff --git a/test/unittest/jsoncheckertest.cpp b/test/unittest/jsoncheckertest.cpp index caa1b8a..a89b8d2 100644 --- a/test/unittest/jsoncheckertest.cpp +++ b/test/unittest/jsoncheckertest.cpp @@ -25,9 +25,22 @@ using namespace rapidjson; static char* ReadFile(const char* filename, size_t& length) { - FILE *fp = fopen(filename, "rb"); - if (!fp) - fp = fopen(filename, "rb"); + const char *paths[] = { + "jsonchecker/%s", + "bin/jsonchecker/%s", + "../bin/jsonchecker/%s", + "../../bin/jsonchecker/%s", + "../../../bin/jsonchecker/%s" + }; + char buffer[1024]; + FILE *fp = 0; + for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) { + sprintf(buffer, paths[i], filename); + fp = fopen(buffer, "rb"); + if (fp) + break; + } + if (!fp) return 0; @@ -51,17 +64,13 @@ TEST(JsonChecker, Reader) { if (i == 18) // fail18.json is valid in rapidjson, which has no limitation on depth of nesting. continue; - sprintf(filename, "jsonchecker/fail%d.json", i); + sprintf(filename, "fail%d.json", i); size_t length; char* json = ReadFile(filename, length); if (!json) { - sprintf(filename, "../../bin/jsonchecker/fail%d.json", i); - json = ReadFile(filename, length); - if (!json) { - printf("jsonchecker file %s not found", filename); - ADD_FAILURE(); - continue; - } + printf("jsonchecker file %s not found", filename); + ADD_FAILURE(); + continue; } GenericDocument, CrtAllocator> document; // Use Crt allocator to check exception-safety (no memory leak) @@ -76,16 +85,12 @@ TEST(JsonChecker, Reader) { // passX.json for (int i = 1; i <= 3; i++) { - sprintf(filename, "jsonchecker/pass%d.json", i); + sprintf(filename, "pass%d.json", i); size_t length; char* json = ReadFile(filename, length); if (!json) { - sprintf(filename, "../../bin/jsonchecker/pass%d.json", i); - json = ReadFile(filename, length); - if (!json) { - printf("jsonchecker file %s not found", filename); - continue; - } + printf("jsonchecker file %s not found", filename); + continue; } GenericDocument, CrtAllocator> document; // Use Crt allocator to check exception-safety (no memory leak) From f89c4b52b6644e19fb8becc3d4c999a6330e557b Mon Sep 17 00:00:00 2001 From: miloyip Date: Tue, 14 Apr 2015 10:50:39 +0800 Subject: [PATCH 32/64] Use PrettyWriter to cover FileWriteStream::PutN() --- test/unittest/prettywritertest.cpp | 81 +++++++++++++++++------------- 1 file changed, 45 insertions(+), 36 deletions(-) diff --git a/test/unittest/prettywritertest.cpp b/test/unittest/prettywritertest.cpp index b18ad3f..6ae14b9 100644 --- a/test/unittest/prettywritertest.cpp +++ b/test/unittest/prettywritertest.cpp @@ -22,10 +22,28 @@ #include "rapidjson/reader.h" #include "rapidjson/prettywriter.h" #include "rapidjson/stringbuffer.h" +#include "rapidjson/filewritestream.h" using namespace rapidjson; static const char kJson[] = "{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3,-1],\"u64\":1234567890123456789,\"i64\":-1234567890123456789}"; +static const char kPrettyJson[] = +"{\n" +" \"hello\": \"world\",\n" +" \"t\": true,\n" +" \"f\": false,\n" +" \"n\": null,\n" +" \"i\": 123,\n" +" \"pi\": 3.1416,\n" +" \"a\": [\n" +" 1,\n" +" 2,\n" +" 3,\n" +" -1\n" +" ],\n" +" \"u64\": 1234567890123456789,\n" +" \"i64\": -1234567890123456789\n" +"}"; TEST(PrettyWriter, Basic) { StringBuffer buffer; @@ -33,24 +51,7 @@ TEST(PrettyWriter, Basic) { Reader reader; StringStream s(kJson); reader.Parse(s, writer); - EXPECT_STREQ( - "{\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" - "}", - buffer.GetString()); + EXPECT_STREQ(kPrettyJson, buffer.GetString()); } TEST(PrettyWriter, SetIndent) { @@ -137,22 +138,30 @@ TEST(PrettyWriter, OStreamWrapper) { reader.Parse(s, writer); std::string actual = ss.str(); - EXPECT_STREQ( - "{\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" - "}", - actual.c_str()); + EXPECT_STREQ(kPrettyJson, actual.c_str()); } + +// For covering FileWriteStream::PutN() +TEST(PrettyWriter, FileWriteStream) { + char filename[L_tmpnam]; + FILE* fp = TempFile(filename); + char buffer[16]; + FileWriteStream os(fp, buffer, sizeof(buffer)); + PrettyWriter writer(os); + Reader reader; + StringStream s(kJson); + reader.Parse(s, writer); + fclose(fp); + + fp = fopen(filename, "rb"); + fseek(fp, 0, SEEK_END); + size_t size = (size_t)ftell(fp); + fseek(fp, 0, SEEK_SET); + char* json = (char*)malloc(size + 1); + size_t readLength = fread(json, 1, size, fp); + json[readLength] = '\0'; + fclose(fp); + remove(filename); + EXPECT_STREQ(kPrettyJson, json); + free(json); +} \ No newline at end of file From 5dc52afd1e15ac72f3ef7a964f2d4b486a5a9439 Mon Sep 17 00:00:00 2001 From: miloyip Date: Tue, 14 Apr 2015 11:40:09 +0800 Subject: [PATCH 33/64] Ignore files for coverage --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c64d62b..be06f35 100644 --- a/.travis.yml +++ b/.travis.yml @@ -48,4 +48,4 @@ script: - make travis_doc after_success: - - coveralls -r .. --gcov-options '\-lp' -e thirdparty -e example -e test -e build/CMakeFiles -e include/rapidjson/msinttypes + - coveralls -r .. --gcov-options '\-lp' -e thirdparty -e example -e test -e build/CMakeFiles -e include/rapidjson/msinttypes -e include/rapidjson/internal/meta.h -e include/rapidjson/error/en.h From 3a92374011bb7e7b8f1dac3819d7a0e0cfc3aebc Mon Sep 17 00:00:00 2001 From: miloyip Date: Tue, 14 Apr 2015 12:01:41 +0800 Subject: [PATCH 34/64] Try turning on slow test on number parsing --- test/unittest/readertest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unittest/readertest.cpp b/test/unittest/readertest.cpp index f5f6cce..38abe68 100644 --- a/test/unittest/readertest.cpp +++ b/test/unittest/readertest.cpp @@ -263,7 +263,7 @@ static void TestParseDouble() { TEST_DOUBLE(fullPrecision, n1e308, 1E308); } -#if 0 // Very slow +#if 1 // Very slow static const unsigned count = 10000000; // Random test for double { From 4cc62876aee842156c6b2e550dd4d70599a62b0e Mon Sep 17 00:00:00 2001 From: miloyip Date: Tue, 14 Apr 2015 13:22:39 +0800 Subject: [PATCH 35/64] Add some parsing number tests --- test/unittest/readertest.cpp | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/test/unittest/readertest.cpp b/test/unittest/readertest.cpp index 38abe68..6115609 100644 --- a/test/unittest/readertest.cpp +++ b/test/unittest/readertest.cpp @@ -254,6 +254,32 @@ static void TestParseDouble() { TEST_DOUBLE(fullPrecision, "1.00000000000000011102230246251565404236316680908203124", 1.0); // previous double TEST_DOUBLE(fullPrecision, "1.00000000000000011102230246251565404236316680908203126", 1.00000000000000022); // next double + // Numbers from https://github.com/floitsch/double-conversion/blob/master/test/cctest/test-strtod.cc + + TEST_DOUBLE(fullPrecision, "72057594037927928.0", 72057594037927928.0); + TEST_DOUBLE(fullPrecision, "72057594037927936.0", 72057594037927936.0); + TEST_DOUBLE(fullPrecision, "72057594037927932.0", 72057594037927936.0); + TEST_DOUBLE(fullPrecision, "7205759403792793199999e-5", 72057594037927928.0); + TEST_DOUBLE(fullPrecision, "7205759403792793200001e-5", 72057594037927936.0); + + TEST_DOUBLE(fullPrecision, "9223372036854774784.0", 9223372036854774784.0); + TEST_DOUBLE(fullPrecision, "9223372036854775808.0", 9223372036854775808.0); + TEST_DOUBLE(fullPrecision, "9223372036854775296.0", 9223372036854775808.0); + TEST_DOUBLE(fullPrecision, "922337203685477529599999e-5", 9223372036854774784.0); + TEST_DOUBLE(fullPrecision, "922337203685477529600001e-5", 9223372036854775808.0); + + TEST_DOUBLE(fullPrecision, "10141204801825834086073718800384", 10141204801825834086073718800384.0); + TEST_DOUBLE(fullPrecision, "10141204801825835211973625643008", 10141204801825835211973625643008.0); + TEST_DOUBLE(fullPrecision, "10141204801825834649023672221696", 10141204801825835211973625643008.0); + TEST_DOUBLE(fullPrecision, "1014120480182583464902367222169599999e-5", 10141204801825834086073718800384.0); + TEST_DOUBLE(fullPrecision, "1014120480182583464902367222169600001e-5", 10141204801825835211973625643008.0); + + TEST_DOUBLE(fullPrecision, "5708990770823838890407843763683279797179383808", 5708990770823838890407843763683279797179383808.0); + TEST_DOUBLE(fullPrecision, "5708990770823839524233143877797980545530986496", 5708990770823839524233143877797980545530986496.0); + TEST_DOUBLE(fullPrecision, "5708990770823839207320493820740630171355185152", 5708990770823839524233143877797980545530986496.0); + TEST_DOUBLE(fullPrecision, "5708990770823839207320493820740630171355185151999e-3", 5708990770823838890407843763683279797179383808.0); + TEST_DOUBLE(fullPrecision, "5708990770823839207320493820740630171355185152001e-3", 5708990770823839524233143877797980545530986496.0); + { char n1e308[310]; // '1' followed by 308 '0' n1e308[0] = '1'; @@ -263,7 +289,7 @@ static void TestParseDouble() { TEST_DOUBLE(fullPrecision, n1e308, 1E308); } -#if 1 // Very slow +#if 0 // Very slow static const unsigned count = 10000000; // Random test for double { From 4824f12efbf01af72b8cb6fc96fae7b097b73015 Mon Sep 17 00:00:00 2001 From: miloyip Date: Tue, 14 Apr 2015 13:59:05 +0800 Subject: [PATCH 36/64] Fixed a bug in trimming long number sequence --- include/rapidjson/internal/strtod.h | 4 +++- test/unittest/readertest.cpp | 13 +++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/include/rapidjson/internal/strtod.h b/include/rapidjson/internal/strtod.h index 805c752..980b2c5 100644 --- a/include/rapidjson/internal/strtod.h +++ b/include/rapidjson/internal/strtod.h @@ -258,7 +258,9 @@ inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t // Trim right-most digits const int kMaxDecimalDigit = 780; if ((int)length > kMaxDecimalDigit) { - exp += (int(length) - kMaxDecimalDigit); + int delta = (int(length) - kMaxDecimalDigit); + exp += delta; + decimalPosition -= delta; length = kMaxDecimalDigit; } diff --git a/test/unittest/readertest.cpp b/test/unittest/readertest.cpp index 6115609..bccfbd6 100644 --- a/test/unittest/readertest.cpp +++ b/test/unittest/readertest.cpp @@ -289,6 +289,19 @@ static void TestParseDouble() { TEST_DOUBLE(fullPrecision, n1e308, 1E308); } + // Cover trimming + TEST_DOUBLE(fullPrecision, +"2.22507385850720113605740979670913197593481954635164564802342610972482222202107694551652952390813508" +"7914149158913039621106870086438694594645527657207407820621743379988141063267329253552286881372149012" +"9811224514518898490572223072852551331557550159143974763979834118019993239625482890171070818506906306" +"6665599493827577257201576306269066333264756530000924588831643303777979186961204949739037782970490505" +"1080609940730262937128958950003583799967207254304360284078895771796150945516748243471030702609144621" +"5722898802581825451803257070188608721131280795122334262883686223215037756666225039825343359745688844" +"2390026549819838548794829220689472168983109969836584681402285424333066033985088644580400103493397042" +"7567186443383770486037861622771738545623065874679014086723327636718751234567890123456789012345678901" +"e-308", + 2.2250738585072014e-308); + #if 0 // Very slow static const unsigned count = 10000000; // Random test for double From 3621235cd0b4b75bb72293af077a0322df6d27d3 Mon Sep 17 00:00:00 2001 From: miloyip Date: Tue, 14 Apr 2015 14:52:42 +0800 Subject: [PATCH 37/64] Improve dtoa coverage --- test/unittest/writertest.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/unittest/writertest.cpp b/test/unittest/writertest.cpp index d8193ca..f6e023a 100644 --- a/test/unittest/writertest.cpp +++ b/test/unittest/writertest.cpp @@ -103,6 +103,7 @@ TEST(Writer, String) { TEST(Writer, Double) { TEST_ROUNDTRIP("[1.2345,1.2345678,0.123456789012,1234567.8]"); TEST_ROUNDTRIP("[-0.0]"); // Issue #289 + TEST_ROUNDTRIP("1e30"); } TEST(Writer, Transcode) { From 4be4857a19d48430fc32b9939cc4ce30403fe522 Mon Sep 17 00:00:00 2001 From: miloyip Date: Tue, 14 Apr 2015 14:58:48 +0800 Subject: [PATCH 38/64] Remove ununused BigInteger::FullAdd() --- include/rapidjson/internal/biginteger.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/include/rapidjson/internal/biginteger.h b/include/rapidjson/internal/biginteger.h index e4703d6..b088204 100755 --- a/include/rapidjson/internal/biginteger.h +++ b/include/rapidjson/internal/biginteger.h @@ -269,12 +269,6 @@ private: #endif } - static Type FullAdd(Type a, Type b, bool inCarry, bool* outCarry) { - Type c = a + b + (inCarry ? 1 : 0); - *outCarry = c < a; - return c; - } - static const size_t kBitCount = 3328; // 64bit * 54 > 10^1000 static const size_t kCapacity = kBitCount / sizeof(Type); static const size_t kTypeBit = sizeof(Type) * 8; From 872aba660cb1f22b8871ff5a28e8560aac3fbfa7 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Tue, 14 Apr 2015 21:08:33 +0800 Subject: [PATCH 39/64] Improve coverage of encoded streams --- include/rapidjson/encodedstream.h | 7 +++--- test/unittest/encodedstreamtest.cpp | 34 +++++++++++++++++++---------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/include/rapidjson/encodedstream.h b/include/rapidjson/encodedstream.h index f2f3ea3..25936d2 100644 --- a/include/rapidjson/encodedstream.h +++ b/include/rapidjson/encodedstream.h @@ -109,6 +109,7 @@ public: \param type UTF encoding type if it is not detected from the stream. */ AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) { + RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE); DetectType(); static const TakeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Take) }; takeFunc_ = f[type_]; @@ -189,8 +190,6 @@ private: case kUTF32BE: RAPIDJSON_ASSERT(sizeof(Ch) >= 4); break; - default: - RAPIDJSON_ASSERT(false); // Invalid type } } @@ -220,6 +219,8 @@ public: \param putBOM Whether to write BOM at the beginning of the stream. */ AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) { + RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE); + // RUntime check whether the size of character type is sufficient. It only perform checks with assertion. switch (type_) { case kUTF16LE: @@ -233,8 +234,6 @@ public: case kUTF8: // Do nothing break; - default: - RAPIDJSON_ASSERT(false); // Invalid UTFType } static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) }; diff --git a/test/unittest/encodedstreamtest.cpp b/test/unittest/encodedstreamtest.cpp index c2920a0..af2171a 100644 --- a/test/unittest/encodedstreamtest.cpp +++ b/test/unittest/encodedstreamtest.cpp @@ -119,10 +119,11 @@ protected: } EXPECT_EQ('\0', s.Peek()); free(data); + EXPECT_EQ(size, eis.Tell()); } } - void TestAutoUTFInputStream(const char *filename) { + void TestAutoUTFInputStream(const char *filename, bool expectHasBOM) { // Test FileReadStream { char buffer[16]; @@ -130,6 +131,7 @@ protected: ASSERT_TRUE(fp != 0); FileReadStream fs(fp, buffer, sizeof(buffer)); AutoUTFInputStream eis(fs); + EXPECT_EQ(expectHasBOM, eis.HasBOM()); StringStream s(json_); while (eis.Peek() != '\0') { unsigned expected, actual; @@ -147,6 +149,7 @@ protected: char* data = ReadFile(filename, true, &size); MemoryStream ms(data, size); AutoUTFInputStream eis(ms); + EXPECT_EQ(expectHasBOM, eis.HasBOM()); StringStream s(json_); while (eis.Peek() != '\0') { @@ -264,16 +267,25 @@ TEST_F(EncodedStreamTest, EncodedInputStream) { } TEST_F(EncodedStreamTest, AutoUTFInputStream) { - TestAutoUTFInputStream("utf8.json"); - TestAutoUTFInputStream("utf8bom.json"); - TestAutoUTFInputStream("utf16le.json"); - TestAutoUTFInputStream("utf16lebom.json"); - TestAutoUTFInputStream("utf16be.json"); - TestAutoUTFInputStream("utf16bebom.json"); - TestAutoUTFInputStream("utf32le.json"); - TestAutoUTFInputStream("utf32lebom.json"); - TestAutoUTFInputStream("utf32be.json"); - TestAutoUTFInputStream("utf32bebom.json"); + TestAutoUTFInputStream("utf8.json", false); + TestAutoUTFInputStream("utf8bom.json", true); + TestAutoUTFInputStream("utf16le.json", false); + TestAutoUTFInputStream("utf16lebom.json",true); + TestAutoUTFInputStream("utf16be.json", false); + TestAutoUTFInputStream("utf16bebom.json",true); + TestAutoUTFInputStream("utf32le.json", false); + TestAutoUTFInputStream("utf32lebom.json",true); + TestAutoUTFInputStream("utf32be.json", false); + TestAutoUTFInputStream("utf32bebom.json", true); + + { + // Auto detection fail, use user defined UTF type + const char json[] = "{}"; + MemoryStream ms(json, sizeof(json)); + AutoUTFInputStream eis(ms, kUTF8); + EXPECT_FALSE(eis.HasBOM()); + EXPECT_EQ(kUTF8, eis.GetType()); + } } TEST_F(EncodedStreamTest, EncodedOutputStream) { From 84e57412047aa214695dcb4a8d300b1e5a6a2034 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Tue, 14 Apr 2015 21:23:44 +0800 Subject: [PATCH 40/64] Fix gcc warning --- include/rapidjson/encodedstream.h | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/include/rapidjson/encodedstream.h b/include/rapidjson/encodedstream.h index 25936d2..f2cfd61 100644 --- a/include/rapidjson/encodedstream.h +++ b/include/rapidjson/encodedstream.h @@ -178,19 +178,10 @@ private: } // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. - switch (type_) { - case kUTF8: - // Do nothing - break; - case kUTF16LE: - case kUTF16BE: + if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2); - break; - case kUTF32LE: - case kUTF32BE: + else if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4); - break; - } } typedef Ch (*TakeFunc)(InputByteStream& is); @@ -221,20 +212,11 @@ public: AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) { RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE); - // RUntime check whether the size of character type is sufficient. It only perform checks with assertion. - switch (type_) { - case kUTF16LE: - case kUTF16BE: + // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. + if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2); - break; - case kUTF32LE: - case kUTF32BE: + else if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4); - break; - case kUTF8: - // Do nothing - break; - } static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) }; putFunc_ = f[type_]; From afe2fbdc3fd908e8d673bdf2d44408744e61f723 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Tue, 14 Apr 2015 21:30:57 +0800 Subject: [PATCH 41/64] Fix the warnings again --- include/rapidjson/encodedstream.h | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/include/rapidjson/encodedstream.h b/include/rapidjson/encodedstream.h index f2cfd61..9a93b38 100644 --- a/include/rapidjson/encodedstream.h +++ b/include/rapidjson/encodedstream.h @@ -178,10 +178,8 @@ private: } // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. - if (type_ == kUTF16LE || type_ == kUTF16BE) - RAPIDJSON_ASSERT(sizeof(Ch) >= 2); - else if (type_ == kUTF32LE || type_ == kUTF32BE) - RAPIDJSON_ASSERT(sizeof(Ch) >= 4); + if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2); + if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4); } typedef Ch (*TakeFunc)(InputByteStream& is); @@ -213,10 +211,8 @@ public: RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE); // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. - if (type_ == kUTF16LE || type_ == kUTF16BE) - RAPIDJSON_ASSERT(sizeof(Ch) >= 2); - else if (type_ == kUTF32LE || type_ == kUTF32BE) - RAPIDJSON_ASSERT(sizeof(Ch) >= 4); + if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2); + if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4); static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) }; putFunc_ = f[type_]; From f8909e875b367ee56003fd23fca69288e8f1e7e3 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Tue, 14 Apr 2015 21:50:13 +0800 Subject: [PATCH 42/64] Improve coverage of encoded streams --- test/unittest/encodedstreamtest.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/unittest/encodedstreamtest.cpp b/test/unittest/encodedstreamtest.cpp index af2171a..5affb5d 100644 --- a/test/unittest/encodedstreamtest.cpp +++ b/test/unittest/encodedstreamtest.cpp @@ -160,6 +160,7 @@ protected: } EXPECT_EQ('\0', s.Peek()); free(data); + EXPECT_EQ(size, eis.Tell()); } } @@ -280,7 +281,7 @@ TEST_F(EncodedStreamTest, AutoUTFInputStream) { { // Auto detection fail, use user defined UTF type - const char json[] = "{}"; + const char json[] = "{ }"; MemoryStream ms(json, sizeof(json)); AutoUTFInputStream eis(ms, kUTF8); EXPECT_FALSE(eis.HasBOM()); From 5f5758bc228a4831b94416b2ea41a103e613d377 Mon Sep 17 00:00:00 2001 From: miloyip Date: Tue, 14 Apr 2015 23:26:00 +0800 Subject: [PATCH 43/64] Try coverage on gcc/release also --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index be06f35..f26c944 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,7 +21,7 @@ before_install: - sudo apt-get install -qq cmake valgrind - sudo apt-get --no-install-recommends install doxygen # Don't install LaTeX stuffs - if [ "$ARCH" = "x86" ]; then sudo apt-get install -qq g++-multilib libc6-dbg:i386; fi - - if [ "$CC" = "gcc" ] && [ "$CONF" = "debug" ]; then sudo pip install cpp-coveralls; export GCOV_FLAGS='--coverage'; fi + - if [ "$CC" = "gcc" ]; then sudo pip install cpp-coveralls; export GCOV_FLAGS='--coverage'; fi install: true From 5b89f331c580f41132c359c2b4b2715aa1a83d7e Mon Sep 17 00:00:00 2001 From: miloyip Date: Wed, 15 Apr 2015 00:18:22 +0800 Subject: [PATCH 44/64] Add more test numbers for writer --- test/unittest/writertest.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/test/unittest/writertest.cpp b/test/unittest/writertest.cpp index f6e023a..85d533d 100644 --- a/test/unittest/writertest.cpp +++ b/test/unittest/writertest.cpp @@ -46,7 +46,7 @@ TEST(Writer, Compact) { StringBuffer buffer; \ Writer writer(buffer); \ Reader reader; \ - reader.Parse<0>(s, writer); \ + reader.Parse(s, writer); \ EXPECT_STREQ(json, buffer.GetString()); \ EXPECT_TRUE(writer.IsComplete()); \ } @@ -102,8 +102,15 @@ TEST(Writer, String) { TEST(Writer, Double) { TEST_ROUNDTRIP("[1.2345,1.2345678,0.123456789012,1234567.8]"); - TEST_ROUNDTRIP("[-0.0]"); // Issue #289 + TEST_ROUNDTRIP("0.0"); + TEST_ROUNDTRIP("-0.0"); // Issue #289 TEST_ROUNDTRIP("1e30"); + TEST_ROUNDTRIP("1.0"); + TEST_ROUNDTRIP("5e-324"); // Min subnormal positive double + TEST_ROUNDTRIP("2.225073858507201e-308"); // Max subnormal positive double + TEST_ROUNDTRIP("2.2250738585072014e-308"); // Min normal positive double + TEST_ROUNDTRIP("1.7976931348623157e308"); // Max double + } TEST(Writer, Transcode) { From 37d820a13e66e57a6ebb9444593485614ed5e982 Mon Sep 17 00:00:00 2001 From: miloyip Date: Wed, 15 Apr 2015 10:58:49 +0800 Subject: [PATCH 45/64] Remove unused code paths in double conversions --- include/rapidjson/internal/diyfp.h | 22 +++--------------- include/rapidjson/internal/dtoa.h | 19 +++++----------- include/rapidjson/internal/ieee754.h | 8 ------- include/rapidjson/internal/strtod.h | 34 ++++++++++++---------------- 4 files changed, 23 insertions(+), 60 deletions(-) diff --git a/include/rapidjson/internal/diyfp.h b/include/rapidjson/internal/diyfp.h index 0b098af..de3d1f0 100644 --- a/include/rapidjson/internal/diyfp.h +++ b/include/rapidjson/internal/diyfp.h @@ -135,25 +135,9 @@ struct DiyFp { double d; uint64_t u64; }u; - uint64_t significand = f; - int exponent = e; - while (significand > kDpHiddenBit + kDpSignificandMask) { - significand >>= 1; - exponent++; - } - while (exponent > kDpDenormalExponent && (significand & kDpHiddenBit) == 0) { - significand <<= 1; - exponent--; - } - if (exponent >= kDpMaxExponent) { - u.u64 = kDpExponentMask; // Infinity - return u.d; - } - else if (exponent < kDpDenormalExponent) - return 0.0; - const uint64_t be = (exponent == kDpDenormalExponent && (significand & kDpHiddenBit) == 0) ? 0 : - static_cast(exponent + kDpExponentBias); - u.u64 = (significand & kDpSignificandMask) | (be << kDpSignificandSize); + const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 : + static_cast(e + kDpExponentBias); + u.u64 = (f & kDpSignificandMask) | (be << kDpSignificandSize); return u.d; } diff --git a/include/rapidjson/internal/dtoa.h b/include/rapidjson/internal/dtoa.h index aec6bcd..55dae8a 100644 --- a/include/rapidjson/internal/dtoa.h +++ b/include/rapidjson/internal/dtoa.h @@ -50,8 +50,10 @@ inline unsigned CountDecimalDigit32(uint32_t n) { if (n < 1000000) return 6; if (n < 10000000) return 7; if (n < 100000000) return 8; - if (n < 1000000000) return 9; - return 10; + // Will not reach 10 digits in DigitGen() + //if (n < 1000000000) return 9; + //return 10; + return 9; } inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) { @@ -60,13 +62,12 @@ inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buff const DiyFp wp_w = Mp - W; uint32_t p1 = static_cast(Mp.f >> -one.e); uint64_t p2 = Mp.f & (one.f - 1); - int kappa = CountDecimalDigit32(p1); + int kappa = CountDecimalDigit32(p1); // kappa in [0, 9] *len = 0; while (kappa > 0) { - uint32_t d; + uint32_t d = 0; switch (kappa) { - case 10: d = p1 / 1000000000; p1 %= 1000000000; break; case 9: d = p1 / 100000000; p1 %= 100000000; break; case 8: d = p1 / 10000000; p1 %= 10000000; break; case 7: d = p1 / 1000000; p1 %= 1000000; break; @@ -76,14 +77,6 @@ inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buff case 3: d = p1 / 100; p1 %= 100; break; case 2: d = p1 / 10; p1 %= 10; break; case 1: d = p1; p1 = 0; break; - default: -#if defined(_MSC_VER) - __assume(0); -#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) - __builtin_unreachable(); -#else - d = 0; -#endif } if (d || *len) buffer[(*len)++] = static_cast('0' + static_cast(d)); diff --git a/include/rapidjson/internal/ieee754.h b/include/rapidjson/internal/ieee754.h index 152be8f..9a82880 100644 --- a/include/rapidjson/internal/ieee754.h +++ b/include/rapidjson/internal/ieee754.h @@ -34,14 +34,6 @@ public: return Double(u + 1).Value(); } - double PreviousPositiveDouble() const { - RAPIDJSON_ASSERT(!Sign()); - if (IsZero()) - return 0.0; - else - return Double(u - 1).Value(); - } - bool Sign() const { return (u & kSignMask) != 0; } uint64_t Significand() const { return u & kSignificandMask; } int Exponent() const { return ((u & kExponentMask) >> kSignificandSize) - kExponentBias; } diff --git a/include/rapidjson/internal/strtod.h b/include/rapidjson/internal/strtod.h index 980b2c5..6644f37 100644 --- a/include/rapidjson/internal/strtod.h +++ b/include/rapidjson/internal/strtod.h @@ -52,7 +52,7 @@ inline T Min3(T a, T b, T c) { return m; } -inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp, bool* adjustToNegative) { +inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp) { const Double db(b); const uint64_t bInt = db.IntegerSignificand(); const int bExp = db.IntegerExponent(); @@ -104,12 +104,12 @@ inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp, bool* adj hS.MultiplyPow5(hS_Exp5) <<= hS_Exp2; BigInteger delta(0); - *adjustToNegative = dS.Difference(bS, &delta); + bool adjustToNegative = dS.Difference(bS, &delta); int cmp = delta.Compare(hS); // If delta is within 1/2 ULP, check for special case when significand is power of two. // In this case, need to compare with 1/2h in the lower bound. - if (cmp < 0 && *adjustToNegative && // within and dS < bS + if (cmp < 0 && adjustToNegative && // within and dS < bS db.IsNormal() && (bInt & (bInt - 1)) == 0 && // Power of 2 db.Uint64Value() != RAPIDJSON_UINT64_C2(0x00100000, 0x00000000)) // minimum normal number must not do this { @@ -213,24 +213,18 @@ inline double StrtodBigInteger(double approx, const char* decimals, size_t lengt const BigInteger dInt(decimals, length); const int dExp = (int)decimalPosition - (int)length + exp; Double a(approx); - for (int i = 0; i < 10; i++) { - bool adjustToNegative; - int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp, &adjustToNegative); - if (cmp < 0) - return a.Value(); // within half ULP - else if (cmp == 0) { - // Round towards even - if (a.Significand() & 1) - return adjustToNegative ? a.PreviousPositiveDouble() : a.NextPositiveDouble(); - else - return a.Value(); - } - else // adjustment - a = adjustToNegative ? a.PreviousPositiveDouble() : a.NextPositiveDouble(); + int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp); + if (cmp < 0) + return a.Value(); // within half ULP + else if (cmp == 0) { + // Round towards even + if (a.Significand() & 1) + return a.NextPositiveDouble(); + else + return a.Value(); } - - // This should not happen, but in case there is really a bug, break the infinite-loop - return a.Value(); + else // adjustment + return a.NextPositiveDouble(); } inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t length, size_t decimalPosition, int exp) { From 402d75a80100dc01ae87e152ab47c2d120cd4cf9 Mon Sep 17 00:00:00 2001 From: miloyip Date: Wed, 15 Apr 2015 11:07:13 +0800 Subject: [PATCH 46/64] Fix gcc warning --- include/rapidjson/internal/dtoa.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/rapidjson/internal/dtoa.h b/include/rapidjson/internal/dtoa.h index 55dae8a..2d8d2e4 100644 --- a/include/rapidjson/internal/dtoa.h +++ b/include/rapidjson/internal/dtoa.h @@ -77,6 +77,7 @@ inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buff case 3: d = p1 / 100; p1 %= 100; break; case 2: d = p1 / 10; p1 %= 10; break; case 1: d = p1; p1 = 0; break; + default:; } if (d || *len) buffer[(*len)++] = static_cast('0' + static_cast(d)); From 2689cc4974f314b99716b6fbad137e7e1bcecfdb Mon Sep 17 00:00:00 2001 From: miloyip Date: Wed, 15 Apr 2015 11:52:24 +0800 Subject: [PATCH 47/64] Remove more unused code paths in double conversions --- include/rapidjson/internal/biginteger.h | 7 ++----- include/rapidjson/internal/strtod.h | 14 ++------------ 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/include/rapidjson/internal/biginteger.h b/include/rapidjson/internal/biginteger.h index b088204..5baba9b 100755 --- a/include/rapidjson/internal/biginteger.h +++ b/include/rapidjson/internal/biginteger.h @@ -172,13 +172,10 @@ public: } // Compute absolute difference of this and rhs. - // Return false if this < rhs + // Assume this != rhs bool Difference(const BigInteger& rhs, BigInteger* out) const { int cmp = Compare(rhs); - if (cmp == 0) { - *out = BigInteger(0); - return false; - } + RAPIDJSON_ASSERT(cmp != 0); const BigInteger *a, *b; // Makes a > b bool ret; if (cmp < 0) { a = &rhs; b = this; ret = true; } diff --git a/include/rapidjson/internal/strtod.h b/include/rapidjson/internal/strtod.h index 6644f37..fa85286 100644 --- a/include/rapidjson/internal/strtod.h +++ b/include/rapidjson/internal/strtod.h @@ -104,19 +104,9 @@ inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp) { hS.MultiplyPow5(hS_Exp5) <<= hS_Exp2; BigInteger delta(0); - bool adjustToNegative = dS.Difference(bS, &delta); + dS.Difference(bS, &delta); - int cmp = delta.Compare(hS); - // If delta is within 1/2 ULP, check for special case when significand is power of two. - // In this case, need to compare with 1/2h in the lower bound. - if (cmp < 0 && adjustToNegative && // within and dS < bS - db.IsNormal() && (bInt & (bInt - 1)) == 0 && // Power of 2 - db.Uint64Value() != RAPIDJSON_UINT64_C2(0x00100000, 0x00000000)) // minimum normal number must not do this - { - delta <<= 1; - return delta.Compare(hS); - } - return cmp; + return delta.Compare(hS); } inline bool StrtodFast(double d, int p, double* result) { From 76d67b7eae55f48223d815184348bcdb0701f6ad Mon Sep 17 00:00:00 2001 From: miloyip Date: Wed, 15 Apr 2015 12:16:16 +0800 Subject: [PATCH 48/64] Improves coverage of Value::Accept() --- include/rapidjson/document.h | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index e436b8a..9ef0d91 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -1459,17 +1459,14 @@ public: case kStringType: return handler.String(GetString(), GetStringLength(), (flags_ & kCopyFlag) != 0); - case kNumberType: + default: + RAPIDJSON_ASSERT(GetType() == kNumberType); if (IsInt()) return handler.Int(data_.n.i.i); else if (IsUint()) return handler.Uint(data_.n.u.u); else if (IsInt64()) return handler.Int64(data_.n.i64); else if (IsUint64()) return handler.Uint64(data_.n.u64); else return handler.Double(data_.n.d); - - default: - RAPIDJSON_ASSERT(false); } - return false; } private: From c69ff41fc26731bd5b83ce5f242184eea0358bc7 Mon Sep 17 00:00:00 2001 From: miloyip Date: Wed, 15 Apr 2015 14:23:00 +0800 Subject: [PATCH 49/64] Add tests for parsing number with exhaustive exponents and random signifcant --- test/unittest/readertest.cpp | 42 +++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/test/unittest/readertest.cpp b/test/unittest/readertest.cpp index bccfbd6..7c70bc9 100644 --- a/test/unittest/readertest.cpp +++ b/test/unittest/readertest.cpp @@ -302,28 +302,40 @@ static void TestParseDouble() { "e-308", 2.2250738585072014e-308); -#if 0 // Very slow - static const unsigned count = 10000000; - // Random test for double { + static const unsigned count = 1000; // Tested with 1000000 locally Random r; + Reader reader; // Reusing reader to prevent heap allocation - for (unsigned i = 0; i < count; i++) { - internal::Double d; - do { + // Exhaustively test different exponents with random significant + for (uint64_t exp = 0; exp < 2047; exp++) { + ; + for (unsigned i = 0; i < count; i++) { // Need to call r() in two statements for cross-platform coherent sequence. - uint64_t u = uint64_t(r()) << 32; + uint64_t u = (exp << 52) | uint64_t(r() & 0x000FFFFF) << 32; u |= uint64_t(r()); - d = internal::Double(u); - } while (d.IsNan() || d.IsInf()/* || !d.IsNormal()*/); // Also work for subnormal now + internal::Double d = internal::Double(u); - char buffer[32]; - *internal::dtoa(d.Value(), buffer) = '\0'; - TEST_DOUBLE(fullPrecision, buffer, d.Value()); + char buffer[32]; + *internal::dtoa(d.Value(), buffer) = '\0'; + + StringStream s(buffer); + ParseDoubleHandler h; + ASSERT_EQ(kParseErrorNone, reader.Parse(s, h).Code()); + EXPECT_EQ(1u, h.step_); + internal::Double a(h.actual_); + if (fullPrecision) { + EXPECT_EQ(d.Uint64Value(), a.Uint64Value()); + if (d.Uint64Value() != a.Uint64Value()) + printf(" String: %sn Actual: %.17gnExpected: %.17gn", buffer, h.actual_, d.Value()); + } + else { + EXPECT_EQ(d.Sign(), a.Sign()); /* for 0.0 != -0.0 */ + EXPECT_DOUBLE_EQ(d.Value(), h.actual_); + } + } } } -#endif - #undef TEST_DOUBLE } @@ -651,7 +663,7 @@ TEST(Reader, ParseString_Error) { TEST_STRINGENCODING_ERROR(UTF32<>, UTF8<>, unsigned, ARRAY('[', '\"', 0x110000, '\"', ']', '\0')); // Malform ASCII sequence - TEST_STRINGENCODING_ERROR(ASCII<>, UTF8<>, char, ARRAY('[', '\"', 0x80, '\"', ']', '\0')); + TEST_STRINGENCODING_ERROR(ASCII<>, UTF8<>, char, ARRAY('[', '\"', char(0x80), '\"', ']', '\0')); #undef ARRAY #undef TEST_STRINGARRAY_ERROR From 631302e68e888f77fbbf22123137b575d9271fa0 Mon Sep 17 00:00:00 2001 From: miloyip Date: Wed, 15 Apr 2015 14:41:33 +0800 Subject: [PATCH 50/64] Reduce random test iterations to speedup travis --- test/unittest/readertest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unittest/readertest.cpp b/test/unittest/readertest.cpp index 7c70bc9..1ff90ca 100644 --- a/test/unittest/readertest.cpp +++ b/test/unittest/readertest.cpp @@ -303,7 +303,7 @@ static void TestParseDouble() { 2.2250738585072014e-308); { - static const unsigned count = 1000; // Tested with 1000000 locally + static const unsigned count = 100; // Tested with 1000000 locally Random r; Reader reader; // Reusing reader to prevent heap allocation From 7cb031cc03d510d05f2d7ea4348503e81bc6cf25 Mon Sep 17 00:00:00 2001 From: thebusytypist Date: Wed, 15 Apr 2015 14:45:07 +0800 Subject: [PATCH 51/64] Add unittests for parsing root JSON value other than array and object. --- test/unittest/readertest.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/unittest/readertest.cpp b/test/unittest/readertest.cpp index bccfbd6..b9a5adc 100644 --- a/test/unittest/readertest.cpp +++ b/test/unittest/readertest.cpp @@ -1041,6 +1041,15 @@ TEST(Reader, IterativeParsing_ErrorHandling) { TESTERRORHANDLING("{\"a\"}", kParseErrorObjectMissColon, 4u); TESTERRORHANDLING("{\"a\": 1", kParseErrorObjectMissCommaOrCurlyBracket, 7u); TESTERRORHANDLING("[1 2 3]", kParseErrorArrayMissCommaOrSquareBracket, 3u); + + // Any JSON value can be a valid root element in RFC7159. + TESTERRORHANDLING("\"ab", kParseErrorStringMissQuotationMark, 2u); + TESTERRORHANDLING("truE", kParseErrorValueInvalid, 3u); + TESTERRORHANDLING("False", kParseErrorValueInvalid, 0u); + TESTERRORHANDLING("true, false", kParseErrorDocumentRootNotSingular, 4u); + TESTERRORHANDLING("false, false", kParseErrorDocumentRootNotSingular, 5u); + TESTERRORHANDLING("nulL", kParseErrorValueInvalid, 3u); + TESTERRORHANDLING("null , null", kParseErrorDocumentRootNotSingular, 5u); } template > From 857674737345c5aa2b4251480455578f1787d62f Mon Sep 17 00:00:00 2001 From: thebusytypist Date: Wed, 15 Apr 2015 14:51:48 +0800 Subject: [PATCH 52/64] Add unittest for state transition to IterativeParsingMemberKeyState. --- test/unittest/readertest.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/unittest/readertest.cpp b/test/unittest/readertest.cpp index b9a5adc..942f8d6 100644 --- a/test/unittest/readertest.cpp +++ b/test/unittest/readertest.cpp @@ -1041,6 +1041,7 @@ TEST(Reader, IterativeParsing_ErrorHandling) { TESTERRORHANDLING("{\"a\"}", kParseErrorObjectMissColon, 4u); TESTERRORHANDLING("{\"a\": 1", kParseErrorObjectMissCommaOrCurlyBracket, 7u); TESTERRORHANDLING("[1 2 3]", kParseErrorArrayMissCommaOrSquareBracket, 3u); + TESTERRORHANDLING("{\"a: 1", kParseErrorStringMissQuotationMark, 5u); // Any JSON value can be a valid root element in RFC7159. TESTERRORHANDLING("\"ab", kParseErrorStringMissQuotationMark, 2u); From 399333226b64f389e9358a07018c2798b91f7543 Mon Sep 17 00:00:00 2001 From: thebusytypist Date: Wed, 15 Apr 2015 14:54:44 +0800 Subject: [PATCH 53/64] Use assertion for impossible case(The Predict() can ensure the token is ColonToken, otherwise it would be marked as Error state. So there is no need to check ColonToken again). --- include/rapidjson/reader.h | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index d9da869..a2b6379 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -1273,12 +1273,9 @@ private: return dst; case IterativeParsingKeyValueDelimiterState: - if (token == ColonToken) { - is.Take(); - return dst; - } - else - return IterativeParsingErrorState; + RAPIDJSON_ASSERT(token == ColonToken); + is.Take(); + return dst; case IterativeParsingMemberValueState: // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. From 5ae48a0380b3377d43f1cb06d6061af796b5fa6d Mon Sep 17 00:00:00 2001 From: thebusytypist Date: Wed, 15 Apr 2015 15:21:42 +0800 Subject: [PATCH 54/64] Assert on impossible state transition in Transit(); Put the last case and all non-enumerated cases(also supply assertion for them) in for code coverage. --- include/rapidjson/reader.h | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index a2b6379..e0b9c8c 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -1228,13 +1228,6 @@ private: template RAPIDJSON_FORCEINLINE IterativeParsingState Transit(IterativeParsingState src, Token token, IterativeParsingState dst, InputStream& is, Handler& handler) { switch (dst) { - case IterativeParsingStartState: - RAPIDJSON_ASSERT(false); - return IterativeParsingErrorState; - - case IterativeParsingFinishState: - return dst; - case IterativeParsingErrorState: return dst; @@ -1350,17 +1343,25 @@ private: } } - case IterativeParsingValueState: + default: + // This branch is for IterativeParsingValueState actually. + // Use `default:` rather than + // `case IterativeParsingValueState:` is for code coverage. + + // The IterativeParsingStartState is not enumerated in this switch-case. + // It is impossible for that case. And it can be caught by following assertion. + + // The IterativeParsingFinishState is not enumerated in this switch-case either. + // It is a "derivative" state which cannot triggered from Predict() directly. + // Therefore it cannot happen here. And it can be caught by following assertion. + RAPIDJSON_ASSERT(dst == IterativeParsingValueState); + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. ParseValue(is, handler); if (HasParseError()) { return IterativeParsingErrorState; } return IterativeParsingFinishState; - - default: - RAPIDJSON_ASSERT(false); - return IterativeParsingErrorState; } } From 0d28bb13c7833a8cdf5c946a9ab1622eed528492 Mon Sep 17 00:00:00 2001 From: thebusytypist Date: Wed, 15 Apr 2015 15:46:31 +0800 Subject: [PATCH 55/64] Add a missing error handling check(a single number as JSON root). --- test/unittest/readertest.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/unittest/readertest.cpp b/test/unittest/readertest.cpp index 942f8d6..11f3e78 100644 --- a/test/unittest/readertest.cpp +++ b/test/unittest/readertest.cpp @@ -1051,6 +1051,7 @@ TEST(Reader, IterativeParsing_ErrorHandling) { TESTERRORHANDLING("false, false", kParseErrorDocumentRootNotSingular, 5u); TESTERRORHANDLING("nulL", kParseErrorValueInvalid, 3u); TESTERRORHANDLING("null , null", kParseErrorDocumentRootNotSingular, 5u); + TESTERRORHANDLING("1a", kParseErrorDocumentRootNotSingular, 1u); } template > From 6ef29ff431ece1a7a63283bfc568f919dc78b311 Mon Sep 17 00:00:00 2001 From: thebusytypist Date: Wed, 15 Apr 2015 16:09:29 +0800 Subject: [PATCH 56/64] Fix warning about unused argument. --- include/rapidjson/reader.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index e0b9c8c..31a145f 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -1227,6 +1227,8 @@ private: // May return a new state on state pop. template RAPIDJSON_FORCEINLINE IterativeParsingState Transit(IterativeParsingState src, Token token, IterativeParsingState dst, InputStream& is, Handler& handler) { + (void)token; + switch (dst) { case IterativeParsingErrorState: return dst; From a32d8b765089cbfaf73cab0ca9ea695eb06e50ca Mon Sep 17 00:00:00 2001 From: miloyip Date: Wed, 15 Apr 2015 18:18:49 +0800 Subject: [PATCH 57/64] Add SIMD SkipWhitespace() unit test which don't run in Valgrind --- test/unittest/CMakeLists.txt | 4 ++- test/unittest/simdtest.cpp | 59 ++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 test/unittest/simdtest.cpp diff --git a/test/unittest/CMakeLists.txt b/test/unittest/CMakeLists.txt index 52d5acd..bcc16d9 100644 --- a/test/unittest/CMakeLists.txt +++ b/test/unittest/CMakeLists.txt @@ -10,6 +10,7 @@ set(UNITTEST_SOURCES namespacetest.cpp prettywritertest.cpp readertest.cpp + simdtest.cpp stringbuffertest.cpp strtodtest.cpp unittest.cpp @@ -36,8 +37,9 @@ add_test(NAME unittest WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/bin) if(NOT MSVC) + # Not running SIMD.* unit test cases for Valgrind add_test(NAME valgrind_unittest - COMMAND valgrind --leak-check=full --error-exitcode=1 ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unittest + COMMAND valgrind --leak-check=full --error-exitcode=1 ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unittest --gtest_filter=-SIMD.* WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/bin) if(CMAKE_BUILD_TYPE STREQUAL "Debug") diff --git a/test/unittest/simdtest.cpp b/test/unittest/simdtest.cpp new file mode 100644 index 0000000..42fb10f --- /dev/null +++ b/test/unittest/simdtest.cpp @@ -0,0 +1,59 @@ +// Copyright (C) 2011 Milo Yip +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Since Travis CI installs old Valgrind 3.7.0, which fails with some SSE4.2 +// The unit tests prefix with SIMD should be skipped by Valgrind test + +// __SSE2__ and __SSE4_2__ are recognized by gcc, clang, and the Intel compiler. +// We use -march=native with gmake to enable -msse2 and -msse4.2, if supported. +#if defined(__SSE4_2__) +# define RAPIDJSON_SSE42 +#elif defined(__SSE2__) +# define RAPIDJSON_SSE2 +#endif + +#include "unittest.h" + +#include "rapidjson/reader.h" + +using namespace rapidjson; + +#ifdef RAPIDJSON_SSE2 +#define SIMD_SUFFIX(name) name##_SSE2 +#elif defined(RAPIDJSON_SSE42) +#define SIMD_SUFFIX(name) name##_SSE42 +#else +#define SIMD_SUFFIX(name) name +#endif + +TEST(SIMD, SIMD_SUFFIX(SkipWhitespace)) { + char buffer[258]; + for (size_t i = 0; i < 256; i++) + buffer[i] = " \t\r\n"[i % 4]; + buffer[256] = 'X'; + buffer[257] = '\0'; + + // Try to start from different position, to test different memory alignments + for (size_t i = 0; i < 256; i++) { + StringStream s(buffer + i); + SkipWhitespace(s); + EXPECT_EQ('X', s.Peek()); + } +} From ee505261c13b5901a1bd63b2df781bd8f4eb5a49 Mon Sep 17 00:00:00 2001 From: miloyip Date: Wed, 15 Apr 2015 18:34:18 +0800 Subject: [PATCH 58/64] Try to use another namespace for SIMD version --- test/unittest/simdtest.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/unittest/simdtest.cpp b/test/unittest/simdtest.cpp index 42fb10f..ebbbead 100644 --- a/test/unittest/simdtest.cpp +++ b/test/unittest/simdtest.cpp @@ -29,11 +29,13 @@ # define RAPIDJSON_SSE2 #endif +#define RAPIDJSON_NAMESPACE rapidjson_simd + #include "unittest.h" #include "rapidjson/reader.h" -using namespace rapidjson; +using namespace rapidjson_simd; #ifdef RAPIDJSON_SSE2 #define SIMD_SUFFIX(name) name##_SSE2 From 998e76fecc9495a5738ce5747f41a28a68ce9bee Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Wed, 15 Apr 2015 20:38:46 +0800 Subject: [PATCH 59/64] Improves SkipWhitespace coverage --- test/unittest/simdtest.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/test/unittest/simdtest.cpp b/test/unittest/simdtest.cpp index ebbbead..66d1e8b 100644 --- a/test/unittest/simdtest.cpp +++ b/test/unittest/simdtest.cpp @@ -46,16 +46,18 @@ using namespace rapidjson_simd; #endif TEST(SIMD, SIMD_SUFFIX(SkipWhitespace)) { - char buffer[258]; + char buffer[257]; for (size_t i = 0; i < 256; i++) buffer[i] = " \t\r\n"[i % 4]; - buffer[256] = 'X'; - buffer[257] = '\0'; + for (size_t i = 0; i < 256; i += 15) + buffer[i] = 'X'; + buffer[256] = '\0'; - // Try to start from different position, to test different memory alignments - for (size_t i = 0; i < 256; i++) { - StringStream s(buffer + i); + StringStream s(buffer); + for(;;) { SkipWhitespace(s); - EXPECT_EQ('X', s.Peek()); + if (s.Peek() == '\0') + break; + EXPECT_EQ('X', s.Take()); } } From a81585b5e23fbb0e1831f9d81eba152571149c56 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Wed, 15 Apr 2015 20:51:36 +0800 Subject: [PATCH 60/64] Further improve SkipWhitespace coverage --- test/unittest/simdtest.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/unittest/simdtest.cpp b/test/unittest/simdtest.cpp index 66d1e8b..4f103ef 100644 --- a/test/unittest/simdtest.cpp +++ b/test/unittest/simdtest.cpp @@ -46,12 +46,12 @@ using namespace rapidjson_simd; #endif TEST(SIMD, SIMD_SUFFIX(SkipWhitespace)) { - char buffer[257]; - for (size_t i = 0; i < 256; i++) + char buffer[1025]; + for (size_t i = 0; i < 1024; i++) buffer[i] = " \t\r\n"[i % 4]; - for (size_t i = 0; i < 256; i += 15) + for (size_t i = 0; i < 1024; i += 31) buffer[i] = 'X'; - buffer[256] = '\0'; + buffer[1024] = '\0'; StringStream s(buffer); for(;;) { From 4d3c64acee7e07e94be5ba46407736d1dd2c8111 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Wed, 15 Apr 2015 21:07:30 +0800 Subject: [PATCH 61/64] Improves SkipWhitespace test --- test/unittest/simdtest.cpp | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/test/unittest/simdtest.cpp b/test/unittest/simdtest.cpp index 4f103ef..6b49034 100644 --- a/test/unittest/simdtest.cpp +++ b/test/unittest/simdtest.cpp @@ -46,18 +46,23 @@ using namespace rapidjson_simd; #endif TEST(SIMD, SIMD_SUFFIX(SkipWhitespace)) { - 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 += 31) - buffer[i] = 'X'; - buffer[1024] = '\0'; + for (int step = 1; step < 32; step++) { + char buffer[1025]; + for (size_t i = 0; i < 1024; i++) + buffer[i] = " \t\r\n"[i % 4]; + for (size_t i = 0; i < 1024; i += step) + buffer[i] = 'X'; + buffer[1024] = '\0'; - StringStream s(buffer); - for(;;) { - SkipWhitespace(s); - if (s.Peek() == '\0') - break; - EXPECT_EQ('X', s.Take()); + StringStream 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; + } } } From 8f2add7527c6035a719217c4dc01075392c4d3ed Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Wed, 15 Apr 2015 22:23:00 +0800 Subject: [PATCH 62/64] Not enforce force inline for debug configuration --- include/rapidjson/rapidjson.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/rapidjson/rapidjson.h b/include/rapidjson/rapidjson.h index b847ac7..602dabd 100644 --- a/include/rapidjson/rapidjson.h +++ b/include/rapidjson/rapidjson.h @@ -122,9 +122,9 @@ #ifndef RAPIDJSON_FORCEINLINE //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN -#ifdef _MSC_VER +#if defined(_MSC_VER) && !defined(NDEBUG) #define RAPIDJSON_FORCEINLINE __forceinline -#elif defined(__GNUC__) && __GNUC__ >= 4 +#elif defined(__GNUC__) && __GNUC__ >= 4 && !defined(NDEBUG) #define RAPIDJSON_FORCEINLINE __attribute__((always_inline)) #else #define RAPIDJSON_FORCEINLINE From 0571a211bdd33ba00b46e207695b359d38d9b1ab Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Wed, 15 Apr 2015 22:36:00 +0800 Subject: [PATCH 63/64] Cover SkipWhiteSpace for InsituStringStream --- test/unittest/simdtest.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/test/unittest/simdtest.cpp b/test/unittest/simdtest.cpp index 6b49034..217db07 100644 --- a/test/unittest/simdtest.cpp +++ b/test/unittest/simdtest.cpp @@ -45,7 +45,8 @@ using namespace rapidjson_simd; #define SIMD_SUFFIX(name) name #endif -TEST(SIMD, SIMD_SUFFIX(SkipWhitespace)) { +template +void TestSkipWhitespace() { for (int step = 1; step < 32; step++) { char buffer[1025]; for (size_t i = 0; i < 1024; i++) @@ -54,7 +55,7 @@ TEST(SIMD, SIMD_SUFFIX(SkipWhitespace)) { buffer[i] = 'X'; buffer[1024] = '\0'; - StringStream s(buffer); + StreamType s(buffer); size_t i = 0; for (;;) { SkipWhitespace(s); @@ -66,3 +67,8 @@ TEST(SIMD, SIMD_SUFFIX(SkipWhitespace)) { } } } + +TEST(SIMD, SIMD_SUFFIX(SkipWhitespace)) { + TestSkipWhitespace(); + TestSkipWhitespace(); +} From 0edf27fa0cb7a1b076f1f3ad96b39eeb2c9a337a Mon Sep 17 00:00:00 2001 From: miloyip Date: Wed, 15 Apr 2015 22:55:35 +0800 Subject: [PATCH 64/64] Only do coverage on gcc/debug --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f26c944..8c29fe5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,7 +21,7 @@ before_install: - sudo apt-get install -qq cmake valgrind - sudo apt-get --no-install-recommends install doxygen # Don't install LaTeX stuffs - if [ "$ARCH" = "x86" ]; then sudo apt-get install -qq g++-multilib libc6-dbg:i386; fi - - if [ "$CC" = "gcc" ]; then sudo pip install cpp-coveralls; export GCOV_FLAGS='--coverage'; fi + - if [ "$CC" = "gcc" ] && [ "CONF" = "debug" ]; then sudo pip install cpp-coveralls; export GCOV_FLAGS='--coverage'; fi install: true