aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/scheme/tests
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/scheme/tests
downloadydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'library/cpp/scheme/tests')
-rw-r--r--library/cpp/scheme/tests/fuzz_json/fuzz_json.cpp6
-rw-r--r--library/cpp/scheme/tests/fuzz_json/lib/fuzz_json.cpp115
-rw-r--r--library/cpp/scheme/tests/fuzz_json/lib/fuzz_json.h7
-rw-r--r--library/cpp/scheme/tests/fuzz_json/lib/ya.make18
-rw-r--r--library/cpp/scheme/tests/fuzz_json/ya.make20
-rw-r--r--library/cpp/scheme/tests/fuzz_ops/fuzz_ops.cpp6
-rw-r--r--library/cpp/scheme/tests/fuzz_ops/lib/fuzz_ops.cpp37
-rw-r--r--library/cpp/scheme/tests/fuzz_ops/lib/fuzz_ops.h7
-rw-r--r--library/cpp/scheme/tests/fuzz_ops/lib/vm_apply.cpp302
-rw-r--r--library/cpp/scheme/tests/fuzz_ops/lib/vm_apply.h9
-rw-r--r--library/cpp/scheme/tests/fuzz_ops/lib/vm_defs.cpp168
-rw-r--r--library/cpp/scheme/tests/fuzz_ops/lib/vm_defs.h277
-rw-r--r--library/cpp/scheme/tests/fuzz_ops/lib/vm_parse.cpp265
-rw-r--r--library/cpp/scheme/tests/fuzz_ops/lib/vm_parse.h16
-rw-r--r--library/cpp/scheme/tests/fuzz_ops/lib/ya.make23
-rw-r--r--library/cpp/scheme/tests/fuzz_ops/ut/vm_parse_ut.cpp225
-rw-r--r--library/cpp/scheme/tests/fuzz_ops/ut/ya.make15
-rw-r--r--library/cpp/scheme/tests/fuzz_ops/ya.make18
-rw-r--r--library/cpp/scheme/tests/ut/fuzz_ops_found_bugs_ut.cpp12
-rw-r--r--library/cpp/scheme/tests/ut/scheme_cast_ut.cpp162
-rw-r--r--library/cpp/scheme/tests/ut/scheme_json_ut.cpp161
-rw-r--r--library/cpp/scheme/tests/ut/scheme_merge_ut.cpp172
-rw-r--r--library/cpp/scheme/tests/ut/scheme_path_ut.cpp159
-rw-r--r--library/cpp/scheme/tests/ut/scheme_proto_ut.cpp220
-rw-r--r--library/cpp/scheme/tests/ut/scheme_ut.cpp879
-rw-r--r--library/cpp/scheme/tests/ut/scheme_ut.proto84
-rw-r--r--library/cpp/scheme/tests/ut/ya.make24
-rw-r--r--library/cpp/scheme/tests/ya.make13
28 files changed, 3420 insertions, 0 deletions
diff --git a/library/cpp/scheme/tests/fuzz_json/fuzz_json.cpp b/library/cpp/scheme/tests/fuzz_json/fuzz_json.cpp
new file mode 100644
index 0000000000..8d4c0fa8a0
--- /dev/null
+++ b/library/cpp/scheme/tests/fuzz_json/fuzz_json.cpp
@@ -0,0 +1,6 @@
+#include <library/cpp/scheme/tests/fuzz_json/lib/fuzz_json.h>
+
+extern "C" int LLVMFuzzerTestOneInput(const ui8* wireData, const size_t wireSize) {
+ NSc::NUt::FuzzJson({(const char*)wireData, wireSize});
+ return 0;
+}
diff --git a/library/cpp/scheme/tests/fuzz_json/lib/fuzz_json.cpp b/library/cpp/scheme/tests/fuzz_json/lib/fuzz_json.cpp
new file mode 100644
index 0000000000..7c16527c23
--- /dev/null
+++ b/library/cpp/scheme/tests/fuzz_json/lib/fuzz_json.cpp
@@ -0,0 +1,115 @@
+#include "fuzz_json.h"
+#include "util/generic/fwd.h"
+
+#include <library/cpp/scheme/scheme.h>
+#include <util/stream/null.h>
+
+namespace {
+ static constexpr size_t MAX_DEPTH = 4;
+ static constexpr size_t MAX_PATH_LEN = 256;
+ static constexpr size_t MAX_ITERATIONS = 4;
+
+ void SplitOnDepth(const TStringBuf src, const size_t depth, const size_t maxPathLen,
+ TStringBuf& left, TStringBuf& right)
+ {
+ size_t pos = 0;
+ size_t prevPos = 0;
+ for(size_t i = 0; i < depth; ++i) {
+ if (pos > maxPathLen) {
+ break;
+ }
+ prevPos = pos;
+ pos = src.find_first_of(TStringBuf("/]"), pos + 1);
+ if (pos == TStringBuf::npos) {
+ break;
+ }
+ }
+ if (pos == TStringBuf::npos && prevPos > 0) {
+ pos = prevPos;
+ }
+ if (src.length() > maxPathLen) {
+ if (pos == TStringBuf::npos || pos > maxPathLen) {
+ pos = maxPathLen;
+ }
+ }
+ if (pos == TStringBuf::npos || pos == 0) {
+ left = src;
+ right = TStringBuf();
+ } else {
+ src.SplitAt(pos + 1, left, right);
+ }
+ }
+
+ TString tmp;
+ //Limit max array size in the path to 256
+ TStringBuf ProcessPath(TStringBuf path) {
+ size_t pos = 0;
+ while(pos != TStringBuf::npos) {
+ pos = path.find(']', pos + 1);
+ if (pos == TStringBuf::npos) {
+ continue;
+ }
+ size_t open = path.rfind('[', pos);
+ if (open == TStringBuf::npos) {
+ continue;
+ }
+ bool allDigit = true;
+ for(size_t i = open + 1; i < pos; ++i) {
+ if (path[i] < '0' || path[i] > '9') {
+ allDigit = false;
+ break;
+ }
+ }
+ if (!allDigit) {
+ continue;
+ }
+ if (pos - open > 4) {
+ TString str = TString::Join(path.Head(open + 1), "256", path.Tail(pos));
+ tmp = std::move(str);
+ path = tmp;
+ pos = (open + 1) + 3;
+ continue;
+ }
+ }
+ return path;
+ }
+}
+
+namespace NSc::NUt {
+
+
+ void FuzzJson(TStringBuf wire) {
+ if (wire.size() < 2) {
+ return;
+ }
+
+
+ ProcessPath("[123][1234][12][2134][12312312][1][12]");
+ ui8 len1 = wire[0];
+ ui8 len2 = wire[1];
+ wire.Skip(2);
+ auto json1 = wire.NextTokAt(len1);
+ auto json2 = wire.NextTokAt(len2);
+ NSc::TValue val1 = NSc::TValue::FromJson(json1);
+ NSc::TValue val2 = NSc::TValue::FromJson(json2);
+ NSc::TValue val3;
+ val3.MergeUpdate(val1);
+
+ size_t i = 0;
+ while (!wire.empty()) {
+ TStringBuf path;
+ SplitOnDepth(wire, MAX_DEPTH, MAX_PATH_LEN, path, wire);
+ path = ProcessPath(path);
+ if (auto* target = val3.TrySelectOrAdd(path)) {
+ target->MergeUpdate(val2);
+ }
+ ++i;
+ // Release memory since there are up to MAX_DICT_SIZE * MAX_DEPTH elements
+ if (i > MAX_ITERATIONS) {
+ Cnull << val3.ToJson();
+ val3 = NSc::TValue();
+ }
+ }
+ Cnull << val3.ToJson();
+ }
+}
diff --git a/library/cpp/scheme/tests/fuzz_json/lib/fuzz_json.h b/library/cpp/scheme/tests/fuzz_json/lib/fuzz_json.h
new file mode 100644
index 0000000000..f8cf7a4770
--- /dev/null
+++ b/library/cpp/scheme/tests/fuzz_json/lib/fuzz_json.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#include <util/generic/strbuf.h>
+
+namespace NSc::NUt {
+ void FuzzJson(TStringBuf wire);
+}
diff --git a/library/cpp/scheme/tests/fuzz_json/lib/ya.make b/library/cpp/scheme/tests/fuzz_json/lib/ya.make
new file mode 100644
index 0000000000..b30a6c9350
--- /dev/null
+++ b/library/cpp/scheme/tests/fuzz_json/lib/ya.make
@@ -0,0 +1,18 @@
+LIBRARY()
+
+OWNER(
+ g:blender
+ g:middle
+ g:upper
+ velavokr
+)
+
+SRCS(
+ fuzz_json.cpp
+)
+
+PEERDIR(
+ library/cpp/scheme
+)
+
+END()
diff --git a/library/cpp/scheme/tests/fuzz_json/ya.make b/library/cpp/scheme/tests/fuzz_json/ya.make
new file mode 100644
index 0000000000..0d91c70585
--- /dev/null
+++ b/library/cpp/scheme/tests/fuzz_json/ya.make
@@ -0,0 +1,20 @@
+FUZZ()
+
+OWNER(
+ g:blender
+ g:middle
+ g:upper
+ velavokr
+)
+
+SIZE(MEDIUM)
+
+SRCS(
+ fuzz_json.cpp
+)
+
+PEERDIR(
+ library/cpp/scheme/tests/fuzz_json/lib
+)
+
+END()
diff --git a/library/cpp/scheme/tests/fuzz_ops/fuzz_ops.cpp b/library/cpp/scheme/tests/fuzz_ops/fuzz_ops.cpp
new file mode 100644
index 0000000000..facde50f5a
--- /dev/null
+++ b/library/cpp/scheme/tests/fuzz_ops/fuzz_ops.cpp
@@ -0,0 +1,6 @@
+#include <library/cpp/scheme/tests/fuzz_ops/lib/fuzz_ops.h>
+
+extern "C" int LLVMFuzzerTestOneInput(const ui8* wireData, const size_t wireSize) {
+ NSc::NUt::FuzzOps({(const char*)wireData, wireSize}, false);
+ return 0;
+}
diff --git a/library/cpp/scheme/tests/fuzz_ops/lib/fuzz_ops.cpp b/library/cpp/scheme/tests/fuzz_ops/lib/fuzz_ops.cpp
new file mode 100644
index 0000000000..8a7facba24
--- /dev/null
+++ b/library/cpp/scheme/tests/fuzz_ops/lib/fuzz_ops.cpp
@@ -0,0 +1,37 @@
+#include "fuzz_ops.h"
+#include "vm_apply.h"
+#include "vm_defs.h"
+#include "vm_parse.h"
+
+#include <library/cpp/bit_io/bitinput.h>
+
+#include <library/cpp/scheme/scheme.h>
+#include <library/cpp/scheme/scimpl_private.h>
+
+#include <util/generic/maybe.h>
+
+namespace NSc::NUt {
+
+ void FuzzOps(TStringBuf wire, bool log) {
+ if (log) {
+ NImpl::GetTlsInstance<NImpl::TSelfLoopContext>().ReportingMode = NImpl::TSelfLoopContext::EMode::Stderr;
+ }
+
+ // We start with a single TValue node
+ TVMState st {wire, 1, 0};
+
+ while (auto act = ParseNextAction(st)) {
+ if (log) {
+ Cerr << " STATE: " << st.ToString() << Endl;
+ Cerr << "ACTION: " << (act ? act->ToString() : TString("(empty)")) << Endl;
+ }
+
+ if (!ApplyNextAction(st, *act)) {
+ break;
+ }
+ if (!NSc::TValue::DefaultValue().IsNull()) {
+ std::terminate();
+ }
+ }
+ }
+}
diff --git a/library/cpp/scheme/tests/fuzz_ops/lib/fuzz_ops.h b/library/cpp/scheme/tests/fuzz_ops/lib/fuzz_ops.h
new file mode 100644
index 0000000000..26ba42eaef
--- /dev/null
+++ b/library/cpp/scheme/tests/fuzz_ops/lib/fuzz_ops.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#include <util/generic/strbuf.h>
+
+namespace NSc::NUt {
+ void FuzzOps(TStringBuf wire, bool log);
+}
diff --git a/library/cpp/scheme/tests/fuzz_ops/lib/vm_apply.cpp b/library/cpp/scheme/tests/fuzz_ops/lib/vm_apply.cpp
new file mode 100644
index 0000000000..ada7b8854f
--- /dev/null
+++ b/library/cpp/scheme/tests/fuzz_ops/lib/vm_apply.cpp
@@ -0,0 +1,302 @@
+#include "vm_apply.h"
+
+namespace NSc::NUt {
+
+#define Y_GEN_TRY_OP(op) \
+ if (!op) { return false; }
+
+#define Y_GEN_SRC_OP(op, arg, st, act) \
+ switch (act.GetSrc(arg).Type) { \
+ case TSrc::T_LREF__POS: \
+ op(st.LRef(act.GetSrc(arg).Pos)); \
+ break; \
+ case TSrc::T_CREF__POS: \
+ op(st.CRef(act.GetSrc(arg).Pos)); \
+ break; \
+ case TSrc::T_RREF__POS: \
+ op(st.RRef(act.GetSrc(arg).Pos)); \
+ break; \
+ default: \
+ Y_FAIL(); \
+ }
+
+
+#define Y_GEN_SRC_TRY_OP(op, arg, st, act) \
+ if (st.CRef(act.GetSrc(arg).Pos).IsSameOrAncestorOf(st.Current())) { \
+ return false; \
+ } \
+ Y_GEN_SRC_OP(op, arg, st, act);
+
+
+#define Y_GEN_DST_OP(op, arg, st, act) \
+ switch (act.GetDst(arg).Type) { \
+ case TDst::T_CREATE_BACK_LREF: \
+ Y_GEN_TRY_OP(st.TryPushBack(op)) \
+ break; \
+ case TDst::T_CREATE_BACK_CREF: \
+ Y_GEN_TRY_OP(st.TryPushBack(std::as_const(op))) \
+ break; \
+ case TDst::T_CREATE_BACK_RREF: \
+ Y_GEN_TRY_OP(st.TryPushBack(std::move(op))) \
+ break; \
+ case TDst::T_CREATE_FRONT_LREF: \
+ Y_GEN_TRY_OP(st.TryPushFront(op)) \
+ break; \
+ case TDst::T_CREATE_FRONT_CREF: \
+ Y_GEN_TRY_OP(st.TryPushFront(std::as_const(op))) \
+ break; \
+ case TDst::T_CREATE_FRONT_RREF: \
+ Y_GEN_TRY_OP(st.TryPushFront(std::move(op))) \
+ break; \
+ case TDst::T_LREF__POS: \
+ st.LRef(act.GetDst(arg).Pos) = op; \
+ break; \
+ case TDst::T_CREF__POS: \
+ st.LRef(act.GetDst(arg).Pos) = std::as_const(op); \
+ break; \
+ case TDst::T_RREF__POS: \
+ st.LRef(act.GetDst(arg).Pos) = std::move(op); \
+ break; \
+ default: \
+ Y_FAIL(); \
+ }
+
+
+#define Y_GEN_REF_OP(op, arg, st, act) \
+ switch (act.GetRef(arg).Type) { \
+ case TRef::T_CREATE_BACK: \
+ Y_GEN_TRY_OP(st.TryPushBack(op)) \
+ break; \
+ case TRef::T_CREATE_FRONT: \
+ Y_GEN_TRY_OP(st.TryPushFront(op)) \
+ break; \
+ case TRef::T_REF__POS: \
+ st.LRef(act.GetRef(arg).Pos) = op; \
+ break; \
+ default: \
+ Y_FAIL(); \
+ }
+
+#define Y_GEN_PTR_OP(op, arg, st, act) \
+ if (auto* r = (op)) { \
+ switch (act.GetRef(arg).Type) { \
+ case TRef::T_CREATE_BACK: \
+ Y_GEN_TRY_OP(st.TryPushBack(*r)) \
+ break; \
+ case TRef::T_CREATE_FRONT: \
+ Y_GEN_TRY_OP(st.TryPushFront(*r)) \
+ break; \
+ case TRef::T_REF__POS: \
+ st.LRef(act.GetRef(arg).Pos) = *r; \
+ break; \
+ default: \
+ Y_FAIL(); \
+ } \
+ }
+
+ bool ApplyNextAction(TVMState& st, TVMAction act) {
+ switch (act.Type) {
+ case VMA_JMP__POS:
+ st.Pos = act.GetPos(0);
+ return true;
+
+ case VMA_CREATE_BACK:
+ Y_GEN_TRY_OP(st.TryPushBack())
+ return true;
+
+ case VMA_CREATE_BACK__SRC:
+ switch (act.GetSrc(0).Type) {
+ case TSrc::T_LREF__POS:
+ Y_GEN_TRY_OP(st.TryPushBack(st.LRef(act.GetSrc(0).Pos)))
+ break;
+ case TSrc::T_CREF__POS:
+ Y_GEN_TRY_OP(st.TryPushBack(st.CRef(act.GetSrc(0).Pos)))
+ break;
+ case TSrc::T_RREF__POS:
+ Y_GEN_TRY_OP(st.TryPushBack(st.RRef(act.GetSrc(0).Pos)))
+ break;
+ default:
+ Y_FAIL();
+ }
+
+ return true;
+
+ case VMA_CREATE_FRONT:
+ Y_GEN_TRY_OP(st.TryPushFront())
+ return true;
+
+ case VMA_CREATE_FRONT__SRC:
+ switch (act.GetSrc(0).Type) {
+ case TSrc::T_LREF__POS:
+ Y_GEN_TRY_OP(st.TryPushFront(st.LRef(act.GetSrc(0).Pos)))
+ break;
+ case TSrc::T_CREF__POS:
+ Y_GEN_TRY_OP(st.TryPushFront(st.CRef(act.GetSrc(0).Pos)))
+ break;
+ case TSrc::T_RREF__POS:
+ Y_GEN_TRY_OP(st.TryPushFront(st.RRef(act.GetSrc(0).Pos)))
+ break;
+ default:
+ Y_FAIL();
+ }
+ return true;
+
+ case VMA_DESTROY_BACK:
+ return st.TryPopBack();
+
+ case VMA_DESTROY_FRONT:
+ return st.TryPopFront();
+
+ case VMA_ASSIGN__SRC:
+ Y_GEN_SRC_OP(st.Current() = , 0, st, act);
+ return true;
+
+ case VMA_SET_STRING__IDX:
+ st.Current().SetString(act.GetString(0));
+ return true;
+
+ case VMA_SET_INT_NUMBER__IDX:
+ st.Current().SetIntNumber(act.GetIntNumber(0));
+ return true;
+
+ case VMA_SET_DICT:
+ st.Current().SetDict();
+ return true;
+
+ case VMA_SET_ARRAY:
+ st.Current().SetArray();
+ return true;
+
+ case VMA_SET_NULL:
+ st.Current().SetNull();
+ return true;
+
+ case VMA_GET_JSON:
+ st.Current().ToJson();
+ return true;
+
+ case VMA_ARRAY_CLEAR:
+ st.Current().ClearArray();
+ return true;
+
+ case VMA_ARRAY_PUSH:
+ st.Current().Push();
+ return true;
+
+ case VMA_ARRAY_PUSH__SRC:
+ Y_GEN_SRC_TRY_OP(st.Current().Push, 0, st, act);
+ return true;
+
+ case VMA_ARRAY_PUSH__DST:
+ Y_GEN_DST_OP(st.Current().Push(), 0, st, act);
+ return true;
+
+ case VMA_ARRAY_POP__REF:
+ Y_GEN_REF_OP(st.Current().Pop(), 0, st, act);
+ return true;
+
+ case VMA_ARRAY_INSERT__IDX:
+ st.Current().Insert(act.GetIdx(0));
+ return true;
+
+ case VMA_ARRAY_INSERT__IDX_SRC:
+ Y_GEN_SRC_TRY_OP(st.Current().Insert(act.GetIdx(0)) = , 1, st, act);
+ return true;
+
+ case VMA_ARRAY_INSERT__IDX_DST:
+ Y_GEN_DST_OP(st.Current().Insert(act.GetIdx(0)), 1, st, act);
+ return true;
+
+ case VMA_ARRAY_DELETE__IDX_REF:
+ Y_GEN_REF_OP(st.Current().Delete(act.GetIdx(0)), 1, st, act);
+ return true;
+
+ case VMA_ARRAY_GET__IDX_REF:
+ Y_GEN_REF_OP(st.Current().Get(act.GetIdx(0)), 1, st, act);
+ return true;
+
+ case VMA_ARRAY_GET_OR_ADD__IDX:
+ st.Current().GetOrAdd(act.GetIdx(0));
+ return true;
+
+ case VMA_ARRAY_GET_OR_ADD__IDX_SRC:
+ Y_GEN_SRC_TRY_OP(st.Current().GetOrAdd(act.GetIdx(0)) = , 1, st, act);
+ return true;
+
+ case VMA_ARRAY_GET_OR_ADD__IDX_DST:
+ Y_GEN_DST_OP(st.Current().GetOrAdd(act.GetIdx(0)), 1, st, act);
+ return true;
+
+ case VMA_ARRAY_GET_NO_ADD__IDX_REF:
+ Y_GEN_PTR_OP(st.Current().GetNoAdd(act.GetIdx(0)), 1, st, act);
+ return true;
+
+ case VMA_DICT_CLEAR:
+ st.Current().ClearDict();
+ return true;
+
+ case VMA_DICT_DELETE__IDX:
+ st.Current().Delete(act.GetKey(0));
+ return true;
+
+ case VMA_DICT_DELETE__IDX_REF:
+ Y_GEN_REF_OP(st.Current().Delete(act.GetKey(0)), 1, st, act);
+ return true;
+
+ case VMA_DICT_GET__IDX_REF:
+ Y_GEN_REF_OP(st.Current().Get(act.GetKey(0)), 1, st, act);
+ return true;
+
+ case VMA_DICT_GET_OR_ADD__IDX:
+ st.Current().GetOrAdd(act.GetKey(0));
+ return true;
+
+ case VMA_DICT_GET_OR_ADD__IDX_SRC:
+ Y_GEN_SRC_TRY_OP(st.Current().GetOrAdd(act.GetKey(0)) = , 1, st, act);
+ return true;
+
+ case VMA_DICT_GET_OR_ADD__IDX_DST:
+ Y_GEN_DST_OP(st.Current().GetOrAdd(act.GetKey(0)), 1, st, act);
+ return true;
+
+ case VMA_DICT_GET_NO_ADD__IDX_REF:
+ Y_GEN_PTR_OP(st.Current().GetNoAdd(act.GetKey(0)), 1, st, act);
+ return true;
+
+ case VMA_MERGE_UPDATE__POS:
+ st.Current().MergeUpdate(st.LRef(act.GetPos(0)));
+ return true;
+
+ case VMA_MERGE_REVERSE__POS:
+ st.Current().ReverseMerge(st.LRef(act.GetPos(0)));
+ return true;
+
+ case VMA_MERGE_COPY_FROM__POS:
+ st.Current().CopyFrom(st.LRef(act.GetPos(0)));
+ return true;
+
+ case VMA_SWAP__POS:
+ st.Current().Swap(st.LRef(act.GetPos(0)));
+ return true;
+
+ case VMA_EQUAL__POS_POS:
+ TValue::Equal(st.CRef(act.GetPos(0)), st.CRef(act.GetPos(1)));
+ return true;
+
+ case VMA_SELECT_NO_ADD__PATH_REF:
+ Y_GEN_REF_OP(st.Current().TrySelect(act.GetPath(0)), 1, st, act);
+ return true;
+
+ case VMA_SELECT_OR_ADD__PATH_REF:
+ Y_GEN_PTR_OP(st.Current().TrySelectOrAdd(act.GetPath(0)), 1, st, act);
+ return true;
+
+ case VMA_SELECT_AND_DELETE__PATH_REF:
+ Y_GEN_REF_OP(st.Current().TrySelectAndDelete(act.GetPath(0)), 1, st, act);
+ return true;
+
+ default:
+ Y_FAIL();
+ }
+ }
+}
diff --git a/library/cpp/scheme/tests/fuzz_ops/lib/vm_apply.h b/library/cpp/scheme/tests/fuzz_ops/lib/vm_apply.h
new file mode 100644
index 0000000000..82906b53fb
--- /dev/null
+++ b/library/cpp/scheme/tests/fuzz_ops/lib/vm_apply.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include "vm_defs.h"
+
+namespace NSc::NUt {
+
+ bool ApplyNextAction(TVMState& st, TVMAction act);
+
+}
diff --git a/library/cpp/scheme/tests/fuzz_ops/lib/vm_defs.cpp b/library/cpp/scheme/tests/fuzz_ops/lib/vm_defs.cpp
new file mode 100644
index 0000000000..55a971d9e4
--- /dev/null
+++ b/library/cpp/scheme/tests/fuzz_ops/lib/vm_defs.cpp
@@ -0,0 +1,168 @@
+#include "vm_defs.h"
+
+#include <util/generic/xrange.h>
+#include <util/string/builder.h>
+
+namespace NSc::NUt {
+ namespace {
+ TStringBuf GetStringByIdx(ui32 idx) {
+ static const TStringBuf strings[TIdx::ValueCount] {{}, "a", "aa", "aaa"};
+ return strings[idx % TIdx::ValueCount];
+ }
+ }
+
+
+ TVMState::TVMState(TStringBuf wire, ui32 memSz, ui32 pos)
+ : Input(wire)
+ , Memory(memSz)
+ , Pos(pos)
+ {}
+
+ bool TVMState::TryPushBack() {
+ if (MayAddMoreMemory()) {
+ Memory.emplace_back();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ bool TVMState::TryPushFront() {
+ if (MayAddMoreMemory()) {
+ Pos += 1;
+ Memory.emplace_front();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ bool TVMState::TryPopBack() {
+ if (Memory.size() > 1 && Pos < Memory.size() - 1) {
+ Memory.pop_back();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ bool TVMState::TryPopFront() {
+ if (Memory.size() > 1 && Pos > 0) {
+ Memory.pop_front();
+ Pos -= 1;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ TString TVMState::ToString() const {
+ TStringBuilder b;
+ b << "pos=" << Pos << ";";
+ for (const auto i : xrange(Memory.size())) {
+ b << " " << i << (i == Pos ? "@" : ".") << Memory[i].ToJson().Quote();
+ }
+
+ return b;
+ }
+
+ TString TIdx::ToString() const {
+ return TStringBuilder() << "IDX:" << Idx;
+ }
+
+ TString TPos::ToString() const {
+ return TStringBuilder() << "POS:" << Pos;
+ }
+
+ template <class T>
+ TString RenderToString(TStringBuf name, T type, ui32 pos) {
+ TStringBuilder b;
+ b << name << ":" << type;
+ if ((ui32)-1 != pos) {
+ b << "," << pos;
+ }
+ return b;
+ }
+
+ TString TRef::ToString() const {
+ return RenderToString("REF", Type, Pos);
+ }
+
+ TString TSrc::ToString() const {
+ return RenderToString("SRC", Type, Pos);
+ }
+
+ TString TDst::ToString() const {
+ return RenderToString("DST", Type, Pos);
+ }
+
+ TString TPath::ToString() const {
+ return TStringBuilder() << "PATH:" << Path.Quote();
+ }
+
+
+ TRef TVMAction::GetRef(ui32 arg) const noexcept {
+ return std::get<TRef>(Arg[arg]);
+ }
+
+ TSrc TVMAction::GetSrc(ui32 arg) const noexcept {
+ return std::get<TSrc>(Arg[arg]);
+ }
+
+ TDst TVMAction::GetDst(ui32 arg) const noexcept {
+ return std::get<TDst>(Arg[arg]);
+ }
+
+ ui32 TVMAction::GetPos(ui32 arg) const noexcept {
+ return std::get<TPos>(Arg[arg]).Pos;
+ }
+
+ ui32 TVMAction::GetIdx(ui32 arg) const noexcept {
+ return std::get<TIdx>(Arg[arg]).Idx;
+ }
+
+ TStringBuf TVMAction::GetKey(ui32 arg) const noexcept {
+ return GetString(arg);
+ }
+
+ TStringBuf TVMAction::GetString(ui32 arg) const noexcept {
+ return GetStringByIdx(GetIdx(arg));
+ }
+
+ i64 TVMAction::GetIntNumber(ui32 arg) const noexcept {
+ return GetIdx(arg);
+ }
+
+ TStringBuf TVMAction::GetPath(ui32 arg) const noexcept {
+ return std::get<TPath>(Arg[arg]).Path;
+ }
+
+
+ struct TActionPrinter {
+ TActionPrinter(IOutputStream& out)
+ : Out(out)
+ {}
+
+ bool operator()(const std::monostate&) const {
+ return true;
+ }
+ //TIdx, TPos, TRef, TSrc, TDst, TPath
+ template <class T>
+ bool operator()(const T& t) const {
+ Out << "; " << t.ToString();
+ return false;
+ }
+ IOutputStream& Out;
+ };
+
+ TString TVMAction::ToString() const {
+ TStringBuilder out;
+ out << Type;
+ for (const auto& arg : Arg) {
+ if (std::visit(TActionPrinter(out.Out), arg)) {
+ break;
+ }
+ }
+ return out;
+ }
+}
diff --git a/library/cpp/scheme/tests/fuzz_ops/lib/vm_defs.h b/library/cpp/scheme/tests/fuzz_ops/lib/vm_defs.h
new file mode 100644
index 0000000000..9a0ddf7351
--- /dev/null
+++ b/library/cpp/scheme/tests/fuzz_ops/lib/vm_defs.h
@@ -0,0 +1,277 @@
+#pragma once
+
+#include <library/cpp/bit_io/bitinput.h>
+#include <library/cpp/scheme/scheme.h>
+
+#include <util/generic/deque.h>
+#include <util/generic/maybe.h>
+#include <util/generic/variant.h>
+#include <util/string/builder.h>
+
+namespace NSc::NUt {
+
+ enum EVMAction {
+ VMA_JMP__POS /* "pos=POS" */,
+
+ VMA_CREATE_BACK /* "TValue()->emplace_back" */,
+ VMA_CREATE_BACK__SRC /* "TValue(SRC)->emplace_back" */,
+ VMA_CREATE_FRONT /* "TValue()->emplace_front,pos+=1" */,
+ VMA_CREATE_FRONT__SRC /* "TValue(SRC)->emplace_front,pos+=1" */,
+
+ VMA_DESTROY_BACK /* "pop_back" */,
+ VMA_DESTROY_FRONT /* "pop_front,pos-=1" */,
+
+ VMA_ASSIGN__SRC /* "operator=(SRC)" */,
+
+ VMA_SET_STRING__IDX /* "SetString(str[IDX])" */,
+ VMA_SET_INT_NUMBER__IDX /* "SetIntNumber(IDX)" */,
+ VMA_SET_DICT /* "SetDict()" */,
+ VMA_SET_ARRAY /* "SetArray()" */,
+ VMA_SET_NULL /* "SetNull()" */,
+
+ VMA_GET_JSON /* "ToJson()" */,
+
+ VMA_ARRAY_CLEAR /* "ClearArray()" */,
+ VMA_ARRAY_PUSH /* "Push()" */,
+ VMA_ARRAY_PUSH__SRC /* "Push()=SRC" */,
+ VMA_ARRAY_PUSH__DST /* "Push()->DST" */,
+ VMA_ARRAY_POP /* "Pop()" */,
+ VMA_ARRAY_POP__REF /* "Pop()->REF" */,
+ VMA_ARRAY_INSERT__IDX /* "Insert(IDX)" */,
+ VMA_ARRAY_INSERT__IDX_SRC /* "Insert(IDX)=SRC" */,
+ VMA_ARRAY_INSERT__IDX_DST /* "Insert(IDX)->DST" */,
+ VMA_ARRAY_DELETE__IDX /* "Delete(IDX)" */,
+ VMA_ARRAY_DELETE__IDX_REF /* "Delete(IDX)->REF" */,
+ VMA_ARRAY_GET__IDX_REF /* "Get(IDX)->REF" */,
+ VMA_ARRAY_GET_OR_ADD__IDX /* "GetOrAdd(IDX)" */,
+ VMA_ARRAY_GET_OR_ADD__IDX_SRC /* "GetOrAdd(IDX)=SRC" */,
+ VMA_ARRAY_GET_OR_ADD__IDX_DST /* "GetOrAdd(IDX)->DST" */,
+ VMA_ARRAY_GET_NO_ADD__IDX_REF /* "GetNoAdd(IDX)->REF" */,
+
+ VMA_DICT_CLEAR /* "ClearDict()" */,
+ VMA_DICT_DELETE__IDX /* "Delete(str[IDX])" */,
+ VMA_DICT_DELETE__IDX_REF /* "Delete(str[IDX])->REF" */,
+ VMA_DICT_GET__IDX_REF /* "Get(str[IDX])->REF" */,
+ VMA_DICT_GET_OR_ADD__IDX /* "GetOrAdd(str[IDX])" */,
+ VMA_DICT_GET_OR_ADD__IDX_SRC /* "GetOrAdd(str[IDX])=SRC" */,
+ VMA_DICT_GET_OR_ADD__IDX_DST /* "GetOrAdd(str[IDX])->DST" */,
+ VMA_DICT_GET_NO_ADD__IDX_REF /* "GetNoAdd(str[IDX])->REF" */,
+
+ VMA_MERGE_UPDATE__POS /* "MergeUpdate(POS)" */,
+ VMA_MERGE_REVERSE__POS /* "ReverseMerge(POS)" */,
+ VMA_MERGE_COPY_FROM__POS /* "CopyFrom(POS)" */,
+
+ VMA_SWAP__POS /* "Swap(POS)" */,
+ VMA_EQUAL__POS_POS /* "Equal(POS, POS)" */,
+
+ VMA_SELECT_NO_ADD__PATH_REF /* "TrySelect(PATH)->REF" */,
+ VMA_SELECT_OR_ADD__PATH_REF /* "TrySelectOrAdd(PATH)->REF" */,
+ VMA_SELECT_AND_DELETE__PATH_REF /* "TrySelectAndDelete(PATH)->REF" */,
+
+ VMA_COUNT,
+ };
+
+
+ struct TVMState {
+ NBitIO::TBitInput Input;
+ TDeque<TValue> Memory { 1 };
+ ui32 Pos = 0;
+
+ static const ui32 MaxMemory = 16;
+
+ public:
+ explicit TVMState(TStringBuf wire, ui32 memSz, ui32 pos);
+
+ TValue& Current() {
+ return Memory[Pos];
+ }
+
+ TValue& LRef(ui32 pos) {
+ return Memory[pos];
+ }
+
+ const TValue& CRef(ui32 pos) {
+ return Memory[pos];
+ }
+
+ TValue&& RRef(ui32 pos) {
+ return std::move(Memory[pos]);
+ }
+
+ [[nodiscard]]
+ bool TryPushBack();
+
+ template <class T>
+ [[nodiscard]]
+ bool TryPushBack(T&& t) {
+ if (MayAddMoreMemory()) {
+ Memory.emplace_back(std::forward<T>(t));
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ [[nodiscard]]
+ bool TryPushFront();
+
+ template <class T>
+ [[nodiscard]]
+ bool TryPushFront(T&& t) {
+ if (MayAddMoreMemory()) {
+ Pos += 1;
+ Memory.emplace_front(std::forward<T>(t));
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ [[nodiscard]]
+ bool TryPopBack();
+
+ [[nodiscard]]
+ bool TryPopFront();
+
+ TString ToString() const;
+
+ private:
+ [[nodiscard]]
+ bool MayAddMoreMemory() const noexcept {
+ return Memory.size() < MaxMemory;
+ }
+ };
+
+
+ struct TIdx {
+ ui32 Idx = -1;
+ static const ui32 ValueCount = 4;
+
+ public:
+ TString ToString() const;
+ };
+
+
+ struct TPos {
+ ui32 Pos = -1;
+ static const ui32 ValueCount = TVMState::MaxMemory;
+
+ public:
+ TString ToString() const;
+ };
+
+
+ struct TRef : public TPos {
+ enum EType {
+ T_CREATE_FRONT /* "emplace_front(RES),pos+=1" */,
+ T_CREATE_BACK /* "emplace_back(RES)" */,
+ T_REF__POS /* "pos@(RES)" */,
+ T_COUNT
+ };
+
+ EType Type = T_COUNT;
+ static const ui32 TypeCount = T_COUNT;
+
+ public:
+ TString ToString() const;
+ };
+
+
+ struct TSrc : public TPos {
+ enum EType {
+ T_LREF__POS /* "pos@(TValue&)" */,
+ T_CREF__POS /* "pos@(const TValue&)" */,
+ T_RREF__POS /* "pos@(TValue&&)" */,
+ T_COUNT
+ };
+
+ EType Type = T_COUNT;
+ static const ui32 TypeCount = T_COUNT;
+
+ public:
+ TString ToString() const;
+ };
+
+
+ struct TDst : public TPos {
+ enum EType {
+ T_CREATE_FRONT_LREF /* "emplace_front(TValue&),pos+=1" */,
+ T_CREATE_FRONT_CREF /* "emplace_front(const TValue&),pos+=1" */,
+ T_CREATE_FRONT_RREF /* "emplace_front(TValue&&),pos+=1" */,
+ T_CREATE_BACK_LREF /* "emplace_back(TValue&)" */,
+ T_CREATE_BACK_CREF /* "emplace_back(TValue&),pos+=1" */,
+ T_CREATE_BACK_RREF /* "emplace_back(TValue&),pos+=1" */,
+ T_LREF__POS /* "pos@(TValue&)" */,
+ T_CREF__POS /* "pos@(const TValue&)" */,
+ T_RREF__POS /* "pos@(TValue&&)" */,
+ T_COUNT
+ };
+
+ EType Type = T_COUNT;
+ static const ui32 TypeCount = T_COUNT;
+
+ public:
+ TString ToString() const;
+ };
+
+
+ struct TPath {
+ TString Path;
+ static const ui32 MaxLength = 32;
+
+ public:
+ TString ToString() const;
+ };
+
+ using TArg = std::variant<std::monostate, TIdx, TPos, TRef, TSrc, TDst, TPath>;
+
+ struct TVMAction {
+ using EType = EVMAction;
+ EVMAction Type = VMA_COUNT;
+ static const ui32 TypeCount = VMA_COUNT;
+
+ public:
+ template <class T>
+ bool SetArg(TMaybe<T> arg) {
+ Y_VERIFY(CurrArg < Y_ARRAY_SIZE(Arg));
+ if (arg) {
+ Arg[CurrArg++] = *arg;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public:
+ TRef GetRef(ui32 arg) const noexcept;
+
+ TSrc GetSrc(ui32 arg) const noexcept;
+
+ TDst GetDst(ui32 arg) const noexcept;
+
+
+ ui32 GetPos(ui32 arg) const noexcept;
+
+ ui32 GetIdx(ui32 arg) const noexcept;
+
+ TStringBuf GetKey(ui32 arg) const noexcept;
+
+ TStringBuf GetString(ui32 arg) const noexcept;
+
+ i64 GetIntNumber(ui32 arg) const noexcept;
+
+ TStringBuf GetPath(ui32 arg) const noexcept;
+
+ TString ToString() const;
+
+ private:
+ TArg Arg[2];
+ ui32 CurrArg = 0;
+ };
+
+ [[nodiscard]]
+ inline ui32 GetCountWidth(ui32 cnt) {
+ return MostSignificantBit(cnt - 1) + 1;
+ }
+
+}
diff --git a/library/cpp/scheme/tests/fuzz_ops/lib/vm_parse.cpp b/library/cpp/scheme/tests/fuzz_ops/lib/vm_parse.cpp
new file mode 100644
index 0000000000..a03f5aef53
--- /dev/null
+++ b/library/cpp/scheme/tests/fuzz_ops/lib/vm_parse.cpp
@@ -0,0 +1,265 @@
+#include "vm_parse.h"
+
+namespace NSc::NUt {
+#define Y_TRY_READ_BITS(state, out, bits, res) do { if (!state.Input.Read(out, bits)) { return res; } } while (false)
+#define Y_TRY_READ_BITS_BOOL(state, out, bits) Y_TRY_READ_BITS(state, out, bits, false)
+#define Y_TRY_READ_BITS_MAYBE(state, out, bits) Y_TRY_READ_BITS(state, out, bits, Nothing())
+
+ TMaybe<TIdx> ParseIdx(TVMState& st) {
+ static_assert(IsPowerOf2(TIdx::ValueCount));
+ static const auto bits = GetCountWidth(TIdx::ValueCount);
+
+ TIdx idx;
+ if (!st.Input.Read(idx.Idx, bits)) {
+ return Nothing();
+ }
+ return idx;
+ }
+
+ namespace {
+ bool DoParsePos(TVMState& st, TPos& pos) {
+ static const auto bits = GetCountWidth(TPos::ValueCount);
+ const ui32 sz = st.Memory.size();
+
+ ui32 neg = -1;
+ Y_TRY_READ_BITS_BOOL(st, neg, 1);
+
+ ui32 delta = -1;
+ Y_TRY_READ_BITS_BOOL(st, delta, bits);
+
+ if (neg) {
+ if (st.Pos < delta) {
+ return false;
+ }
+ pos.Pos = st.Pos - delta;
+ } else {
+ if (st.Pos + delta >= sz) {
+ return false;
+ }
+ pos.Pos = st.Pos + delta;
+ }
+
+ return true;
+ }
+
+ template <class T>
+ bool DoParseType(TVMState& state, T& res) {
+ static const auto bits = GetCountWidth(T::TypeCount);
+
+ ui32 type = -1;
+ if (state.Input.Read(type, bits) && type < T::TypeCount) {
+ res.Type = (typename T::EType)(type);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ TMaybe<TPos> ParsePos(TVMState& state) {
+ TPos res;
+ if (DoParsePos(state, res)) {
+ return res;
+ }
+ return Nothing();
+ }
+
+ TMaybe<TRef> ParseRef(TVMState& state) {
+ TRef ref;
+ if (!DoParseType(state, ref)) {
+ return Nothing();
+ }
+
+ switch (ref.Type) {
+ case TRef::T_REF__POS:
+ if (!DoParsePos(state, ref)) {
+ return Nothing();
+ }
+ [[fallthrough]];
+ case TRef::T_CREATE_FRONT:
+ case TRef::T_CREATE_BACK:
+ return ref;
+ default:
+ Y_FAIL();
+ }
+ }
+
+ TMaybe<TSrc> ParseSrc(TVMState& state) {
+ TSrc src;
+ if (!DoParseType(state, src)) {
+ return Nothing();
+ }
+
+ switch (src.Type) {
+ case TSrc::T_LREF__POS:
+ case TSrc::T_CREF__POS:
+ case TSrc::T_RREF__POS:
+ if (!DoParsePos(state, src)) {
+ return Nothing();
+ }
+ return src;
+ default:
+ Y_FAIL();
+ }
+ }
+
+ TMaybe<TDst> ParseDst(TVMState& state) {
+ TDst dst;
+ if (!DoParseType(state, dst)) {
+ return Nothing();
+ }
+
+ switch (dst.Type) {
+ case TDst::T_LREF__POS:
+ case TDst::T_CREF__POS:
+ case TDst::T_RREF__POS:
+ if (!DoParsePos(state, dst)) {
+ return Nothing();
+ }
+ [[fallthrough]];
+ case TDst::T_CREATE_FRONT_LREF:
+ case TDst::T_CREATE_FRONT_CREF:
+ case TDst::T_CREATE_FRONT_RREF:
+ case TDst::T_CREATE_BACK_LREF:
+ case TDst::T_CREATE_BACK_CREF:
+ case TDst::T_CREATE_BACK_RREF:
+ return dst;
+ default:
+ Y_FAIL();
+ }
+ }
+
+ TMaybe<TPath> ParsePath(TVMState& state) {
+ static const ui32 bits = GetCountWidth(TPath::MaxLength);
+ TPath path;
+
+ ui32 len = -1;
+ Y_TRY_READ_BITS_MAYBE(state, len, bits);
+ while (len--) {
+ ui8 c;
+ Y_TRY_READ_BITS_MAYBE(state, c, 8);
+ path.Path.push_back(c);
+ }
+ return path;
+ }
+
+ TMaybe<TVMAction> ParseNextAction(TVMState& state) {
+ TVMAction res;
+
+ if (!DoParseType(state, res)) {
+ return Nothing();
+ }
+
+ switch (res.Type) {
+ case VMA_CREATE_BACK:
+ case VMA_CREATE_FRONT:
+ case VMA_DESTROY_BACK:
+ case VMA_DESTROY_FRONT:
+
+ case VMA_SET_DICT:
+ case VMA_SET_ARRAY:
+ case VMA_SET_NULL:
+ case VMA_GET_JSON:
+
+ case VMA_ARRAY_CLEAR:
+ case VMA_ARRAY_PUSH:
+ case VMA_DICT_CLEAR:
+ return res;
+
+ case VMA_JMP__POS:
+ case VMA_MERGE_UPDATE__POS:
+ case VMA_MERGE_REVERSE__POS:
+ case VMA_MERGE_COPY_FROM__POS:
+ case VMA_SWAP__POS:
+ if (res.SetArg(ParsePos(state))) {
+ return res;
+ } else {
+ return Nothing();
+ }
+
+ case VMA_ARRAY_POP__REF:
+ if (res.SetArg(ParseRef(state))) {
+ return res;
+ } else {
+ return Nothing();
+ }
+
+ case VMA_SET_STRING__IDX:
+ case VMA_SET_INT_NUMBER__IDX:
+ case VMA_ARRAY_INSERT__IDX:
+ case VMA_ARRAY_GET_OR_ADD__IDX:
+ case VMA_DICT_GET_OR_ADD__IDX:
+ if (res.SetArg(ParseIdx(state))) {
+ return res;
+ } else {
+ return Nothing();
+ }
+
+ case VMA_CREATE_BACK__SRC:
+ case VMA_CREATE_FRONT__SRC:
+ case VMA_ASSIGN__SRC:
+ case VMA_ARRAY_PUSH__SRC:
+ if (res.SetArg(ParseSrc(state))) {
+ return res;
+ } else {
+ return Nothing();
+ }
+
+ case VMA_ARRAY_PUSH__DST:
+ if (res.SetArg(ParseDst(state))) {
+ return res;
+ } else {
+ return Nothing();
+ }
+
+ case VMA_ARRAY_DELETE__IDX_REF:
+ case VMA_ARRAY_GET__IDX_REF:
+ case VMA_ARRAY_GET_NO_ADD__IDX_REF:
+ case VMA_DICT_DELETE__IDX_REF:
+ case VMA_DICT_GET__IDX_REF:
+ case VMA_DICT_GET_NO_ADD__IDX_REF:
+ if (res.SetArg(ParseIdx(state)) && res.SetArg(ParseRef(state))) {
+ return res;
+ } else {
+ return Nothing();
+ }
+
+ case VMA_ARRAY_INSERT__IDX_SRC:
+ case VMA_ARRAY_GET_OR_ADD__IDX_SRC:
+ case VMA_DICT_GET_OR_ADD__IDX_SRC:
+ if (res.SetArg(ParseIdx(state)) && res.SetArg(ParseSrc(state))) {
+ return res;
+ } else {
+ return Nothing();
+ }
+
+ case VMA_ARRAY_INSERT__IDX_DST:
+ case VMA_ARRAY_GET_OR_ADD__IDX_DST:
+ case VMA_DICT_GET_OR_ADD__IDX_DST:
+ if (res.SetArg(ParseIdx(state)) && res.SetArg(ParseDst(state))) {
+ return res;
+ } else {
+ return Nothing();
+ }
+
+ case VMA_EQUAL__POS_POS:
+ if (res.SetArg(ParsePos(state)) && res.SetArg(ParsePos(state))) {
+ return res;
+ } else {
+ return Nothing();
+ }
+
+ case VMA_SELECT_NO_ADD__PATH_REF:
+ case VMA_SELECT_OR_ADD__PATH_REF:
+ case VMA_SELECT_AND_DELETE__PATH_REF:
+ if (res.SetArg(ParsePath(state)) && res.SetArg(ParseRef(state))) {
+ return res;
+ } else {
+ return Nothing();
+ }
+
+ default:
+ return Nothing();
+ }
+ }
+}
diff --git a/library/cpp/scheme/tests/fuzz_ops/lib/vm_parse.h b/library/cpp/scheme/tests/fuzz_ops/lib/vm_parse.h
new file mode 100644
index 0000000000..b4aba4e4d4
--- /dev/null
+++ b/library/cpp/scheme/tests/fuzz_ops/lib/vm_parse.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "vm_defs.h"
+
+namespace NSc::NUt {
+
+ TMaybe<TIdx> ParseIdx(TVMState& st);
+ TMaybe<TPos> ParsePos(TVMState& state);
+ TMaybe<TRef> ParseRef(TVMState& state);
+ TMaybe<TSrc> ParseSrc(TVMState& state);
+ TMaybe<TDst> ParseDst(TVMState& state);
+ TMaybe<TPath> ParsePath(TVMState& state);
+
+ TMaybe<TVMAction> ParseNextAction(TVMState& state);
+
+}
diff --git a/library/cpp/scheme/tests/fuzz_ops/lib/ya.make b/library/cpp/scheme/tests/fuzz_ops/lib/ya.make
new file mode 100644
index 0000000000..279a2ca2d4
--- /dev/null
+++ b/library/cpp/scheme/tests/fuzz_ops/lib/ya.make
@@ -0,0 +1,23 @@
+LIBRARY()
+
+OWNER(
+ g:blender
+ g:middle
+ g:upper
+ velavokr
+)
+
+GENERATE_ENUM_SERIALIZATION(vm_defs.h)
+
+SRCS(
+ fuzz_ops.cpp
+ vm_apply.cpp
+ vm_defs.cpp
+ vm_parse.cpp
+)
+
+PEERDIR(
+ library/cpp/scheme
+)
+
+END()
diff --git a/library/cpp/scheme/tests/fuzz_ops/ut/vm_parse_ut.cpp b/library/cpp/scheme/tests/fuzz_ops/ut/vm_parse_ut.cpp
new file mode 100644
index 0000000000..ce3786a671
--- /dev/null
+++ b/library/cpp/scheme/tests/fuzz_ops/ut/vm_parse_ut.cpp
@@ -0,0 +1,225 @@
+#include <library/cpp/scheme/tests/fuzz_ops/lib/vm_parse.h>
+#include <library/cpp/testing/unittest/registar.h>
+
+Y_UNIT_TEST_SUITE(TestParseNextAction) {
+ using namespace NSc::NUt;
+
+ Y_UNIT_TEST(TestWidth) {
+ UNIT_ASSERT_VALUES_EQUAL(GetCountWidth(TIdx::ValueCount), 2);
+ UNIT_ASSERT_VALUES_EQUAL(GetCountWidth(TPos::ValueCount), 4);
+ UNIT_ASSERT_VALUES_EQUAL(GetCountWidth(TRef::TypeCount), 2);
+ UNIT_ASSERT_VALUES_EQUAL(GetCountWidth(TSrc::TypeCount), 2);
+ UNIT_ASSERT_VALUES_EQUAL(GetCountWidth(TDst::TypeCount), 4);
+ UNIT_ASSERT_VALUES_EQUAL(GetCountWidth(TPath::MaxLength), 5);
+ UNIT_ASSERT_VALUES_EQUAL(GetCountWidth(TVMAction::TypeCount), 6);
+ }
+
+ Y_UNIT_TEST(TestParseIdx) {
+ {
+ TVMState st{"", 1, 0};
+ UNIT_ASSERT(!ParseIdx(st));
+ }
+ {
+ TVMState st{"\x03", 1, 0};
+ auto idx = ParseIdx(st);
+ UNIT_ASSERT(idx);
+ UNIT_ASSERT_VALUES_EQUAL(idx->Idx, 3);
+ }
+ }
+
+ void DoTestParsePosFailure(const TStringBuf inp, const ui32 memSz, const ui32 curPos) {
+ TVMState st{inp, memSz, curPos};
+ UNIT_ASSERT(!ParsePos(st));
+ }
+
+ [[nodiscard]]
+ ui32 DoTestParsePosSuccess(TVMState& st) {
+ const auto pos = ParsePos(st);
+ UNIT_ASSERT(pos);
+ return pos->Pos;
+ }
+
+ [[nodiscard]]
+ ui32 DoTestParsePosSuccess(const TStringBuf inp, const ui32 memSz, const ui32 curPos) {
+ TVMState st{inp, memSz, curPos};
+ return DoTestParsePosSuccess(st);
+ }
+
+ Y_UNIT_TEST(TestParsePos) {
+ DoTestParsePosFailure("", 1, 0);
+
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParsePosSuccess(TStringBuf("\x00"sv), 1, 0), 0);
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParsePosSuccess(TStringBuf("\x01"sv), 1, 0), 0);
+
+ DoTestParsePosFailure(TStringBuf("\x02"sv), 1, 0);
+ DoTestParsePosFailure(TStringBuf("\x03"sv), 2, 0);
+
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParsePosSuccess(TStringBuf("\x02"sv), 2, 0), 1);
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParsePosSuccess(TStringBuf("\x03"sv), 2, 1), 0);
+
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParsePosSuccess(TStringBuf("\x0E"sv), 8, 0), 7);
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParsePosSuccess(TStringBuf("\x0F"sv), 8, 7), 0);
+
+ {
+ TVMState st{TStringBuf("\xDE\x7B"), 16, 0};
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParsePosSuccess(st), 15);
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParsePosSuccess(st), 15);
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParsePosSuccess(st), 15);
+ UNIT_ASSERT(!ParsePos(st));
+ }
+ {
+ TVMState st{TStringBuf("\xFF\x7F"), 16, 15};
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParsePosSuccess(st), 0);
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParsePosSuccess(st), 0);
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParsePosSuccess(st), 0);
+ UNIT_ASSERT(!ParsePos(st));
+ }
+ }
+
+ void DoTestParseRefFailure(const TStringBuf inp, const ui32 memSz, const ui32 curPos) {
+ TVMState st{inp, memSz, curPos};
+ UNIT_ASSERT(!ParseRef(st));
+ }
+
+ [[nodiscard]]
+ auto DoTestParseRefSuccess(TVMState& st) {
+ const auto ref = ParseRef(st);
+ UNIT_ASSERT(ref);
+ return std::make_pair(ref->Pos, ref->Type);
+ }
+
+ [[nodiscard]]
+ auto DoTestParseRefSuccess(const TStringBuf inp, const ui32 memSz, const ui32 curPos) {
+ TVMState st{inp, memSz, curPos};
+ return DoTestParseRefSuccess(st);
+ }
+
+ Y_UNIT_TEST(TestParseRef) {
+ DoTestParseRefFailure("", 1, 0);
+
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParseRefSuccess(TStringBuf("\x00"sv), 1, 0), std::make_pair((ui32)-1, TRef::T_CREATE_FRONT));
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParseRefSuccess(TStringBuf("\x01"sv), 1, 0), std::make_pair((ui32)-1, TRef::T_CREATE_BACK));
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParseRefSuccess(TStringBuf("\x0A"sv), 2, 0), std::make_pair(1u, TRef::T_REF__POS));
+
+ DoTestParseRefFailure(TStringBuf("\x12"), 1, 0);
+ DoTestParseRefFailure(TStringBuf("\x03"sv), 1, 0);
+
+ {
+ TVMState st{TStringBuf("\x7A\x7D"), 16, 0};
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParseRefSuccess(st), std::make_pair(15u, TRef::T_REF__POS));
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParseRefSuccess(st), std::make_pair(15u, TRef::T_REF__POS));
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParseRefSuccess(st), std::make_pair((ui32)-1, TRef::T_CREATE_BACK));
+ UNIT_ASSERT(!ParseRef(st));
+ }
+ }
+
+ void DoTestParseSrcFailure(const TStringBuf inp, const ui32 memSz, const ui32 curPos) {
+ TVMState st{inp, memSz, curPos};
+ UNIT_ASSERT(!ParseSrc(st));
+ }
+
+ [[nodiscard]]
+ auto DoTestParseSrcSuccess(TVMState& st) {
+ const auto src = ParseSrc(st);
+ UNIT_ASSERT(src);
+ return std::make_pair(src->Pos, src->Type);
+ }
+
+ [[nodiscard]]
+ auto DoTestParseSrcSuccess(const TStringBuf inp, const ui32 memSz, const ui32 curPos) {
+ TVMState st{inp, memSz, curPos};
+ return DoTestParseSrcSuccess(st);
+ }
+
+ Y_UNIT_TEST(TestParseSrc) {
+ DoTestParseSrcFailure("", 1, 0);
+
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParseSrcSuccess(TStringBuf("\x08"sv), 2, 0), std::make_pair(1u, TSrc::T_LREF__POS));
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParseSrcSuccess(TStringBuf("\x09"sv), 2, 0), std::make_pair(1u, TSrc::T_CREF__POS));
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParseSrcSuccess(TStringBuf("\x0A"sv), 2, 0), std::make_pair(1u, TSrc::T_RREF__POS));
+
+ DoTestParseSrcFailure(TStringBuf("\x03"sv), 1, 0);
+
+ {
+ TVMState st{TStringBuf("\x7A\x7D"), 16, 0};
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParseSrcSuccess(st), std::make_pair(15u, TSrc::T_RREF__POS));
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParseSrcSuccess(st), std::make_pair(15u, TSrc::T_RREF__POS));
+ UNIT_ASSERT(!ParseSrc(st));
+ }
+ }
+
+ void DoTestParseDstFailure(const TStringBuf inp, const ui32 memSz, const ui32 curPos) {
+ TVMState st{inp, memSz, curPos};
+ UNIT_ASSERT(!ParseDst(st));
+ }
+
+ [[nodiscard]]
+ auto DoTestParseDstSuccess(TVMState& st) {
+ const auto dst = ParseDst(st);
+ UNIT_ASSERT(dst);
+ return std::make_pair(dst->Pos, dst->Type);
+ }
+
+ [[nodiscard]]
+ auto DoTestParseDstSuccess(const TStringBuf inp, const ui32 memSz, const ui32 curPos) {
+ TVMState st{inp, memSz, curPos};
+ return DoTestParseDstSuccess(st);
+ }
+
+ Y_UNIT_TEST(TestParseDst) {
+ DoTestParseDstFailure("", 1, 0);
+
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParseDstSuccess(TStringBuf("\x00"sv), 1, 0), std::make_pair((ui32)-1, TDst::T_CREATE_FRONT_LREF));
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParseDstSuccess(TStringBuf("\x01"sv), 1, 0), std::make_pair((ui32)-1, TDst::T_CREATE_FRONT_CREF));
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParseDstSuccess(TStringBuf("\x02"sv), 1, 0), std::make_pair((ui32)-1, TDst::T_CREATE_FRONT_RREF));
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParseDstSuccess(TStringBuf("\x03"sv), 1, 0), std::make_pair((ui32)-1, TDst::T_CREATE_BACK_LREF));
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParseDstSuccess(TStringBuf("\x04"sv), 1, 0), std::make_pair((ui32)-1, TDst::T_CREATE_BACK_CREF));
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParseDstSuccess(TStringBuf("\x05"sv), 1, 0), std::make_pair((ui32)-1, TDst::T_CREATE_BACK_RREF));
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParseDstSuccess(TStringBuf("\x26\x00"sv), 2, 0), std::make_pair(1u, TDst::T_LREF__POS));
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParseDstSuccess(TStringBuf("\x27\x00"sv), 2, 0), std::make_pair(1u, TDst::T_CREF__POS));
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParseDstSuccess(TStringBuf("\x28\x00"sv), 2, 0), std::make_pair(1u, TDst::T_RREF__POS));
+
+ DoTestParseDstFailure(TStringBuf("\x06"sv), 1, 0);
+ DoTestParseDstFailure(TStringBuf("\x09\x00"sv), 1, 0);
+
+ {
+ TVMState st{TStringBuf("\x14\xE7\x09"sv), 16, 0};
+ // 4=4
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParseDstSuccess(st), std::make_pair((ui32)-1, TDst::T_CREATE_BACK_CREF));
+ // 4=8
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParseDstSuccess(st), std::make_pair((ui32)-1, TDst::T_CREATE_FRONT_CREF));
+ // 4+1+4=17
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParseDstSuccess(st), std::make_pair(15u, TDst::T_CREF__POS));
+ // 4=21
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParseDstSuccess(st), std::make_pair((ui32)-1, TDst::T_CREATE_BACK_CREF));
+ UNIT_ASSERT(!ParseDst(st));
+ }
+ }
+
+ void DoTestParsePathFailure(const TStringBuf inp, const ui32 memSz, const ui32 curPos) {
+ TVMState st{inp, memSz, curPos};
+ UNIT_ASSERT(!ParsePath(st));
+ }
+
+ [[nodiscard]]
+ auto DoTestParsePathSuccess(TVMState& st) {
+ const auto path = ParsePath(st);
+ UNIT_ASSERT(path);
+ return path->Path;
+ }
+
+ [[nodiscard]]
+ auto DoTestParsePathSuccess(const TStringBuf inp, const ui32 memSz, const ui32 curPos) {
+ TVMState st{inp, memSz, curPos};
+ return DoTestParsePathSuccess(st);
+ }
+
+ Y_UNIT_TEST(TestParsePath) {
+ DoTestParsePathFailure("", 1, 0);
+
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParsePathSuccess(TStringBuf("\x00"sv), 1, 0), TStringBuf(""));
+ UNIT_ASSERT_VALUES_EQUAL(DoTestParsePathSuccess(TStringBuf("\x21\x0C"sv), 1, 0), TStringBuf("a"));
+
+ DoTestParsePathFailure("\x22\x0C", 1, 0);
+ }
+};
diff --git a/library/cpp/scheme/tests/fuzz_ops/ut/ya.make b/library/cpp/scheme/tests/fuzz_ops/ut/ya.make
new file mode 100644
index 0000000000..5c933518ea
--- /dev/null
+++ b/library/cpp/scheme/tests/fuzz_ops/ut/ya.make
@@ -0,0 +1,15 @@
+UNITTEST()
+
+OWNER(velavokr)
+
+PEERDIR(
+ library/cpp/testing/unittest
+ library/cpp/scheme
+ library/cpp/scheme/tests/fuzz_ops/lib
+)
+
+SRCS(
+ vm_parse_ut.cpp
+)
+
+END()
diff --git a/library/cpp/scheme/tests/fuzz_ops/ya.make b/library/cpp/scheme/tests/fuzz_ops/ya.make
new file mode 100644
index 0000000000..025241ef20
--- /dev/null
+++ b/library/cpp/scheme/tests/fuzz_ops/ya.make
@@ -0,0 +1,18 @@
+FUZZ()
+
+OWNER(
+ g:blender
+ g:middle
+ g:upper
+ velavokr
+)
+
+SRCS(
+ fuzz_ops.cpp
+)
+
+PEERDIR(
+ library/cpp/scheme/tests/fuzz_ops/lib
+)
+
+END()
diff --git a/library/cpp/scheme/tests/ut/fuzz_ops_found_bugs_ut.cpp b/library/cpp/scheme/tests/ut/fuzz_ops_found_bugs_ut.cpp
new file mode 100644
index 0000000000..a445b0f87c
--- /dev/null
+++ b/library/cpp/scheme/tests/ut/fuzz_ops_found_bugs_ut.cpp
@@ -0,0 +1,12 @@
+#include <library/cpp/scheme/scheme.h>
+#include <library/cpp/scheme/tests/fuzz_ops/lib/fuzz_ops.h>
+#include <library/cpp/testing/unittest/registar.h>
+#include <util/string/hex.h>
+
+Y_UNIT_TEST_SUITE(TTestSchemeFuzzOpsFoundBugs) {
+ using namespace NSc::NUt;
+
+ Y_UNIT_TEST(TestBug1) {
+ FuzzOps(HexDecode("98040129000525"), true);
+ }
+};
diff --git a/library/cpp/scheme/tests/ut/scheme_cast_ut.cpp b/library/cpp/scheme/tests/ut/scheme_cast_ut.cpp
new file mode 100644
index 0000000000..4f907157e9
--- /dev/null
+++ b/library/cpp/scheme/tests/ut/scheme_cast_ut.cpp
@@ -0,0 +1,162 @@
+#include <library/cpp/scheme/scheme.h>
+#include <library/cpp/scheme/scheme_cast.h>
+
+#include <library/cpp/testing/unittest/registar.h>
+
+#include <util/stream/null.h>
+#include <util/string/subst.h>
+#include <util/string/util.h>
+
+using namespace NJsonConverters;
+
+using TVI = TVector<int>;
+using THI = THashMap<int, int>;
+using TMI = TMap<int, int>;
+using THSI = THashSet<int>;
+using TSI = TSet<int>;
+using TPI = std::pair<int, int>;
+
+Y_UNIT_TEST_SUITE(TSchemeCastTest) {
+ Y_UNIT_TEST(TestYVector) {
+ TVI v;
+ for (int i = 0; i < 3; ++i)
+ v.push_back(i);
+
+ UNIT_ASSERT_VALUES_EQUAL("[0,1,2]", ToJson(v));
+
+ TVI y(FromJson<TVI>(ToJson(v)));
+ UNIT_ASSERT_VALUES_EQUAL(v.size(), y.size());
+ UNIT_ASSERT(std::equal(v.begin(), v.end(), y.begin()));
+ }
+
+ Y_UNIT_TEST(TestYHash) {
+ THI h;
+ for (int i = 0; i < 3; ++i)
+ h[i] = i * i;
+
+ const TString etalon = "{\"0\":0,\"1\":1,\"2\":4}";
+ UNIT_ASSERT_VALUES_EQUAL(etalon, ToJson(h, true));
+
+ THI h2(FromJson<THI>(ToJson(h)));
+ UNIT_ASSERT_VALUES_EQUAL(ToJson(h2, true), ToJson(h, true));
+ }
+
+ Y_UNIT_TEST(TestYMap) {
+ TMI h;
+ for (int i = 0; i < 3; ++i)
+ h[i] = i * i;
+
+ const TString etalon = "{\"0\":0,\"1\":1,\"2\":4}";
+ UNIT_ASSERT_VALUES_EQUAL(etalon, ToJson(h, true));
+
+ TMI h2(FromJson<TMI>(ToJson(h)));
+ UNIT_ASSERT_VALUES_EQUAL(ToJson(h2, true), ToJson(h, true));
+ }
+
+ Y_UNIT_TEST(TestYHashSet) {
+ THSI h;
+ for (int i = 0; i < 3; ++i)
+ h.insert(i * i);
+
+ const TString etalon = "{\"0\":null,\"1\":null,\"4\":null}";
+ UNIT_ASSERT_VALUES_EQUAL(etalon, ToJson(h, true));
+
+ THSI h2(FromJson<THSI>(ToJson(h)));
+ UNIT_ASSERT_VALUES_EQUAL(ToJson(h2, true), ToJson(h, true));
+ }
+
+ Y_UNIT_TEST(TestYSet) {
+ TSI h;
+ for (int i = 0; i < 3; ++i)
+ h.insert(i * i);
+
+ const TString etalon = "{\"0\":null,\"1\":null,\"4\":null}";
+ UNIT_ASSERT_VALUES_EQUAL(etalon, ToJson(h, true));
+
+ TSI h2(FromJson<TSI>(ToJson(h)));
+ UNIT_ASSERT_VALUES_EQUAL(ToJson(h2, true), ToJson(h, true));
+ }
+
+ Y_UNIT_TEST(TestTPair) {
+ TPI p(1, 1);
+
+ const TString etalon = "[1,1]";
+ UNIT_ASSERT_VALUES_EQUAL(etalon, ToJson(p, true));
+
+ TPI p2(FromJson<TPI>(ToJson(p)));
+ UNIT_ASSERT_VALUES_EQUAL(ToJson(p2, true), ToJson(p, true));
+ }
+
+ struct TCustom: public IJsonSerializable {
+ int A, B;
+ TCustom()
+ : A(0)
+ , B(0){};
+ TCustom(int a, int b)
+ : A(a)
+ , B(b)
+ {
+ }
+ NSc::TValue ToTValue() const override {
+ NSc::TValue res;
+ res["a"] = A;
+ res["b"] = B;
+ return res;
+ }
+ void FromTValue(const NSc::TValue& v, const bool) override {
+ A = v["a"].GetNumber();
+ B = v["b"].GetNumber();
+ }
+
+ bool operator<(const TCustom& rhs) const {
+ return A < rhs.A;
+ }
+ };
+
+ Y_UNIT_TEST(TestTCustom) {
+ TCustom x(2, 3);
+
+ const TString etalon = "{\"a\":2,\"b\":3}";
+ UNIT_ASSERT_VALUES_EQUAL(etalon, ToJson(x, true));
+
+ TCustom x2(FromJson<TCustom>(ToJson(x)));
+ UNIT_ASSERT_VALUES_EQUAL(ToJson(x2, true), ToJson(x, true));
+ }
+
+ Y_UNIT_TEST(TestVectorOfPairs) {
+ typedef TVector<TPI> TVPI;
+ TVPI v;
+
+ for (int i = 0; i < 3; ++i)
+ v.push_back(TPI(i, i * i));
+
+ const TString etalon = "[[0,0],[1,1],[2,4]]";
+ UNIT_ASSERT_VALUES_EQUAL(etalon, ToJson(v, true));
+
+ TVPI v2(FromJson<TVPI>(ToJson(v)));
+ UNIT_ASSERT_VALUES_EQUAL(ToJson(v2, true), ToJson(v, true));
+ }
+
+ Y_UNIT_TEST(TestSetOfCustom) {
+ typedef TSet<TCustom> TSC;
+ TSC s;
+ s.insert(TCustom(2, 3));
+
+ const TString etalon = "{\"{\\\"a\\\":2,\\\"b\\\":3}\":null}";
+ UNIT_ASSERT_VALUES_EQUAL(etalon, ToJson(s, true));
+
+ TSC s2(FromJson<TSC>(ToJson(s)));
+ UNIT_ASSERT_VALUES_EQUAL(ToJson(s2, true), ToJson(s, true));
+ }
+
+ Y_UNIT_TEST(TestExceptions) {
+ NSc::TValue v = 1;
+ const TString json = v.ToJson();
+ UNIT_ASSERT_EXCEPTION(FromJson<TVI>(json, true), yexception);
+ UNIT_ASSERT_EXCEPTION(FromJson<THI>(json, true), yexception);
+ UNIT_ASSERT_EXCEPTION(FromJson<TMI>(json, true), yexception);
+ UNIT_ASSERT_EXCEPTION(FromJson<THSI>(json, true), yexception);
+ UNIT_ASSERT_EXCEPTION(FromJson<TSI>(json, true), yexception);
+ UNIT_ASSERT_EXCEPTION(FromJson<TPI>(json, true), yexception);
+ }
+};
diff --git a/library/cpp/scheme/tests/ut/scheme_json_ut.cpp b/library/cpp/scheme/tests/ut/scheme_json_ut.cpp
new file mode 100644
index 0000000000..daeb2654f9
--- /dev/null
+++ b/library/cpp/scheme/tests/ut/scheme_json_ut.cpp
@@ -0,0 +1,161 @@
+#include <library/cpp/scheme/scimpl_private.h>
+#include <library/cpp/scheme/ut_utils/scheme_ut_utils.h>
+#include <library/cpp/testing/unittest/registar.h>
+
+#include <util/stream/null.h>
+#include <util/string/subst.h>
+#include <util/string/util.h>
+
+#include <type_traits>
+#include <library/cpp/string_utils/quote/quote.h>
+
+using namespace std::string_view_literals;
+
+Y_UNIT_TEST_SUITE(TSchemeJsonTest) {
+ Y_UNIT_TEST(TestJson) {
+ const char* json = "[\n"
+ " {\n"
+ " \"url\":\"foo\",\n"
+ " \"title\":\"bar\",\n"
+ " \"passages\":[\"foo\", \"bar\"],\n"
+ " }\n"
+ "]";
+
+ {
+ const NSc::TValue& v = NSc::TValue::FromJson(json);
+ UNIT_ASSERT_VALUES_EQUAL("bar", (TStringBuf)v.TrySelect("0/passages/1"));
+ UNIT_ASSERT(v.PathExists("0/passages/0"));
+ UNIT_ASSERT(v.PathExists("[0]/[passages]/[0]"));
+ UNIT_ASSERT(v.PathExists("[0][passages][0]"));
+ UNIT_ASSERT(v.PathExists(""));
+ UNIT_ASSERT(!v.PathExists("`"));
+ UNIT_ASSERT(v.TrySelect("").Has(0));
+ UNIT_ASSERT(!v.PathExists("1"));
+ UNIT_ASSERT(!v.PathExists("0/passages1"));
+ UNIT_ASSERT(!v.PathExists("0/passages/2"));
+ UNIT_ASSERT(!v.PathExists("0/passages/2"));
+ UNIT_ASSERT_VALUES_EQUAL(0, (double)v.TrySelect("0/passages/2"));
+ UNIT_ASSERT(!v.PathExists("0/passages/2"));
+ }
+ {
+ const NSc::TValue& vv = NSc::TValue::FromJson("[ test ]]");
+ UNIT_ASSERT(vv.IsNull());
+ }
+ {
+ const char* json = "[a,b],[a,b]";
+ const NSc::TValue& v = NSc::TValue::FromJson(json);
+ UNIT_ASSERT(v.IsNull());
+ }
+ {
+ const char* json = "[null,null]";
+ const NSc::TValue& v = NSc::TValue::FromJson(json);
+ UNIT_ASSERT(v.PathExists("1"));
+ UNIT_ASSERT(!v.PathExists("2"));
+ }
+ {
+ const char* json = "{ a : b : c }";
+ NSc::TValue v;
+ UNIT_ASSERT(!NSc::TValue::FromJson(v, json));
+ UNIT_ASSERT(v.IsNull());
+ }
+ {
+ const char* json = "[a:b]";
+ UNIT_ASSERT(NSc::TValue::FromJson(json).IsNull());
+ }
+ {
+ UNIT_ASSERT_VALUES_EQUAL("{\n \"a\" : \"b\",\n \"c\" : \"d\"\n}",
+ NSc::TValue::FromJson("{a:b,c:d}").ToJson(NSc::TValue::JO_PRETTY));
+ }
+ }
+
+ Y_UNIT_TEST(TestSafeJson) {
+ TString ss;
+ ss.reserve(256);
+
+ for (int i = 0; i < 256; ++i) {
+ ss.append((char)i);
+ }
+
+ NSc::TValue v;
+ v[ss] = "xxx";
+ v["xxx"] = ss;
+
+ UNIT_ASSERT_VALUES_EQUAL("{\"xxx\":null}", v.ToJson(NSc::TValue::JO_SKIP_UNSAFE));
+ UNIT_ASSERT_VALUES_EQUAL("{\"xxx\":null}", v.ToJson(NSc::TValue::JO_SAFE));
+
+ UNIT_ASSERT_VALUES_EQUAL("{"
+ "\"\\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\b\\t\\n\\u000B\\f\\r"
+ "\\u000E\\u000F\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017\\u0018"
+ "\\u0019\\u001A\\u001B\\u001C\\u001D\\u001E\\u001F !\\\"#$%&'()*+,-./0123456789"
+ ":;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\\u007F"
+ "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93"
+ "\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7"
+ "\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB"
+ "\xBC\xBD\xBE\xBF\\xC0\\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE"
+ "\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1"
+ "\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4"
+ "\\xF5\\xF6\\xF7\\xF8\\xF9\\xFA\\xFB\\xFC\\xFD\\xFE\\xFF\":"
+ "\"xxx\","
+ "\"xxx\":"
+ "\"\\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\b\\t\\n\\u000B\\f\\r"
+ "\\u000E\\u000F\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017\\u0018"
+ "\\u0019\\u001A\\u001B\\u001C\\u001D\\u001E\\u001F !\\\"#$%&'()*+,-./0123456789"
+ ":;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\\u007F"
+ "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93"
+ "\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7"
+ "\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB"
+ "\xBC\xBD\xBE\xBF\\xC0\\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE"
+ "\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1"
+ "\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4"
+ "\\xF5\\xF6\\xF7\\xF8\\xF9\\xFA\\xFB\\xFC\\xFD\\xFE\\xFF\""
+ "}",
+ v.ToJson(NSc::TValue::JO_SORT_KEYS));
+ UNIT_ASSERT(NSc::TValue::Equal(v, NSc::TValue::FromJson(v.ToJson())));
+
+ {
+ NSc::TValue value;
+ TString articleName{"\xC2\xC2\xCF"};
+ value["text"] = articleName;
+ UNIT_ASSERT_VALUES_EQUAL(value.ToJson(), "{\"text\":\"\xC2\xC2\xCF\"}");
+ UNIT_ASSERT_VALUES_EQUAL(value.ToJsonSafe(), "{\"text\":null}");
+ }
+ }
+
+ Y_UNIT_TEST(TestJsonEscape) {
+ NSc::TValue v("\10\7\6\5\4\3\2\1\0"sv);
+ UNIT_ASSERT_VALUES_EQUAL(v.ToJson(), "\"\\b\\u0007\\u0006\\u0005\\u0004\\u0003\\u0002\\u0001\\u0000\"");
+ }
+
+ Y_UNIT_TEST(TestStrictJson) {
+ UNIT_ASSERT_NO_EXCEPTION(NSc::TValue::FromJsonThrow("{a:b}"));
+ UNIT_ASSERT_EXCEPTION(NSc::TValue::FromJsonThrow("{a:b}", NSc::TValue::JO_PARSER_STRICT), yexception);
+ UNIT_ASSERT_NO_EXCEPTION(NSc::TValue::FromJsonThrow("{\"a\":\"b\"}", NSc::TValue::JO_PARSER_STRICT));
+ }
+
+ Y_UNIT_TEST(TestJsonValue) {
+ NSc::TValue a = NSc::NUt::AssertFromJson("{a:[null,-1,2,3.4,str,{b:{c:d}}],e:f}");
+ NSc::TValue b = NSc::TValue::FromJsonValue(a.ToJsonValue());
+ UNIT_ASSERT_JSON_EQ_JSON(a, b);
+ }
+
+ Y_UNIT_TEST(TestJsonEmptyContainers) {
+ {
+ NSc::TValue a = NSc::NUt::AssertFromJson("{a:[]}");
+ NSc::TValue b = NSc::TValue::FromJsonValue(a.ToJsonValue());
+ UNIT_ASSERT_JSON_EQ_JSON(a, b);
+ }
+ {
+ NSc::TValue a = NSc::NUt::AssertFromJson("{a:{}}");
+ NSc::TValue b = NSc::TValue::FromJsonValue(a.ToJsonValue());
+ UNIT_ASSERT_JSON_EQ_JSON(a, b);
+ }
+ }
+
+ Y_UNIT_TEST(TestDuplicateKeys) {
+ const TStringBuf duplicatedKeys = "{\"a\":[{\"b\":1, \"b\":42}]}";
+ UNIT_ASSERT_NO_EXCEPTION(NSc::TValue::FromJsonThrow(duplicatedKeys));
+ UNIT_ASSERT_EXCEPTION(NSc::TValue::FromJsonThrow(duplicatedKeys, NSc::TValue::JO_PARSER_DISALLOW_DUPLICATE_KEYS), yexception);
+ UNIT_ASSERT(NSc::TValue::FromJson(duplicatedKeys).IsDict());
+ UNIT_ASSERT(NSc::TValue::FromJson(duplicatedKeys, NSc::TValue::JO_PARSER_DISALLOW_DUPLICATE_KEYS).IsNull());
+ }
+};
diff --git a/library/cpp/scheme/tests/ut/scheme_merge_ut.cpp b/library/cpp/scheme/tests/ut/scheme_merge_ut.cpp
new file mode 100644
index 0000000000..2a06cf110d
--- /dev/null
+++ b/library/cpp/scheme/tests/ut/scheme_merge_ut.cpp
@@ -0,0 +1,172 @@
+#include <library/cpp/scheme/scimpl_private.h>
+#include <library/cpp/scheme/ut_utils/scheme_ut_utils.h>
+#include <library/cpp/testing/unittest/registar.h>
+
+#include <util/stream/null.h>
+#include <library/cpp/string_utils/quote/quote.h>
+#include <util/string/subst.h>
+#include <util/string/util.h>
+
+#include <type_traits>
+
+
+Y_UNIT_TEST_SUITE(TSchemeMergeTest) {
+
+ void DoTestReverseMerge(TStringBuf lhs, TStringBuf rhs, TStringBuf res) {
+ NSc::TValue v = NSc::TValue::FromJson(lhs);
+ v.ReverseMerge(NSc::TValue::FromJson(rhs));
+ UNIT_ASSERT(NSc::TValue::Equal(v, NSc::TValue::FromJson(res)));
+ }
+
+ Y_UNIT_TEST(TestReverseMerge) {
+ DoTestReverseMerge("{a:{x:y, b:c}}", "{a:{u:w, b:d}}", "{a:{u:w, x:y, b:c}}");
+ DoTestReverseMerge("null", "{x:y}", "{x:y}");
+ DoTestReverseMerge("null", "[b]", "[b]");
+ DoTestReverseMerge("[a]", "[b]", "[a]");
+ DoTestReverseMerge("{x:null}", "{x:b}", "{x:b}");
+ }
+
+ Y_UNIT_TEST(TestMerge) {
+ TStringBuf data = "{ a : [ { b : 1, d : { e : -1.e5 } }, { f : 0, g : [ h, i ] } ] }";
+ NSc::TValue v = NSc::TValue::FromJson(data);
+ UNIT_ASSERT_VALUES_EQUAL(v.ToJson(true), v.Clone().ToJson(true));
+ UNIT_ASSERT(v.Has("a"));
+ UNIT_ASSERT(v["a"].Has(1));
+ UNIT_ASSERT(v["a"][0].Has("b"));
+ UNIT_ASSERT(v["a"][0].Has("d"));
+ UNIT_ASSERT(1 == v["a"][0]["b"]);
+ UNIT_ASSERT(v["a"][0]["d"].Has("e"));
+ UNIT_ASSERT(-1.e5 == v["a"][0]["d"]["e"]);
+ UNIT_ASSERT(v["a"][1].Has("f"));
+ UNIT_ASSERT(v["a"][1].Has("g"));
+ UNIT_ASSERT(0. == v["a"][1]["f"]);
+ UNIT_ASSERT(v["a"][1]["g"].IsArray());
+ UNIT_ASSERT(v["a"][1]["g"].Has(1));
+ UNIT_ASSERT(TStringBuf("h") == v["a"][1]["g"][0]);
+ UNIT_ASSERT(TStringBuf("i") == v["a"][1]["g"][1]);
+
+ {
+ TStringBuf data = "{ a : [ { d : 42 }, { g : [ 3 ] } ], q : r }";
+
+ NSc::TValue v1 = NSc::TValue::FromJson(data);
+ UNIT_ASSERT_VALUES_EQUAL(v1.ToJson(true), v1.Clone().ToJson(true));
+ UNIT_ASSERT(NSc::TValue::Equal(v1, v1.FromJson(v1.ToJson())));
+
+ NSc::TValue v2;
+ v2.MergeUpdate(v["a"]);
+ UNIT_ASSERT_C(NSc::TValue::Equal(v["a"], v2), Sprintf("\n%s\n!=\n%s\n", v["a"].ToJson().data(), v2.ToJson().data()));
+
+ v.MergeUpdate(v1);
+ UNIT_ASSERT_C(!NSc::TValue::Equal(v["a"], v2), Sprintf("\n%s\n!=\n%s\n", v["a"].ToJson().data(), v2.ToJson().data()));
+ v2.MergeUpdate(v1["a"]);
+ UNIT_ASSERT_C(NSc::TValue::Equal(v["a"], v2), Sprintf("\n%s\n!=\n%s\n", v["a"].ToJson().data(), v2.ToJson().data()));
+ }
+
+ UNIT_ASSERT(v.Has("a"));
+ UNIT_ASSERT(v.Has("q"));
+ UNIT_ASSERT(TStringBuf("r") == v["q"]);
+ UNIT_ASSERT(v["a"].Has(1));
+ UNIT_ASSERT(!v["a"][0].Has("b"));
+ UNIT_ASSERT(v["a"][0].Has("d"));
+ UNIT_ASSERT(!v["a"][0]["d"].IsArray());
+ UNIT_ASSERT(!v["a"][0]["d"].IsDict());
+ UNIT_ASSERT(42 == v["a"][0]["d"]);
+ UNIT_ASSERT(!v["a"][1].Has("f"));
+ UNIT_ASSERT(v["a"][1].Has("g"));
+ UNIT_ASSERT(v["a"][1]["g"].IsArray());
+ UNIT_ASSERT(!v["a"][1]["g"].Has(1));
+ UNIT_ASSERT(3 == v["a"][1]["g"][0]);
+ }
+
+ Y_UNIT_TEST(TestMerge1) {
+ TStringBuf data = "[ { a : { b : d } } ]";
+
+ NSc::TValue wcopy = NSc::TValue::FromJson(data);
+
+ TStringBuf data1 = "[ { a : { b : c } } ]";
+
+ wcopy.MergeUpdateJson(data1);
+
+ {
+ TString json = wcopy.ToJson(true);
+ SubstGlobal(json, "\"", "");
+ UNIT_ASSERT_VALUES_EQUAL(json, "[{a:{b:c}}]");
+ }
+ }
+
+ Y_UNIT_TEST(TestMerge2) {
+ TStringBuf data = "{ a : { b : c }, q : { x : y } }";
+
+ NSc::TValue wcopy = NSc::TValue::FromJson(data);
+
+ TStringBuf data1 = "{ a : { e : f } }";
+
+ wcopy.MergeUpdateJson(data1);
+
+ {
+ TString json = wcopy.ToJson(true);
+ SubstGlobal(json, "\"", "");
+ UNIT_ASSERT_VALUES_EQUAL(json, "{a:{b:c,e:f},q:{x:y}}");
+ }
+ }
+
+ Y_UNIT_TEST(TestMerge3) {
+ TStringBuf data = "{ g : { x : { a : { b : c }, q : { x : y } }, y : fff } }";
+
+ NSc::TValue wcopy = NSc::TValue::FromJson(data);
+
+ TStringBuf data1 = "{ g : { x : { a : { e : f } } } }";
+
+ wcopy.MergeUpdateJson(data1);
+
+ {
+ TString json = wcopy.ToJson(true);
+ SubstGlobal(json, "\"", "");
+ UNIT_ASSERT_VALUES_EQUAL(json, "{g:{x:{a:{b:c,e:f},q:{x:y}},y:fff}}");
+ }
+ }
+
+ Y_UNIT_TEST(TestMerge4) {
+ TStringBuf data = "{ a : 1, b : { c : 2, d : { q : f } } }";
+ NSc::TValue val = NSc::TValue::FromJson(data);
+
+ TStringBuf data1 = "{ a : 2, b : { c : 3, d : { q : e }, g : h } }";
+
+ val.MergeUpdateJson(data1);
+
+ {
+ TString json = val.ToJson(true);
+ SubstGlobal(json, "\"", "");
+
+ UNIT_ASSERT_VALUES_EQUAL(json, "{a:2,b:{c:3,d:{q:e},g:h}}");
+ }
+ }
+
+ Y_UNIT_TEST(TestMerge5) {
+ NSc::TValue v0;
+ v0.GetOrAdd("x").MergeUpdate(NSc::TValue(1));
+ UNIT_ASSERT_VALUES_EQUAL(v0.ToJson(), "{\"x\":1}");
+ }
+
+ Y_UNIT_TEST(TestMerge6) {
+ NSc::TValue va = NSc::TValue::FromJson("{\"x\":\"abc\",\"y\":\"def\"}");
+ NSc::TValue vb = va.Get("y");
+ NSc::TValue diff;
+ diff["y"] = vb;
+ va.MergeUpdate(diff);
+ UNIT_ASSERT_VALUES_EQUAL(va.ToJson(), "{\"x\":\"abc\",\"y\":\"def\"}");
+ UNIT_ASSERT_VALUES_EQUAL(vb.ToJson(), "\"def\"");
+ UNIT_ASSERT_VALUES_EQUAL(diff.ToJson(), "{\"y\":\"def\"}");
+ }
+
+ Y_UNIT_TEST(TestMerge7) {
+ NSc::TValue v;
+ v["a"] = NSc::TValue::FromJson("[0.125,0.12,0.1,0.08,0.06]");
+ UNIT_ASSERT_JSON_EQ_JSON(v, "{a:[0.125,0.12,0.1,0.08,0.06]}");
+
+ NSc::TValue a = v.TrySelectOrAdd("a")->MergeUpdateJson("[1,2,3]");
+
+ UNIT_ASSERT_JSON_EQ_JSON(a, "[1,2,3]");
+ UNIT_ASSERT_JSON_EQ_JSON(v, "{a:[1,2,3]}");
+ }
+};
diff --git a/library/cpp/scheme/tests/ut/scheme_path_ut.cpp b/library/cpp/scheme/tests/ut/scheme_path_ut.cpp
new file mode 100644
index 0000000000..0d4d79d483
--- /dev/null
+++ b/library/cpp/scheme/tests/ut/scheme_path_ut.cpp
@@ -0,0 +1,159 @@
+#include <library/cpp/scheme/scimpl_private.h>
+#include <library/cpp/scheme/ut_utils/scheme_ut_utils.h>
+#include <library/cpp/testing/unittest/registar.h>
+
+#include <util/stream/null.h>
+#include <util/string/subst.h>
+#include <util/string/util.h>
+
+#include <type_traits>
+#include <library/cpp/string_utils/quote/quote.h>
+
+Y_UNIT_TEST_SUITE(TSchemePathTest) {
+ void DoTestSelect(TStringBuf path, TStringBuf expected, TStringBuf delexpected) {
+ NSc::TValue v;
+ UNIT_ASSERT(!v.PathExists(path));
+ UNIT_ASSERT(NSc::TValue::PathValid(path));
+ UNIT_ASSERT(NSc::TValue::Same(v.TrySelect(path), NSc::Null()));
+ *v.TrySelectOrAdd(path) = 1;
+ NSc::NUt::AssertSchemeJson(expected, v);
+ UNIT_ASSERT(v.PathExists(path));
+ UNIT_ASSERT(1 == v.TrySelectOrAdd(path)->GetNumber());
+ UNIT_ASSERT(1 == v.TrySelect(path).GetNumber());
+ UNIT_ASSERT(1 == v.TrySelectAndDelete(path).GetNumber());
+ UNIT_ASSERT(NSc::TValue::Same(v.TrySelectAndDelete(path), NSc::Null()));
+ NSc::NUt::AssertSchemeJson(delexpected, v);
+ UNIT_ASSERT(!v.PathExists(path));
+ UNIT_ASSERT(NSc::TValue::Same(v.TrySelect(path), NSc::Null()));
+ }
+
+ Y_UNIT_TEST(TestSelect) {
+ NSc::TValue v;
+ UNIT_ASSERT(!v.PathValid(" "));
+ UNIT_ASSERT(v.PathExists(""));
+ UNIT_ASSERT(v.PathExists("//"));
+
+ UNIT_ASSERT(NSc::TValue::Same(v, *v.TrySelectOrAdd("//")));
+ NSc::NUt::AssertSchemeJson("null", v);
+ UNIT_ASSERT(NSc::TValue::Same(v.TrySelectAndDelete("//"), NSc::Null()));
+ NSc::NUt::AssertSchemeJson("null", v);
+
+ v.SetDict();
+ UNIT_ASSERT(NSc::TValue::Same(v, *v.TrySelectOrAdd("//")));
+ NSc::NUt::AssertSchemeJson("{}", v);
+ UNIT_ASSERT(NSc::TValue::Same(v.TrySelectAndDelete("//"), NSc::Null()));
+ NSc::NUt::AssertSchemeJson("{}", v);
+
+ v.SetArray();
+ UNIT_ASSERT(NSc::TValue::Same(v, *v.TrySelectOrAdd("//")));
+ NSc::NUt::AssertSchemeJson("[]", v);
+ UNIT_ASSERT(NSc::TValue::Same(v.TrySelectAndDelete("//"), NSc::Null()));
+ NSc::NUt::AssertSchemeJson("[]", v);
+
+ DoTestSelect("[]", "{'':1}", "{}");
+ DoTestSelect("[ ]", "{' ':1}", "{}");
+ DoTestSelect("[0]", "[1]", "[]");
+ DoTestSelect("[1]", "[null,1]", "[null]");
+ DoTestSelect("foo/[0]/bar", "{foo:[{bar:1}]}", "{foo:[{}]}");
+ DoTestSelect("foo/1/bar", "{foo:[null,{bar:1}]}", "{foo:[null,{}]}");
+ DoTestSelect("foo[-1]bar", "{foo:{'-1':{bar:1}}}", "{foo:{'-1':{}}}");
+ DoTestSelect("'foo'/\"0\"/'bar'", "{foo:{'0':{bar:1}}}", "{foo:{'0':{}}}");
+ DoTestSelect("'\\''", "{'\\'':1}", "{}");
+ }
+
+ Y_UNIT_TEST(TestSelectAndMerge) {
+ NSc::TValue v;
+ v.TrySelectOrAdd("blender/enabled")->MergeUpdateJson("1");
+ UNIT_ASSERT_VALUES_EQUAL(NSc::TValue::FromJson("1").ToJson(), "1");
+ UNIT_ASSERT_VALUES_EQUAL(v.ToJson(), "{\"blender\":{\"enabled\":1}}");
+ }
+
+ Y_UNIT_TEST(TestPathEscapes) {
+ UNIT_ASSERT_VALUES_EQUAL(NSc::TValue::EscapeForPath("a"), "a");
+ UNIT_ASSERT_VALUES_EQUAL(NSc::TValue::EscapeForPath(""), R"=("")=");
+ UNIT_ASSERT_VALUES_EQUAL(NSc::TValue::EscapeForPath("[]"), R"=("[]")=");
+ UNIT_ASSERT_VALUES_EQUAL(NSc::TValue::EscapeForPath("[]ab"), R"=("[]ab")=");
+ UNIT_ASSERT_VALUES_EQUAL(NSc::TValue::EscapeForPath("a[]b"), R"=("a[]b")=");
+ UNIT_ASSERT_VALUES_EQUAL(NSc::TValue::EscapeForPath("ab[]"), R"=("ab[]")=");
+ UNIT_ASSERT_VALUES_EQUAL(NSc::TValue::EscapeForPath("[ab]"), R"=("[ab]")=");
+ UNIT_ASSERT_VALUES_EQUAL(NSc::TValue::EscapeForPath("[ab"), R"=("[ab")=");
+ UNIT_ASSERT_VALUES_EQUAL(NSc::TValue::EscapeForPath("a[b"), R"=("a[b")=");
+ UNIT_ASSERT_VALUES_EQUAL(NSc::TValue::EscapeForPath("ab["), R"=("ab[")=");
+ UNIT_ASSERT_VALUES_EQUAL(NSc::TValue::EscapeForPath("]ab"), R"=("]ab")=");
+ UNIT_ASSERT_VALUES_EQUAL(NSc::TValue::EscapeForPath("a]b"), R"=("a]b")=");
+ UNIT_ASSERT_VALUES_EQUAL(NSc::TValue::EscapeForPath("ab]"), R"=("ab]")=");
+ UNIT_ASSERT_VALUES_EQUAL(NSc::TValue::EscapeForPath(R"=(\)="), R"=("\\")=");
+ UNIT_ASSERT_VALUES_EQUAL(NSc::TValue::EscapeForPath(R"=(\\)="), R"=("\\\\")=");
+ UNIT_ASSERT_VALUES_EQUAL(NSc::TValue::EscapeForPath("/"), R"=("/")=");
+ UNIT_ASSERT_VALUES_EQUAL(NSc::TValue::EscapeForPath("//"), R"=("//")=");
+ UNIT_ASSERT_VALUES_EQUAL(NSc::TValue::EscapeForPath("///"), R"=("///")=");
+ UNIT_ASSERT_VALUES_EQUAL(NSc::TValue::EscapeForPath("/ab"), R"=("/ab")=");
+ UNIT_ASSERT_VALUES_EQUAL(NSc::TValue::EscapeForPath("a/b"), R"=("a/b")=");
+ UNIT_ASSERT_VALUES_EQUAL(NSc::TValue::EscapeForPath("ab/"), R"=("ab/")=");
+ UNIT_ASSERT_VALUES_EQUAL(NSc::TValue::EscapeForPath("//ab"), R"=("//ab")=");
+ UNIT_ASSERT_VALUES_EQUAL(NSc::TValue::EscapeForPath("a//b"), R"=("a//b")=");
+ UNIT_ASSERT_VALUES_EQUAL(NSc::TValue::EscapeForPath("ab//"), R"=("ab//")=");
+ UNIT_ASSERT_VALUES_EQUAL(NSc::TValue::EscapeForPath("6400"), R"=("6400")=");
+
+ {
+ NSc::TValue val;
+ *val.TrySelectOrAdd("") = 100;
+ const TString res = R"=(100)=";
+ UNIT_ASSERT_VALUES_EQUAL(val.ToJson(), res);
+ }
+ {
+ NSc::TValue val;
+ *val.TrySelectOrAdd("a") = 100;
+ const TString res = R"=({"a":100})=";
+ UNIT_ASSERT_VALUES_EQUAL(val.ToJson(), res);
+ }
+ {
+ NSc::TValue val;
+ *val.TrySelectOrAdd(R"=(////)=") = 100;
+ const TString res = R"=(100)=";
+ UNIT_ASSERT_VALUES_EQUAL(val.ToJson(), res);
+ }
+ {
+ NSc::TValue val;
+ *val.TrySelectOrAdd(R"=()=") = 100;
+ const TString res = R"=(100)=";
+ UNIT_ASSERT_VALUES_EQUAL(val.ToJson(), res);
+ }
+ {
+ NSc::TValue val;
+ *val.TrySelectOrAdd(R"=("")=") = 100;
+ const TString res = R"=({"":100})=";
+ UNIT_ASSERT_VALUES_EQUAL(val.ToJson(), res);
+ }
+ {
+ NSc::TValue val;
+ *val.TrySelectOrAdd(R"=("[1]")=") = 100;
+ const TString res = R"=({"[1]":100})=";
+ UNIT_ASSERT_VALUES_EQUAL(val.ToJson(), res);
+ }
+ {
+ NSc::TValue val;
+ *val.TrySelectOrAdd(R"=("\"\"")=") = 100;
+ const TString res = R"=({"\"\"":100})=";
+ UNIT_ASSERT_VALUES_EQUAL(val.ToJson(), res);
+ }
+ {
+ NSc::TValue val;
+ *val.TrySelectOrAdd(R"=("/10/")=") = 100;
+ const TString res = R"=({"/10/":100})=";
+ UNIT_ASSERT_VALUES_EQUAL(val.ToJson(), res);
+ }
+ {
+ NSc::TValue val;
+ *val.TrySelectOrAdd(R"=(/"[10]"//""/"\"/10/\""///)=") = 100;
+ const TString res = R"=({"[10]":{"":{"\"/10/\"":100}}})=";
+ UNIT_ASSERT_VALUES_EQUAL(val.ToJson(), res);
+ }
+ {
+ NSc::TValue val;
+ *val.TrySelectOrAdd(R"=(/"[10]"//""/"\"/10/\""///)=") = 100;
+ const TString res = R"=({"[10]":{"":{"\"/10/\"":100}}})=";
+ UNIT_ASSERT_VALUES_EQUAL(val.ToJson(), res);
+ }
+ }
+};
diff --git a/library/cpp/scheme/tests/ut/scheme_proto_ut.cpp b/library/cpp/scheme/tests/ut/scheme_proto_ut.cpp
new file mode 100644
index 0000000000..e711a0d092
--- /dev/null
+++ b/library/cpp/scheme/tests/ut/scheme_proto_ut.cpp
@@ -0,0 +1,220 @@
+#include <library/cpp/protobuf/util/is_equal.h>
+#include <library/cpp/scheme/scheme.h>
+#include <library/cpp/scheme/tests/ut/scheme_ut.pb.h>
+#include <library/cpp/testing/unittest/registar.h>
+
+Y_UNIT_TEST_SUITE(TSchemeProtoTest) {
+ void DoTestProtobuf(bool fromProto, bool mapAsDict);
+
+ Y_UNIT_TEST(TestFromProtobuf) {
+ DoTestProtobuf(true, false);
+ }
+
+ Y_UNIT_TEST(TestToProtobuf) {
+ DoTestProtobuf(false, false);
+ }
+
+ Y_UNIT_TEST(TestFromProtobufWithDict) {
+ DoTestProtobuf(true, true);
+ }
+
+ Y_UNIT_TEST(TestToProtobufWithDict) {
+ DoTestProtobuf(false, true);
+ }
+
+ template <class T>
+ void AddMapPair(NSc::TValue& v, TStringBuf key, T value, bool mapAsDict) {
+ if (mapAsDict) {
+ v[key] = value;
+ } else {
+ auto& newElement = v.Push();
+ newElement["key"] = key;
+ newElement["value"] = value;
+ }
+ }
+
+ void DoTestProtobuf(bool fromProto, bool mapAsDict) {
+ NSc::TMessage m;
+ NSc::TValue v;
+ m.SetDouble((double)1 / 3), v["Double"] = (double)1 / 3;
+ m.SetFloat((float)1 / 3), v["Float"] = (float)1 / 3;
+ m.SetInt32(1000000), v["Int32"] = 1000000;
+ m.SetInt64(1000000000000LL), v["Int64"] = 1000000000000LL;
+ m.SetUInt32(555555), v["UInt32"] = 555555;
+ m.SetUInt64(555555555555LL), v["UInt64"] = 555555555555LL;
+ m.SetSInt32(-555555), v["SInt32"] = -555555;
+ m.SetSInt64(-555555555555LL), v["SInt64"] = -555555555555LL;
+ m.SetFixed32(123456), v["Fixed32"] = 123456;
+ m.SetFixed64(123456123456LL), v["Fixed64"] = 123456123456LL;
+ m.SetSFixed32(-123456), v["SFixed32"] = -123456;
+ m.SetSFixed64(-123456123456LL), v["SFixed64"] = -123456123456LL;
+ m.SetBool(true), v["Bool"] = true;
+ m.SetString("String"), v["String"] = "String";
+ m.SetBytes("Bytes"), v["Bytes"] = "Bytes";
+ m.SetEnum(NSc::VALUE1), v["Enum"] = "VALUE1";
+
+ auto& mapDoublesP = *m.mutable_mapdoubles();
+ auto& mapDoublesV = v["MapDoubles"];
+ mapDoublesP["pi"] = 3.14;
+ AddMapPair(mapDoublesV, "pi", 3.14, mapAsDict);
+ if (fromProto && mapAsDict) {
+ // can not add two entries in dict, as it represent over array with unstable order
+ mapDoublesP["back_e"] = 1 / 2.7;
+ AddMapPair(mapDoublesV, "back_e", 1 / 2.7, mapAsDict);
+ }
+
+ auto& mapInt32sP = *m.mutable_mapint32s();
+ auto& mapInt32sV = v["MapInt32s"];
+ mapInt32sP["pi"] = -7;
+ AddMapPair(mapInt32sV, "pi", -7, mapAsDict);
+ if (fromProto && mapAsDict) {
+ // can not add two entries in dict, as it represent over array with unstable order
+ mapInt32sP["back_e"] = 42;
+ AddMapPair(mapInt32sV, "back_e", 42, mapAsDict);
+ }
+
+ auto& mapStringP = *m.mutable_mapstring();
+ auto& mapStringV = v["MapString"];
+ mapStringP["intro"] = "body";
+ AddMapPair(mapStringV, "intro", "body", mapAsDict);
+ if (fromProto && mapAsDict) {
+ // can not add two entries in dict, as it represent over array with unstable order
+ mapStringP["deep"] = "blue";
+ AddMapPair(mapStringV, "deep", "blue", mapAsDict);
+ }
+
+ m.AddDoubles((double)1 / 3), v["Doubles"][0] = (double)1 / 3;
+ m.AddDoubles((double)1 / 4), v["Doubles"][1] = (double)1 / 4;
+
+ m.AddFloats((float)1 / 3), v["Floats"][0] = (float)1 / 3;
+ m.AddFloats((float)1 / 4), v["Floats"][1] = (float)1 / 4;
+
+ m.AddInt32s(1000000), v["Int32s"][0] = 1000000;
+ m.AddInt32s(2000000), v["Int32s"][1] = 2000000;
+
+ m.AddInt64s(1000000000000LL), v["Int64s"][0] = 1000000000000LL;
+ m.AddInt64s(2000000000000LL), v["Int64s"][1] = 2000000000000LL;
+
+ m.AddUInt32s(555555), v["UInt32s"][0] = 555555;
+ m.AddUInt32s(655555), v["UInt32s"][1] = 655555;
+
+ m.AddUInt64s(555555555555LL);
+ v["UInt64s"][0] = 555555555555LL;
+ m.AddUInt64s(655555555555LL);
+ v["UInt64s"][1] = 655555555555LL;
+
+ m.AddSInt32s(-555555), v["SInt32s"][0] = -555555;
+ m.AddSInt32s(-655555), v["SInt32s"][1] = -655555;
+
+ m.AddSInt64s(-555555555555LL), v["SInt64s"][0] = -555555555555LL;
+ m.AddSInt64s(-655555555555LL), v["SInt64s"][1] = -655555555555LL;
+
+ m.AddFixed32s(123456), v["Fixed32s"][0] = 123456;
+ m.AddFixed32s(223456), v["Fixed32s"][1] = 223456;
+
+ m.AddFixed64s(123456123456LL), v["Fixed64s"][0] = 123456123456LL;
+ m.AddFixed64s(223456123456LL), v["Fixed64s"][1] = 223456123456LL;
+
+ m.AddSFixed32s(-123456), v["SFixed32s"][0] = -123456;
+ m.AddSFixed32s(-223456), v["SFixed32s"][1] = -223456;
+
+ m.AddSFixed64s(-123456123456LL), v["SFixed64s"][0] = -123456123456LL;
+ m.AddSFixed64s(-223456123456LL), v["SFixed64s"][1] = -223456123456LL;
+
+ m.AddBools(false), v["Bools"][0] = false;
+ m.AddBools(true), v["Bools"][1] = true;
+
+ m.AddStrings("String1"), v["Strings"][0] = "String1";
+ m.AddStrings("String2"), v["Strings"][1] = "String2";
+
+ m.AddBytess("Bytes1"), v["Bytess"][0] = "Bytes1";
+ m.AddBytess("Bytes2"), v["Bytess"][1] = "Bytes2";
+
+ m.AddEnums(NSc::VALUE1), v["Enums"][0] = "VALUE1";
+ m.AddEnums(NSc::VALUE2), v["Enums"][1] = "VALUE2";
+
+ NSc::TMessage2 m2;
+ NSc::TValue v2;
+ m2.SetDouble((double)1 / 3), v2["Double"] = (double)1 / 3;
+ m2.SetFloat((float)1 / 3), v2["Float"] = (float)1 / 3;
+ m2.SetInt32(1000000), v2["Int32"] = 1000000;
+ m2.SetInt64(1000000000000LL), v2["Int64"] = 1000000000000LL;
+ m2.SetUInt32(555555), v2["UInt32"] = 555555;
+ m2.SetUInt64(555555555555LL), v2["UInt64"] = 555555555555LL;
+ m2.SetSInt32(-555555), v2["SInt32"] = -555555;
+ m2.SetSInt64(-555555555555LL), v2["SInt64"] = -555555555555LL;
+ m2.SetFixed32(123456), v2["Fixed32"] = 123456;
+ m2.SetFixed64(123456123456LL), v2["Fixed64"] = 123456123456LL;
+ m2.SetSFixed32(-123456), v2["SFixed32"] = -123456;
+ m2.SetSFixed64(-123456123456LL), v2["SFixed64"] = -123456123456LL;
+ m2.SetBool(true), v2["Bool"] = true;
+ m2.SetString("String"), v2["String"] = "String";
+ m2.SetBytes("Bytes"), v2["Bytes"] = "Bytes";
+ m2.SetEnum(NSc::VALUE1), v2["Enum"] = "VALUE1";
+
+ m2.AddDoubles((double)1 / 3), v2["Doubles"][0] = (double)1 / 3;
+ m2.AddDoubles((double)1 / 4), v2["Doubles"][1] = (double)1 / 4;
+
+ m2.AddFloats((float)1 / 3), v2["Floats"][0] = (float)1 / 3;
+ m2.AddFloats((float)1 / 4), v2["Floats"][1] = (float)1 / 4;
+
+ m2.AddInt32s(1000000), v2["Int32s"][0] = 1000000;
+ m2.AddInt32s(2000000), v2["Int32s"][1] = 2000000;
+
+ m2.AddInt64s(1000000000000LL), v2["Int64s"][0] = 1000000000000LL;
+ m2.AddInt64s(2000000000000LL), v2["Int64s"][1] = 2000000000000LL;
+
+ m2.AddUInt32s(555555), v2["UInt32s"][0] = 555555;
+ m2.AddUInt32s(655555), v2["UInt32s"][1] = 655555;
+
+ m2.AddUInt64s(555555555555LL);
+ v2["UInt64s"][0] = 555555555555LL;
+ m2.AddUInt64s(655555555555LL);
+ v2["UInt64s"][1] = 655555555555LL;
+
+ m2.AddSInt32s(-555555), v2["SInt32s"][0] = -555555;
+ m2.AddSInt32s(-655555), v2["SInt32s"][1] = -655555;
+
+ m2.AddSInt64s(-555555555555LL), v2["SInt64s"][0] = -555555555555LL;
+ m2.AddSInt64s(-655555555555LL), v2["SInt64s"][1] = -655555555555LL;
+
+ m2.AddFixed32s(123456), v2["Fixed32s"][0] = 123456;
+ m2.AddFixed32s(223456), v2["Fixed32s"][1] = 223456;
+
+ m2.AddFixed64s(123456123456LL), v2["Fixed64s"][0] = 123456123456LL;
+ m2.AddFixed64s(223456123456LL), v2["Fixed64s"][1] = 223456123456LL;
+
+ m2.AddSFixed32s(-123456), v2["SFixed32s"][0] = -123456;
+ m2.AddSFixed32s(-223456), v2["SFixed32s"][1] = -223456;
+
+ m2.AddSFixed64s(-123456123456LL), v2["SFixed64s"][0] = -123456123456LL;
+ m2.AddSFixed64s(-223456123456LL), v2["SFixed64s"][1] = -223456123456LL;
+
+ m2.AddBools(false), v2["Bools"][0] = false;
+ m2.AddBools(true), v2["Bools"][1] = true;
+
+ m2.AddStrings("String1"), v2["Strings"][0] = "String1";
+ m2.AddStrings("String2"), v2["Strings"][1] = "String2";
+
+ m2.AddBytess("Bytes1"), v2["Bytess"][0] = "Bytes1";
+ m2.AddBytess("Bytes2"), v2["Bytess"][1] = "Bytes2";
+
+ m2.AddEnums(NSc::VALUE1), v2["Enums"][0] = "VALUE1";
+ m2.AddEnums(NSc::VALUE2), v2["Enums"][1] = "VALUE2";
+
+ *(m.MutableMessage()) = m2, v["Message"] = v2;
+
+ *(m.AddMessages()) = m2, v["Messages"][0] = v2;
+ *(m.AddMessages()) = m2, v["Messages"][1] = v2;
+
+ if (fromProto) {
+ UNIT_ASSERT(NSc::TValue::Equal(v, NSc::TValue::From(m, mapAsDict)));
+ } else {
+ NSc::TMessage proto;
+ v.To(proto);
+
+ TString differentPath;
+ UNIT_ASSERT_C(NProtoBuf::IsEqual(m, proto, &differentPath), differentPath);
+ }
+ }
+};
diff --git a/library/cpp/scheme/tests/ut/scheme_ut.cpp b/library/cpp/scheme/tests/ut/scheme_ut.cpp
new file mode 100644
index 0000000000..1a5d07c31b
--- /dev/null
+++ b/library/cpp/scheme/tests/ut/scheme_ut.cpp
@@ -0,0 +1,879 @@
+#include <library/cpp/scheme/scimpl_private.h>
+#include <library/cpp/scheme/ut_utils/scheme_ut_utils.h>
+#include <library/cpp/testing/unittest/registar.h>
+
+#include <util/stream/null.h>
+#include <library/cpp/string_utils/quote/quote.h>
+#include <util/string/subst.h>
+#include <util/string/util.h>
+
+#include <type_traits>
+
+Y_UNIT_TEST_SUITE(TSchemeTest) {
+
+ Y_UNIT_TEST(TestNaN) {
+ UNIT_ASSERT_VALUES_EQUAL("null", NSc::TValue(std::numeric_limits<double>::quiet_NaN()).ToJson());
+ UNIT_ASSERT_VALUES_EQUAL("null", NSc::TValue(-std::numeric_limits<double>::infinity()).ToJson());
+ UNIT_ASSERT_VALUES_EQUAL("null", NSc::TValue(std::numeric_limits<double>::infinity()).ToJson());
+ UNIT_ASSERT_VALUES_EQUAL("1", NSc::TValue(1.0).ToJson());
+ }
+
+ Y_UNIT_TEST(TestNumbers) {
+ {
+ NSc::TValue vd;
+ UNIT_ASSERT_VALUES_EQUAL(2.5, vd.GetNumberMutable(2.5));
+ UNIT_ASSERT_VALUES_EQUAL(2, vd.GetIntNumberMutable(-1));
+ }
+ {
+ NSc::TValue vi;
+ UNIT_ASSERT_VALUES_EQUAL(2, vi.GetIntNumberMutable(2));
+ UNIT_ASSERT_VALUES_EQUAL(2., vi.GetNumberMutable(-1));
+ }
+ {
+ NSc::TValue vb = NSc::TValue::FromJson("true");
+
+ UNIT_ASSERT_VALUES_EQUAL("true", vb.ToJson());
+
+ UNIT_ASSERT(vb.IsBool());
+ UNIT_ASSERT(vb.IsIntNumber());
+ UNIT_ASSERT(vb.IsNumber());
+ UNIT_ASSERT_VALUES_EQUAL(true, vb.GetBool());
+ UNIT_ASSERT_VALUES_EQUAL(1, vb.GetIntNumber());
+ UNIT_ASSERT_VALUES_EQUAL(1.0, vb.GetNumber());
+
+ NSc::TValue vb1 = vb.Clone();
+
+ UNIT_ASSERT(NSc::TValue::Equal(vb, vb1));
+
+ UNIT_ASSERT_VALUES_EQUAL(true, vb.GetBool());
+ UNIT_ASSERT_VALUES_EQUAL(1, vb.GetIntNumber());
+ UNIT_ASSERT_VALUES_EQUAL(1.0, vb.GetNumber());
+ UNIT_ASSERT(vb.IsBool());
+ UNIT_ASSERT_VALUES_EQUAL(1, vb.GetIntNumberMutable());
+ UNIT_ASSERT(!vb.IsBool());
+
+ UNIT_ASSERT(NSc::TValue::Equal(vb, vb1));
+
+ UNIT_ASSERT(vb1.IsBool());
+ UNIT_ASSERT_VALUES_EQUAL(1.0, vb1.GetNumberMutable());
+ UNIT_ASSERT(!vb1.IsBool());
+
+ vb.SetBool(true);
+
+ UNIT_ASSERT(vb.IsBool());
+ UNIT_ASSERT(NSc::TValue::Equal(vb, vb1));
+
+ vb = NSc::TValue::FromJson("false");
+
+ UNIT_ASSERT_VALUES_EQUAL("false", vb.ToJson());
+ UNIT_ASSERT(!NSc::TValue::Equal(vb, vb1));
+
+ UNIT_ASSERT(vb.IsBool());
+ UNIT_ASSERT(vb.IsIntNumber());
+ UNIT_ASSERT(vb.IsNumber());
+ UNIT_ASSERT_VALUES_EQUAL(false, vb.GetBool());
+ UNIT_ASSERT_VALUES_EQUAL(0.0, vb.GetNumber());
+ UNIT_ASSERT_VALUES_EQUAL(0, vb.GetIntNumber());
+
+ NSc::TValue vd = NSc::TValue::FromJson("1.0");
+
+ UNIT_ASSERT(vd.IsNumber());
+ UNIT_ASSERT(!vd.IsIntNumber());
+ UNIT_ASSERT_VALUES_EQUAL(1.0, vd.GetNumber());
+ UNIT_ASSERT_VALUES_EQUAL(1, vd.GetIntNumber());
+ UNIT_ASSERT_VALUES_EQUAL(1.0, vd.GetNumberMutable());
+
+ NSc::TValue vi = NSc::TValue::FromJson("1");
+
+ UNIT_ASSERT(vi.IsNumber());
+ UNIT_ASSERT(vi.IsIntNumber());
+ UNIT_ASSERT_VALUES_EQUAL(1.0, vi.GetNumber());
+ UNIT_ASSERT_VALUES_EQUAL(1, vi.GetIntNumber());
+ UNIT_ASSERT_VALUES_EQUAL(1, vi.GetIntNumberMutable());
+
+ UNIT_ASSERT(NSc::TValue::Equal(vd, vi));
+
+ vd.SetNumber(1.5);
+ UNIT_ASSERT(vd.IsNumber());
+ UNIT_ASSERT(!vd.IsIntNumber());
+ UNIT_ASSERT_VALUES_EQUAL(1.5, vd.GetNumber());
+ UNIT_ASSERT_VALUES_EQUAL(1, vd.GetIntNumber());
+ UNIT_ASSERT_VALUES_EQUAL(1.5, vd.GetNumberMutable());
+
+ UNIT_ASSERT(!NSc::TValue::Equal(vd, vi));
+
+ UNIT_ASSERT_VALUES_EQUAL("1", vi.ToJson());
+ UNIT_ASSERT_VALUES_EQUAL("1.5", vd.ToJson());
+
+ UNIT_ASSERT_VALUES_EQUAL(1, vd.GetIntNumberMutable());
+ UNIT_ASSERT(NSc::TValue::Equal(vd, vi));
+ vd.SetIntNumber(2);
+ UNIT_ASSERT(!NSc::TValue::Equal(vd, vi));
+ vi.SetNumber(2.);
+ UNIT_ASSERT(NSc::TValue::Equal(vd, vi));
+ vd.SetNumber(2.);
+ UNIT_ASSERT(NSc::TValue::Equal(vd, vi));
+ vi.SetIntNumber(5);
+ vd.MergeUpdate(vi);
+ UNIT_ASSERT(vd.IsNumber());
+ UNIT_ASSERT(vd.IsIntNumber());
+ UNIT_ASSERT_VALUES_EQUAL(5, vd.GetIntNumber());
+ vd.SetNumber(3.3);
+ vi.MergeUpdate(vd);
+ UNIT_ASSERT(vi.IsNumber());
+ UNIT_ASSERT(!vi.IsIntNumber());
+ UNIT_ASSERT_VALUES_EQUAL(3.3, vi.GetNumber());
+
+ vi.SetIntNumber(Max<i64>());
+ UNIT_ASSERT_VALUES_EQUAL("9223372036854775807", vi.ToJson());
+ }
+ }
+
+ template <typename T>
+ void DoTestForce(T t) {
+ UNIT_ASSERT_VALUES_EQUAL_C(i64(t), NSc::TValue(i64(t)).ForceIntNumber(), ToString(t));
+ UNIT_ASSERT_VALUES_EQUAL_C(double(t), NSc::TValue(double(t)).ForceNumber(), ToString(t));
+
+ UNIT_ASSERT_VALUES_EQUAL_C(i64(t), NSc::TValue(TStringBuf(ToString(i64(t)))).ForceIntNumber(), ToString(t));
+ UNIT_ASSERT_VALUES_EQUAL_C(ToString(double(t)), ToString(NSc::TValue(TStringBuf(ToString(double(t)))).ForceNumber()), ToString(t));
+
+ UNIT_ASSERT_VALUES_EQUAL_C(ToString(i64(t)), NSc::TValue(TStringBuf(ToString(i64(t)))).ForceString(), ToString(t));
+ UNIT_ASSERT_VALUES_EQUAL_C(ToString(double(t)), NSc::TValue(TStringBuf(ToString(double(t)))).ForceString(), ToString(t));
+ }
+
+ Y_UNIT_TEST(TestForce) {
+ DoTestForce(Max<i64>());
+ DoTestForce(Min<i64>());
+ DoTestForce(1.5);
+ DoTestForce(-1.5);
+
+ UNIT_ASSERT_VALUES_EQUAL(1, NSc::TValue("32a").ForceIntNumber(1));
+ UNIT_ASSERT_VALUES_EQUAL(1.5, NSc::TValue("32a").ForceNumber(1.5));
+ }
+
+ template <typename T>
+ void DoCheckRelations(T t, T tless, T tmore, const NSc::TValue& v, TStringBuf ss) {
+ UNIT_ASSERT_C((t == v), ss);
+ UNIT_ASSERT_C(!(t != v), ss);
+ UNIT_ASSERT_C((t <= v), ss);
+ UNIT_ASSERT_C((t >= v), ss);
+ UNIT_ASSERT_C(!(t < v), ss);
+ UNIT_ASSERT_C(!(t > v), ss);
+
+ UNIT_ASSERT_C(!(tless == v), ss);
+ UNIT_ASSERT_C((tless != v), ss);
+ UNIT_ASSERT_C((tless <= v), ss);
+ UNIT_ASSERT_C(!(tless >= v), ss);
+ UNIT_ASSERT_C((tless < v), ss);
+ UNIT_ASSERT_C(!(tless > v), ss);
+ UNIT_ASSERT_C(!(tmore == v), ss);
+ UNIT_ASSERT_C((tmore != v), ss);
+ UNIT_ASSERT_C(!(tmore <= v), ss);
+ UNIT_ASSERT_C((tmore >= v), ss);
+ UNIT_ASSERT_C(!(tmore < v), ss);
+ UNIT_ASSERT_C((tmore > v), ss);
+ }
+
+ void DoCheckRelations(const NSc::TValue& t, const NSc::TValue&, const NSc::TValue&, const NSc::TValue& v, TStringBuf ss) {
+ UNIT_ASSERT_C((t == v), ss);
+ UNIT_ASSERT_C(!(t != v), ss);
+ }
+
+ // void DoCheckRelations(bool t, bool, bool, const NSc::TValue& v, TStringBuf ss) {
+ // UNIT_ASSERT_C((t == v), ss);
+ // UNIT_ASSERT_C(!(t != v), ss);
+ // }
+
+ template <typename T>
+ void DoCheckAssignment(T t, T tless, T tmore, TStringBuf s, TStringBuf ss) {
+ bool expectint = std::is_integral<T>::value;
+ bool expectnum = std::is_arithmetic<T>::value;
+ bool expectbool = std::is_same<bool, T>::value;
+
+ {
+ NSc::TValue v(t);
+ UNIT_ASSERT_VALUES_EQUAL_C(expectnum, v.IsNumber(), ss);
+ UNIT_ASSERT_VALUES_EQUAL_C(expectint, v.IsIntNumber(), ss);
+ UNIT_ASSERT_VALUES_EQUAL_C(expectbool, v.IsBool(), ss);
+ UNIT_ASSERT_VALUES_EQUAL_C(s, v.ToJson(), ss);
+ DoCheckRelations(t, tless, tmore, v, ss);
+ }
+ {
+ NSc::TValue v;
+ UNIT_ASSERT(v.IsNull());
+ v = t;
+ UNIT_ASSERT(!v.IsNull());
+ UNIT_ASSERT_VALUES_EQUAL_C(expectnum, v.IsNumber(), ss);
+ UNIT_ASSERT_VALUES_EQUAL_C(expectint, v.IsIntNumber(), ss);
+ UNIT_ASSERT_VALUES_EQUAL_C(expectbool, v.IsBool(), ss);
+ UNIT_ASSERT_VALUES_EQUAL_C(s, v.ToJson(), ss);
+ DoCheckRelations(t, tless, tmore, v, ss);
+ }
+ }
+
+ template <size_t N>
+ void DoCheckAssignmentArr(const char (&t)[N], const char (&tless)[N], const char (&tmore)[N], TStringBuf s, TStringBuf ss) {
+ {
+ NSc::TValue v(t);
+
+ UNIT_ASSERT_C(v.IsString(), ss);
+ UNIT_ASSERT_VALUES_EQUAL_C(s, v.ToJson(), ss);
+ DoCheckRelations(t, tless, tmore, v, ss);
+ }
+ {
+ NSc::TValue v;
+ v = t;
+ UNIT_ASSERT_C(v.IsString(), ss);
+ UNIT_ASSERT_VALUES_EQUAL_C(s, v.ToJson(), ss);
+ DoCheckRelations(t, tless, tmore, v, ss);
+ }
+ }
+
+ template <typename T>
+ void DoCheckAssignmentNum(T t, T tless, T tmore, TStringBuf s, TStringBuf ss) {
+ DoCheckAssignment(t, tless, tmore, s, ss);
+ {
+ NSc::TValue v;
+ T tt = (v = t);
+ UNIT_ASSERT_VALUES_EQUAL_C(t, tt, ss);
+ }
+ }
+
+ Y_UNIT_TEST(TestAssignments) {
+ for (int i = -2; i < 3; ++i) {
+ TString ii = ToString(i);
+ int iless = i - 1;
+ int imore = i + 1;
+ DoCheckAssignmentNum<signed char>(i, iless, imore, ii, "schar");
+ DoCheckAssignmentNum<short>(i, iless, imore, ii, "short");
+ DoCheckAssignmentNum<int>(i, iless, imore, ii, "int");
+ DoCheckAssignmentNum<long>(i, iless, imore, ii, "long");
+ DoCheckAssignmentNum<long long>(i, iless, imore, ii, "longlong");
+ DoCheckAssignmentNum<i8>(i, iless, imore, ii, "i8");
+ DoCheckAssignmentNum<i16>(i, iless, imore, ii, "i16");
+ DoCheckAssignmentNum<i32>(i, iless, imore, ii, "i32");
+ DoCheckAssignmentNum<i64>(i, iless, imore, ii, "i64");
+
+ DoCheckAssignmentNum<float>(i, iless, imore, ii, "float");
+ DoCheckAssignmentNum<double>(i, iless, imore, ii, "double");
+ }
+
+ // DoCheckAssignment<bool>(true, true, true, "true", "bool");
+ // DoCheckAssignment<bool>(false, false, false, "false", "bool");
+
+ for (int i = 1; i < 3; ++i) {
+ TString ii = ToString(i);
+ int iless = i - 1;
+ int imore = i + 1;
+
+ DoCheckAssignmentNum<char>(i, iless, imore, ii, "char");
+
+ DoCheckAssignmentNum<signed char>(i, iless, imore, ii, "schar");
+ DoCheckAssignmentNum<short>(i, iless, imore, ii, "short");
+ DoCheckAssignmentNum<int>(i, iless, imore, ii, "int");
+ DoCheckAssignmentNum<long>(i, iless, imore, ii, "long");
+ DoCheckAssignmentNum<long long>(i, iless, imore, ii, "longlong");
+ DoCheckAssignmentNum<i8>(i, iless, imore, ii, "i8");
+ DoCheckAssignmentNum<i16>(i, iless, imore, ii, "i16");
+ DoCheckAssignmentNum<i32>(i, iless, imore, ii, "i32");
+ DoCheckAssignmentNum<i64>(i, iless, imore, ii, "i64");
+
+ DoCheckAssignmentNum<unsigned char>(i, iless, imore, ii, "uchar");
+ DoCheckAssignmentNum<unsigned short>(i, iless, imore, ii, "ushort");
+ DoCheckAssignmentNum<unsigned int>(i, iless, imore, ii, "uint");
+ DoCheckAssignmentNum<unsigned long>(i, iless, imore, ii, "ulong");
+ DoCheckAssignmentNum<unsigned long long>(i, iless, imore, ii, "ulonglong");
+ DoCheckAssignmentNum<ui8>(i, iless, imore, ii, "ui8");
+ DoCheckAssignmentNum<ui16>(i, iless, imore, ii, "ui16");
+ DoCheckAssignmentNum<ui32>(i, iless, imore, ii, "ui32");
+ DoCheckAssignmentNum<ui64>(i, iless, imore, ii, "ui64");
+
+ DoCheckAssignmentNum<float>(i, iless, imore, ii, "float");
+ DoCheckAssignmentNum<double>(i, iless, imore, ii, "double");
+ }
+
+ TString uuu = "uuu";
+ TString uua = "uua";
+ TString uuz = "uuz";
+ DoCheckAssignment<char*>(uuu.begin(), uua.begin(), uuz.begin(), "\"uuu\"", "char*");
+ DoCheckAssignment<const char*>("www", "wwa", "wwz", "\"www\"", "const char*");
+ DoCheckAssignmentArr("xxx", "xxa", "xxz", "\"xxx\"", "const char[]");
+ DoCheckAssignment<TStringBuf>("yyy", "yya", "yyz", "\"yyy\"", "TStringBuf");
+
+#if defined(_MSC_VER)
+ //TODO
+#else
+ DoCheckAssignment<TString>("ttt", "tta", "ttz", "\"ttt\"", "TString");
+#endif
+
+ NSc::TValue v;
+ v.SetDict();
+ DoCheckAssignment<NSc::TValue>(v, v, v, "{}", "TValue");
+ DoCheckAssignment<NSc::TValue&>(v, v, v, "{}", "TValue&");
+ DoCheckAssignment<const NSc::TValue&>(v, v, v, "{}", "const TValue&");
+
+ NSc::TValue v1{1};
+ UNIT_ASSERT_VALUES_EQUAL(v1.ToJson(), "1");
+ }
+
+ Y_UNIT_TEST(TestAssignmentDictChild) {
+ {
+ NSc::TValue v;
+ {
+ NSc::TValue b;
+ v["a"] = b;
+ }
+ v = v["a"];
+ }
+ {
+ NSc::TValue v;
+ {
+ NSc::TValue b;
+ v["a"] = b;
+ }
+ v = v.Get("a");
+ }
+ {
+ NSc::TValue v;
+ {
+ NSc::TValue b;
+ v["a"] = b;
+ }
+ v = std::move(v["a"]);
+ }
+ }
+
+ Y_UNIT_TEST(TestInsert) {
+ NSc::TValue v;
+ v.Insert(0, "b");
+ v.Insert(0, "a");
+ v.Insert(2, "d");
+ v.Insert(2, "c");
+ UNIT_ASSERT_VALUES_EQUAL(v.ToJson(), R"(["a","b","c","d"])");
+
+ v.AppendAll({1, 2, 3});
+ UNIT_ASSERT_VALUES_EQUAL(v.ToJson(), R"(["a","b","c","d",1,2,3])");
+
+ TVector<int> d{4, 5, 6};
+ v.AppendAll(d);
+ UNIT_ASSERT_VALUES_EQUAL(v.ToJson(), R"(["a","b","c","d",1,2,3,4,5,6])");
+ UNIT_ASSERT_VALUES_EQUAL(d.size(), 3u);
+ TVector<TStringBuf> s{"x", "y", "z"};
+ v.AppendAll(s.begin(), s.end());
+ UNIT_ASSERT_VALUES_EQUAL(v.ToJson(), R"(["a","b","c","d",1,2,3,4,5,6,"x","y","z"])");
+
+ UNIT_ASSERT_VALUES_EQUAL(v.Clone().Clear().AppendAll(s).ToJson(), R"(["x","y","z"])");
+ UNIT_ASSERT_VALUES_EQUAL(v.Clone().Clear().AppendAll(TVector<TStringBuf>()).ToJson(), R"([])");
+
+ v.AddAll({{"a", "b"}, {"c", "d"}});
+ UNIT_ASSERT_VALUES_EQUAL(v.ToJson(), R"({"a":"b","c":"d"})");
+ }
+
+ Y_UNIT_TEST(TestFrontBack) {
+ NSc::TValue v;
+ const NSc::TValue& vv = v;
+ UNIT_ASSERT(NSc::TValue::Same(vv.Front(), NSc::Null()));
+ UNIT_ASSERT(NSc::TValue::Same(vv.Back(), NSc::Null()));
+ UNIT_ASSERT(!vv.IsArray());
+ v.Back() = "a";
+ UNIT_ASSERT_VALUES_EQUAL("a", vv.Front().GetString());
+ UNIT_ASSERT_VALUES_EQUAL("a", vv.Back().GetString());
+ UNIT_ASSERT(vv.IsArray());
+ v.Push("b");
+ UNIT_ASSERT_VALUES_EQUAL("a", vv.Front().GetString());
+ UNIT_ASSERT_VALUES_EQUAL("b", vv.Back().GetString());
+
+ UNIT_ASSERT_VALUES_EQUAL("b", v.Pop().GetString());
+
+ UNIT_ASSERT_VALUES_EQUAL("a", vv.Front().GetString());
+ UNIT_ASSERT_VALUES_EQUAL("a", vv.Back().GetString());
+
+ UNIT_ASSERT_VALUES_EQUAL("a", v.Pop().GetString());
+
+ UNIT_ASSERT(NSc::TValue::Same(vv.Front(), NSc::Null()));
+ UNIT_ASSERT(NSc::TValue::Same(vv.Back(), NSc::Null()));
+
+ v.Front() = "a";
+ UNIT_ASSERT_VALUES_EQUAL("a", vv.Front().GetString());
+ UNIT_ASSERT_VALUES_EQUAL("a", vv.Back().GetString());
+ }
+
+ Y_UNIT_TEST(TestAssign) {
+ NSc::TValue v;
+ v.SetArray();
+ v.Push() = "test";
+ UNIT_ASSERT_VALUES_EQUAL(v.ToJson(), "[\"test\"]");
+ UNIT_ASSERT(NSc::TValue::SamePool(v[0], v));
+ }
+
+ NSc::TValue MutableRef(const NSc::TValue& v) {
+ return v;
+ }
+
+ NSc::TValue Clone(const NSc::TValue& v) {
+ NSc::TValue v1 = v.Clone();
+ UNIT_ASSERT_VALUES_EQUAL(v1.ToJson(true), v.ToJson(true));
+ return v1;
+ }
+
+ Y_UNIT_TEST(TestCOW) {
+ NSc::TValue vd = NSc::TValue::FromJson("{ a : 1, b : c}");
+ NSc::TValue va = NSc::TValue::FromJson("[ x, y]");
+ NSc::TValue vs = NSc::TValue::FromJson("foo");
+ NSc::TValue vn = NSc::TValue::FromJson("1");
+ TString sd = "{\"a\":1,\"b\":\"c\"}";
+ TString sa = "[\"x\",\"y\"]";
+ TString ss = "\"foo\"";
+ TString sn = "1";
+ UNIT_ASSERT_VALUES_EQUAL(sd, vd.ToJson(true));
+ UNIT_ASSERT_VALUES_EQUAL(sa, va.ToJson(true));
+ UNIT_ASSERT_VALUES_EQUAL(ss, vs.ToJson(true));
+ UNIT_ASSERT_VALUES_EQUAL(sn, vn.ToJson(true));
+
+ {
+ NSc::TValue v2 = MutableRef(vn);
+ v2 = -1;
+
+ UNIT_ASSERT_VALUES_EQUAL(sn, vn.ToJson(true));
+
+ NSc::TValue v3 = Clone(vn);
+ v3 = -1;
+
+ UNIT_ASSERT_VALUES_EQUAL(v3.ToJson(true), v2.ToJson(true));
+ }
+
+ {
+ NSc::TValue v2 = MutableRef(vs);
+ v2 = "xxx";
+
+ UNIT_ASSERT_VALUES_EQUAL(ss, vs.ToJson(true));
+
+ NSc::TValue v3 = Clone(vs);
+ v3 = "xxx";
+
+ UNIT_ASSERT_VALUES_EQUAL(v3.ToJson(true), v2.ToJson(true));
+ }
+
+ {
+ NSc::TValue v2 = MutableRef(vd);
+ v2["a"] = "zzz";
+
+ UNIT_ASSERT_VALUES_EQUAL(sd, vd.ToJson(true));
+
+ NSc::TValue v3 = Clone(vd);
+ v3["a"] = "zzz";
+
+ UNIT_ASSERT_VALUES_EQUAL(v3.ToJson(true), v2.ToJson(true));
+ }
+
+ {
+ NSc::TValue v2 = MutableRef(va);
+ v2[0] = "zzz";
+
+ UNIT_ASSERT_VALUES_EQUAL(sa, va.ToJson(true));
+
+ NSc::TValue v3 = Clone(va);
+ v3[0] = "zzz";
+
+ UNIT_ASSERT_VALUES_EQUAL(v3.ToJson(true), v2.ToJson(true));
+ }
+
+ {
+ NSc::TValue v2 = MutableRef(va);
+
+ UNIT_ASSERT_VALUES_EQUAL(sa, va.ToJson(true));
+
+ NSc::TValue v3 = Clone(va);
+
+ UNIT_ASSERT_VALUES_EQUAL(v3.ToJson(true), v2.ToJson(true));
+ }
+
+ {
+ NSc::TValue v2 = MutableRef(va);
+ v2.ClearArray();
+
+ UNIT_ASSERT_VALUES_EQUAL(sa, va.ToJson(true));
+
+ NSc::TValue v3 = Clone(va);
+ v3.ClearArray();
+
+ UNIT_ASSERT_VALUES_EQUAL(v3.ToJson(true), v2.ToJson(true));
+ }
+ }
+
+ Y_UNIT_TEST(TestOperators) {
+ UNIT_ASSERT("test" == NSc::TValue("test"));
+ UNIT_ASSERT(NSc::TValue("test") == "test");
+
+ UNIT_ASSERT("test1" != NSc::TValue("test"));
+ UNIT_ASSERT(NSc::TValue("test") != "test1");
+
+ UNIT_ASSERT("test1" > NSc::TValue("test"));
+ UNIT_ASSERT(NSc::TValue("test") < "test1");
+
+ UNIT_ASSERT("test" < NSc::TValue("test1"));
+ UNIT_ASSERT(NSc::TValue("test1") > "test");
+
+ UNIT_ASSERT(1 == NSc::TValue(1));
+ UNIT_ASSERT(NSc::TValue(1) == 1);
+
+ UNIT_ASSERT(2 != NSc::TValue(1));
+ UNIT_ASSERT(NSc::TValue(1) != 2);
+
+ UNIT_ASSERT(1 < NSc::TValue(2));
+ UNIT_ASSERT(NSc::TValue(2) > 1);
+
+ UNIT_ASSERT(2 > NSc::TValue(1));
+ UNIT_ASSERT(NSc::TValue(1) < 2);
+
+ UNIT_ASSERT(TString("test") == NSc::TValue("test"));
+ }
+
+ Y_UNIT_TEST(TestDestructor) {
+ NSc::TValue v;
+ const NSc::TValue& v1 = v;
+ v1.GetString();
+ v1.GetArray();
+ v1.GetNumber();
+ v1.GetDict();
+ v.GetString();
+ v.GetArray();
+ v.GetNumber();
+ v.GetDict();
+ }
+
+ void DoTestSamePool(TStringBuf json, TStringBuf jpath) {
+ NSc::TValue v = NSc::TValue::FromJson(json);
+ UNIT_ASSERT_C(NSc::TValue::SamePool(v, v.TrySelect(jpath)), json);
+ }
+
+ Y_UNIT_TEST(TestSamePool) {
+ DoTestSamePool("", "");
+ DoTestSamePool("a", "");
+ DoTestSamePool("[a]", "0");
+ DoTestSamePool("{a:b}", "a");
+ DoTestSamePool("{a:{b:c}}", "a/b");
+ DoTestSamePool("{a:{b:[c, {}]}}", "a/b/1");
+ DoTestSamePool("{a:{b:[c, {d:{e:[]}}]}}", "a/b/1/d/e");
+ UNIT_ASSERT(!NSc::TValue::SamePool(NSc::TValue(), NSc::TValue()));
+ UNIT_ASSERT(!NSc::TValue::SamePool(NSc::Null().Clone(), NSc::Null()));
+ UNIT_ASSERT(!NSc::TValue::SamePool(NSc::TValue() = 0, NSc::TValue()));
+ UNIT_ASSERT(!NSc::TValue::SamePool(NSc::TValue::FromJson("a"), NSc::TValue::FromJson("a")));
+ NSc::TValue v, vv;
+ v["x"] = vv;
+ UNIT_ASSERT(!NSc::TValue::SamePool(v, vv));
+ UNIT_ASSERT(!NSc::TValue::SamePool(v, v["x"]));
+ v = vv;
+ UNIT_ASSERT(NSc::TValue::SamePool(v, vv));
+ }
+
+ Y_UNIT_TEST(TestLoopDetection) {
+ NSc::NImpl::GetTlsInstance<NSc::NImpl::TSelfLoopContext>().ReportingMode
+ = NSc::NImpl::TSelfLoopContext::EMode::Stderr;
+
+ NSc::TValue x;
+
+ x["a"]["x"] = x;
+ x["b"][0] = x;
+
+ UNIT_ASSERT(x.IsSameOrAncestorOf(x));
+ UNIT_ASSERT(x.IsSameOrAncestorOf(x.Get("a")));
+ UNIT_ASSERT(x.Get("a").IsSameOrAncestorOf(x));
+
+ UNIT_ASSERT_VALUES_EQUAL(x.ToJson(), "{\"a\":{\"x\":null},\"b\":[null]}");
+
+ NSc::TValue y = x.Clone();
+
+ UNIT_ASSERT(y.Has("a"));
+ UNIT_ASSERT(y.Get("a").Has("x"));
+ UNIT_ASSERT(y.Get("a").Get("x").IsNull());
+ UNIT_ASSERT(y.Get("a").Get("x").IsNull());
+
+ UNIT_ASSERT(y.Has("b"));
+ UNIT_ASSERT(y.Get("b").Has(0));
+ UNIT_ASSERT(y.Get("b").Get(0).IsNull());
+
+ UNIT_ASSERT_VALUES_EQUAL(y.ToJson(), "{\"a\":{\"x\":null},\"b\":[null]}");
+
+ NSc::TValue z;
+ z.MergeUpdate(x);
+
+ UNIT_ASSERT(z.Has("a"));
+ UNIT_ASSERT(z.Get("a").Has("x"));
+ UNIT_ASSERT(z.Get("a").Get("x").IsNull());
+
+ UNIT_ASSERT(z.Has("b"));
+ UNIT_ASSERT(z.Get("b").Has(0));
+ UNIT_ASSERT(z.Get("b").Get(0).IsNull());
+
+ UNIT_ASSERT_VALUES_EQUAL(z.ToJson(), "{\"a\":{\"x\":null},\"b\":[null]}");
+
+ x["a"].Delete("x");
+ x["b"].Delete(0);
+
+ UNIT_ASSERT(x.IsSameOrAncestorOf(x));
+ UNIT_ASSERT(x.IsSameOrAncestorOf(x.Get("a")));
+ UNIT_ASSERT(!x.Get("a").IsSameOrAncestorOf(x));
+ }
+
+ Y_UNIT_TEST(TestLoopDetectionThrow) {
+ NSc::NImpl::GetTlsInstance<NSc::NImpl::TSelfLoopContext>().ReportingMode
+ = NSc::NImpl::TSelfLoopContext::EMode::Throw;
+
+ {
+ NSc::TValue x;
+ x["a"]["x"] = x;
+
+ UNIT_ASSERT(x.IsSameOrAncestorOf(x));
+ UNIT_ASSERT(x.IsSameOrAncestorOf(x.Get("a")));
+ UNIT_ASSERT(x.Get("a").IsSameOrAncestorOf(x));
+
+ UNIT_ASSERT_EXCEPTION(x.ToJson(), NSc::TSchemeException);
+ UNIT_ASSERT_EXCEPTION(x.Clone(), NSc::TSchemeException);
+
+ NSc::TValue z;
+ UNIT_ASSERT_EXCEPTION(z.MergeUpdate(x), NSc::TSchemeException);
+
+ UNIT_ASSERT_VALUES_EQUAL(z.ToJson(), "{\"a\":{\"x\":null}}");
+
+ x["a"].Delete("x");
+
+ UNIT_ASSERT(x.IsSameOrAncestorOf(x));
+ UNIT_ASSERT(x.IsSameOrAncestorOf(x.Get("a")));
+ UNIT_ASSERT(!x.Get("a").IsSameOrAncestorOf(x));
+
+ UNIT_ASSERT_VALUES_EQUAL(x.ToJson(), "{\"a\":{}}");
+ }
+
+ {
+ NSc::TValue x;
+ x["a"][0] = x;
+
+ UNIT_ASSERT(x.IsSameOrAncestorOf(x));
+ UNIT_ASSERT(x.IsSameOrAncestorOf(x.Get("a")));
+ UNIT_ASSERT(x.Get("a").IsSameOrAncestorOf(x));
+
+ UNIT_ASSERT_EXCEPTION(x.ToJson(), NSc::TSchemeException);
+ UNIT_ASSERT_EXCEPTION(x.Clone(), NSc::TSchemeException);
+
+ NSc::TValue z;
+ UNIT_ASSERT_EXCEPTION(z.MergeUpdate(x), NSc::TSchemeException);
+
+ UNIT_ASSERT_VALUES_EQUAL(z.ToJson(), "{\"a\":[null]}");
+
+ x["a"].Delete(0);
+
+ UNIT_ASSERT(x.IsSameOrAncestorOf(x));
+ UNIT_ASSERT(x.IsSameOrAncestorOf(x.Get("a")));
+ UNIT_ASSERT(!x.Get("a").IsSameOrAncestorOf(x));
+
+ UNIT_ASSERT_VALUES_EQUAL(x.ToJson(), "{\"a\":[]}");
+ }
+ }
+
+ Y_UNIT_TEST(TestIsSameOrAncestorOf) {
+ NSc::TValue x;
+ UNIT_ASSERT(x.IsSameOrAncestorOf(x));
+
+ x["a"] = NSc::Null();
+ UNIT_ASSERT(x.IsSameOrAncestorOf(x.Get("a")));
+
+ NSc::TValue a = 1;
+ NSc::TValue b = 2;
+ NSc::TValue c = 3;
+ NSc::TValue d = 4;
+
+ x["a"] = a;
+ x["b"] = b;
+ x["c"] = a;
+ UNIT_ASSERT(x.IsSameOrAncestorOf(a));
+ UNIT_ASSERT(x.IsSameOrAncestorOf(b));
+ UNIT_ASSERT(x.Get("a").IsSameOrAncestorOf(a));
+ UNIT_ASSERT(x.Get("b").IsSameOrAncestorOf(b));
+ UNIT_ASSERT(x.Get("c").IsSameOrAncestorOf(a));
+
+ UNIT_ASSERT(!x.Get("a").IsSameOrAncestorOf(b));
+ UNIT_ASSERT(!x.Get("b").IsSameOrAncestorOf(a));
+
+ UNIT_ASSERT(!x.Get("a").Get(0).IsSameOrAncestorOf(a));
+
+ b.Push() = c;
+ b.Push() = d;
+ b.Push() = c;
+
+ UNIT_ASSERT(x.Get("b").IsSameOrAncestorOf(b));
+ UNIT_ASSERT(x.IsSameOrAncestorOf(c));
+ UNIT_ASSERT(x.IsSameOrAncestorOf(d));
+ UNIT_ASSERT(x.Get("b").Get(0).IsSameOrAncestorOf(c));
+ UNIT_ASSERT(x.Get("b").Get(1).IsSameOrAncestorOf(d));
+ UNIT_ASSERT(x.Get("b").Get(2).IsSameOrAncestorOf(c));
+
+ UNIT_ASSERT(b.Get(0).IsSameOrAncestorOf(b.Get(2)));
+ UNIT_ASSERT(b.Get(2).IsSameOrAncestorOf(b.Get(0)));
+ UNIT_ASSERT(b.Get(0).IsSameOrAncestorOf(c));
+ UNIT_ASSERT(b.Get(1).IsSameOrAncestorOf(d));
+
+ UNIT_ASSERT(!b.Get(0).IsSameOrAncestorOf(d));
+ UNIT_ASSERT(!b.Get(1).IsSameOrAncestorOf(c));
+ }
+
+ static void ByVal(NSc::TValue v) {
+ v["VAL"] = 1;
+ }
+
+ static void ByRef(NSc::TValue& v) {
+ ByVal(v);
+ }
+
+ static void ByRefAndModify(NSc::TValue& v) {
+ v["REF"] = 1;
+ ByVal(v);
+ }
+
+ Y_UNIT_TEST(TestMove) {
+ using namespace NSc;
+ {
+ TValue v = TValue::FromJson("{}");
+ ByRef(v);
+ UNIT_ASSERT_VALUES_EQUAL(v.ToJson(), "{\"VAL\":1}");
+ }
+ {
+ TValue v = TValue::FromJson("{}");
+ ByVal(v);
+ UNIT_ASSERT_VALUES_EQUAL(v.ToJson(), "{\"VAL\":1}");
+ }
+ {
+ TValue v;
+ ByVal(v);
+ UNIT_ASSERT_VALUES_EQUAL(v.ToJson(), "{\"VAL\":1}");
+ }
+ {
+ TValue v = TValue::FromJson("{}");
+ ByRefAndModify(v);
+ UNIT_ASSERT_VALUES_EQUAL(v.ToJson(), "{\"REF\":1,\"VAL\":1}");
+ }
+ {
+ TValue v = TValue::FromJson("{foo:bar}");
+ TValue w(std::move(v));
+ UNIT_ASSERT(v.IsNull());
+ v = static_cast<TValue&>(v);
+ UNIT_ASSERT(v.IsNull());
+ UNIT_ASSERT_VALUES_EQUAL(w.Get("foo").GetString(), "bar");
+ v = std::move(w);
+ UNIT_ASSERT_VALUES_EQUAL(v.Get("foo").GetString(), "bar");
+ UNIT_ASSERT(w.IsNull());
+ UNIT_ASSERT(w.Get("foo").IsNull()); // no crash here
+ w["foo"] = "baz"; // no crash here
+ UNIT_ASSERT(w.IsDict());
+ UNIT_ASSERT_VALUES_EQUAL(w.Get("foo").GetString(), "baz");
+ }
+ UNIT_ASSERT(NSc::TValue::DefaultValue().IsNull());
+ }
+
+ //SPI-25156
+ Y_UNIT_TEST(TestMoveNotCorruptingDefault) {
+ using namespace NSc;
+ TValue w = TValue::FromJson("{foo:bar}");
+ TValue v = std::move(w);
+ w["foo"] = "baz"; // no crash here
+ UNIT_ASSERT(NSc::TValue::DefaultValue().IsNull());
+ }
+
+ Y_UNIT_TEST(TestCopyFrom) {
+ {
+ TString sa = "[1,2]";
+ const NSc::TValue& va = NSc::TValue::FromJson(sa);
+ NSc::TValue vb = va;
+ vb.CopyFrom(va);
+ UNIT_ASSERT_VALUES_EQUAL(va.ToJson(), sa);
+ UNIT_ASSERT_VALUES_EQUAL(vb.ToJson(), sa);
+ }
+ {
+ TString sa = "[1,2]";
+ NSc::TValue va = NSc::TValue::FromJson(sa);
+ NSc::TValue vb = va;
+ vb.CopyFrom(va);
+ UNIT_ASSERT_VALUES_EQUAL(va.ToJson(), sa);
+ UNIT_ASSERT_VALUES_EQUAL(vb.ToJson(), sa);
+ }
+ {
+ TString sa = "[1,2]";
+ NSc::TValue va = NSc::TValue::FromJson(sa);
+ const NSc::TValue& vb = va;
+ va.CopyFrom(vb);
+ UNIT_ASSERT_VALUES_EQUAL(va.ToJson(), sa);
+ UNIT_ASSERT_VALUES_EQUAL(vb.ToJson(), sa);
+ }
+ {
+ TString sa = "[1,2]";
+ NSc::TValue va = NSc::TValue::FromJson(sa);
+ NSc::TValue vb = va;
+ va.CopyFrom(vb);
+ UNIT_ASSERT_VALUES_EQUAL(va.ToJson(), sa);
+ UNIT_ASSERT_VALUES_EQUAL(vb.ToJson(), sa);
+ }
+ {
+ NSc::TValue va = NSc::TValue::FromJson("{\"x\":\"ab\",\"y\":{\"p\":\"cd\",\"q\":\"ef\"}}");
+ NSc::TValue vb = va.Get("y");
+ va.CopyFrom(vb);
+ TString sa = "{\"p\":\"cd\",\"q\":\"ef\"}";
+ UNIT_ASSERT_VALUES_EQUAL(va.ToJson(), sa);
+ UNIT_ASSERT_VALUES_EQUAL(vb.ToJson(), sa);
+ }
+ {
+ NSc::TValue va = NSc::TValue::FromJson("{\"x\":\"ab\",\"y\":{\"p\":\"cd\",\"q\":\"ef\"}}");
+ const NSc::TValue& vb = va.Get("y");
+ va.CopyFrom(vb);
+ TString sa = "{\"p\":\"cd\",\"q\":\"ef\"}";
+ UNIT_ASSERT_VALUES_EQUAL(va.ToJson(), sa);
+ }
+ {
+ NSc::TValue va = NSc::TValue::FromJson("{\"x\":\"ab\",\"y\":{\"p\":\"cd\",\"q\":\"ef\"}}");
+ NSc::TValue vb = va.Get("y");
+ va = vb;
+ TString sa = "{\"p\":\"cd\",\"q\":\"ef\"}";
+ UNIT_ASSERT_VALUES_EQUAL(va.ToJson(), sa);
+ UNIT_ASSERT_VALUES_EQUAL(vb.ToJson(), sa);
+ }
+ {
+ NSc::TValue va = NSc::TValue::FromJson("{\"x\":\"ab\",\"y\":{\"p\":\"cd\",\"q\":\"ef\"}}");
+ const NSc::TValue& vb = va.Get("y");
+ va = vb;
+ TString sa = "{\"p\":\"cd\",\"q\":\"ef\"}";
+ UNIT_ASSERT_VALUES_EQUAL(va.ToJson(), sa);
+ UNIT_ASSERT_VALUES_EQUAL(vb.ToJson(), sa);
+ }
+ }
+
+ Y_UNIT_TEST(TestCopyingDictIntoSelf) { //Found by fuzzing
+ NSc::TValue a;
+ NSc::TValue b = a.GetOrAdd("aa");
+ b.CopyFrom(a);
+ NSc::TValue target = NSc::TValue::FromJsonThrow("{\"aa\":null}");
+ UNIT_ASSERT_VALUES_EQUAL(b, target);
+ UNIT_ASSERT_VALUES_EQUAL(a, target);
+ }
+
+ Y_UNIT_TEST(TestCopyingDictIntoSelfByRef) { //Found by fuzzing
+ NSc::TValue a;
+ NSc::TValue& b = a.GetOrAdd("aa");
+ b.CopyFrom(a);
+ UNIT_ASSERT_VALUES_EQUAL(b, NSc::TValue::FromJsonThrow("{\"aa\":null}"));
+ UNIT_ASSERT_VALUES_EQUAL(a, NSc::TValue::FromJsonThrow("{\"aa\": {\"aa\": null}}"));
+ }
+
+ Y_UNIT_TEST(TestGetNoAdd) {
+ NSc::TValue v = NSc::NUt::AssertFromJson("{a:[null,-1,2,3.4],b:3,c:{d:5}}");
+ UNIT_ASSERT(v.GetNoAdd("a") != nullptr);
+ UNIT_ASSERT(v.GetNoAdd("b") != nullptr);
+ UNIT_ASSERT(v.GetNoAdd("c") != nullptr);
+ UNIT_ASSERT(v.GetNoAdd("d") == nullptr);
+ UNIT_ASSERT(v.GetNoAdd("value") == nullptr);
+
+ NSc::TValue* child = v.GetNoAdd("c");
+ UNIT_ASSERT(child != nullptr);
+ (*child)["e"]["f"] = 42;
+ const NSc::TValue expectedResult = NSc::NUt::AssertFromJson("{a:[null,-1,2,3.4],b:3,c:{d:5,e:{f:42}}}");
+ UNIT_ASSERT_VALUES_EQUAL(v, expectedResult);
+ }
+};
diff --git a/library/cpp/scheme/tests/ut/scheme_ut.proto b/library/cpp/scheme/tests/ut/scheme_ut.proto
new file mode 100644
index 0000000000..7981af7eae
--- /dev/null
+++ b/library/cpp/scheme/tests/ut/scheme_ut.proto
@@ -0,0 +1,84 @@
+package NSc;
+
+message TMessage {
+ optional double Double = 1;
+ optional float Float = 2;
+ optional int32 Int32 = 3;
+ optional int64 Int64 = 4;
+ optional uint32 UInt32 = 5;
+ optional uint64 UInt64 = 6;
+ optional sint32 SInt32 = 7;
+ optional sint64 SInt64 = 8;
+ optional fixed32 Fixed32 = 9;
+ optional fixed64 Fixed64 = 10;
+ optional sfixed32 SFixed32 = 11;
+ optional sfixed64 SFixed64 = 12;
+ optional bool Bool = 13;
+ optional string String = 14;
+ optional bytes Bytes = 15;
+ optional EEnum Enum = 16;
+ optional TMessage2 Message = 17;
+
+ repeated double Doubles = 18;
+ repeated float Floats = 19;
+ repeated int32 Int32s = 20;
+ repeated int64 Int64s = 21;
+ repeated uint32 UInt32s = 22;
+ repeated uint64 UInt64s = 23;
+ repeated sint32 SInt32s = 24;
+ repeated sint64 SInt64s = 25;
+ repeated fixed32 Fixed32s = 26;
+ repeated fixed64 Fixed64s = 27;
+ repeated sfixed32 SFixed32s = 28;
+ repeated sfixed64 SFixed64s = 29;
+ repeated bool Bools = 30;
+ repeated string Strings = 31;
+ repeated bytes Bytess = 32;
+ repeated EEnum Enums = 33;
+ repeated TMessage2 Messages = 34;
+
+ map<string, double> MapDoubles = 35;
+ map<string, int32> MapInt32s = 36;
+ map<string, string> MapString = 37;
+}
+
+enum EEnum {
+ VALUE1 = 0;
+ VALUE2 = 1;
+}
+
+message TMessage2 {
+ optional double Double = 1;
+ optional float Float = 2;
+ optional int32 Int32 = 3;
+ optional int64 Int64 = 4;
+ optional uint32 UInt32 = 5;
+ optional uint64 UInt64 = 6;
+ optional sint32 SInt32 = 7;
+ optional sint64 SInt64 = 8;
+ optional fixed32 Fixed32 = 9;
+ optional fixed64 Fixed64 = 10;
+ optional sfixed32 SFixed32 = 11;
+ optional sfixed64 SFixed64 = 12;
+ optional bool Bool = 13;
+ optional string String = 14;
+ optional bytes Bytes = 15;
+ optional EEnum Enum = 16;
+
+ repeated double Doubles = 18;
+ repeated float Floats = 19;
+ repeated int32 Int32s = 20;
+ repeated int64 Int64s = 21;
+ repeated uint32 UInt32s = 22;
+ repeated uint64 UInt64s = 23;
+ repeated sint32 SInt32s = 24;
+ repeated sint64 SInt64s = 25;
+ repeated fixed32 Fixed32s = 26;
+ repeated fixed64 Fixed64s = 27;
+ repeated sfixed32 SFixed32s = 28;
+ repeated sfixed64 SFixed64s = 29;
+ repeated bool Bools = 30;
+ repeated string Strings = 31;
+ repeated bytes Bytess = 32;
+ repeated EEnum Enums = 33;
+}
diff --git a/library/cpp/scheme/tests/ut/ya.make b/library/cpp/scheme/tests/ut/ya.make
new file mode 100644
index 0000000000..9f54791414
--- /dev/null
+++ b/library/cpp/scheme/tests/ut/ya.make
@@ -0,0 +1,24 @@
+UNITTEST()
+
+OWNER(velavokr)
+
+PEERDIR(
+ library/cpp/protobuf/util
+ library/cpp/scheme/tests/fuzz_ops/lib
+ library/cpp/scheme/ut_utils
+ library/cpp/string_utils/quote
+ library/cpp/testing/unittest
+)
+
+SRCS(
+ fuzz_ops_found_bugs_ut.cpp
+ scheme_cast_ut.cpp
+ scheme_json_ut.cpp
+ scheme_merge_ut.cpp
+ scheme_path_ut.cpp
+ scheme_proto_ut.cpp
+ scheme_ut.cpp
+ scheme_ut.proto
+)
+
+END()
diff --git a/library/cpp/scheme/tests/ya.make b/library/cpp/scheme/tests/ya.make
new file mode 100644
index 0000000000..741cc9a2da
--- /dev/null
+++ b/library/cpp/scheme/tests/ya.make
@@ -0,0 +1,13 @@
+OWNER(
+ g:blender
+ g:middle
+ g:upper
+ velavokr
+)
+
+RECURSE(
+ fuzz_json
+ fuzz_ops
+ fuzz_ops/ut
+ ut
+)