1 | //===-- JSONTest.cpp - JSON unit tests --------------------------*- C++ -*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "llvm/Support/JSON.h" |
10 | #include "llvm/Support/raw_ostream.h" |
11 | #include "llvm/Testing/Support/Error.h" |
12 | |
13 | #include "gmock/gmock.h" |
14 | #include "gtest/gtest.h" |
15 | |
16 | namespace llvm { |
17 | namespace json { |
18 | |
19 | namespace { |
20 | |
21 | std::string s(const Value &E) { return llvm::formatv(Fmt: "{0}" , Vals: E).str(); } |
22 | std::string sp(const Value &E) { return llvm::formatv(Fmt: "{0:2}" , Vals: E).str(); } |
23 | |
24 | TEST(JSONTest, Types) { |
25 | EXPECT_EQ("true" , s(true)); |
26 | EXPECT_EQ("null" , s(nullptr)); |
27 | EXPECT_EQ("2.5" , s(2.5)); |
28 | EXPECT_EQ(R"("foo")" , s("foo" )); |
29 | EXPECT_EQ("[1,2,3]" , s({1, 2, 3})); |
30 | EXPECT_EQ(R"({"x":10,"y":20})" , s(Object{{"x" , 10}, {"y" , 20}})); |
31 | |
32 | #ifdef NDEBUG |
33 | EXPECT_EQ(R"("��")" , s("\xC0\x80" )); |
34 | EXPECT_EQ(R"({"��":0})" , s(Object{{"\xC0\x80" , 0}})); |
35 | #else |
36 | EXPECT_DEATH(s("\xC0\x80" ), "Invalid UTF-8" ); |
37 | EXPECT_DEATH(s(Object{{"\xC0\x80" , 0}}), "Invalid UTF-8" ); |
38 | #endif |
39 | } |
40 | |
41 | TEST(JSONTest, Constructors) { |
42 | // Lots of edge cases around empty and singleton init lists. |
43 | EXPECT_EQ("[[[3]]]" , s({{{3}}})); |
44 | EXPECT_EQ("[[[]]]" , s({{{}}})); |
45 | EXPECT_EQ("[[{}]]" , s({{Object{}}})); |
46 | EXPECT_EQ(R"({"A":{"B":{}}})" , s(Object{{"A" , Object{{"B" , Object{}}}}})); |
47 | EXPECT_EQ(R"({"A":{"B":{"X":"Y"}}})" , |
48 | s(Object{{"A" , Object{{"B" , Object{{"X" , "Y" }}}}}})); |
49 | EXPECT_EQ("null" , s(std::optional<double>())); |
50 | EXPECT_EQ("2.5" , s(std::optional<double>(2.5))); |
51 | EXPECT_EQ("[[2.5,null]]" , s(std::vector<std::vector<std::optional<double>>>{ |
52 | {2.5, std::nullopt}})); |
53 | } |
54 | |
55 | TEST(JSONTest, StringOwnership) { |
56 | char X[] = "Hello" ; |
57 | Value Alias = static_cast<const char *>(X); |
58 | X[1] = 'a'; |
59 | EXPECT_EQ(R"("Hallo")" , s(Alias)); |
60 | |
61 | std::string Y = "Hello" ; |
62 | Value Copy = Y; |
63 | Y[1] = 'a'; |
64 | EXPECT_EQ(R"("Hello")" , s(Copy)); |
65 | } |
66 | |
67 | TEST(JSONTest, CanonicalOutput) { |
68 | // Objects are sorted (but arrays aren't)! |
69 | EXPECT_EQ(R"({"a":1,"b":2,"c":3})" , s(Object{{"a" , 1}, {"c" , 3}, {"b" , 2}})); |
70 | EXPECT_EQ(R"(["a","c","b"])" , s({"a" , "c" , "b" })); |
71 | EXPECT_EQ("3" , s(3.0)); |
72 | } |
73 | |
74 | TEST(JSONTest, Escaping) { |
75 | std::string Test = { |
76 | 0, // Strings may contain nulls. |
77 | '\b', '\f', // Have mnemonics, but we escape numerically. |
78 | '\r', '\n', '\t', // Escaped with mnemonics. |
79 | 'S', '\"', '\\', // Printable ASCII characters. |
80 | '\x7f', // Delete is not escaped. |
81 | '\xce', '\x94', // Non-ASCII UTF-8 is not escaped. |
82 | }; |
83 | |
84 | std::string TestString = R"("\u0000\u0008\u000c\r\n\tS\"\\)" |
85 | "\x7f\xCE\x94\"" ; |
86 | |
87 | EXPECT_EQ(TestString, s(Test)); |
88 | |
89 | EXPECT_EQ(R"({"object keys are\nescaped":true})" , |
90 | s(Object{{"object keys are\nescaped" , true}})); |
91 | } |
92 | |
93 | TEST(JSONTest, PrettyPrinting) { |
94 | const char Str[] = R"({ |
95 | "empty_array": [], |
96 | "empty_object": {}, |
97 | "full_array": [ |
98 | 1, |
99 | null |
100 | ], |
101 | "full_object": { |
102 | "nested_array": [ |
103 | { |
104 | "property": "value" |
105 | } |
106 | ] |
107 | } |
108 | })" ; |
109 | |
110 | EXPECT_EQ(Str, sp(Object{ |
111 | {"empty_object" , Object{}}, |
112 | {"empty_array" , {}}, |
113 | {"full_array" , {1, nullptr}}, |
114 | {"full_object" , |
115 | Object{ |
116 | {"nested_array" , |
117 | {Object{ |
118 | {"property" , "value" }, |
119 | }}}, |
120 | }}, |
121 | })); |
122 | } |
123 | |
124 | TEST(JSONTest, Array) { |
125 | Array A{1, 2}; |
126 | A.emplace_back(A: 3); |
127 | A.emplace(P: ++A.begin(), A: 0); |
128 | A.push_back(E: 4); |
129 | A.insert(P: ++++A.begin(), E: 99); |
130 | |
131 | EXPECT_EQ(A.size(), 6u); |
132 | EXPECT_EQ(R"([1,0,99,2,3,4])" , s(std::move(A))); |
133 | } |
134 | |
135 | TEST(JSONTest, Object) { |
136 | Object O{{.K: "a" , .V: 1}, {.K: "b" , .V: 2}, {.K: "c" , .V: 3}}; |
137 | EXPECT_TRUE(O.try_emplace("d" , 4).second); |
138 | EXPECT_FALSE(O.try_emplace("a" , 4).second); |
139 | |
140 | auto D = O.find(K: "d" ); |
141 | EXPECT_NE(D, O.end()); |
142 | auto E = O.find(K: "e" ); |
143 | EXPECT_EQ(E, O.end()); |
144 | |
145 | O.erase(K: "b" ); |
146 | O.erase(I: D); |
147 | EXPECT_EQ(O.size(), 2u); |
148 | EXPECT_EQ(R"({"a":1,"c":3})" , s(std::move(O))); |
149 | } |
150 | |
151 | TEST(JSONTest, Parse) { |
152 | auto Compare = [](llvm::StringRef S, Value Expected) { |
153 | if (auto E = parse(JSON: S)) { |
154 | // Compare both string forms and with operator==, in case we have bugs. |
155 | EXPECT_EQ(*E, Expected); |
156 | EXPECT_EQ(sp(*E), sp(Expected)); |
157 | } else { |
158 | handleAllErrors(E: E.takeError(), Handlers: [S](const llvm::ErrorInfoBase &E) { |
159 | FAIL() << "Failed to parse JSON >>> " << S << " <<<: " << E.message(); |
160 | }); |
161 | } |
162 | }; |
163 | |
164 | Compare(R"(true)" , true); |
165 | Compare(R"(false)" , false); |
166 | Compare(R"(null)" , nullptr); |
167 | |
168 | Compare(R"(42)" , 42); |
169 | Compare(R"(2.5)" , 2.5); |
170 | Compare(R"(2e50)" , 2e50); |
171 | Compare(R"(1.2e3456789)" , std::numeric_limits<double>::infinity()); |
172 | |
173 | Compare(R"("foo")" , "foo" ); |
174 | Compare(R"("\"\\\b\f\n\r\t")" , "\"\\\b\f\n\r\t" ); |
175 | Compare(R"("\u0000")" , llvm::StringRef("\0" , 1)); |
176 | Compare("\"\x7f\"" , "\x7f" ); |
177 | Compare(R"("\ud801\udc37")" , // UTF-16 surrogate pair escape. |
178 | /*U+10437*/ "\xf0\x90\x90\xb7" ); |
179 | Compare("\"\xE2\x82\xAC\xF0\x9D\x84\x9E\"" , // UTF-8 |
180 | /*U+20AC U+1D11E*/ "\xe2\x82\xac\xf0\x9d\x84\x9e" ); |
181 | Compare( |
182 | // Invalid unicode. |
183 | R"("LoneLeading=\ud801, LoneTrailing=\udc01, LeadLeadTrail=\ud801\ud801\udc37")" , |
184 | "LoneLeading=" /*U+FFFD*/ "\xef\xbf\xbd, " |
185 | "LoneTrailing=" /*U+FFFD*/ "\xef\xbf\xbd, " |
186 | "LeadLeadTrail=" /*U+FFFD U+10437*/ "\xef\xbf\xbd\xf0\x90\x90\xb7" ); |
187 | |
188 | Compare(R"({"":0,"":0})" , Object{{.K: "" , .V: 0}}); |
189 | Compare(R"({"obj":{},"arr":[]})" , Object{{.K: "obj" , .V: Object{}}, {.K: "arr" , .V: {}}}); |
190 | Compare(R"({"\n":{"\u0000":[[[[]]]]}})" , |
191 | Object{{.K: "\n" , .V: Object{ |
192 | {.K: llvm::StringRef("\0" , 1), .V: {{{{}}}}}, |
193 | }}}); |
194 | Compare("\r[\n\t] " , {}); |
195 | } |
196 | |
197 | TEST(JSONTest, ParseErrors) { |
198 | auto ExpectErr = [](llvm::StringRef Msg, llvm::StringRef S) { |
199 | if (auto E = parse(JSON: S)) { |
200 | // Compare both string forms and with operator==, in case we have bugs. |
201 | FAIL() << "Parsed JSON >>> " << S << " <<< but wanted error: " << Msg; |
202 | } else { |
203 | handleAllErrors(E: E.takeError(), Handlers: [S, Msg](const llvm::ErrorInfoBase &E) { |
204 | EXPECT_THAT(E.message(), testing::HasSubstr(std::string(Msg))) << S; |
205 | }); |
206 | } |
207 | }; |
208 | ExpectErr("Unexpected EOF" , "" ); |
209 | ExpectErr("Unexpected EOF" , "[" ); |
210 | ExpectErr("Text after end of document" , "[][]" ); |
211 | ExpectErr("Invalid JSON value (false?)" , "fuzzy" ); |
212 | ExpectErr("Expected , or ]" , "[2?]" ); |
213 | ExpectErr("Expected object key" , "{a:2}" ); |
214 | ExpectErr("Expected : after object key" , R"({"a",2})" ); |
215 | ExpectErr("Expected , or } after object property" , R"({"a":2 "b":3})" ); |
216 | ExpectErr("Invalid JSON value" , R"([&%!])" ); |
217 | ExpectErr("Invalid JSON value (number?)" , "1e1.0" ); |
218 | ExpectErr("Unterminated string" , R"("abc\"def)" ); |
219 | ExpectErr("Control character in string" , "\"abc\ndef\"" ); |
220 | ExpectErr("Invalid escape sequence" , R"("\030")" ); |
221 | ExpectErr("Invalid \\u escape sequence" , R"("\usuck")" ); |
222 | ExpectErr("[3:3, byte=19]" , R"({ |
223 | "valid": 1, |
224 | invalid: 2 |
225 | })" ); |
226 | ExpectErr("Invalid UTF-8 sequence" , "\"\xC0\x80\"" ); // WTF-8 null |
227 | } |
228 | |
229 | // Direct tests of isUTF8 and fixUTF8. Internal uses are also tested elsewhere. |
230 | TEST(JSONTest, UTF8) { |
231 | for (const char *Valid : { |
232 | "this is ASCII text" , |
233 | "thïs tëxt häs BMP chäräctërs" , |
234 | "𐌶𐌰L𐌾𐍈 C𐍈𐌼𐌴𐍃" , |
235 | }) { |
236 | EXPECT_TRUE(isUTF8(Valid)) << Valid; |
237 | EXPECT_EQ(fixUTF8(Valid), Valid); |
238 | } |
239 | for (auto Invalid : std::vector<std::pair<const char *, const char *>>{ |
240 | {"lone trailing \x81\x82 bytes" , "lone trailing �� bytes" }, |
241 | {"missing trailing \xD0 bytes" , "missing trailing � bytes" }, |
242 | {"truncated character \xD0" , "truncated character �" }, |
243 | {"not \xC1\x80 the \xE0\x9f\xBF shortest \xF0\x83\x83\x83 encoding" , |
244 | "not �� the ��� shortest ���� encoding" }, |
245 | {"too \xF9\x80\x80\x80\x80 long" , "too ����� long" }, |
246 | {"surrogate \xED\xA0\x80 invalid \xF4\x90\x80\x80" , |
247 | "surrogate ��� invalid ����" }}) { |
248 | EXPECT_FALSE(isUTF8(Invalid.first)) << Invalid.first; |
249 | EXPECT_EQ(fixUTF8(Invalid.first), Invalid.second); |
250 | } |
251 | } |
252 | |
253 | TEST(JSONTest, Inspection) { |
254 | llvm::Expected<Value> Doc = parse(JSON: R"( |
255 | { |
256 | "null": null, |
257 | "boolean": false, |
258 | "number": 2.78, |
259 | "string": "json", |
260 | "array": [null, true, 3.14, "hello", [1,2,3], {"time": "arrow"}], |
261 | "object": {"fruit": "banana"} |
262 | } |
263 | )" ); |
264 | EXPECT_TRUE(!!Doc); |
265 | |
266 | Object *O = Doc->getAsObject(); |
267 | ASSERT_TRUE(O); |
268 | |
269 | EXPECT_FALSE(O->getNull("missing" )); |
270 | EXPECT_FALSE(O->getNull("boolean" )); |
271 | EXPECT_TRUE(O->getNull("null" )); |
272 | |
273 | EXPECT_EQ(O->getNumber("number" ), std::optional<double>(2.78)); |
274 | EXPECT_FALSE(O->getInteger("number" )); |
275 | EXPECT_EQ(O->getString("string" ), std::optional<llvm::StringRef>("json" )); |
276 | ASSERT_FALSE(O->getObject("missing" )); |
277 | ASSERT_FALSE(O->getObject("array" )); |
278 | ASSERT_TRUE(O->getObject("object" )); |
279 | EXPECT_EQ(*O->getObject("object" ), (Object{{"fruit" , "banana" }})); |
280 | |
281 | Array *A = O->getArray(K: "array" ); |
282 | ASSERT_TRUE(A); |
283 | EXPECT_EQ((*A)[1].getAsBoolean(), std::optional<bool>(true)); |
284 | ASSERT_TRUE((*A)[4].getAsArray()); |
285 | EXPECT_EQ(*(*A)[4].getAsArray(), (Array{1, 2, 3})); |
286 | EXPECT_EQ((*(*A)[4].getAsArray())[1].getAsInteger(), |
287 | std::optional<int64_t>(2)); |
288 | int I = 0; |
289 | for (Value &E : *A) { |
290 | if (I++ == 5) { |
291 | ASSERT_TRUE(E.getAsObject()); |
292 | EXPECT_EQ(E.getAsObject()->getString("time" ), |
293 | std::optional<llvm::StringRef>("arrow" )); |
294 | } else |
295 | EXPECT_FALSE(E.getAsObject()); |
296 | } |
297 | } |
298 | |
299 | // Verify special integer handling - we try to preserve exact int64 values. |
300 | TEST(JSONTest, Integers) { |
301 | struct { |
302 | const char *Desc; |
303 | Value Val; |
304 | const char *Str; |
305 | std::optional<int64_t> AsInt; |
306 | std::optional<double> AsNumber; |
307 | } TestCases[] = { |
308 | { |
309 | .Desc: "Non-integer. Stored as double, not convertible." , |
310 | .Val: double{1.5}, |
311 | .Str: "1.5" , |
312 | .AsInt: std::nullopt, |
313 | .AsNumber: 1.5, |
314 | }, |
315 | |
316 | { |
317 | .Desc: "Integer, not exact double. Stored as int64, convertible." , |
318 | .Val: int64_t{0x4000000000000001}, |
319 | .Str: "4611686018427387905" , |
320 | .AsInt: int64_t{0x4000000000000001}, |
321 | .AsNumber: double{0x4000000000000000}, |
322 | }, |
323 | |
324 | { |
325 | .Desc: "Negative integer, not exact double. Stored as int64, convertible." , |
326 | .Val: int64_t{-0x4000000000000001}, |
327 | .Str: "-4611686018427387905" , |
328 | .AsInt: int64_t{-0x4000000000000001}, |
329 | .AsNumber: double{-0x4000000000000000}, |
330 | }, |
331 | |
332 | // PR46470, |
333 | // https://developercommunity.visualstudio.com/content/problem/1093399/incorrect-result-when-printing-6917529027641081856.html |
334 | #if !defined(_MSC_VER) || _MSC_VER < 1926 |
335 | { |
336 | .Desc: "Dynamically exact integer. Stored as double, convertible." , |
337 | .Val: double{0x6000000000000000}, |
338 | .Str: "6.9175290276410819e+18" , |
339 | .AsInt: int64_t{0x6000000000000000}, |
340 | .AsNumber: double{0x6000000000000000}, |
341 | }, |
342 | #endif |
343 | |
344 | { |
345 | .Desc: "Dynamically integer, >64 bits. Stored as double, not convertible." , |
346 | .Val: 1.5 * double{0x8000000000000000}, |
347 | .Str: "1.3835058055282164e+19" , |
348 | .AsInt: std::nullopt, |
349 | .AsNumber: 1.5 * double{0x8000000000000000}, |
350 | }, |
351 | }; |
352 | for (const auto &T : TestCases) { |
353 | EXPECT_EQ(T.Str, s(T.Val)) << T.Desc; |
354 | llvm::Expected<Value> Doc = parse(JSON: T.Str); |
355 | EXPECT_TRUE(!!Doc) << T.Desc; |
356 | EXPECT_EQ(Doc->getAsInteger(), T.AsInt) << T.Desc; |
357 | EXPECT_EQ(Doc->getAsNumber(), T.AsNumber) << T.Desc; |
358 | EXPECT_EQ(T.Val, *Doc) << T.Desc; |
359 | EXPECT_EQ(T.Str, s(*Doc)) << T.Desc; |
360 | } |
361 | } |
362 | |
363 | // Verify uint64_t type. |
364 | TEST(JSONTest, U64Integers) { |
365 | Value Val = uint64_t{3100100100}; |
366 | uint64_t Var = 3100100100; |
367 | EXPECT_EQ(Val, Var); |
368 | |
369 | Val = uint64_t{std::numeric_limits<uint64_t>::max()}; |
370 | Var = std::numeric_limits<uint64_t>::max(); |
371 | EXPECT_EQ(Val, Var); |
372 | |
373 | // Test the parse() part. |
374 | { |
375 | const char *Str = "4611686018427387905" ; |
376 | llvm::Expected<Value> Doc = parse(JSON: Str); |
377 | |
378 | EXPECT_TRUE(!!Doc); |
379 | EXPECT_EQ(Doc->getAsInteger(), int64_t{4611686018427387905}); |
380 | EXPECT_EQ(Doc->getAsUINT64(), uint64_t{4611686018427387905}); |
381 | } |
382 | |
383 | { |
384 | const char *Str = "-78278238238328222" ; |
385 | llvm::Expected<Value> Doc = parse(JSON: Str); |
386 | |
387 | EXPECT_TRUE(!!Doc); |
388 | EXPECT_EQ(Doc->getAsInteger(), int64_t{-78278238238328222}); |
389 | EXPECT_EQ(Doc->getAsUINT64(), std::nullopt); |
390 | } |
391 | |
392 | // Test with the largest 64 signed int. |
393 | { |
394 | const char *Str = "9223372036854775807" ; |
395 | llvm::Expected<Value> Doc = parse(JSON: Str); |
396 | |
397 | EXPECT_TRUE(!!Doc); |
398 | EXPECT_EQ(Doc->getAsInteger(), int64_t{9223372036854775807}); |
399 | EXPECT_EQ(Doc->getAsUINT64(), uint64_t{9223372036854775807}); |
400 | } |
401 | |
402 | // Test with the largest 64 unsigned int. |
403 | { |
404 | const char *Str = "18446744073709551615" ; |
405 | llvm::Expected<Value> Doc = parse(JSON: Str); |
406 | |
407 | EXPECT_TRUE(!!Doc); |
408 | EXPECT_EQ(Doc->getAsInteger(), std::nullopt); |
409 | EXPECT_EQ(Doc->getAsUINT64(), uint64_t{18446744073709551615u}); |
410 | } |
411 | |
412 | // Test with a number that is too big for 64 bits. |
413 | { |
414 | const char *Str = "184467440737095516150" ; |
415 | llvm::Expected<Value> Doc = parse(JSON: Str); |
416 | |
417 | EXPECT_TRUE(!!Doc); |
418 | EXPECT_EQ(Doc->getAsInteger(), std::nullopt); |
419 | EXPECT_EQ(Doc->getAsUINT64(), std::nullopt); |
420 | // The number was parsed as a double. |
421 | EXPECT_TRUE(!!Doc->getAsNumber()); |
422 | } |
423 | |
424 | // Test with a negative number that is too small for 64 bits. |
425 | { |
426 | const char *Str = "-18446744073709551615" ; |
427 | llvm::Expected<Value> Doc = parse(JSON: Str); |
428 | |
429 | EXPECT_TRUE(!!Doc); |
430 | EXPECT_EQ(Doc->getAsInteger(), std::nullopt); |
431 | EXPECT_EQ(Doc->getAsUINT64(), std::nullopt); |
432 | // The number was parsed as a double. |
433 | EXPECT_TRUE(!!Doc->getAsNumber()); |
434 | } |
435 | // Test with a large number that is malformed. |
436 | { |
437 | const char *Str = "184467440737095516150.12.12" ; |
438 | llvm::Expected<Value> Doc = parse(JSON: Str); |
439 | |
440 | EXPECT_EQ("[1:27, byte=27]: Invalid JSON value (number?)" , |
441 | llvm::toString(Doc.takeError())); |
442 | } |
443 | } |
444 | |
445 | template <typename T> void checkCppIntegers() { |
446 | Value Val = T{10}; |
447 | T Var = 10; |
448 | EXPECT_EQ(Val, Var); |
449 | |
450 | Val = T{std::numeric_limits<T>::max()}; |
451 | Var = std::numeric_limits<T>::max(); |
452 | EXPECT_EQ(Val, Var); |
453 | |
454 | Val = T{std::numeric_limits<T>::min()}; |
455 | Var = std::numeric_limits<T>::min(); |
456 | EXPECT_EQ(Val, Var); |
457 | } |
458 | |
459 | // Test that underlying C++ integer types behave as expected. |
460 | TEST(JSONTest, CppIntegers) { |
461 | checkCppIntegers<char>(); |
462 | checkCppIntegers<signed char>(); |
463 | checkCppIntegers<unsigned char>(); |
464 | |
465 | checkCppIntegers<short>(); |
466 | checkCppIntegers<unsigned short>(); |
467 | |
468 | checkCppIntegers<int>(); |
469 | checkCppIntegers<unsigned int>(); |
470 | |
471 | checkCppIntegers<long>(); |
472 | checkCppIntegers<unsigned long>(); |
473 | |
474 | checkCppIntegers<long long>(); |
475 | checkCppIntegers<unsigned long long>(); |
476 | } |
477 | |
478 | // Sample struct with typical JSON-mapping rules. |
479 | struct CustomStruct { |
480 | CustomStruct() : B(false) {} |
481 | CustomStruct(std::string S, std::optional<int> I, bool B) |
482 | : S(S), I(I), B(B) {} |
483 | std::string S; |
484 | std::optional<int> I; |
485 | bool B; |
486 | }; |
487 | inline bool operator==(const CustomStruct &L, const CustomStruct &R) { |
488 | return L.S == R.S && L.I == R.I && L.B == R.B; |
489 | } |
490 | inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, |
491 | const CustomStruct &S) { |
492 | return OS << "(" << S.S << ", " << (S.I ? std::to_string(val: *S.I) : "None" ) |
493 | << ", " << S.B << ")" ; |
494 | } |
495 | bool fromJSON(const Value &E, CustomStruct &R, Path P) { |
496 | ObjectMapper O(E, P); |
497 | return O && O.map(Prop: "str" , Out&: R.S) && O.map(Prop: "int" , Out&: R.I) && |
498 | O.mapOptional(Prop: "bool" , Out&: R.B); |
499 | } |
500 | |
501 | static std::string errorContext(const Value &V, const Path::Root &R) { |
502 | std::string Context; |
503 | llvm::raw_string_ostream OS(Context); |
504 | R.printErrorContext(V, OS); |
505 | return OS.str(); |
506 | } |
507 | |
508 | TEST(JSONTest, Deserialize) { |
509 | std::map<std::string, std::vector<CustomStruct>> R; |
510 | CustomStruct ExpectedStruct = {"foo" , 42, true}; |
511 | std::map<std::string, std::vector<CustomStruct>> Expected; |
512 | Value J = Object{{.K: "foo" , .V: Array{ |
513 | Object{ |
514 | {.K: "str" , .V: "foo" }, |
515 | {.K: "int" , .V: 42}, |
516 | {.K: "bool" , .V: true}, |
517 | {.K: "unknown" , .V: "ignored" }, |
518 | }, |
519 | Object{{.K: "str" , .V: "bar" }}, |
520 | }}}; |
521 | Expected["foo" ] = { |
522 | CustomStruct("foo" , 42, true), |
523 | CustomStruct("bar" , std::nullopt, false), |
524 | }; |
525 | Path::Root Root("CustomStruct" ); |
526 | ASSERT_TRUE(fromJSON(J, R, Root)); |
527 | EXPECT_EQ(R, Expected); |
528 | |
529 | (*J.getAsObject()->getArray(K: "foo" ))[0] = 123; |
530 | ASSERT_FALSE(fromJSON(J, R, Root)); |
531 | EXPECT_EQ("expected object at CustomStruct.foo[0]" , |
532 | toString(Root.getError())); |
533 | const char *ExpectedDump = R"({ |
534 | "foo": [ |
535 | /* error: expected object */ |
536 | 123, |
537 | { ... } |
538 | ] |
539 | })" ; |
540 | EXPECT_EQ(ExpectedDump, errorContext(J, Root)); |
541 | |
542 | CustomStruct V; |
543 | EXPECT_FALSE(fromJSON(nullptr, V, Root)); |
544 | EXPECT_EQ("expected object when parsing CustomStruct" , |
545 | toString(Root.getError())); |
546 | |
547 | EXPECT_FALSE(fromJSON(Object{}, V, Root)); |
548 | EXPECT_EQ("missing value at CustomStruct.str" , toString(Root.getError())); |
549 | |
550 | EXPECT_FALSE(fromJSON(Object{{"str" , 1}}, V, Root)); |
551 | EXPECT_EQ("expected string at CustomStruct.str" , toString(Root.getError())); |
552 | |
553 | // std::optional<T> must parse as the correct type if present. |
554 | EXPECT_FALSE(fromJSON(Object{{"str" , "1" }, {"int" , "string" }}, V, Root)); |
555 | EXPECT_EQ("expected integer at CustomStruct.int" , toString(Root.getError())); |
556 | |
557 | // mapOptional must parse as the correct type if present. |
558 | EXPECT_FALSE(fromJSON(Object{{"str" , "1" }, {"bool" , "string" }}, V, Root)); |
559 | EXPECT_EQ("expected boolean at CustomStruct.bool" , toString(Root.getError())); |
560 | } |
561 | |
562 | TEST(JSONTest, ParseDeserialize) { |
563 | auto E = parse<std::vector<CustomStruct>>(JSON: R"json( |
564 | [{"str": "foo", "int": 42}, {"int": 42}] |
565 | )json" ); |
566 | EXPECT_THAT_EXPECTED(E, FailedWithMessage("missing value at (root)[1].str" )); |
567 | |
568 | E = parse<std::vector<CustomStruct>>(JSON: R"json( |
569 | [{"str": "foo", "int": 42}, {"str": "bar"} |
570 | )json" ); |
571 | EXPECT_THAT_EXPECTED( |
572 | E, |
573 | FailedWithMessage("[3:2, byte=50]: Expected , or ] after array element" )); |
574 | |
575 | E = parse<std::vector<CustomStruct>>(JSON: R"json( |
576 | [{"str": "foo", "int": 42}] |
577 | )json" ); |
578 | EXPECT_THAT_EXPECTED(E, Succeeded()); |
579 | EXPECT_THAT(*E, testing::SizeIs(1)); |
580 | } |
581 | |
582 | TEST(JSONTest, Stream) { |
583 | auto StreamStuff = [](unsigned Indent) { |
584 | std::string S; |
585 | llvm::raw_string_ostream OS(S); |
586 | OStream J(OS, Indent); |
587 | J.comment("top*/level" ); |
588 | J.object(Contents: [&] { |
589 | J.attributeArray(Key: "foo" , Contents: [&] { |
590 | J.value(V: nullptr); |
591 | J.comment("element" ); |
592 | J.value(V: 42.5); |
593 | J.arrayBegin(); |
594 | J.value(V: 43); |
595 | J.arrayEnd(); |
596 | J.rawValue(Contents: [](raw_ostream &OS) { OS << "'unverified\nraw value'" ; }); |
597 | }); |
598 | J.comment("attribute" ); |
599 | J.attributeBegin(Key: "bar" ); |
600 | J.comment("attribute value" ); |
601 | J.objectBegin(); |
602 | J.objectEnd(); |
603 | J.attributeEnd(); |
604 | J.attribute(Key: "baz" , Contents: "xyz" ); |
605 | }); |
606 | return OS.str(); |
607 | }; |
608 | |
609 | const char *Plain = |
610 | R"(/*top* /level*/{"foo":[null,/*element*/42.5,[43],'unverified |
611 | raw value'],/*attribute*/"bar":/*attribute value*/{},"baz":"xyz"})" ; |
612 | EXPECT_EQ(Plain, StreamStuff(0)); |
613 | const char *Pretty = R"(/* top* /level */ |
614 | { |
615 | "foo": [ |
616 | null, |
617 | /* element */ |
618 | 42.5, |
619 | [ |
620 | 43 |
621 | ], |
622 | 'unverified |
623 | raw value' |
624 | ], |
625 | /* attribute */ |
626 | "bar": /* attribute value */ {}, |
627 | "baz": "xyz" |
628 | })" ; |
629 | EXPECT_EQ(Pretty, StreamStuff(2)); |
630 | } |
631 | |
632 | TEST(JSONTest, Path) { |
633 | Path::Root R("foo" ); |
634 | Path P = R, A = P.field(Field: "a" ), B = P.field(Field: "b" ); |
635 | P.report(Message: "oh no" ); |
636 | EXPECT_THAT_ERROR(R.getError(), FailedWithMessage("oh no when parsing foo" )); |
637 | A.index(Index: 1).field(Field: "c" ).index(Index: 2).report(Message: "boom" ); |
638 | EXPECT_THAT_ERROR(R.getError(), FailedWithMessage("boom at foo.a[1].c[2]" )); |
639 | B.field(Field: "d" ).field(Field: "e" ).report(Message: "bam" ); |
640 | EXPECT_THAT_ERROR(R.getError(), FailedWithMessage("bam at foo.b.d.e" )); |
641 | |
642 | Value V = Object{ |
643 | {.K: "a" , .V: Array{42}}, |
644 | {.K: "b" , |
645 | .V: Object{{.K: "d" , |
646 | .V: Object{ |
647 | {.K: "e" , .V: Array{1, Object{{.K: "x" , .V: "y" }}}}, |
648 | {.K: "f" , .V: "a moderately long string: 48 characters in total" }, |
649 | }}}}, |
650 | }; |
651 | const char *Expected = R"({ |
652 | "a": [ ... ], |
653 | "b": { |
654 | "d": { |
655 | "e": /* error: bam */ [ |
656 | 1, |
657 | { ... } |
658 | ], |
659 | "f": "a moderately long string: 48 characte..." |
660 | } |
661 | } |
662 | })" ; |
663 | EXPECT_EQ(Expected, errorContext(V, R)); |
664 | } |
665 | |
666 | } // namespace |
667 | } // namespace json |
668 | } // namespace llvm |
669 | |