From 0db257e9894b4ce54662ecbe7172ac07a7116c1a Mon Sep 17 00:00:00 2001 From: Dave Cunningham Date: Tue, 27 Mar 2018 12:52:05 -0400 Subject: [PATCH 1/8] Add unit tests for JSON parsing & serialization. --- test/Makefile | 3 + test/json_unittest.cc | 339 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 342 insertions(+) create mode 100644 test/json_unittest.cc diff --git a/test/Makefile b/test/Makefile index 0ad932ff..af1757dd 100644 --- a/test/Makefile +++ b/test/Makefile @@ -33,6 +33,7 @@ TESTS=\ format_unittest \ health_checker_unittest \ resource_unittest \ + json_unittest \ time_unittest GTEST_LIB=gtest_lib.a @@ -87,5 +88,7 @@ time_unittest: $(GTEST_LIB) time_unittest.o $(SRC_DIR)/time.o $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@ health_checker_unittest: $(GTEST_LIB) health_checker_unittest.o $(SRC_DIR)/health_checker.o $(SRC_DIR)/configuration.o $(CXX) $(LDFLAGS) $^ $(LDLIBS) -lboost_filesystem -lboost_system -o $@ +json_unittest: $(GTEST_LIB) json_unittest.o $(SRC_DIR)/json.o + $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@ .PHONY: all test clean purge diff --git a/test/json_unittest.cc b/test/json_unittest.cc new file mode 100644 index 00000000..04b041d4 --- /dev/null +++ b/test/json_unittest.cc @@ -0,0 +1,339 @@ +#include + +#include "../src/json.h" +#include "gtest/gtest.h" + +#define EXPECT_TO_STRING(v, s) EXPECT_EQ(v->ToString(), s) + +namespace { + +void GuardJsonException(std::function test) { + try { + test(); + } catch (const json::Exception& exc) { + FAIL() << exc.what(); + } +} + + +// Trival Null + +TEST(TrivialParseTest, Null) { + GuardJsonException([](){ + std::unique_ptr v = json::Parser::FromString("null"); + EXPECT_EQ(v->type(), json::NullType); + }); +} + +TEST(TrivialToStringTest, Null) { + GuardJsonException([](){ + EXPECT_TO_STRING(json::null(), "null"); + }); +} + + +// Trival True & False + +TEST(TrivialParseTest, True) { + GuardJsonException([](){ + std::unique_ptr v = json::Parser::FromString("true"); + EXPECT_EQ(v->As()->value(), true); + }); +} + +TEST(TrivialToStringTest, True) { + GuardJsonException([](){ + EXPECT_TO_STRING(json::boolean(true), "true"); + }); +} + +TEST(TrivialParseTest, False) { + GuardJsonException([](){ + std::unique_ptr v = json::Parser::FromString("false"); + EXPECT_EQ(v->As()->value(), false); + }); +} + +TEST(TrivialToStringTest, False) { + GuardJsonException([](){ + EXPECT_TO_STRING(json::boolean(false), "false"); + }); +} + + +// Trival Number + +TEST(TrivialParseTest, Number) { + GuardJsonException([](){ + std::unique_ptr v = json::Parser::FromString("2"); + EXPECT_EQ(v->As()->value(), 2.0); + }); +} + +TEST(TrivialToStringTest, Number) { + GuardJsonException([](){ + EXPECT_TO_STRING(json::number(2.0), "2.0"); + }); +} + + +// Trival String + +TEST(TrivialParseTest, StringEmpty) { + GuardJsonException([](){ + std::unique_ptr v = json::Parser::FromString("\"\""); + EXPECT_EQ(v->As()->value().length(), 0); + }); +} + +TEST(TrivialToStringTest, StringEmpty) { + GuardJsonException([](){ + EXPECT_TO_STRING(json::string(""), "\"\""); + }); +} + +TEST(TrivialParseTest, StringOneChar) { + GuardJsonException([](){ + std::unique_ptr v = json::Parser::FromString("\"x\""); + const auto& str = v->As(); + EXPECT_EQ(str->value(), "x"); + }); +} + +TEST(TrivialToStringTest, StringOneChar) { + GuardJsonException([](){ + EXPECT_TO_STRING(json::string("x"), "\"x\""); + }); +} + + +// Trival Array + +TEST(TrivialParseTest, ArrayEmpty) { + GuardJsonException([](){ + std::unique_ptr v = json::Parser::FromString("[]"); + EXPECT_TRUE(v->As()->empty()); + }); +} + +TEST(TrivialToStringTest, ArrayEmpty) { + GuardJsonException([](){ + EXPECT_TO_STRING(json::array({}), "[]"); + }); +} + +TEST(TrivialParseTest, ArrayOneElement) { + GuardJsonException([](){ + std::unique_ptr v = json::Parser::FromString("[2]"); + const auto& arr = v->As(); + EXPECT_EQ(arr->size(), 1); + EXPECT_EQ((*arr)[0]->As()->value(), 2.0); + }); +} + +TEST(TrivialToStringTest, ArrayOneElement) { + GuardJsonException([](){ + EXPECT_TO_STRING(json::array({json::number(2.0)}), "[2.0]"); + }); +} + + +// Trival Dict + +TEST(TrivialParseTest, DictEmpty) { + GuardJsonException([](){ + std::unique_ptr v = json::Parser::FromString("{}"); + EXPECT_TRUE(v->As()->empty()); + }); +} + +TEST(TrivialToStringTest, DictEmpty) { + GuardJsonException([](){ + EXPECT_TO_STRING(json::object({}), "{}"); + }); +} + +TEST(TrivialParseTest, DictOneField) { + GuardJsonException([](){ + std::unique_ptr v = json::Parser::FromString("{\"f\":2}"); + const auto& obj = v->As(); + EXPECT_EQ(obj->size(), 1); + EXPECT_EQ(obj->Get("f"), 2.0); + }); +} + +TEST(TrivialToStringTest, DictOneField) { + GuardJsonException([](){ + std::unique_ptr v = json::object({ + {"f", json::number(2.0)} + }); + EXPECT_TO_STRING(v, "{\"f\":2.0}"); + }); +} + + +// String edge cases + +/* + * TODO(igorp): Seems that yajl does not support valid JSON. +TEST(EdgeTest, StringWithNewLine) { + GuardJsonException([](){ + std::unique_ptr v = json::Parser::FromString("\"\n\""); + EXPECT_EQ(v->As()->value(), "\n"); + // Output should be escaped with \n as that is more canonical. + EXPECT_TO_STRING(v, "\"\n\""); + }); +} +*/ + +TEST(EdgeTest, StringWithNonUnicodeEscape) { + GuardJsonException([](){ + std::unique_ptr v = json::Parser::FromString("\"foo\\nbar\""); + EXPECT_EQ(v->As()->value(), "foo\nbar"); + // Output should be escaped as that is more canonical. + EXPECT_TO_STRING(v, "\"foo\\nbar\""); + }); +} + +TEST(EdgeTest, StringWithEveryEscape) { + GuardJsonException([](){ + std::unique_ptr v = json::Parser::FromString("\"x\\\\x\\/x\\bx\\fx\\nx\\rx\\tx\""); + EXPECT_EQ(v->As()->value(), "x\\x/x\bx\fx\nx\rx\tx"); + // Output should be escaped as that is more canonical. + // Since / does not need to be escaped, output does not escape that. + EXPECT_TO_STRING(v, "\"x\\\\x/x\\bx\\fx\\nx\\rx\\tx\""); + }); +} + +TEST(EdgeTest, StringWithUnicodeEscape) { + GuardJsonException([](){ + std::unique_ptr v = json::Parser::FromString("\"foo\\u000abar\""); + //EXPECT_EQ(v->As()->value(), "foo\nbar"); + // Output should be escaped as that is more canonical. + EXPECT_TO_STRING(v, "\"foo\\nbar\""); + }); +} + +TEST(EdgeTest, StringWithUTF8) { + GuardJsonException([](){ + // This is Korean for "Hello World!". + std::unique_ptr v = json::Parser::FromString("\"안녕 세상아!\""); + EXPECT_EQ(v->As()->value(), "안녕 세상아!"); + EXPECT_TO_STRING(v, "\"안녕 세상아!\""); + }); +} + + +// Number edge cases + +TEST(EdgeTest, PositiveNumbers) { + GuardJsonException([](){ + std::unique_ptr v = json::Parser::FromString( + "[\n" + " 0, 0e+0, 0e-0, 0e0, 0E+0, 0E-0, 0E0,\n" + " 0.0, 0.0e+0, 0.0e-0, 0.0e0, 0.0E+0, 0.0E-0, 0.0E0,\n" + " 10, 10e+0, 10e-0, 10e0, 10E+0, 10E-0, 10E0,\n" + " 10.0, 10.0e+0, 10.0e-0, 10.0e0, 10.0E+0, 10.0E-0, 10.0E0,\n" + " 123, 123.456, 789, 1.05, 1.999e-99\n" + "]" + ); + std::vector numbers; + for (const auto& number : *v->As()) { + numbers.push_back(number->As()->value()); + } + EXPECT_EQ(numbers, std::vector({ + 0, 0e+0, 0e-0, 0e0, 0E+0, 0E-0, 0E0, + 0.0, 0.0e+0, 0.0e-0, 0.0e0, 0.0E+0, 0.0E-0, 0.0E0, + 10, 10e+0, 10e-0, 10e0, 10E+0, 10E-0, 10E0, + 10.0, 10.0e+0, 10.0e-0, 10.0e0, 10.0E+0, 10.0E-0, 10.0E0, + 123, 123.456, 789, 1.05, 1.999e-99, + })); + EXPECT_TO_STRING(v, + "[" + "0.0,0.0,0.0,0.0,0.0,0.0,0.0," + "0.0,0.0,0.0,0.0,0.0,0.0,0.0," + "10.0,10.0,10.0,10.0,10.0,10.0,10.0," + "10.0,10.0,10.0,10.0,10.0,10.0,10.0," + "123.0,123.45600000000000307,789.0," + "1.0500000000000000444,1.9989999999999998999e-99" + "]" + ); + }); +} + +TEST(EdgeTest, NegativeNumbers) { + GuardJsonException([](){ + std::unique_ptr v = json::Parser::FromString( + "[\n" + " -0, -0e+0, -0e-0, -0e0, -0E+0, -0E-0, -0E0,\n" + " -0.0, -0.0e+0, -0.0e-0, -0.0e0, -0.0E+0, -0.0E-0, -0.0E0,\n" + " -10, -10e+0, -10e-0, -10e0, -10E+0, -10E-0, -10E0,\n" + " -10.0, -10.0e+0, -10.0e-0, -10.0e0, -10.0E+0, -10.0E-0, -10.0E0,\n" + " -123, -123.456, -789, -1.05, -1.999e-99\n" + "]" + ); + std::vector numbers; + for (const auto& number : *v->As()) { + numbers.push_back(number->As()->value()); + } + EXPECT_EQ(numbers, std::vector({ + -0, -0e+0, -0e-0, -0e0, -0E+0, -0E-0, -0E0, + -0.0, -0.0e+0, -0.0e-0, -0.0e0, -0.0E+0, -0.0E-0, -0.0E0, + -10, -10e+0, -10e-0, -10e0, -10E+0, -10E-0, -10E0, + -10.0, -10.0e+0, -10.0e-0, -10.0e0, -10.0E+0, -10.0E-0, -10.0E0, + -123, -123.456, -789, -1.05, -1.999e-99, + })); + // TODO(igorp): This first one looks spurious. + EXPECT_TO_STRING(v, + "[" + "0.0,-0.0,-0.0,-0.0,-0.0,-0.0,-0.0," + "-0.0,-0.0,-0.0,-0.0,-0.0,-0.0,-0.0," + "-10.0,-10.0,-10.0,-10.0,-10.0,-10.0,-10.0," + "-10.0,-10.0,-10.0,-10.0,-10.0,-10.0,-10.0," + "-123.0,-123.45600000000000307,-789.0," + "-1.0500000000000000444,-1.9989999999999998999e-99" + "]" + ); + }); +} + + +// Big tests. + +TEST(BigTest, Realistic) { + GuardJsonException([](){ + std::unique_ptr v = json::Parser::FromString( + "{\n" + " \"foo\": [1, 2, 3],\n" + " \"bar\": {\"x\": 0, \"y\": null},\n" + " \"baz\": true,\n" + " \"str\": \"asdfasdf\"\n" + "}\n" + ); + EXPECT_TO_STRING(v, + "{" + "\"bar\":{\"x\":0.0,\"y\":null}," + "\"baz\":true," + "\"foo\":[1.0,2.0,3.0]," + "\"str\":\"asdfasdf\"" + "}" + ); + }); +} + +TEST(BigTest, LotsOfNesting) { + GuardJsonException([](){ + std::unique_ptr v = json::Parser::FromString( + "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" + "]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" + ); + EXPECT_TO_STRING(v, + "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" + "]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" + ); + }); +} + + + +} // namespace From 53530713467ceb349cdf54d9c6b7878f97a55ffe Mon Sep 17 00:00:00 2001 From: Dave Cunningham Date: Tue, 27 Mar 2018 13:58:42 -0400 Subject: [PATCH 2/8] Add some parse error tests --- test/json_unittest.cc | 46 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/test/json_unittest.cc b/test/json_unittest.cc index 04b041d4..229fa24b 100644 --- a/test/json_unittest.cc +++ b/test/json_unittest.cc @@ -335,5 +335,51 @@ TEST(BigTest, LotsOfNesting) { } +// Parse errors + +TEST(ParseError, Empty) { + ASSERT_THROW(json::Parser::FromString(""), json::Exception); +} + +TEST(ParseError, UnexpectedChar) { + ASSERT_THROW(json::Parser::FromString("x"), json::Exception); +} + +TEST(ParseError, UnterminatedArray) { + ASSERT_THROW(json::Parser::FromString("["), json::Exception); +} + +TEST(ParseError, UnmatchedArrayClose) { + ASSERT_THROW(json::Parser::FromString("]"), json::Exception); +} + +TEST(ParseError, UnterminatedObject) { + ASSERT_THROW(json::Parser::FromString("{"), json::Exception); +} + +TEST(ParseError, UnmatchedObjectClose) { + ASSERT_THROW(json::Parser::FromString("}"), json::Exception); +} + +TEST(ParseError, UnterminatedString) { + ASSERT_THROW(json::Parser::FromString("\""), json::Exception); +} + +TEST(ParseError, UnterminatedEscape) { + ASSERT_THROW(json::Parser::FromString("\"\\\""), json::Exception); +} + +TEST(ParseError, UnterminatedUnicodeEscape) { + ASSERT_THROW(json::Parser::FromString("\"\\u\""), json::Exception); + ASSERT_THROW(json::Parser::FromString("\"\\u0\""), json::Exception); + ASSERT_THROW(json::Parser::FromString("\"\\u00\""), json::Exception); + ASSERT_THROW(json::Parser::FromString("\"\\u000\""), json::Exception); +} + +TEST(ParseError, ObjectNoValue) { + ASSERT_THROW(json::Parser::FromString("{\"x\"}"), json::Exception); + ASSERT_THROW(json::Parser::FromString("{\"x\":}"), json::Exception); +} + } // namespace From cee76345c2922e171cf32c887ad933b9dcbdeb42 Mon Sep 17 00:00:00 2001 From: Dave Cunningham Date: Tue, 27 Mar 2018 15:11:35 -0400 Subject: [PATCH 3/8] Respond to comments --- test/Makefile | 2 +- test/json_unittest.cc | 222 +++++++++++++++++++++++++----------------- 2 files changed, 136 insertions(+), 88 deletions(-) diff --git a/test/Makefile b/test/Makefile index af1757dd..7c9fe56a 100644 --- a/test/Makefile +++ b/test/Makefile @@ -32,8 +32,8 @@ TESTS=\ configuration_unittest \ format_unittest \ health_checker_unittest \ - resource_unittest \ json_unittest \ + resource_unittest \ time_unittest GTEST_LIB=gtest_lib.a diff --git a/test/json_unittest.cc b/test/json_unittest.cc index 229fa24b..5a618b3a 100644 --- a/test/json_unittest.cc +++ b/test/json_unittest.cc @@ -1,9 +1,8 @@ -#include - #include "../src/json.h" #include "gtest/gtest.h" +#include -#define EXPECT_TO_STRING(v, s) EXPECT_EQ(v->ToString(), s) +#define EXPECT_TOSTRING_EQ(s, v) EXPECT_EQ(s, v->ToString()) namespace { @@ -20,43 +19,43 @@ void GuardJsonException(std::function test) { TEST(TrivialParseTest, Null) { GuardJsonException([](){ - std::unique_ptr v = json::Parser::FromString("null"); - EXPECT_EQ(v->type(), json::NullType); + json::value v = json::Parser::FromString("null"); + EXPECT_EQ(json::NullType, v->type()); }); } TEST(TrivialToStringTest, Null) { GuardJsonException([](){ - EXPECT_TO_STRING(json::null(), "null"); + EXPECT_TOSTRING_EQ("null", json::null()); }); } -// Trival True & False +// Trival Boolean TEST(TrivialParseTest, True) { GuardJsonException([](){ - std::unique_ptr v = json::Parser::FromString("true"); - EXPECT_EQ(v->As()->value(), true); + json::value v = json::Parser::FromString("true"); + EXPECT_EQ(true, v->As()->value()); }); } TEST(TrivialToStringTest, True) { GuardJsonException([](){ - EXPECT_TO_STRING(json::boolean(true), "true"); + EXPECT_TOSTRING_EQ("true", json::boolean(true)); }); } TEST(TrivialParseTest, False) { GuardJsonException([](){ - std::unique_ptr v = json::Parser::FromString("false"); - EXPECT_EQ(v->As()->value(), false); + json::value v = json::Parser::FromString("false"); + EXPECT_EQ(false, v->As()->value()); }); } TEST(TrivialToStringTest, False) { GuardJsonException([](){ - EXPECT_TO_STRING(json::boolean(false), "false"); + EXPECT_TOSTRING_EQ("false", json::boolean(false)); }); } @@ -65,14 +64,14 @@ TEST(TrivialToStringTest, False) { TEST(TrivialParseTest, Number) { GuardJsonException([](){ - std::unique_ptr v = json::Parser::FromString("2"); - EXPECT_EQ(v->As()->value(), 2.0); + json::value v = json::Parser::FromString("2"); + EXPECT_EQ(2.0, v->As()->value()); }); } TEST(TrivialToStringTest, Number) { GuardJsonException([](){ - EXPECT_TO_STRING(json::number(2.0), "2.0"); + EXPECT_TOSTRING_EQ("2.0", json::number(2.0)); }); } @@ -81,28 +80,28 @@ TEST(TrivialToStringTest, Number) { TEST(TrivialParseTest, StringEmpty) { GuardJsonException([](){ - std::unique_ptr v = json::Parser::FromString("\"\""); - EXPECT_EQ(v->As()->value().length(), 0); + json::value v = json::Parser::FromString("\"\""); + EXPECT_EQ("", v->As()->value()); }); } TEST(TrivialToStringTest, StringEmpty) { GuardJsonException([](){ - EXPECT_TO_STRING(json::string(""), "\"\""); + EXPECT_TOSTRING_EQ("\"\"", json::string("")); }); } TEST(TrivialParseTest, StringOneChar) { GuardJsonException([](){ - std::unique_ptr v = json::Parser::FromString("\"x\""); + json::value v = json::Parser::FromString("\"x\""); const auto& str = v->As(); - EXPECT_EQ(str->value(), "x"); + EXPECT_EQ("x", str->value()); }); } TEST(TrivialToStringTest, StringOneChar) { GuardJsonException([](){ - EXPECT_TO_STRING(json::string("x"), "\"x\""); + EXPECT_TOSTRING_EQ("\"x\"", json::string("x")); }); } @@ -111,115 +110,116 @@ TEST(TrivialToStringTest, StringOneChar) { TEST(TrivialParseTest, ArrayEmpty) { GuardJsonException([](){ - std::unique_ptr v = json::Parser::FromString("[]"); + json::value v = json::Parser::FromString("[]"); EXPECT_TRUE(v->As()->empty()); }); } TEST(TrivialToStringTest, ArrayEmpty) { GuardJsonException([](){ - EXPECT_TO_STRING(json::array({}), "[]"); + EXPECT_TOSTRING_EQ("[]", json::array({})); }); } TEST(TrivialParseTest, ArrayOneElement) { GuardJsonException([](){ - std::unique_ptr v = json::Parser::FromString("[2]"); + json::value v = json::Parser::FromString("[2]"); const auto& arr = v->As(); - EXPECT_EQ(arr->size(), 1); - EXPECT_EQ((*arr)[0]->As()->value(), 2.0); + EXPECT_EQ(1, arr->size()); + EXPECT_EQ(2.0, (*arr)[0]->As()->value()); }); } TEST(TrivialToStringTest, ArrayOneElement) { GuardJsonException([](){ - EXPECT_TO_STRING(json::array({json::number(2.0)}), "[2.0]"); + EXPECT_TOSTRING_EQ("[2.0]", json::array({json::number(2.0)})); }); } -// Trival Dict +// Trival Object -TEST(TrivialParseTest, DictEmpty) { +TEST(TrivialParseTest, ObjectEmpty) { GuardJsonException([](){ - std::unique_ptr v = json::Parser::FromString("{}"); + json::value v = json::Parser::FromString("{}"); EXPECT_TRUE(v->As()->empty()); }); } -TEST(TrivialToStringTest, DictEmpty) { +TEST(TrivialToStringTest, ObjectEmpty) { GuardJsonException([](){ - EXPECT_TO_STRING(json::object({}), "{}"); + EXPECT_TOSTRING_EQ("{}", json::object({})); }); } -TEST(TrivialParseTest, DictOneField) { +TEST(TrivialParseTest, ObjectOneField) { GuardJsonException([](){ - std::unique_ptr v = json::Parser::FromString("{\"f\":2}"); + json::value v = json::Parser::FromString("{\"f\":2}"); const auto& obj = v->As(); - EXPECT_EQ(obj->size(), 1); - EXPECT_EQ(obj->Get("f"), 2.0); + EXPECT_EQ(1, obj->size()); + EXPECT_EQ(2.0, obj->Get("f")); }); } -TEST(TrivialToStringTest, DictOneField) { +TEST(TrivialToStringTest, ObjectOneField) { GuardJsonException([](){ - std::unique_ptr v = json::object({ + json::value v = json::object({ {"f", json::number(2.0)} }); - EXPECT_TO_STRING(v, "{\"f\":2.0}"); + EXPECT_TOSTRING_EQ("{\"f\":2.0}", v); }); } // String edge cases -/* - * TODO(igorp): Seems that yajl does not support valid JSON. -TEST(EdgeTest, StringWithNewLine) { +#if 0 +// TODO: yajl doesn't support newlines in strings. +// (https://github.com/lloyd/yajl/issues/180) +TEST(EdgeTest, StringWithNewline) { GuardJsonException([](){ - std::unique_ptr v = json::Parser::FromString("\"\n\""); - EXPECT_EQ(v->As()->value(), "\n"); + json::value v = json::Parser::FromString("\"\n\""); + EXPECT_EQ("\n", v->As()->value()); // Output should be escaped with \n as that is more canonical. - EXPECT_TO_STRING(v, "\"\n\""); + EXPECT_TOSTRING_EQ("\"\n\"", v); }); } -*/ +#endif TEST(EdgeTest, StringWithNonUnicodeEscape) { GuardJsonException([](){ - std::unique_ptr v = json::Parser::FromString("\"foo\\nbar\""); - EXPECT_EQ(v->As()->value(), "foo\nbar"); + json::value v = json::Parser::FromString("\"foo\\nbar\""); + EXPECT_EQ("foo\nbar", v->As()->value()); // Output should be escaped as that is more canonical. - EXPECT_TO_STRING(v, "\"foo\\nbar\""); + EXPECT_TOSTRING_EQ("\"foo\\nbar\"", v); }); } TEST(EdgeTest, StringWithEveryEscape) { GuardJsonException([](){ - std::unique_ptr v = json::Parser::FromString("\"x\\\\x\\/x\\bx\\fx\\nx\\rx\\tx\""); - EXPECT_EQ(v->As()->value(), "x\\x/x\bx\fx\nx\rx\tx"); + json::value v = json::Parser::FromString("\"x\\\\x\\/x\\bx\\fx\\nx\\rx\\tx\""); + EXPECT_EQ("x\\x/x\bx\fx\nx\rx\tx", v->As()->value()); // Output should be escaped as that is more canonical. // Since / does not need to be escaped, output does not escape that. - EXPECT_TO_STRING(v, "\"x\\\\x/x\\bx\\fx\\nx\\rx\\tx\""); + EXPECT_TOSTRING_EQ("\"x\\\\x/x\\bx\\fx\\nx\\rx\\tx\"", v); }); } TEST(EdgeTest, StringWithUnicodeEscape) { GuardJsonException([](){ - std::unique_ptr v = json::Parser::FromString("\"foo\\u000abar\""); - //EXPECT_EQ(v->As()->value(), "foo\nbar"); + json::value v = json::Parser::FromString("\"foo\\u000abar\""); + EXPECT_EQ("foo\nbar", v->As()->value()); // Output should be escaped as that is more canonical. - EXPECT_TO_STRING(v, "\"foo\\nbar\""); + EXPECT_TOSTRING_EQ("\"foo\\nbar\"", v); }); } TEST(EdgeTest, StringWithUTF8) { GuardJsonException([](){ // This is Korean for "Hello World!". - std::unique_ptr v = json::Parser::FromString("\"안녕 세상아!\""); - EXPECT_EQ(v->As()->value(), "안녕 세상아!"); - EXPECT_TO_STRING(v, "\"안녕 세상아!\""); + json::value v = json::Parser::FromString("\"안녕 세상아!\""); + EXPECT_EQ("안녕 세상아!", v->As()->value()); + EXPECT_TOSTRING_EQ("\"안녕 세상아!\"", v); }); } @@ -228,7 +228,7 @@ TEST(EdgeTest, StringWithUTF8) { TEST(EdgeTest, PositiveNumbers) { GuardJsonException([](){ - std::unique_ptr v = json::Parser::FromString( + json::value v = json::Parser::FromString( "[\n" " 0, 0e+0, 0e-0, 0e0, 0E+0, 0E-0, 0E0,\n" " 0.0, 0.0e+0, 0.0e-0, 0.0e0, 0.0E+0, 0.0E-0, 0.0E0,\n" @@ -241,14 +241,16 @@ TEST(EdgeTest, PositiveNumbers) { for (const auto& number : *v->As()) { numbers.push_back(number->As()->value()); } - EXPECT_EQ(numbers, std::vector({ - 0, 0e+0, 0e-0, 0e0, 0E+0, 0E-0, 0E0, - 0.0, 0.0e+0, 0.0e-0, 0.0e0, 0.0E+0, 0.0E-0, 0.0E0, - 10, 10e+0, 10e-0, 10e0, 10E+0, 10E-0, 10E0, - 10.0, 10.0e+0, 10.0e-0, 10.0e0, 10.0E+0, 10.0E-0, 10.0E0, - 123, 123.456, 789, 1.05, 1.999e-99, - })); - EXPECT_TO_STRING(v, + EXPECT_EQ( + std::vector({ + 0, 0e+0, 0e-0, 0e0, 0E+0, 0E-0, 0E0, + 0.0, 0.0e+0, 0.0e-0, 0.0e0, 0.0E+0, 0.0E-0, 0.0E0, + 10, 10e+0, 10e-0, 10e0, 10E+0, 10E-0, 10E0, + 10.0, 10.0e+0, 10.0e-0, 10.0e0, 10.0E+0, 10.0E-0, 10.0E0, + 123, 123.456, 789, 1.05, 1.999e-99, + }), + numbers); + EXPECT_TOSTRING_EQ( "[" "0.0,0.0,0.0,0.0,0.0,0.0,0.0," "0.0,0.0,0.0,0.0,0.0,0.0,0.0," @@ -256,14 +258,15 @@ TEST(EdgeTest, PositiveNumbers) { "10.0,10.0,10.0,10.0,10.0,10.0,10.0," "123.0,123.45600000000000307,789.0," "1.0500000000000000444,1.9989999999999998999e-99" - "]" + "]", + v ); }); } TEST(EdgeTest, NegativeNumbers) { GuardJsonException([](){ - std::unique_ptr v = json::Parser::FromString( + json::value v = json::Parser::FromString( "[\n" " -0, -0e+0, -0e-0, -0e0, -0E+0, -0E-0, -0E0,\n" " -0.0, -0.0e+0, -0.0e-0, -0.0e0, -0.0E+0, -0.0E-0, -0.0E0,\n" @@ -276,15 +279,17 @@ TEST(EdgeTest, NegativeNumbers) { for (const auto& number : *v->As()) { numbers.push_back(number->As()->value()); } - EXPECT_EQ(numbers, std::vector({ - -0, -0e+0, -0e-0, -0e0, -0E+0, -0E-0, -0E0, - -0.0, -0.0e+0, -0.0e-0, -0.0e0, -0.0E+0, -0.0E-0, -0.0E0, - -10, -10e+0, -10e-0, -10e0, -10E+0, -10E-0, -10E0, - -10.0, -10.0e+0, -10.0e-0, -10.0e0, -10.0E+0, -10.0E-0, -10.0E0, - -123, -123.456, -789, -1.05, -1.999e-99, - })); + EXPECT_EQ( + std::vector({ + -0, -0e+0, -0e-0, -0e0, -0E+0, -0E-0, -0E0, + -0.0, -0.0e+0, -0.0e-0, -0.0e0, -0.0E+0, -0.0E-0, -0.0E0, + -10, -10e+0, -10e-0, -10e0, -10E+0, -10E-0, -10E0, + -10.0, -10.0e+0, -10.0e-0, -10.0e0, -10.0E+0, -10.0E-0, -10.0E0, + -123, -123.456, -789, -1.05, -1.999e-99, + }), + numbers); // TODO(igorp): This first one looks spurious. - EXPECT_TO_STRING(v, + EXPECT_TOSTRING_EQ( "[" "0.0,-0.0,-0.0,-0.0,-0.0,-0.0,-0.0," "-0.0,-0.0,-0.0,-0.0,-0.0,-0.0,-0.0," @@ -292,7 +297,8 @@ TEST(EdgeTest, NegativeNumbers) { "-10.0,-10.0,-10.0,-10.0,-10.0,-10.0,-10.0," "-123.0,-123.45600000000000307,-789.0," "-1.0500000000000000444,-1.9989999999999998999e-99" - "]" + "]", + v ); }); } @@ -300,9 +306,9 @@ TEST(EdgeTest, NegativeNumbers) { // Big tests. -TEST(BigTest, Realistic) { +TEST(BigTest, RealisticParsing) { GuardJsonException([](){ - std::unique_ptr v = json::Parser::FromString( + json::value v = json::Parser::FromString( "{\n" " \"foo\": [1, 2, 3],\n" " \"bar\": {\"x\": 0, \"y\": null},\n" @@ -310,26 +316,68 @@ TEST(BigTest, Realistic) { " \"str\": \"asdfasdf\"\n" "}\n" ); - EXPECT_TO_STRING(v, + EXPECT_TOSTRING_EQ( "{" "\"bar\":{\"x\":0.0,\"y\":null}," "\"baz\":true," "\"foo\":[1.0,2.0,3.0]," "\"str\":\"asdfasdf\"" - "}" + "}", + v ); }); } -TEST(BigTest, LotsOfNesting) { +TEST(BigTest, RealisticConstruction) { GuardJsonException([](){ - std::unique_ptr v = json::Parser::FromString( + json::value v = json::object({ + {"foo", json::array({json::number(1), json::number(2), json::number(3)})}, + {"bar", json::object({{"x", json::number(0)}, {"y", json::null()}})}, + {"baz", json::boolean(true)}, + {"str", json::string("asdfasdf")}, + }); + EXPECT_TOSTRING_EQ( + "{" + "\"bar\":{\"x\":0.0,\"y\":null}," + "\"baz\":true," + "\"foo\":[1.0,2.0,3.0]," + "\"str\":\"asdfasdf\"" + "}", + v + ); + }); +} + +TEST(BigTest, LotsOfArrayNesting) { + GuardJsonException([](){ + json::value v = json::Parser::FromString( "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" "]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" ); - EXPECT_TO_STRING(v, + EXPECT_TOSTRING_EQ( "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" - "]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" + "]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]", + v + ); + }); +} + +TEST(BigTest, LotsOfObjectNesting) { + GuardJsonException([](){ + json::value v = json::Parser::FromString( + "{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":" + "{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":" + "{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":" + "{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":null" + "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}" + ); + EXPECT_TOSTRING_EQ( + "{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":" + "{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":" + "{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":" + "{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":null" + "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", + v ); }); } From 16a328af7bc967f915fe7fbf3d6acd665e1430dc Mon Sep 17 00:00:00 2001 From: Dave Cunningham Date: Tue, 27 Mar 2018 15:41:04 -0400 Subject: [PATCH 4/8] Respond to more comments --- test/json_unittest.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/json_unittest.cc b/test/json_unittest.cc index 5a618b3a..16e4e3fd 100644 --- a/test/json_unittest.cc +++ b/test/json_unittest.cc @@ -1,5 +1,6 @@ #include "../src/json.h" #include "gtest/gtest.h" + #include #define EXPECT_TOSTRING_EQ(s, v) EXPECT_EQ(s, v->ToString()) @@ -200,7 +201,7 @@ TEST(EdgeTest, StringWithEveryEscape) { json::value v = json::Parser::FromString("\"x\\\\x\\/x\\bx\\fx\\nx\\rx\\tx\""); EXPECT_EQ("x\\x/x\bx\fx\nx\rx\tx", v->As()->value()); // Output should be escaped as that is more canonical. - // Since / does not need to be escaped, output does not escape that. + // Since '/' does not need to be escaped, output does not escape that. EXPECT_TOSTRING_EQ("\"x\\\\x/x\\bx\\fx\\nx\\rx\\tx\"", v); }); } From 2bd9b1e0376f9bf1c0d14d4eafd68dddfef26176 Mon Sep 17 00:00:00 2001 From: Dave Cunningham Date: Tue, 27 Mar 2018 15:48:41 -0400 Subject: [PATCH 5/8] titles -> sentences --- test/json_unittest.cc | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/json_unittest.cc b/test/json_unittest.cc index 16e4e3fd..63c51921 100644 --- a/test/json_unittest.cc +++ b/test/json_unittest.cc @@ -16,7 +16,7 @@ void GuardJsonException(std::function test) { } -// Trival Null +// Trival null. TEST(TrivialParseTest, Null) { GuardJsonException([](){ @@ -32,7 +32,7 @@ TEST(TrivialToStringTest, Null) { } -// Trival Boolean +// Trival boolean. TEST(TrivialParseTest, True) { GuardJsonException([](){ @@ -61,7 +61,7 @@ TEST(TrivialToStringTest, False) { } -// Trival Number +// Trival number. TEST(TrivialParseTest, Number) { GuardJsonException([](){ @@ -77,7 +77,7 @@ TEST(TrivialToStringTest, Number) { } -// Trival String +// Trival string. TEST(TrivialParseTest, StringEmpty) { GuardJsonException([](){ @@ -107,7 +107,7 @@ TEST(TrivialToStringTest, StringOneChar) { } -// Trival Array +// Trival array. TEST(TrivialParseTest, ArrayEmpty) { GuardJsonException([](){ @@ -138,7 +138,7 @@ TEST(TrivialToStringTest, ArrayOneElement) { } -// Trival Object +// Trival object. TEST(TrivialParseTest, ObjectEmpty) { GuardJsonException([](){ @@ -172,7 +172,7 @@ TEST(TrivialToStringTest, ObjectOneField) { } -// String edge cases +// String edge cases. #if 0 // TODO: yajl doesn't support newlines in strings. @@ -225,7 +225,7 @@ TEST(EdgeTest, StringWithUTF8) { } -// Number edge cases +// Number edge cases. TEST(EdgeTest, PositiveNumbers) { GuardJsonException([](){ @@ -384,7 +384,7 @@ TEST(BigTest, LotsOfObjectNesting) { } -// Parse errors +// Parse errors. TEST(ParseError, Empty) { ASSERT_THROW(json::Parser::FromString(""), json::Exception); From 101c979dfd33eeb9de4eeaf751b86beb74ae9eb0 Mon Sep 17 00:00:00 2001 From: Dave Cunningham Date: Tue, 27 Mar 2018 15:52:39 -0400 Subject: [PATCH 6/8] terminating parens --- test/json_unittest.cc | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/test/json_unittest.cc b/test/json_unittest.cc index 63c51921..fb2cee93 100644 --- a/test/json_unittest.cc +++ b/test/json_unittest.cc @@ -260,8 +260,7 @@ TEST(EdgeTest, PositiveNumbers) { "123.0,123.45600000000000307,789.0," "1.0500000000000000444,1.9989999999999998999e-99" "]", - v - ); + v); }); } @@ -299,8 +298,7 @@ TEST(EdgeTest, NegativeNumbers) { "-123.0,-123.45600000000000307,-789.0," "-1.0500000000000000444,-1.9989999999999998999e-99" "]", - v - ); + v); }); } @@ -324,8 +322,7 @@ TEST(BigTest, RealisticParsing) { "\"foo\":[1.0,2.0,3.0]," "\"str\":\"asdfasdf\"" "}", - v - ); + v); }); } @@ -344,8 +341,7 @@ TEST(BigTest, RealisticConstruction) { "\"foo\":[1.0,2.0,3.0]," "\"str\":\"asdfasdf\"" "}", - v - ); + v); }); } @@ -353,13 +349,11 @@ TEST(BigTest, LotsOfArrayNesting) { GuardJsonException([](){ json::value v = json::Parser::FromString( "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" - "]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" - ); + "]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"); EXPECT_TOSTRING_EQ( "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" "]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]", - v - ); + v); }); } @@ -370,16 +364,14 @@ TEST(BigTest, LotsOfObjectNesting) { "{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":" "{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":" "{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":null" - "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}" - ); + "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}"); EXPECT_TOSTRING_EQ( "{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":" "{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":" "{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":" "{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":{\"f\":null" "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", - v - ); + v); }); } From 3afceb88d215d3a0fca796878068770ed683ba17 Mon Sep 17 00:00:00 2001 From: Dave Cunningham Date: Tue, 27 Mar 2018 15:53:11 -0400 Subject: [PATCH 7/8] Test 'Has' --- test/json_unittest.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/json_unittest.cc b/test/json_unittest.cc index fb2cee93..88538d30 100644 --- a/test/json_unittest.cc +++ b/test/json_unittest.cc @@ -158,6 +158,8 @@ TEST(TrivialParseTest, ObjectOneField) { json::value v = json::Parser::FromString("{\"f\":2}"); const auto& obj = v->As(); EXPECT_EQ(1, obj->size()); + EXPECT_TRUE(v->As()->Has("f")); + EXPECT_FALSE(v->As()->Has("g")); EXPECT_EQ(2.0, obj->Get("f")); }); } From 5ebf798e4d68415e8e24b7b036de2ae1612e0272 Mon Sep 17 00:00:00 2001 From: Dave Cunningham Date: Tue, 27 Mar 2018 17:03:16 -0400 Subject: [PATCH 8/8] minor syntax & whitespace --- test/json_unittest.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/json_unittest.cc b/test/json_unittest.cc index 88538d30..6ddd153f 100644 --- a/test/json_unittest.cc +++ b/test/json_unittest.cc @@ -167,7 +167,7 @@ TEST(TrivialParseTest, ObjectOneField) { TEST(TrivialToStringTest, ObjectOneField) { GuardJsonException([](){ json::value v = json::object({ - {"f", json::number(2.0)} + {"f", json::number(2.0)}, }); EXPECT_TOSTRING_EQ("{\"f\":2.0}", v); }); @@ -424,5 +424,4 @@ TEST(ParseError, ObjectNoValue) { ASSERT_THROW(json::Parser::FromString("{\"x\":}"), json::Exception); } - } // namespace