json_reader_fast_ut.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. #include <library/cpp/json/json_reader.h>
  2. #include <library/cpp/json/json_prettifier.h>
  3. #include <library/cpp/testing/unittest/registar.h>
  4. #include <library/cpp/string_utils/relaxed_escaper/relaxed_escaper.h>
  5. #include <util/string/cast.h>
  6. #include <util/string/printf.h>
  7. namespace NJson {
  8. namespace NTest {
  9. enum ETestEvent {
  10. E_NO_EVENT = 0,
  11. E_ERROR = 1,
  12. E_DICT_OPEN,
  13. E_DICT_CLOSE,
  14. E_ARR_OPEN,
  15. E_ARR_CLOSE,
  16. E_NULL,
  17. E_BOOL,
  18. E_FLT,
  19. E_INT,
  20. E_LONG_LONG,
  21. E_STR,
  22. E_KEY
  23. };
  24. struct TEvent {
  25. ETestEvent Type = E_NO_EVENT;
  26. i64 INum = 0;
  27. double DNum = 0;
  28. TString Str;
  29. TEvent(ETestEvent e = E_NO_EVENT)
  30. : Type(e)
  31. {
  32. }
  33. TEvent(double v, ETestEvent e)
  34. : Type(e)
  35. , DNum(v)
  36. {
  37. }
  38. TEvent(i64 v, ETestEvent e)
  39. : Type(e)
  40. , INum(v)
  41. {
  42. }
  43. TEvent(TStringBuf t, ETestEvent e)
  44. : Type(e)
  45. , Str(NEscJ::EscapeJ<true, false>(t))
  46. {
  47. }
  48. TString ToString() const {
  49. switch (Type) {
  50. default:
  51. return "YOUFAILED";
  52. case E_ERROR:
  53. return Sprintf("error: %s", Str.data());
  54. case E_DICT_OPEN:
  55. return "{";
  56. case E_DICT_CLOSE:
  57. return "}";
  58. case E_ARR_OPEN:
  59. return "[";
  60. case E_ARR_CLOSE:
  61. return "]";
  62. case E_NULL:
  63. return "null";
  64. case E_BOOL:
  65. return INum ? "true" : "false";
  66. case E_INT:
  67. return ::ToString(INum);
  68. case E_FLT:
  69. return ::ToString(DNum);
  70. case E_STR:
  71. return Sprintf("%s", Str.data());
  72. case E_KEY:
  73. return Sprintf("key: %s", Str.data());
  74. }
  75. }
  76. };
  77. using TEvents = TVector<TEvent>;
  78. struct TTestHandler : TJsonCallbacks {
  79. TEvents Events;
  80. bool OnOpenMap() override {
  81. Events.push_back(E_DICT_OPEN);
  82. return true;
  83. }
  84. bool OnCloseMap() override {
  85. Events.push_back(E_DICT_CLOSE);
  86. return true;
  87. }
  88. bool OnOpenArray() override {
  89. Events.push_back(E_ARR_OPEN);
  90. return true;
  91. }
  92. bool OnCloseArray() override {
  93. Events.push_back(E_ARR_CLOSE);
  94. return true;
  95. }
  96. bool OnNull() override {
  97. Events.push_back(E_NULL);
  98. return true;
  99. }
  100. bool OnBoolean(bool v) override {
  101. Events.push_back(TEvent((i64)v, E_BOOL));
  102. return true;
  103. }
  104. bool OnInteger(long long v) override {
  105. Events.push_back(TEvent((i64)v, E_INT));
  106. return true;
  107. }
  108. bool OnUInteger(unsigned long long v) override {
  109. return OnInteger(v);
  110. }
  111. bool OnDouble(double v) override {
  112. Events.push_back(TEvent(v, E_FLT));
  113. return true;
  114. }
  115. bool OnString(const TStringBuf& v) override {
  116. Events.push_back(TEvent(v, E_STR));
  117. return true;
  118. }
  119. bool OnMapKey(const TStringBuf& v) override {
  120. Events.push_back(TEvent(v, E_KEY));
  121. return true;
  122. }
  123. void OnError(size_t, TStringBuf token) override {
  124. Events.push_back(TEvent(token, E_ERROR));
  125. }
  126. void Assert(const TEvents& e, TString str) {
  127. try {
  128. UNIT_ASSERT_VALUES_EQUAL_C(e.size(), Events.size(), str);
  129. for (ui32 i = 0, sz = e.size(); i < sz; ++i) {
  130. UNIT_ASSERT_VALUES_EQUAL_C((int)e[i].Type, (int)Events[i].Type, Sprintf("'%s' %u", str.data(), i));
  131. UNIT_ASSERT_VALUES_EQUAL_C(e[i].INum, Events[i].INum, Sprintf("'%s' %u", str.data(), i));
  132. UNIT_ASSERT_VALUES_EQUAL_C(e[i].DNum, Events[i].DNum, Sprintf("'%s' %u", str.data(), i));
  133. UNIT_ASSERT_VALUES_EQUAL_C(e[i].Str, Events[i].Str, Sprintf("'%s' %u", str.data(), i));
  134. }
  135. } catch (const yexception&) {
  136. Clog << "Exception at '" << str << "'" << Endl;
  137. for (const auto& event : Events) {
  138. Clog << event.ToString() << Endl;
  139. }
  140. throw;
  141. }
  142. }
  143. };
  144. }
  145. }
  146. class TFastJsonTest: public TTestBase {
  147. UNIT_TEST_SUITE(TFastJsonTest)
  148. UNIT_TEST(TestParse)
  149. UNIT_TEST(TestReadJsonFastTree)
  150. UNIT_TEST(TestNoInlineComment)
  151. UNIT_TEST_SUITE_END();
  152. public:
  153. template <bool accept>
  154. void DoTestParse(TStringBuf json, ui32 amount, ...) {
  155. using namespace NJson::NTest;
  156. TEvents evs;
  157. va_list vl;
  158. va_start(vl, amount);
  159. for (ui32 i = 0; i < amount; i++) {
  160. ETestEvent e = (ETestEvent)va_arg(vl, int);
  161. switch ((int)e) {
  162. case E_NO_EVENT:
  163. case E_DICT_OPEN:
  164. case E_DICT_CLOSE:
  165. case E_ARR_OPEN:
  166. case E_ARR_CLOSE:
  167. case E_NULL:
  168. evs.push_back(e);
  169. break;
  170. case E_BOOL: {
  171. bool v = va_arg(vl, int);
  172. evs.push_back(TEvent((i64)v, E_BOOL));
  173. break;
  174. }
  175. case E_INT: {
  176. i64 i = va_arg(vl, int);
  177. evs.push_back(TEvent(i, E_INT));
  178. break;
  179. }
  180. case E_LONG_LONG: {
  181. i64 i = va_arg(vl, long long);
  182. evs.push_back(TEvent(i, E_INT));
  183. break;
  184. }
  185. case E_FLT: {
  186. double f = va_arg(vl, double);
  187. evs.push_back(TEvent(f, E_FLT));
  188. break;
  189. }
  190. case E_STR: {
  191. const char* s = va_arg(vl, const char*);
  192. evs.push_back(TEvent(TStringBuf(s), E_STR));
  193. break;
  194. }
  195. case E_KEY:
  196. case E_ERROR: {
  197. const char* s = va_arg(vl, const char*);
  198. evs.push_back(TEvent(TStringBuf(s), e));
  199. break;
  200. }
  201. }
  202. }
  203. va_end(vl);
  204. TTestHandler h;
  205. const bool res = ReadJsonFast(json, &h);
  206. UNIT_ASSERT_VALUES_EQUAL_C(res, accept, Sprintf("%s (%s)", ToString(json).data(), h.Events.back().Str.data()));
  207. h.Assert(evs, ToString(json));
  208. }
  209. void TestParse() {
  210. using namespace NJson::NTest;
  211. DoTestParse<true>("", 0);
  212. DoTestParse<true>(" \t \t ", 0);
  213. DoTestParse<true>("a-b-c@аб_вгд909AБ", 1, E_STR, "a-b-c@аб_вгд909AБ");
  214. DoTestParse<true>("'я тестовая строка'", 1, E_STR, "я тестовая строка");
  215. DoTestParse<true>("\"я тестовая строка\"", 1, E_STR, "я тестовая строка");
  216. DoTestParse<true>("'\\xA\\xA\\xA'", 1, E_STR, "\n\n\n");
  217. DoTestParse<true>("12.15", 1, E_FLT, 12.15);
  218. DoTestParse<true>("null", 1, E_NULL);
  219. DoTestParse<true>("true", 1, E_BOOL, true);
  220. DoTestParse<true>("false", 1, E_BOOL, false);
  221. DoTestParse<true>("[]", 2, E_ARR_OPEN, E_ARR_CLOSE);
  222. DoTestParse<true>("[ a ]", 3, E_ARR_OPEN, E_STR, "a", E_ARR_CLOSE);
  223. DoTestParse<true>("[ a, b ]", 4, E_ARR_OPEN, E_STR, "a", E_STR, "b", E_ARR_CLOSE);
  224. DoTestParse<true>("[a,b]", 4, E_ARR_OPEN, E_STR, "a", E_STR, "b", E_ARR_CLOSE);
  225. DoTestParse<false>("[a,b][a,b]", 5, E_ARR_OPEN, E_STR, "a", E_STR, "b", E_ARR_CLOSE, E_ERROR, "invalid syntax at token: '['");
  226. DoTestParse<false>("[a,,b]", 3, E_ARR_OPEN, E_STR, "a", E_ERROR, "invalid syntax at token: ','");
  227. DoTestParse<true>("{ k : v }", 4, E_DICT_OPEN, E_KEY, "k", E_STR, "v", E_DICT_CLOSE);
  228. DoTestParse<true>("{a:'\\b'/*comment*/, k /*comment*/\n : v }", 6, E_DICT_OPEN, E_KEY, "a", E_STR, "\b", E_KEY, "k", E_STR, "v", E_DICT_CLOSE);
  229. DoTestParse<true>("{a:.15, k : v }", 6, E_DICT_OPEN, E_KEY, "a", E_FLT, .15, E_KEY, "k", E_STR, "v", E_DICT_CLOSE);
  230. DoTestParse<true>("[ a, -.1e+5, 1E-7]", 5, E_ARR_OPEN, E_STR, "a", E_FLT, -.1e+5, E_FLT, 1e-7, E_ARR_CLOSE);
  231. DoTestParse<true>("{}", 2, E_DICT_OPEN, E_DICT_CLOSE);
  232. DoTestParse<true>("{ a : x, b : [ c, d, ] }", 9, E_DICT_OPEN, E_KEY, "a", E_STR, "x", E_KEY, "b", E_ARR_OPEN, E_STR, "c", E_STR, "d", E_ARR_CLOSE, E_DICT_CLOSE);
  233. DoTestParse<false>("{ a : x, b : [ c, d,, ] }", 8, E_DICT_OPEN, E_KEY, "a", E_STR, "x", E_KEY, "b", E_ARR_OPEN, E_STR, "c", E_STR, "d", E_ERROR, "invalid syntax at token: ','");
  234. // DoTestParse<false>("{ a : x : y }", 4, E_DICT_OPEN
  235. // , E_KEY, "a", E_STR, "x"
  236. // , E_ERROR
  237. // , ":");
  238. // DoTestParse<false>("{queries:{ref:[]},{nonref:[]}}", 8, E_DICT_OPEN
  239. // , E_KEY, "queries", E_DICT_OPEN
  240. // , E_KEY, "ref", E_ARR_OPEN, E_ARR_CLOSE
  241. // , E_DICT_CLOSE, E_ERROR, "");
  242. DoTestParse<true>("'100x00'", 1, E_STR, "100x00");
  243. DoTestParse<true>("-1", 1, E_INT, -1);
  244. DoTestParse<true>("-9223372036854775808", 1, E_LONG_LONG, (long long)Min<i64>());
  245. DoTestParse<false>("100x00", 1, E_ERROR, "invalid syntax at token: '100x'");
  246. DoTestParse<false>("100 200", 2, E_INT, 100, E_ERROR, "invalid syntax at token: '200'");
  247. DoTestParse<true>("{g:{x:{a:{b:c,e:f},q:{x:y}},y:fff}}", 22, E_DICT_OPEN, E_KEY, "g", E_DICT_OPEN, E_KEY, "x", E_DICT_OPEN, E_KEY, "a", E_DICT_OPEN, E_KEY, "b", E_STR, "c", E_KEY, "e", E_STR, "f", E_DICT_CLOSE, E_KEY, "q", E_DICT_OPEN, E_KEY, "x", E_STR, "y", E_DICT_CLOSE, E_DICT_CLOSE, E_KEY, "y", E_STR, "fff", E_DICT_CLOSE, E_DICT_CLOSE);
  248. }
  249. void TestReadJsonFastTree() {
  250. const TString json = R"(
  251. {
  252. "a": {
  253. "b": {}
  254. }
  255. }}
  256. )";
  257. NJson::TJsonValue value;
  258. UNIT_ASSERT(!ReadJsonFastTree(json, &value));
  259. }
  260. void TestNoInlineComment() {
  261. using namespace NJson::NTest;
  262. DoTestParse<false>("{\"a\":1}//d{\"b\":2}", 5, E_DICT_OPEN, E_KEY, "a", E_INT, 1, E_DICT_CLOSE, E_ERROR, "invalid syntax at token: '/'");
  263. DoTestParse<false>("{\"a\":1}//d{\"b\":2}\n", 5, E_DICT_OPEN, E_KEY, "a", E_INT, 1, E_DICT_CLOSE, E_ERROR, "invalid syntax at token: '/'");
  264. DoTestParse<false>("{\"a\":{//d{\"b\":2}\n}}", 4, E_DICT_OPEN, E_KEY, "a", E_DICT_OPEN, E_ERROR, "invalid syntax at token: '/'");
  265. DoTestParse<false>("{\"a\":{//d{\"b\":2}}}\n", 4, E_DICT_OPEN, E_KEY, "a", E_DICT_OPEN, E_ERROR, "invalid syntax at token: '/'");
  266. DoTestParse<false>("{\"a\":{//d{\"b\":2}}}", 4, E_DICT_OPEN, E_KEY, "a", E_DICT_OPEN, E_ERROR, "invalid syntax at token: '/'");
  267. }
  268. };
  269. UNIT_TEST_SUITE_REGISTRATION(TFastJsonTest)