summaryrefslogtreecommitdiffstats
path: root/library/cpp/json/writer/json_ut.cpp
diff options
context:
space:
mode:
authorDevtools Arcadia <[email protected]>2022-02-07 18:08:42 +0300
committerDevtools Arcadia <[email protected]>2022-02-07 18:08:42 +0300
commit1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch)
treee26c9fed0de5d9873cce7e00bc214573dc2195b7 /library/cpp/json/writer/json_ut.cpp
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'library/cpp/json/writer/json_ut.cpp')
-rw-r--r--library/cpp/json/writer/json_ut.cpp307
1 files changed, 307 insertions, 0 deletions
diff --git a/library/cpp/json/writer/json_ut.cpp b/library/cpp/json/writer/json_ut.cpp
new file mode 100644
index 00000000000..9980555683f
--- /dev/null
+++ b/library/cpp/json/writer/json_ut.cpp
@@ -0,0 +1,307 @@
+#include <library/cpp/testing/unittest/registar.h>
+#include <util/system/sanitizers.h>
+
+#include "json.h"
+#include <library/cpp/json/json_value.h>
+
+#include <limits>
+
+Y_UNIT_TEST_SUITE(JsonWriter) {
+ Y_UNIT_TEST(Struct) {
+ NJsonWriter::TBuf w;
+ w.BeginList();
+ w.BeginObject()
+ .WriteKey("key")
+ .WriteString("value")
+ .UnsafeWritePair("\"xk\":13")
+ .WriteKey("key2")
+ .BeginList()
+ .BeginObject()
+ .EndObject()
+ .BeginObject()
+ .EndObject()
+ .EndList()
+ .EndObject();
+ w.WriteInt(43);
+ w.UnsafeWriteValue("\"x\"");
+ w.WriteString("...");
+ w.EndList();
+ const char* exp = "[{\"key\":\"value\",\"xk\":13,\"key2\":[{},{}]},43,\"x\",\"...\"]";
+ UNIT_ASSERT_EQUAL(w.Str(), exp);
+ }
+ Y_UNIT_TEST(EscapedString) {
+ NJsonWriter::TBuf w(NJsonWriter::HEM_ESCAPE_HTML);
+ w.WriteString(" \n \r \t \007 \b \f ' <tag> &ent; \"txt\" ");
+ TString ws = w.Str();
+ const char* exp = "\" \\n \\r \\t \\u0007 \\b \\f &#39; &lt;tag&gt; &amp;ent; &quot;txt&quot; \"";
+ UNIT_ASSERT_STRINGS_EQUAL(ws.c_str(), exp);
+ }
+ Y_UNIT_TEST(UnescapedString) {
+ NJsonWriter::TBuf w;
+ w.WriteString(" \n \r \t \b \f '; -- <tag> &ent; \"txt\"", NJsonWriter::HEM_DONT_ESCAPE_HTML);
+ TString ws = w.Str();
+ const char* exp = "\" \\n \\r \\t \\b \\f \\u0027; -- \\u003Ctag\\u003E &ent; \\\"txt\\\"\"";
+ UNIT_ASSERT_STRINGS_EQUAL(ws.c_str(), exp);
+ }
+ Y_UNIT_TEST(UnescapedChaining) {
+ NJsonWriter::TBuf w(NJsonWriter::HEM_DONT_ESCAPE_HTML);
+ w.UnsafeWriteRawBytes("(", 1);
+ w.BeginList().WriteString("<>&'\\").BeginList();
+ w.EndList().EndList();
+ TString ws = w.Str();
+ const char* exp = "([\"\\u003C\\u003E&\\u0027\\\\\",[]]";
+ UNIT_ASSERT_STRINGS_EQUAL(ws.c_str(), exp);
+ }
+ Y_UNIT_TEST(Utf8) {
+ TString ws = NJsonWriter::TBuf().WriteString("яЯ σΣ ש א").Str();
+ const char* exp = "\"яЯ σΣ ש א\"";
+ UNIT_ASSERT_STRINGS_EQUAL(ws.c_str(), exp);
+ }
+ Y_UNIT_TEST(WrongObject) {
+ NJsonWriter::TBuf w;
+ w.BeginObject();
+ UNIT_ASSERT_EXCEPTION(w.WriteString("hehe"), NJsonWriter::TError);
+ }
+ Y_UNIT_TEST(WrongList) {
+ NJsonWriter::TBuf w;
+ w.BeginList();
+ UNIT_ASSERT_EXCEPTION(w.WriteKey("hehe"), NJsonWriter::TError);
+ }
+ Y_UNIT_TEST(Incomplete) {
+ NJsonWriter::TBuf w;
+ w.BeginList();
+ UNIT_ASSERT_EXCEPTION(w.Str(), NJsonWriter::TError);
+ }
+ Y_UNIT_TEST(BareKey) {
+ NJsonWriter::TBuf w;
+ w.BeginObject()
+ .CompatWriteKeyWithoutQuotes("p")
+ .WriteInt(1)
+ .CompatWriteKeyWithoutQuotes("n")
+ .WriteInt(0)
+ .EndObject();
+ TString ws = w.Str();
+ const char* exp = "{p:1,n:0}";
+ UNIT_ASSERT_STRINGS_EQUAL(ws.c_str(), exp);
+ }
+ Y_UNIT_TEST(UnescapedStringInObject) {
+ NJsonWriter::TBuf w(NJsonWriter::HEM_DONT_ESCAPE_HTML);
+ w.BeginObject().WriteKey("key").WriteString("</&>'").EndObject();
+ TString ws = w.Str();
+ const char* exp = "{\"key\":\"\\u003C\\/&\\u003E\\u0027\"}";
+ UNIT_ASSERT_STRINGS_EQUAL(ws.c_str(), exp);
+ }
+ Y_UNIT_TEST(ForeignStreamStr) {
+ NJsonWriter::TBuf w(NJsonWriter::HEM_DONT_ESCAPE_HTML, &Cerr);
+ UNIT_ASSERT_EXCEPTION(w.Str(), NJsonWriter::TError);
+ }
+ Y_UNIT_TEST(ForeignStreamValue) {
+ TStringStream ss;
+ NJsonWriter::TBuf w(NJsonWriter::HEM_DONT_ESCAPE_HTML, &ss);
+ w.WriteInt(1543);
+ UNIT_ASSERT_STRINGS_EQUAL(ss.Str(), "1543");
+ }
+ Y_UNIT_TEST(Indentation) {
+ NJsonWriter::TBuf w(NJsonWriter::HEM_DONT_ESCAPE_HTML);
+ w.SetIndentSpaces(2);
+ w.BeginList()
+ .WriteInt(1)
+ .WriteString("hello")
+ .BeginObject()
+ .WriteKey("abc")
+ .WriteInt(3)
+ .WriteKey("def")
+ .WriteInt(4)
+ .EndObject()
+ .EndList();
+ const char* exp = "[\n"
+ " 1,\n"
+ " \"hello\",\n"
+ " {\n"
+ " \"abc\":3,\n"
+ " \"def\":4\n"
+ " }\n"
+ "]";
+ UNIT_ASSERT_STRINGS_EQUAL(exp, w.Str());
+ }
+ Y_UNIT_TEST(WriteJsonValue) {
+ using namespace NJson;
+ TJsonValue val;
+ val.AppendValue(1);
+ val.AppendValue("2");
+ val.AppendValue(3.5);
+ TJsonValue obj;
+ obj.InsertValue("key", TJsonValue("value"));
+
+ val.AppendValue(obj);
+ val.AppendValue(TJsonValue(JSON_NULL));
+
+ NJsonWriter::TBuf w(NJsonWriter::HEM_DONT_ESCAPE_HTML);
+ w.WriteJsonValue(&val);
+
+ const char exp[] = "[1,\"2\",3.5,{\"key\":\"value\"},null]";
+ UNIT_ASSERT_STRINGS_EQUAL(exp, w.Str());
+ }
+ Y_UNIT_TEST(WriteJsonValueSorted) {
+ using namespace NJson;
+ TJsonValue val;
+ val.InsertValue("1", TJsonValue(1));
+ val.InsertValue("2", TJsonValue(2));
+
+ TJsonValue obj;
+ obj.InsertValue("zero", TJsonValue(0));
+ obj.InsertValue("succ", TJsonValue(1));
+ val.InsertValue("0", obj);
+
+ NJsonWriter::TBuf w(NJsonWriter::HEM_DONT_ESCAPE_HTML);
+ w.WriteJsonValue(&val, true);
+
+ const char exp[] = "{\"0\":{\"succ\":1,\"zero\":0},\"1\":1,\"2\":2}";
+ UNIT_ASSERT_STRINGS_EQUAL(exp, w.Str());
+ }
+ Y_UNIT_TEST(Unescaped) {
+ NJsonWriter::TBuf buf(NJsonWriter::HEM_UNSAFE);
+ buf.WriteString("</security>'");
+ UNIT_ASSERT_STRINGS_EQUAL("\"</security>'\"", buf.Str());
+ }
+ Y_UNIT_TEST(LittleBobbyJsonp) {
+ NJsonWriter::TBuf buf;
+ buf.WriteString("hello\xe2\x80\xa8\xe2\x80\xa9stranger");
+ UNIT_ASSERT_STRINGS_EQUAL("\"hello\\u2028\\u2029stranger\"", buf.Str());
+ }
+ Y_UNIT_TEST(LittleBobbyInvalid) {
+ NJsonWriter::TBuf buf;
+ TStringBuf incomplete("\xe2\x80\xa8", 2);
+ buf.WriteString(incomplete);
+ // garbage in - garbage out
+ UNIT_ASSERT_STRINGS_EQUAL("\"\xe2\x80\"", buf.Str());
+ }
+ Y_UNIT_TEST(OverlyZealous) {
+ NJsonWriter::TBuf buf;
+ buf.WriteString("—");
+ UNIT_ASSERT_STRINGS_EQUAL("\"—\"", buf.Str());
+ }
+ Y_UNIT_TEST(RelaxedEscaping) {
+ NJsonWriter::TBuf buf(NJsonWriter::HEM_RELAXED);
+ buf.WriteString("</>");
+ UNIT_ASSERT_STRINGS_EQUAL("\"\\u003C/\\u003E\"", buf.Str());
+ }
+
+ Y_UNIT_TEST(FloatFormatting) {
+ NJsonWriter::TBuf buf(NJsonWriter::HEM_DONT_ESCAPE_HTML);
+ buf.BeginList()
+ .WriteFloat(0.12345678987654321f)
+ .WriteDouble(0.12345678987654321)
+ .WriteFloat(0.315501, PREC_NDIGITS, 3)
+ .WriteFloat(244.13854, PREC_NDIGITS, 4)
+ .WriteFloat(10385.8324, PREC_POINT_DIGITS, 2)
+ .BeginObject()
+ .WriteKey("1")
+ .WriteDouble(1111.71, PREC_POINT_DIGITS, 0)
+ .WriteKey("2")
+ .WriteDouble(1111.71, PREC_NDIGITS, 1)
+ .EndObject()
+ .EndList();
+ const char exp[] = "[0.123457,0.1234567899,0.316,244.1,10385.83,{\"1\":1112,\"2\":1e+03}]";
+ UNIT_ASSERT_STRINGS_EQUAL(exp, buf.Str());
+ }
+
+ Y_UNIT_TEST(NanFormatting) {
+ {
+ NJsonWriter::TBuf buf;
+ buf.BeginObject();
+ buf.WriteKey("nanvalue");
+ UNIT_ASSERT_EXCEPTION(buf.WriteFloat(std::numeric_limits<double>::quiet_NaN()), yexception);
+ }
+
+ {
+ NJsonWriter::TBuf buf;
+ buf.BeginObject();
+ buf.WriteKey("infvalue");
+ UNIT_ASSERT_EXCEPTION(buf.WriteFloat(std::numeric_limits<double>::infinity()), yexception);
+ }
+
+ {
+ NJsonWriter::TBuf buf;
+ buf.BeginList();
+ UNIT_ASSERT_EXCEPTION(buf.WriteFloat(std::numeric_limits<double>::quiet_NaN()), yexception);
+ }
+
+ {
+ NJsonWriter::TBuf buf;
+ buf.BeginList();
+ UNIT_ASSERT_EXCEPTION(buf.WriteFloat(std::numeric_limits<double>::infinity()), yexception);
+ }
+
+ {
+ NJsonWriter::TBuf buf;
+ buf.SetWriteNanAsString();
+
+ buf.BeginObject()
+ .WriteKey("nanvalue")
+ .WriteFloat(std::numeric_limits<double>::quiet_NaN())
+ .WriteKey("infvalue")
+ .WriteFloat(std::numeric_limits<double>::infinity())
+ .WriteKey("minus_infvalue")
+ .WriteFloat(-std::numeric_limits<float>::infinity())
+ .WriteKey("l")
+ .BeginList()
+ .WriteFloat(std::numeric_limits<float>::quiet_NaN())
+ .EndList()
+ .EndObject();
+
+ UNIT_ASSERT_STRINGS_EQUAL(buf.Str(), R"raw_json({"nanvalue":"nan","infvalue":"inf","minus_infvalue":"-inf","l":["nan"]})raw_json");
+ }
+
+ {
+ NJsonWriter::TBuf buf;
+ buf.BeginObject()
+ .WriteKey("<>&")
+ .WriteString("Ololo")
+ .UnsafeWriteKey("<>&")
+ .WriteString("Ololo2")
+ .EndObject();
+
+ UNIT_ASSERT_STRINGS_EQUAL(buf.Str(), R"({"\u003C\u003E&":"Ololo","<>&":"Ololo2"})");
+ }
+ }
+
+ Y_UNIT_TEST(WriteUninitializedBoolDoesntCrashProgram) {
+ // makes sense only in release build w/ address sanitizer
+ //
+ // passing uninitialized boolean into WriteBool can make cleverly optimized code which is emitted by compiler crash program
+ // https://stackoverflow.com/questions/54120862/does-the-c-standard-allow-for-an-uninitialized-bool-to-crash-a-program
+
+ // looks like compiler can detect UB at compile time in simple cases, but not in this one
+ class TSensorConf {
+ public:
+ class TAggrRuleItem {
+ public:
+ TVector<TString> Cond;
+ TVector<TString> Target;
+ };
+
+ TString ToString() const {
+ NJson::TJsonValue jsonValue;
+ NJsonWriter::TBuf jsonOutput;
+ jsonOutput.BeginObject()
+ .WriteKey("rawDataMemOnly").WriteBool(RawDataMemOnly)
+ .WriteKey("aggrRules").BeginList();
+
+ jsonOutput.EndList()
+ .EndObject();
+
+ return jsonOutput.Str();
+ }
+
+ TVector<TAggrRuleItem> AggrRules;
+ bool RawDataMemOnly;
+ };
+
+ TSensorConf s;
+ NSan::Unpoison(&s.RawDataMemOnly, sizeof(s.RawDataMemOnly));
+ auto p = s.ToString();
+ // doesn't really matter
+ UNIT_ASSERT(!p.empty());
+ }
+}