1
0

test-json-partial.cpp 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. #include "common.h"
  2. #include "json-partial.h"
  3. #include <exception>
  4. #include <iostream>
  5. #include <stdexcept>
  6. template <class T> static void assert_equals(const T & expected, const T & actual) {
  7. if (expected != actual) {
  8. std::cerr << "Expected: " << expected << std::endl;
  9. std::cerr << "Actual: " << actual << std::endl;
  10. std::cerr << std::flush;
  11. throw std::runtime_error("Test failed");
  12. }
  13. }
  14. static void test_json_healing() {
  15. auto parse = [](const std::string & str) {
  16. std::cerr << "# Parsing: " << str << '\n';
  17. std::string::const_iterator it = str.begin();
  18. const auto end = str.end();
  19. common_json out;
  20. std::string healing_marker = "$llama.cpp.json$";
  21. if (common_json_parse(it, end, healing_marker, out)) {
  22. auto dump = out.json.dump();
  23. std::cerr << "Parsed: " << dump << '\n';
  24. std::cerr << "Magic: " << out.healing_marker.json_dump_marker << '\n';
  25. std::string result;
  26. if (!out.healing_marker.json_dump_marker.empty()) {
  27. auto i = dump.find(out.healing_marker.json_dump_marker);
  28. if (i == std::string::npos) {
  29. throw std::runtime_error("Failed to find magic in dump " + dump + " (magic: " + out.healing_marker.json_dump_marker + ")");
  30. }
  31. result = dump.substr(0, i);
  32. } else {
  33. result = dump;
  34. }
  35. std::cerr << "Result: " << result << '\n';
  36. if (string_starts_with(str, result)) {
  37. std::cerr << "Failure!\n";
  38. }
  39. // return dump;
  40. } else {
  41. throw std::runtime_error("Failed to parse: " + str);
  42. }
  43. };
  44. auto parse_all = [&](const std::string & str) {
  45. for (size_t i = 1; i < str.size(); i++) {
  46. parse(str.substr(0, i));
  47. }
  48. };
  49. parse_all("{\"a\": \"b\"}");
  50. parse_all("{\"hey\": 1, \"ho\\\"ha\": [1]}");
  51. parse_all("[{\"a\": \"b\"}]");
  52. auto test = [&](const std::vector<std::string> & inputs, const std::string & expected, const std::string & expected_marker) {
  53. for (const auto & input : inputs) {
  54. common_json out;
  55. assert_equals(true, common_json_parse(input, "$foo", out));
  56. assert_equals<std::string>(expected, out.json.dump(/* indent */ -1, /* indent_char */ ' ', /* ensure_ascii */ true));
  57. assert_equals<std::string>(expected_marker, out.healing_marker.json_dump_marker);
  58. }
  59. };
  60. // No healing needed:
  61. test(
  62. {
  63. R"([{"a":"b"}, "y"])",
  64. },
  65. R"([{"a":"b"},"y"])",
  66. ""
  67. );
  68. // Partial literals can't be healed:
  69. test(
  70. {
  71. R"([1)",
  72. R"([tru)",
  73. R"([n)",
  74. R"([nul)",
  75. R"([23.2)",
  76. },
  77. R"(["$foo"])",
  78. R"("$foo)"
  79. );
  80. test(
  81. {
  82. R"({"a": 1)",
  83. R"({"a": tru)",
  84. R"({"a": n)",
  85. R"({"a": nul)",
  86. R"({"a": 23.2)",
  87. },
  88. R"({"a":"$foo"})",
  89. R"("$foo)"
  90. );
  91. test(
  92. {
  93. R"({)",
  94. },
  95. R"({"$foo":1})",
  96. R"("$foo)"
  97. );
  98. test(
  99. {
  100. R"([)",
  101. },
  102. R"(["$foo"])",
  103. R"("$foo)"
  104. );
  105. // Healing right after a full literal
  106. test(
  107. {
  108. R"(1 )",
  109. },
  110. R"(1)",
  111. ""
  112. );
  113. test(
  114. {
  115. R"(true)",
  116. R"(true )",
  117. },
  118. R"(true)",
  119. ""
  120. );
  121. test(
  122. {
  123. R"(null)",
  124. R"(null )",
  125. },
  126. R"(null)",
  127. ""
  128. );
  129. test(
  130. {
  131. R"([1 )",
  132. },
  133. R"([1,"$foo"])",
  134. R"(,"$foo)"
  135. );
  136. test(
  137. {
  138. R"([{})",
  139. R"([{} )",
  140. },
  141. R"([{},"$foo"])",
  142. R"(,"$foo)"
  143. );
  144. test(
  145. {
  146. R"([true)",
  147. },
  148. // TODO: detect the true/false/null literal was complete
  149. R"(["$foo"])",
  150. R"("$foo)"
  151. );
  152. test(
  153. {
  154. R"([true )",
  155. },
  156. R"([true,"$foo"])",
  157. R"(,"$foo)"
  158. );
  159. test(
  160. {
  161. R"([true,)",
  162. },
  163. R"([true,"$foo"])",
  164. R"("$foo)"
  165. );
  166. // Test nesting
  167. test(
  168. {
  169. R"([{"a": [{"b": [{)",
  170. },
  171. R"([{"a":[{"b":[{"$foo":1}]}]}])",
  172. R"("$foo)"
  173. );
  174. test(
  175. {
  176. R"([{"a": [{"b": [)",
  177. },
  178. R"([{"a":[{"b":["$foo"]}]}])",
  179. R"("$foo)"
  180. );
  181. test(
  182. {
  183. R"([{"a": "b"})",
  184. R"([{"a": "b"} )",
  185. },
  186. R"([{"a":"b"},"$foo"])",
  187. R"(,"$foo)"
  188. );
  189. test(
  190. {
  191. R"([{"a": "b"},)",
  192. R"([{"a": "b"}, )",
  193. },
  194. R"([{"a":"b"},"$foo"])",
  195. R"("$foo)"
  196. );
  197. test(
  198. {
  199. R"({ "code)",
  200. },
  201. R"({"code$foo":1})",
  202. R"($foo)"
  203. );
  204. test(
  205. {
  206. R"({ "code\)",
  207. },
  208. R"({"code\\$foo":1})",
  209. R"(\$foo)"
  210. );
  211. test(
  212. {
  213. R"({ "code")",
  214. },
  215. R"({"code":"$foo"})",
  216. R"(:"$foo)"
  217. );
  218. test(
  219. {
  220. R"({ "key")",
  221. },
  222. R"({"key":"$foo"})",
  223. R"(:"$foo)"
  224. );
  225. // Test unicode escape sequences
  226. test(
  227. {
  228. R"({"a":"\u)",
  229. },
  230. R"({"a":"\u0000$foo"})",
  231. R"(0000$foo)"
  232. );
  233. test(
  234. {
  235. R"({"a":"\u00)",
  236. },
  237. R"({"a":"\u0000$foo"})",
  238. R"(00$foo)"
  239. );
  240. test(
  241. {
  242. R"({"a":"\ud300)",
  243. },
  244. R"({"a":"\ud300$foo"})",
  245. R"($foo)"
  246. );
  247. test(
  248. {
  249. R"({"a":"\ud800)",
  250. },
  251. R"({"a":"\ud800\udc00$foo"})",
  252. R"(\udc00$foo)"
  253. );
  254. test(
  255. {
  256. R"({"a":"\ud800\)",
  257. },
  258. R"({"a":"\ud800\udc00$foo"})",
  259. R"(udc00$foo)"
  260. );
  261. test(
  262. {
  263. R"({"a":"\ud800\u)",
  264. },
  265. R"({"a":"\ud800\udc00$foo"})",
  266. R"(dc00$foo)"
  267. );
  268. test(
  269. {
  270. R"({"a":"\ud800\udc00)",
  271. },
  272. R"({"a":"\ud800\udc00$foo"})",
  273. R"($foo)"
  274. );
  275. }
  276. int main() {
  277. test_json_healing();
  278. std::cerr << "All tests passed.\n";
  279. return 0;
  280. }