diff options
author | Devtools Arcadia <arcadia-devtools@yandex-team.ru> | 2022-02-07 18:08:42 +0300 |
---|---|---|
committer | Devtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net> | 2022-02-07 18:08:42 +0300 |
commit | 1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch) | |
tree | e26c9fed0de5d9873cce7e00bc214573dc2195b7 /library/cpp/json/ut | |
download | ydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz |
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'library/cpp/json/ut')
-rw-r--r-- | library/cpp/json/ut/json_prettifier_ut.cpp | 204 | ||||
-rw-r--r-- | library/cpp/json/ut/json_reader_fast_ut.cpp | 304 | ||||
-rw-r--r-- | library/cpp/json/ut/json_reader_ut.cpp | 430 | ||||
-rw-r--r-- | library/cpp/json/ut/json_saveload_ut.cpp | 36 | ||||
-rw-r--r-- | library/cpp/json/ut/json_writer_ut.cpp | 228 | ||||
-rw-r--r-- | library/cpp/json/ut/ya.make | 17 |
6 files changed, 1219 insertions, 0 deletions
diff --git a/library/cpp/json/ut/json_prettifier_ut.cpp b/library/cpp/json/ut/json_prettifier_ut.cpp new file mode 100644 index 00000000000..ae5f8dd81a4 --- /dev/null +++ b/library/cpp/json/ut/json_prettifier_ut.cpp @@ -0,0 +1,204 @@ +#include <library/cpp/json/json_prettifier.h> + +#include <library/cpp/testing/unittest/registar.h> + +Y_UNIT_TEST_SUITE(JsonPrettifier) { + Y_UNIT_TEST(PrettifyJsonShort) { + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson(""), ""); + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("null"), "null"); + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("true"), "true"); + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("false"), "false"); + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("1.5"), "1.5"); + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("test", false, 2, true), "'test'"); + + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("[]"), "[ ]"); + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("[a]", false, 2), "[\n \"a\"\n]"); + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("[a,b]", false, 2, true), "[\n 'a',\n 'b'\n]"); + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("[{},b]", false, 2, true), "[\n { },\n 'b'\n]"); + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("[a,{}]", false, 2, true), "[\n 'a',\n { }\n]"); + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("[{},{}]"), "[\n { },\n { }\n]"); + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("{}"), "{ }"); + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("{}"), "{ }"); + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("{k:v}", false, 2, true), "{\n 'k' : 'v'\n}"); + + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("Test545", true, 2), "Test545"); + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("'null'", true, 2, true), "'null'"); + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("'true'", true, 2, true), "'true'"); + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("'false'", true, 2, true), "'false'"); + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("'\"'", true, 2, true), "'\"'"); + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("'\"'", true, 2, false), "\"\\\"\""); + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("'\\\''", true, 2, true), "'\\u0027'"); + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("'\\\''", true, 2, false), "\"'\""); + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("'1b'", true, 2, true), "'1b'"); + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("'Test*545'", true, 2, true), "'Test*545'"); + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("{k:v}", true, 2), "{\n k : v\n}"); + } + + Y_UNIT_TEST(PrettifyJsonLong) { + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("[{k:v},{a:b}]", false, 2, true), + "[\n" + " {\n" + " 'k' : 'v'\n" + " },\n" + " {\n" + " 'a' : 'b'\n" + " }\n" + "]"); + + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("{k:v,a:b,x:[1,2,3]}", false, 2, true), + "{\n" + " 'k' : 'v',\n" + " 'a' : 'b',\n" + " 'x' : [\n" + " 1,\n" + " 2,\n" + " 3\n" + " ]\n" + "}"); + + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("{k:v,a:b,x:[1,{f:b},3],m:n}", false, 2, true), + "{\n" + " 'k' : 'v',\n" + " 'a' : 'b',\n" + " 'x' : [\n" + " 1,\n" + " {\n" + " 'f' : 'b'\n" + " },\n" + " 3\n" + " ],\n" + " 'm' : 'n'\n" + "}"); + + NJson::TJsonPrettifier prettifierMaxLevel1 = NJson::TJsonPrettifier::Prettifier(false, 2, true); + prettifierMaxLevel1.MaxPaddingLevel = 1; + UNIT_ASSERT_STRINGS_EQUAL(prettifierMaxLevel1.Prettify("{k:v,a:b,x:[1,{f:b},3],m:n}"), + "{\n" + " 'k' : 'v',\n" + " 'a' : 'b',\n" + " 'x' : [ 1, { 'f' : 'b' }, 3 ],\n" + " 'm' : 'n'\n" + "}"); + + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("{g:{x:{a:{b:c,e:f},q:{x:y}},y:fff}}", true, 2), + "{\n" + " g : {\n" + " x : {\n" + " a : {\n" + " b : c,\n" + " e : f\n" + " },\n" + " q : {\n" + " x : y\n" + " }\n" + " },\n" + " y : fff\n" + " }\n" + "}"); + + NJson::TJsonPrettifier prettifierMaxLevel3 = NJson::TJsonPrettifier::Prettifier(true, 2); + prettifierMaxLevel3.MaxPaddingLevel = 3; + UNIT_ASSERT_STRINGS_EQUAL(prettifierMaxLevel3.Prettify("{g:{x:{a:{b:c,e:f},q:{x:y}},y:fff}}"), + "{\n" + " g : {\n" + " x : {\n" + " a : { b : c, e : f },\n" + " q : { x : y }\n" + " },\n" + " y : fff\n" + " }\n" + "}"); + } + + Y_UNIT_TEST(PrettifyJsonInvalid) { + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("}"), ""); + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("}}"), ""); + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("{}}"), ""); + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("{}}}"), ""); + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("]"), ""); + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("]]"), ""); + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("[]]"), ""); + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("[]]]"), ""); + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("[,,,]"), ""); + UNIT_ASSERT_STRINGS_EQUAL(NJson::PrettifyJson("{,,,}"), ""); + } + + Y_UNIT_TEST(CompactifyJsonShort) { + UNIT_ASSERT_STRINGS_EQUAL(NJson::CompactifyJson(""), ""); + UNIT_ASSERT_STRINGS_EQUAL(NJson::CompactifyJson("null"), "null"); + UNIT_ASSERT_STRINGS_EQUAL(NJson::CompactifyJson("true"), "true"); + UNIT_ASSERT_STRINGS_EQUAL(NJson::CompactifyJson("false"), "false"); + UNIT_ASSERT_STRINGS_EQUAL(NJson::CompactifyJson("1.5"), "1.5"); + UNIT_ASSERT_STRINGS_EQUAL(NJson::CompactifyJson("test", true), "test"); + UNIT_ASSERT_STRINGS_EQUAL(NJson::CompactifyJson("test", false), "\"test\""); + + UNIT_ASSERT_STRINGS_EQUAL(NJson::CompactifyJson("[ ]"), "[]"); + UNIT_ASSERT_STRINGS_EQUAL(NJson::CompactifyJson("[\n \"a\"\n]", true), "[a]"); + UNIT_ASSERT_STRINGS_EQUAL(NJson::CompactifyJson("[\n 'a',\n 'b'\n]", true), "[a,b]"); + UNIT_ASSERT_STRINGS_EQUAL(NJson::CompactifyJson("[\n { },\n 'b'\n]", true), "[{},b]"); + UNIT_ASSERT_STRINGS_EQUAL(NJson::CompactifyJson("[\n 'a',\n { }\n]", true), "[a,{}]"); + UNIT_ASSERT_STRINGS_EQUAL(NJson::CompactifyJson("[\n { },\n { }\n]", true), "[{},{}]"); + UNIT_ASSERT_STRINGS_EQUAL(NJson::CompactifyJson("{ }"), "{}"); + UNIT_ASSERT_STRINGS_EQUAL(NJson::CompactifyJson("{\n 'k' : 'v'\n}", true), "{k:v}"); + } + + Y_UNIT_TEST(CompactifyJsonLong) { + UNIT_ASSERT_STRINGS_EQUAL(NJson::CompactifyJson( + "[\n" + " {\n" + " 'k' : 'v'\n" + " },\n" + " {\n" + " 'a' : 'b'\n" + " }\n" + "]", + true), + "[{k:v},{a:b}]"); + UNIT_ASSERT_STRINGS_EQUAL(NJson::CompactifyJson( + "{\n" + " 'k' : 'v',\n" + " 'a' : 'b',\n" + " 'x' : [\n" + " 1,\n" + " 2,\n" + " 3\n" + " ]\n" + "}", + true), + "{k:v,a:b,x:[1,2,3]}"); + + UNIT_ASSERT_STRINGS_EQUAL(NJson::CompactifyJson( + "{\n" + " 'k' : 'v',\n" + " 'a' : 'b',\n" + " 'x' : [\n" + " 1,\n" + " {\n" + " 'f' : 'b'\n" + " },\n" + " 3\n" + " ],\n" + " 'm' : 'n'\n" + "}", + true), + "{k:v,a:b,x:[1,{f:b},3],m:n}"); + + UNIT_ASSERT_STRINGS_EQUAL(NJson::CompactifyJson( + "{\n" + " g : {\n" + " x : {\n" + " a : {\n" + " b : c,\n" + " e : f\n" + " },\n" + " q : {\n" + " x : y\n" + " }\n" + " },\n" + " y : fff\n" + " }\n" + "}", + true), + "{g:{x:{a:{b:c,e:f},q:{x:y}},y:fff}}"); + } +} diff --git a/library/cpp/json/ut/json_reader_fast_ut.cpp b/library/cpp/json/ut/json_reader_fast_ut.cpp new file mode 100644 index 00000000000..60dffc91c73 --- /dev/null +++ b/library/cpp/json/ut/json_reader_fast_ut.cpp @@ -0,0 +1,304 @@ +#include <library/cpp/json/json_reader.h> +#include <library/cpp/json/json_prettifier.h> +#include <library/cpp/testing/unittest/registar.h> + +#include <library/cpp/string_utils/relaxed_escaper/relaxed_escaper.h> +#include <util/string/cast.h> +#include <util/string/printf.h> + +namespace NJson { + namespace NTest { + enum ETestEvent { + E_NO_EVENT = 0, + E_ERROR = 1, + E_DICT_OPEN, + E_DICT_CLOSE, + E_ARR_OPEN, + E_ARR_CLOSE, + E_NULL, + E_BOOL, + E_FLT, + E_INT, + E_LONG_LONG, + E_STR, + E_KEY + }; + + struct TEvent { + ETestEvent Type = E_NO_EVENT; + + i64 INum = 0; + double DNum = 0; + TString Str; + + TEvent(ETestEvent e = E_NO_EVENT) + : Type(e) + { + } + + TEvent(double v, ETestEvent e) + : Type(e) + , DNum(v) + { + } + + TEvent(i64 v, ETestEvent e) + : Type(e) + , INum(v) + { + } + + TEvent(TStringBuf t, ETestEvent e) + : Type(e) + , Str(NEscJ::EscapeJ<true, false>(t)) + { + } + + TString ToString() const { + switch (Type) { + default: + return "YOUFAILED"; + case E_ERROR: + return Sprintf("error: %s", Str.data()); + case E_DICT_OPEN: + return "{"; + case E_DICT_CLOSE: + return "}"; + case E_ARR_OPEN: + return "["; + case E_ARR_CLOSE: + return "]"; + case E_NULL: + return "null"; + case E_BOOL: + return INum ? "true" : "false"; + case E_INT: + return ::ToString(INum); + case E_FLT: + return ::ToString(DNum); + case E_STR: + return Sprintf("%s", Str.data()); + case E_KEY: + return Sprintf("key: %s", Str.data()); + } + } + }; + + using TEvents = TVector<TEvent>; + + struct TTestHandler : TJsonCallbacks { + TEvents Events; + + bool OnOpenMap() override { + Events.push_back(E_DICT_OPEN); + return true; + } + + bool OnCloseMap() override { + Events.push_back(E_DICT_CLOSE); + return true; + } + + bool OnOpenArray() override { + Events.push_back(E_ARR_OPEN); + return true; + } + + bool OnCloseArray() override { + Events.push_back(E_ARR_CLOSE); + return true; + } + + bool OnNull() override { + Events.push_back(E_NULL); + return true; + } + + bool OnBoolean(bool v) override { + Events.push_back(TEvent((i64)v, E_BOOL)); + return true; + } + + bool OnInteger(long long v) override { + Events.push_back(TEvent((i64)v, E_INT)); + return true; + } + + bool OnUInteger(unsigned long long v) override { + return OnInteger(v); + } + + bool OnDouble(double v) override { + Events.push_back(TEvent(v, E_FLT)); + return true; + } + + bool OnString(const TStringBuf& v) override { + Events.push_back(TEvent(v, E_STR)); + return true; + } + + bool OnMapKey(const TStringBuf& v) override { + Events.push_back(TEvent(v, E_KEY)); + return true; + } + + void OnError(size_t, TStringBuf token) override { + Events.push_back(TEvent(token, E_ERROR)); + } + + void Assert(const TEvents& e, TString str) { + try { + UNIT_ASSERT_VALUES_EQUAL_C(e.size(), Events.size(), str); + + for (ui32 i = 0, sz = e.size(); i < sz; ++i) { + UNIT_ASSERT_VALUES_EQUAL_C((int)e[i].Type, (int)Events[i].Type, Sprintf("'%s' %u", str.data(), i)); + UNIT_ASSERT_VALUES_EQUAL_C(e[i].INum, Events[i].INum, Sprintf("'%s' %u", str.data(), i)); + UNIT_ASSERT_VALUES_EQUAL_C(e[i].DNum, Events[i].DNum, Sprintf("'%s' %u", str.data(), i)); + UNIT_ASSERT_VALUES_EQUAL_C(e[i].Str, Events[i].Str, Sprintf("'%s' %u", str.data(), i)); + } + } catch (const yexception&) { + Clog << "Exception at '" << str << "'" << Endl; + for (const auto& event : Events) { + Clog << event.ToString() << Endl; + } + + throw; + } + } + }; + } +} + +class TFastJsonTest: public TTestBase { + UNIT_TEST_SUITE(TFastJsonTest) + UNIT_TEST(TestParse) + UNIT_TEST(TestReadJsonFastTree) + UNIT_TEST(TestNoInlineComment) + UNIT_TEST_SUITE_END(); + +public: + template <bool accept> + void DoTestParse(TStringBuf json, ui32 amount, ...) { + using namespace NJson::NTest; + TEvents evs; + va_list vl; + va_start(vl, amount); + for (ui32 i = 0; i < amount; i++) { + ETestEvent e = (ETestEvent)va_arg(vl, int); + + switch ((int)e) { + case E_NO_EVENT: + case E_DICT_OPEN: + case E_DICT_CLOSE: + case E_ARR_OPEN: + case E_ARR_CLOSE: + case E_NULL: + evs.push_back(e); + break; + case E_BOOL: { + bool v = va_arg(vl, int); + evs.push_back(TEvent((i64)v, E_BOOL)); + break; + } + case E_INT: { + i64 i = va_arg(vl, int); + evs.push_back(TEvent(i, E_INT)); + break; + } + case E_LONG_LONG: { + i64 i = va_arg(vl, long long); + evs.push_back(TEvent(i, E_INT)); + break; + } + case E_FLT: { + double f = va_arg(vl, double); + evs.push_back(TEvent(f, E_FLT)); + break; + } + case E_STR: { + const char* s = va_arg(vl, const char*); + evs.push_back(TEvent(TStringBuf(s), E_STR)); + break; + } + case E_KEY: + case E_ERROR: { + const char* s = va_arg(vl, const char*); + evs.push_back(TEvent(TStringBuf(s), e)); + break; + } + } + } + va_end(vl); + + TTestHandler h; + const bool res = ReadJsonFast(json, &h); + UNIT_ASSERT_VALUES_EQUAL_C(res, accept, Sprintf("%s (%s)", ToString(json).data(), h.Events.back().Str.data())); + h.Assert(evs, ToString(json)); + } + + void TestParse() { + using namespace NJson::NTest; + + DoTestParse<true>("", 0); + DoTestParse<true>(" \t \t ", 0); + DoTestParse<true>("a-b-c@аб_вгд909AБ", 1, E_STR, "a-b-c@аб_вгд909AБ"); + DoTestParse<true>("'я тестовая строка'", 1, E_STR, "я тестовая строка"); + DoTestParse<true>("\"я тестовая строка\"", 1, E_STR, "я тестовая строка"); + DoTestParse<true>("'\\xA\\xA\\xA'", 1, E_STR, "\n\n\n"); + DoTestParse<true>("12.15", 1, E_FLT, 12.15); + DoTestParse<true>("null", 1, E_NULL); + DoTestParse<true>("true", 1, E_BOOL, true); + DoTestParse<true>("false", 1, E_BOOL, false); + DoTestParse<true>("[]", 2, E_ARR_OPEN, E_ARR_CLOSE); + DoTestParse<true>("[ a ]", 3, E_ARR_OPEN, E_STR, "a", E_ARR_CLOSE); + DoTestParse<true>("[ a, b ]", 4, E_ARR_OPEN, E_STR, "a", E_STR, "b", E_ARR_CLOSE); + DoTestParse<true>("[a,b]", 4, E_ARR_OPEN, E_STR, "a", E_STR, "b", E_ARR_CLOSE); + DoTestParse<false>("[a,b][a,b]", 5, E_ARR_OPEN, E_STR, "a", E_STR, "b", E_ARR_CLOSE, E_ERROR, "invalid syntax at token: '['"); + DoTestParse<false>("[a,,b]", 3, E_ARR_OPEN, E_STR, "a", E_ERROR, "invalid syntax at token: ','"); + DoTestParse<true>("{ k : v }", 4, E_DICT_OPEN, E_KEY, "k", E_STR, "v", E_DICT_CLOSE); + DoTestParse<true>("{a:'\\b'/*comment*/, k /*comment*/\n : v }", 6, E_DICT_OPEN, E_KEY, "a", E_STR, "\b", E_KEY, "k", E_STR, "v", E_DICT_CLOSE); + DoTestParse<true>("{a:.15, k : v }", 6, E_DICT_OPEN, E_KEY, "a", E_FLT, .15, E_KEY, "k", E_STR, "v", E_DICT_CLOSE); + DoTestParse<true>("[ a, -.1e+5, 1E-7]", 5, E_ARR_OPEN, E_STR, "a", E_FLT, -.1e+5, E_FLT, 1e-7, E_ARR_CLOSE); + DoTestParse<true>("{}", 2, E_DICT_OPEN, E_DICT_CLOSE); + DoTestParse<true>("{ a : x, b : [ c, d, ] }", 9, E_DICT_OPEN, E_KEY, "a", E_STR, "x", E_KEY, "b", E_ARR_OPEN, E_STR, "c", E_STR, "d", E_ARR_CLOSE, E_DICT_CLOSE); + DoTestParse<false>("{ a : x, b : [ c, d,, ] }", 8, E_DICT_OPEN, E_KEY, "a", E_STR, "x", E_KEY, "b", E_ARR_OPEN, E_STR, "c", E_STR, "d", E_ERROR, "invalid syntax at token: ','"); + // DoTestParse<false>("{ a : x : y }", 4, E_DICT_OPEN + // , E_KEY, "a", E_STR, "x" + // , E_ERROR + // , ":"); + // DoTestParse<false>("{queries:{ref:[]},{nonref:[]}}", 8, E_DICT_OPEN + // , E_KEY, "queries", E_DICT_OPEN + // , E_KEY, "ref", E_ARR_OPEN, E_ARR_CLOSE + // , E_DICT_CLOSE, E_ERROR, ""); + DoTestParse<true>("'100x00'", 1, E_STR, "100x00"); + DoTestParse<true>("-1", 1, E_INT, -1); + DoTestParse<true>("-9223372036854775808", 1, E_LONG_LONG, (long long)Min<i64>()); + DoTestParse<false>("100x00", 1, E_ERROR, "invalid syntax at token: '100x'"); + DoTestParse<false>("100 200", 2, E_INT, 100, E_ERROR, "invalid syntax at token: '200'"); + DoTestParse<true>("{g:{x:{a:{b:c,e:f},q:{x:y}},y:fff}}", 22, E_DICT_OPEN, E_KEY, "g", E_DICT_OPEN, E_KEY, "x", E_DICT_OPEN, E_KEY, "a", E_DICT_OPEN, E_KEY, "b", E_STR, "c", E_KEY, "e", E_STR, "f", E_DICT_CLOSE, E_KEY, "q", E_DICT_OPEN, E_KEY, "x", E_STR, "y", E_DICT_CLOSE, E_DICT_CLOSE, E_KEY, "y", E_STR, "fff", E_DICT_CLOSE, E_DICT_CLOSE); + } + + void TestReadJsonFastTree() { + const TString json = R"( + { + "a": { + "b": {} + } + }} + )"; + NJson::TJsonValue value; + UNIT_ASSERT(!ReadJsonFastTree(json, &value)); + } + + void TestNoInlineComment() { + using namespace NJson::NTest; + DoTestParse<false>("{\"a\":1}//d{\"b\":2}", 5, E_DICT_OPEN, E_KEY, "a", E_INT, 1, E_DICT_CLOSE, E_ERROR, "invalid syntax at token: '/'"); + DoTestParse<false>("{\"a\":1}//d{\"b\":2}\n", 5, E_DICT_OPEN, E_KEY, "a", E_INT, 1, E_DICT_CLOSE, E_ERROR, "invalid syntax at token: '/'"); + DoTestParse<false>("{\"a\":{//d{\"b\":2}\n}}", 4, E_DICT_OPEN, E_KEY, "a", E_DICT_OPEN, E_ERROR, "invalid syntax at token: '/'"); + DoTestParse<false>("{\"a\":{//d{\"b\":2}}}\n", 4, E_DICT_OPEN, E_KEY, "a", E_DICT_OPEN, E_ERROR, "invalid syntax at token: '/'"); + DoTestParse<false>("{\"a\":{//d{\"b\":2}}}", 4, E_DICT_OPEN, E_KEY, "a", E_DICT_OPEN, E_ERROR, "invalid syntax at token: '/'"); + } +}; + +UNIT_TEST_SUITE_REGISTRATION(TFastJsonTest) diff --git a/library/cpp/json/ut/json_reader_ut.cpp b/library/cpp/json/ut/json_reader_ut.cpp new file mode 100644 index 00000000000..cd31afa0b8b --- /dev/null +++ b/library/cpp/json/ut/json_reader_ut.cpp @@ -0,0 +1,430 @@ +#include <library/cpp/json/json_reader.h> +#include <library/cpp/json/json_writer.h> + +#include <library/cpp/testing/unittest/registar.h> +#include <util/stream/str.h> + +using namespace NJson; + +class TReformatCallbacks: public TJsonCallbacks { + TJsonWriter& Writer; + +public: + TReformatCallbacks(TJsonWriter& writer) + : Writer(writer) + { + } + + bool OnBoolean(bool val) override { + Writer.Write(val); + return true; + } + + bool OnInteger(long long val) override { + Writer.Write(val); + return true; + } + + bool OnUInteger(unsigned long long val) override { + Writer.Write(val); + return true; + } + + bool OnString(const TStringBuf& val) override { + Writer.Write(val); + return true; + } + + bool OnDouble(double val) override { + Writer.Write(val); + return true; + } + + bool OnOpenArray() override { + Writer.OpenArray(); + return true; + } + + bool OnCloseArray() override { + Writer.CloseArray(); + return true; + } + + bool OnOpenMap() override { + Writer.OpenArray(); + return true; + } + + bool OnCloseMap() override { + Writer.CloseArray(); + return true; + } + + bool OnMapKey(const TStringBuf& val) override { + Writer.Write(val); + return true; + } +}; + +Y_UNIT_TEST_SUITE(TJsonReaderTest) { + Y_UNIT_TEST(JsonReformatTest) { + TString data = "{\"null value\": null, \"intkey\": 10, \"double key\": 11.11, \"string key\": \"string\", \"array\": [1,2,3,\"TString\"], \"bool key\": true}"; + + TString result1, result2; + { + TStringStream in; + in << data; + TStringStream out; + TJsonWriter writer(&out, false); + TReformatCallbacks cb(writer); + ReadJson(&in, &cb); + writer.Flush(); + result1 = out.Str(); + } + + { + TStringStream in; + in << result1; + TStringStream out; + TJsonWriter writer(&out, false); + TReformatCallbacks cb(writer); + ReadJson(&in, &cb); + writer.Flush(); + result2 = out.Str(); + } + + UNIT_ASSERT_VALUES_EQUAL(result1, result2); + } + + Y_UNIT_TEST(TJsonEscapedApostrophe) { + TString jsonString = "{ \"foo\" : \"bar\\'buzz\" }"; + { + TStringStream in; + in << jsonString; + TStringStream out; + TJsonWriter writer(&out, false); + TReformatCallbacks cb(writer); + UNIT_ASSERT(!ReadJson(&in, &cb)); + } + + { + TStringStream in; + in << jsonString; + TStringStream out; + TJsonWriter writer(&out, false); + TReformatCallbacks cb(writer); + UNIT_ASSERT(ReadJson(&in, false, true, &cb)); + writer.Flush(); + UNIT_ASSERT_EQUAL(out.Str(), "[\"foo\",\"bar'buzz\"]"); + } + } + + Y_UNIT_TEST(TJsonTreeTest) { + TString data = "{\"intkey\": 10, \"double key\": 11.11, \"null value\":null, \"string key\": \"string\", \"array\": [1,2,3,\"TString\"], \"bool key\": true}"; + TStringStream in; + in << data; + TJsonValue value; + ReadJsonTree(&in, &value); + + UNIT_ASSERT_VALUES_EQUAL(value["intkey"].GetInteger(), 10); + UNIT_ASSERT_DOUBLES_EQUAL(value["double key"].GetDouble(), 11.11, 0.001); + UNIT_ASSERT_VALUES_EQUAL(value["bool key"].GetBoolean(), true); + UNIT_ASSERT_VALUES_EQUAL(value["absent string key"].GetString(), TString("")); + UNIT_ASSERT_VALUES_EQUAL(value["string key"].GetString(), TString("string")); + UNIT_ASSERT_VALUES_EQUAL(value["array"][0].GetInteger(), 1); + UNIT_ASSERT_VALUES_EQUAL(value["array"][1].GetInteger(), 2); + UNIT_ASSERT_VALUES_EQUAL(value["array"][2].GetInteger(), 3); + UNIT_ASSERT_VALUES_EQUAL(value["array"][3].GetString(), TString("TString")); + UNIT_ASSERT(value["null value"].IsNull()); + + // AsString + UNIT_ASSERT_VALUES_EQUAL(value["intkey"].GetStringRobust(), "10"); + UNIT_ASSERT_VALUES_EQUAL(value["double key"].GetStringRobust(), "11.11"); + UNIT_ASSERT_VALUES_EQUAL(value["bool key"].GetStringRobust(), "true"); + UNIT_ASSERT_VALUES_EQUAL(value["string key"].GetStringRobust(), "string"); + UNIT_ASSERT_VALUES_EQUAL(value["array"].GetStringRobust(), "[1,2,3,\"TString\"]"); + UNIT_ASSERT_VALUES_EQUAL(value["null value"].GetStringRobust(), "null"); + + const TJsonValue::TArray* array; + UNIT_ASSERT(GetArrayPointer(value, "array", &array)); + UNIT_ASSERT_VALUES_EQUAL(value["array"].GetArray().size(), array->size()); + UNIT_ASSERT_VALUES_EQUAL(value["array"][0].GetInteger(), (*array)[0].GetInteger()); + UNIT_ASSERT_VALUES_EQUAL(value["array"][1].GetInteger(), (*array)[1].GetInteger()); + UNIT_ASSERT_VALUES_EQUAL(value["array"][2].GetInteger(), (*array)[2].GetInteger()); + UNIT_ASSERT_VALUES_EQUAL(value["array"][3].GetString(), (*array)[3].GetString()); + } + + Y_UNIT_TEST(TJsonRomaTest) { + TString data = "{\"test\": [ {\"name\": \"A\"} ]}"; + + TStringStream in; + in << data; + TJsonValue value; + ReadJsonTree(&in, &value); + + UNIT_ASSERT_VALUES_EQUAL(value["test"][0]["name"].GetString(), TString("A")); + } + + Y_UNIT_TEST(TJsonReadTreeWithComments) { + { + TString leadingCommentData = "{ // \"test\" : 1 \n}"; + { + // No comments allowed + TStringStream in; + in << leadingCommentData; + TJsonValue value; + UNIT_ASSERT(!ReadJsonTree(&in, false, &value)); + } + + { + // Comments allowed + TStringStream in; + in << leadingCommentData; + TJsonValue value; + UNIT_ASSERT(ReadJsonTree(&in, true, &value)); + UNIT_ASSERT(!value.Has("test")); + } + } + + { + TString trailingCommentData = "{ \"test1\" : 1 // \"test2\" : 2 \n }"; + { + // No comments allowed + TStringStream in; + in << trailingCommentData; + TJsonValue value; + UNIT_ASSERT(!ReadJsonTree(&in, false, &value)); + } + + { + // Comments allowed + TStringStream in; + in << trailingCommentData; + TJsonValue value; + UNIT_ASSERT(ReadJsonTree(&in, true, &value)); + UNIT_ASSERT(value.Has("test1")); + UNIT_ASSERT_EQUAL(value["test1"].GetInteger(), 1); + UNIT_ASSERT(!value.Has("test2")); + } + } + } + + Y_UNIT_TEST(TJsonSignedIntegerTest) { + { + TStringStream in; + in << "{ \"test\" : " << Min<i64>() << " }"; + TJsonValue value; + UNIT_ASSERT(ReadJsonTree(&in, &value)); + UNIT_ASSERT(value.Has("test")); + UNIT_ASSERT(value["test"].IsInteger()); + UNIT_ASSERT(!value["test"].IsUInteger()); + UNIT_ASSERT_EQUAL(value["test"].GetInteger(), Min<i64>()); + UNIT_ASSERT_EQUAL(value["test"].GetIntegerRobust(), Min<i64>()); + } // Min<i64>() + + { + TStringStream in; + in << "{ \"test\" : " << Max<i64>() + 1ull << " }"; + TJsonValue value; + UNIT_ASSERT(ReadJsonTree(&in, &value)); + UNIT_ASSERT(value.Has("test")); + UNIT_ASSERT(!value["test"].IsInteger()); + UNIT_ASSERT(value["test"].IsUInteger()); + UNIT_ASSERT_EQUAL(value["test"].GetIntegerRobust(), (i64)(Max<i64>() + 1ull)); + } // Max<i64>() + 1 + } + + Y_UNIT_TEST(TJsonUnsignedIntegerTest) { + { + TStringStream in; + in << "{ \"test\" : 1 }"; + TJsonValue value; + UNIT_ASSERT(ReadJsonTree(&in, &value)); + UNIT_ASSERT(value.Has("test")); + UNIT_ASSERT(value["test"].IsInteger()); + UNIT_ASSERT(value["test"].IsUInteger()); + UNIT_ASSERT_EQUAL(value["test"].GetInteger(), 1); + UNIT_ASSERT_EQUAL(value["test"].GetIntegerRobust(), 1); + UNIT_ASSERT_EQUAL(value["test"].GetUInteger(), 1); + UNIT_ASSERT_EQUAL(value["test"].GetUIntegerRobust(), 1); + } // 1 + + { + TStringStream in; + in << "{ \"test\" : -1 }"; + TJsonValue value; + UNIT_ASSERT(ReadJsonTree(&in, &value)); + UNIT_ASSERT(value.Has("test")); + UNIT_ASSERT(value["test"].IsInteger()); + UNIT_ASSERT(!value["test"].IsUInteger()); + UNIT_ASSERT_EQUAL(value["test"].GetInteger(), -1); + UNIT_ASSERT_EQUAL(value["test"].GetIntegerRobust(), -1); + UNIT_ASSERT_EQUAL(value["test"].GetUInteger(), 0); + UNIT_ASSERT_EQUAL(value["test"].GetUIntegerRobust(), static_cast<unsigned long long>(-1)); + } // -1 + + { + TStringStream in; + in << "{ \"test\" : 18446744073709551615 }"; + TJsonValue value; + UNIT_ASSERT(ReadJsonTree(&in, &value)); + UNIT_ASSERT(value.Has("test")); + UNIT_ASSERT(!value["test"].IsInteger()); + UNIT_ASSERT(value["test"].IsUInteger()); + UNIT_ASSERT_EQUAL(value["test"].GetInteger(), 0); + UNIT_ASSERT_EQUAL(value["test"].GetIntegerRobust(), static_cast<long long>(18446744073709551615ull)); + UNIT_ASSERT_EQUAL(value["test"].GetUInteger(), 18446744073709551615ull); + UNIT_ASSERT_EQUAL(value["test"].GetUIntegerRobust(), 18446744073709551615ull); + } // 18446744073709551615 + + { + TStringStream in; + in << "{ \"test\" : 1.1 }"; + TJsonValue value; + UNIT_ASSERT(ReadJsonTree(&in, &value)); + UNIT_ASSERT(value.Has("test")); + UNIT_ASSERT(!value["test"].IsInteger()); + UNIT_ASSERT(!value["test"].IsUInteger()); + UNIT_ASSERT_EQUAL(value["test"].GetInteger(), 0); + UNIT_ASSERT_EQUAL(value["test"].GetIntegerRobust(), static_cast<long long>(1.1)); + UNIT_ASSERT_EQUAL(value["test"].GetUInteger(), 0); + UNIT_ASSERT_EQUAL(value["test"].GetUIntegerRobust(), static_cast<unsigned long long>(1.1)); + } // 1.1 + + { + TStringStream in; + in << "{ \"test\" : [1, 18446744073709551615] }"; + TJsonValue value; + UNIT_ASSERT(ReadJsonTree(&in, &value)); + UNIT_ASSERT(value.Has("test")); + UNIT_ASSERT(value["test"].IsArray()); + UNIT_ASSERT_EQUAL(value["test"].GetArray().size(), 2); + UNIT_ASSERT(value["test"][0].IsInteger()); + UNIT_ASSERT(value["test"][0].IsUInteger()); + UNIT_ASSERT_EQUAL(value["test"][0].GetInteger(), 1); + UNIT_ASSERT_EQUAL(value["test"][0].GetUInteger(), 1); + UNIT_ASSERT(!value["test"][1].IsInteger()); + UNIT_ASSERT(value["test"][1].IsUInteger()); + UNIT_ASSERT_EQUAL(value["test"][1].GetUInteger(), 18446744073709551615ull); + } + } // TJsonUnsignedIntegerTest + + Y_UNIT_TEST(TJsonDoubleTest) { + { + TStringStream in; + in << "{ \"test\" : 1.0 }"; + TJsonValue value; + UNIT_ASSERT(ReadJsonTree(&in, &value)); + UNIT_ASSERT(value.Has("test")); + UNIT_ASSERT(value["test"].IsDouble()); + UNIT_ASSERT_EQUAL(value["test"].GetDouble(), 1.0); + UNIT_ASSERT_EQUAL(value["test"].GetDoubleRobust(), 1.0); + } // 1.0 + + { + TStringStream in; + in << "{ \"test\" : 1 }"; + TJsonValue value; + UNIT_ASSERT(ReadJsonTree(&in, &value)); + UNIT_ASSERT(value.Has("test")); + UNIT_ASSERT(value["test"].IsDouble()); + UNIT_ASSERT_EQUAL(value["test"].GetDouble(), 1.0); + UNIT_ASSERT_EQUAL(value["test"].GetDoubleRobust(), 1.0); + } // 1 + + { + TStringStream in; + in << "{ \"test\" : -1 }"; + TJsonValue value; + UNIT_ASSERT(ReadJsonTree(&in, &value)); + UNIT_ASSERT(value.Has("test")); + UNIT_ASSERT(value["test"].IsDouble()); + UNIT_ASSERT_EQUAL(value["test"].GetDouble(), -1.0); + UNIT_ASSERT_EQUAL(value["test"].GetDoubleRobust(), -1.0); + } // -1 + + { + TStringStream in; + in << "{ \"test\" : " << Max<ui64>() << " }"; + TJsonValue value; + UNIT_ASSERT(ReadJsonTree(&in, &value)); + UNIT_ASSERT(value.Has("test")); + UNIT_ASSERT(!value["test"].IsDouble()); + UNIT_ASSERT_EQUAL(value["test"].GetDouble(), 0.0); + UNIT_ASSERT_EQUAL(value["test"].GetDoubleRobust(), static_cast<double>(Max<ui64>())); + } // Max<ui64>() + } // TJsonDoubleTest + + Y_UNIT_TEST(TJsonInvalidTest) { + { + // No exceptions mode. + TStringStream in; + in << "{ \"test\" : }"; + TJsonValue value; + UNIT_ASSERT(!ReadJsonTree(&in, &value)); + } + + { + // Exception throwing mode. + TStringStream in; + in << "{ \"test\" : }"; + TJsonValue value; + UNIT_ASSERT_EXCEPTION(ReadJsonTree(&in, &value, true), TJsonException); + } + } + + Y_UNIT_TEST(TJsonMemoryLeakTest) { + // after https://clubs.at.yandex-team.ru/stackoverflow/3691 + TString s = "."; + NJson::TJsonValue json; + try { + TStringInput in(s); + NJson::ReadJsonTree(&in, &json, true); + } catch (...) { + } + } // TJsonMemoryLeakTest + + Y_UNIT_TEST(TJsonDuplicateKeysWithNullValuesTest) { + const TString json = "{\"\":null,\"\":\"\"}"; + + TStringInput in(json); + NJson::TJsonValue v; + UNIT_ASSERT(ReadJsonTree(&in, &v)); + UNIT_ASSERT(v.IsMap()); + UNIT_ASSERT_VALUES_EQUAL(1, v.GetMap().size()); + UNIT_ASSERT_VALUES_EQUAL("", v.GetMap().begin()->first); + UNIT_ASSERT(v.GetMap().begin()->second.IsString()); + UNIT_ASSERT_VALUES_EQUAL("", v.GetMap().begin()->second.GetString()); + } +} + + +static const TString YANDEX_STREAMING_JSON("{\"a\":1}//d{\"b\":2}"); + + +Y_UNIT_TEST_SUITE(TCompareReadJsonFast) { + Y_UNIT_TEST(NoEndl) { + NJson::TJsonValue parsed; + + bool success = NJson::ReadJsonTree(YANDEX_STREAMING_JSON, &parsed, false); + bool fast_success = NJson::ReadJsonFastTree(YANDEX_STREAMING_JSON, &parsed, false); + UNIT_ASSERT(success == fast_success); + } + Y_UNIT_TEST(WithEndl) { + NJson::TJsonValue parsed1; + NJson::TJsonValue parsed2; + + bool success = NJson::ReadJsonTree(YANDEX_STREAMING_JSON + "\n", &parsed1, false); + bool fast_success = NJson::ReadJsonFastTree(YANDEX_STREAMING_JSON + "\n", &parsed2, false); + + UNIT_ASSERT_VALUES_EQUAL(success, fast_success); + } + Y_UNIT_TEST(NoQuotes) { + TString streamingJson = "{a:1}"; + NJson::TJsonValue parsed; + + bool success = NJson::ReadJsonTree(streamingJson, &parsed, false); + bool fast_success = NJson::ReadJsonFastTree(streamingJson, &parsed, false); + UNIT_ASSERT(success != fast_success); + } +} diff --git a/library/cpp/json/ut/json_saveload_ut.cpp b/library/cpp/json/ut/json_saveload_ut.cpp new file mode 100644 index 00000000000..b480a80fe4c --- /dev/null +++ b/library/cpp/json/ut/json_saveload_ut.cpp @@ -0,0 +1,36 @@ +#include <library/cpp/json/json_value.h> + +#include <library/cpp/testing/unittest/registar.h> +#include <util/stream/buffer.h> +#include <util/generic/buffer.h> +#include <util/ysaveload.h> + +Y_UNIT_TEST_SUITE(JsonSaveLoad) { + Y_UNIT_TEST(Serialize) { + + NJson::TJsonValue expected; + + expected["ui64"] = ui64(1); + expected["i64"] = i64(2); + expected["double"] = 2.0; + expected["string"] = "text"; + expected["map"] = expected; + expected["array"].SetType(NJson::JSON_ARRAY).GetArraySafe().emplace_back(expected); + expected["null"].SetType(NJson::JSON_NULL); + expected["undefined"].SetType(NJson::JSON_UNDEFINED); + + TBuffer buffer; + { + TBufferOutput output(buffer); + ::Save(&output, expected); + } + + NJson::TJsonValue load; + { + TBufferInput input(buffer); + ::Load(&input, load); + } + + UNIT_ASSERT_EQUAL_C(expected, load, "expected: " << expected << ", got: " << load); + } +} diff --git a/library/cpp/json/ut/json_writer_ut.cpp b/library/cpp/json/ut/json_writer_ut.cpp new file mode 100644 index 00000000000..ca11d34dad9 --- /dev/null +++ b/library/cpp/json/ut/json_writer_ut.cpp @@ -0,0 +1,228 @@ +#include <library/cpp/json/json_writer.h> +#include <library/cpp/testing/unittest/registar.h> + +#include <util/stream/str.h> + +using namespace NJson; + +Y_UNIT_TEST_SUITE(TJsonWriterTest) { + Y_UNIT_TEST(SimpleWriteTest) { + TString expected1 = "{\"key1\":1,\"key2\":2,\"key3\":3"; + TString expected2 = expected1 + ",\"array\":[\"stroka\",false]"; + TString expected3 = expected2 + "}"; + + TStringStream out; + + TJsonWriter json(&out, false); + json.OpenMap(); + json.Write("key1", (ui16)1); + json.WriteKey("key2"); + json.Write((i32)2); + json.Write("key3", (ui64)3); + + UNIT_ASSERT(out.Empty()); + json.Flush(); + UNIT_ASSERT_VALUES_EQUAL(out.Str(), expected1); + + json.Write("array"); + json.OpenArray(); + json.Write("stroka"); + json.Write(false); + json.CloseArray(); + + UNIT_ASSERT_VALUES_EQUAL(out.Str(), expected1); + json.Flush(); + UNIT_ASSERT_VALUES_EQUAL(out.Str(), expected2); + + json.CloseMap(); + + UNIT_ASSERT_VALUES_EQUAL(out.Str(), expected2); + json.Flush(); + UNIT_ASSERT_VALUES_EQUAL(out.Str(), expected3); + } + + Y_UNIT_TEST(SimpleWriteValueTest) { + TString expected = "{\"key1\":null,\"key2\":{\"subkey1\":[1,{\"subsubkey\":\"test2\"},null,true],\"subkey2\":\"test\"}}"; + TJsonValue v; + v["key1"] = JSON_NULL; + v["key2"]["subkey1"].AppendValue(1); + v["key2"]["subkey1"].AppendValue(JSON_MAP)["subsubkey"] = "test2"; + v["key2"]["subkey1"].AppendValue(JSON_NULL); + v["key2"]["subkey1"].AppendValue(true); + v["key2"]["subkey2"] = "test"; + TStringStream out; + WriteJson(&out, &v); + UNIT_ASSERT_VALUES_EQUAL(out.Str(), expected); + } + + Y_UNIT_TEST(FormatOutput) { + TString expected = "{\n \"key1\":null,\n \"key2\":\n {\n \"subkey1\":\n [\n 1,\n {\n \"subsubkey\":\"test2\"\n },\n null,\n true\n ],\n \"subkey2\":\"test\"\n }\n}"; + TJsonValue v; + v["key1"] = JSON_NULL; + v["key2"]["subkey1"].AppendValue(1); + v["key2"]["subkey1"].AppendValue(JSON_MAP)["subsubkey"] = "test2"; + v["key2"]["subkey1"].AppendValue(JSON_NULL); + v["key2"]["subkey1"].AppendValue(true); + v["key2"]["subkey2"] = "test"; + TStringStream out; + WriteJson(&out, &v, true); + UNIT_ASSERT_STRINGS_EQUAL(out.Str(), expected); + } + + Y_UNIT_TEST(SortKeys) { + TString expected = "{\"a\":null,\"j\":null,\"n\":null,\"y\":null,\"z\":null}"; + TJsonValue v; + v["z"] = JSON_NULL; + v["n"] = JSON_NULL; + v["a"] = JSON_NULL; + v["y"] = JSON_NULL; + v["j"] = JSON_NULL; + TStringStream out; + WriteJson(&out, &v, false, true); + UNIT_ASSERT_STRINGS_EQUAL(out.Str(), expected); + } + + Y_UNIT_TEST(SimpleUnsignedIntegerWriteTest) { + { + TString expected = "{\"test\":1}"; + TJsonValue v; + v.InsertValue("test", 1ull); + TStringStream out; + WriteJson(&out, &v); + UNIT_ASSERT_VALUES_EQUAL(out.Str(), expected); + } // 1 + + { + TString expected = "{\"test\":-1}"; + TJsonValue v; + v.InsertValue("test", -1); + TStringStream out; + WriteJson(&out, &v); + UNIT_ASSERT_VALUES_EQUAL(out.Str(), expected); + } // -1 + + { + TString expected = "{\"test\":18446744073709551615}"; + TJsonValue v; + v.InsertValue("test", 18446744073709551615ull); + TStringStream out; + WriteJson(&out, &v); + UNIT_ASSERT_VALUES_EQUAL(out.Str(), expected); + } // 18446744073709551615 + + { + TString expected = "{\"test\":[1,18446744073709551615]}"; + TJsonValue v; + v.InsertValue("test", TJsonValue()); + v["test"].AppendValue(1); + v["test"].AppendValue(18446744073709551615ull); + TStringStream out; + WriteJson(&out, &v); + UNIT_ASSERT_VALUES_EQUAL(out.Str(), expected); + } // 18446744073709551615 + } // SimpleUnsignedIntegerWriteTest + + Y_UNIT_TEST(WriteOptionalTest) { + { + TString expected = "{\"test\":1}"; + + TStringStream out; + + TJsonWriter json(&out, false); + json.OpenMap(); + json.WriteOptional("test", MakeMaybe<int>(1)); + json.CloseMap(); + json.Flush(); + UNIT_ASSERT_VALUES_EQUAL(out.Str(), expected); + } + + { + TString expected = "{}"; + + TStringStream out; + + TMaybe<int> nothing = Nothing(); + + TJsonWriter json(&out, false); + json.OpenMap(); + json.WriteOptional("test", nothing); + json.CloseMap(); + json.Flush(); + UNIT_ASSERT_VALUES_EQUAL(out.Str(), expected); + } + + { + TString expected = "{}"; + + TStringStream out; + + TMaybe<int> empty; + + TJsonWriter json(&out, false); + json.OpenMap(); + json.WriteOptional("test", empty); + json.CloseMap(); + json.Flush(); + UNIT_ASSERT_VALUES_EQUAL(out.Str(), expected); + } + + { + TString expected = "{}"; + + TStringStream out; + + TJsonWriter json(&out, false); + json.OpenMap(); + json.WriteOptional("test", Nothing()); + json.CloseMap(); + json.Flush(); + UNIT_ASSERT_VALUES_EQUAL(out.Str(), expected); + } + } + + Y_UNIT_TEST(Callback) { + NJsonWriter::TBuf json; + json.WriteString("A"); + UNIT_ASSERT_VALUES_EQUAL(json.Str(), "\"A\""); + UNIT_ASSERT_VALUES_EQUAL(WrapJsonToCallback(json, ""), "\"A\""); + UNIT_ASSERT_VALUES_EQUAL(WrapJsonToCallback(json, "Foo"), "Foo(\"A\")"); + } + + Y_UNIT_TEST(FloatPrecision) { + const double value = 1517933989.4242; + const NJson::TJsonValue json(value); + NJson::TJsonWriterConfig config; + { + TString expected = "1517933989"; + TString actual = NJson::WriteJson(json); + UNIT_ASSERT_VALUES_EQUAL(actual, expected); + } + { + TString expected = "1517933989"; + + TStringStream ss; + NJson::WriteJson(&ss, &json, config); + TString actual = ss.Str(); + UNIT_ASSERT_VALUES_EQUAL(actual, expected); + } + { + config.DoubleNDigits = 13; + TString expected = "1517933989.424"; + + TStringStream ss; + NJson::WriteJson(&ss, &json, config); + TString actual = ss.Str(); + UNIT_ASSERT_VALUES_EQUAL(actual, expected); + } + { + config.DoubleNDigits = 6; + config.FloatToStringMode = PREC_POINT_DIGITS; + TString expected = "1517933989.424200"; + + TStringStream ss; + NJson::WriteJson(&ss, &json, config); + TString actual = ss.Str(); + UNIT_ASSERT_VALUES_EQUAL(actual, expected); + } + } +} diff --git a/library/cpp/json/ut/ya.make b/library/cpp/json/ut/ya.make new file mode 100644 index 00000000000..8e0362d84b2 --- /dev/null +++ b/library/cpp/json/ut/ya.make @@ -0,0 +1,17 @@ +OWNER(velavokr) + +UNITTEST_FOR(library/cpp/json) + +PEERDIR( + library/cpp/string_utils/relaxed_escaper +) + +SRCS( + json_reader_fast_ut.cpp + json_reader_ut.cpp + json_prettifier_ut.cpp + json_writer_ut.cpp + json_saveload_ut.cpp +) + +END() |