aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/json/ut
diff options
context:
space:
mode:
authorDevtools Arcadia <arcadia-devtools@yandex-team.ru>2022-02-07 18:08:42 +0300
committerDevtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net>2022-02-07 18:08:42 +0300
commit1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch)
treee26c9fed0de5d9873cce7e00bc214573dc2195b7 /library/cpp/json/ut
downloadydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'library/cpp/json/ut')
-rw-r--r--library/cpp/json/ut/json_prettifier_ut.cpp204
-rw-r--r--library/cpp/json/ut/json_reader_fast_ut.cpp304
-rw-r--r--library/cpp/json/ut/json_reader_ut.cpp430
-rw-r--r--library/cpp/json/ut/json_saveload_ut.cpp36
-rw-r--r--library/cpp/json/ut/json_writer_ut.cpp228
-rw-r--r--library/cpp/json/ut/ya.make17
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()