blob: 93efcf2f6addd6d67dc2d35dd8783811199ab0d5 [file] [log] [blame]
Sam McCall6be38242018-07-09 10:05:41 +00001//===-- JSONTest.cpp - JSON unit tests --------------------------*- C++ -*-===//
2//
Chandler Carruth2946cd72019-01-19 08:50:56 +00003// 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
Sam McCall6be38242018-07-09 10:05:41 +00006//
7//===----------------------------------------------------------------------===//
8
9#include "llvm/Support/JSON.h"
Sam McCalla7edcfb2019-04-25 12:51:42 +000010#include "llvm/Support/raw_ostream.h"
Sam McCall6be38242018-07-09 10:05:41 +000011
12#include "gmock/gmock.h"
13#include "gtest/gtest.h"
14
15namespace llvm {
16namespace json {
17
18namespace {
19
20std::string s(const Value &E) { return llvm::formatv("{0}", E).str(); }
21std::string sp(const Value &E) { return llvm::formatv("{0:2}", E).str(); }
22
23TEST(JSONTest, Types) {
24 EXPECT_EQ("true", s(true));
25 EXPECT_EQ("null", s(nullptr));
26 EXPECT_EQ("2.5", s(2.5));
27 EXPECT_EQ(R"("foo")", s("foo"));
28 EXPECT_EQ("[1,2,3]", s({1, 2, 3}));
29 EXPECT_EQ(R"({"x":10,"y":20})", s(Object{{"x", 10}, {"y", 20}}));
Sam McCalle6057bc2018-07-10 11:51:26 +000030
31#ifdef NDEBUG
32 EXPECT_EQ(R"("��")", s("\xC0\x80"));
33 EXPECT_EQ(R"({"��":0})", s(Object{{"\xC0\x80", 0}}));
34#else
35 EXPECT_DEATH(s("\xC0\x80"), "Invalid UTF-8");
36 EXPECT_DEATH(s(Object{{"\xC0\x80", 0}}), "Invalid UTF-8");
37#endif
Sam McCall6be38242018-07-09 10:05:41 +000038}
39
40TEST(JSONTest, Constructors) {
41 // Lots of edge cases around empty and singleton init lists.
42 EXPECT_EQ("[[[3]]]", s({{{3}}}));
43 EXPECT_EQ("[[[]]]", s({{{}}}));
44 EXPECT_EQ("[[{}]]", s({{Object{}}}));
45 EXPECT_EQ(R"({"A":{"B":{}}})", s(Object{{"A", Object{{"B", Object{}}}}}));
46 EXPECT_EQ(R"({"A":{"B":{"X":"Y"}}})",
47 s(Object{{"A", Object{{"B", Object{{"X", "Y"}}}}}}));
Sam McCall7e4234f2018-07-09 12:26:09 +000048 EXPECT_EQ("null", s(llvm::Optional<double>()));
49 EXPECT_EQ("2.5", s(llvm::Optional<double>(2.5)));
Sam McCall5ee01882018-10-18 08:47:24 +000050 EXPECT_EQ("[[2.5,null]]", s(std::vector<std::vector<llvm::Optional<double>>>{
51 {2.5, llvm::None}}));
Sam McCall6be38242018-07-09 10:05:41 +000052}
53
54TEST(JSONTest, StringOwnership) {
55 char X[] = "Hello";
56 Value Alias = static_cast<const char *>(X);
57 X[1] = 'a';
58 EXPECT_EQ(R"("Hallo")", s(Alias));
59
60 std::string Y = "Hello";
61 Value Copy = Y;
62 Y[1] = 'a';
63 EXPECT_EQ(R"("Hello")", s(Copy));
64}
65
66TEST(JSONTest, CanonicalOutput) {
67 // Objects are sorted (but arrays aren't)!
68 EXPECT_EQ(R"({"a":1,"b":2,"c":3})", s(Object{{"a", 1}, {"c", 3}, {"b", 2}}));
69 EXPECT_EQ(R"(["a","c","b"])", s({"a", "c", "b"}));
70 EXPECT_EQ("3", s(3.0));
71}
72
73TEST(JSONTest, Escaping) {
Sam McCall41628752019-11-11 18:25:01 +010074 std::string Test = {
Sam McCall6be38242018-07-09 10:05:41 +000075 0, // Strings may contain nulls.
76 '\b', '\f', // Have mnemonics, but we escape numerically.
77 '\r', '\n', '\t', // Escaped with mnemonics.
78 'S', '\"', '\\', // Printable ASCII characters.
79 '\x7f', // Delete is not escaped.
80 '\xce', '\x94', // Non-ASCII UTF-8 is not escaped.
81 };
82
Sam McCall41628752019-11-11 18:25:01 +010083 std::string TestString = R"("\u0000\u0008\u000c\r\n\tS\"\\)"
Sam McCall6be38242018-07-09 10:05:41 +000084 "\x7f\xCE\x94\"";
85
Sam McCall41628752019-11-11 18:25:01 +010086 EXPECT_EQ(TestString, s(Test));
Sam McCall6be38242018-07-09 10:05:41 +000087
88 EXPECT_EQ(R"({"object keys are\nescaped":true})",
89 s(Object{{"object keys are\nescaped", true}}));
90}
91
92TEST(JSONTest, PrettyPrinting) {
Sam McCall41628752019-11-11 18:25:01 +010093 const char Str[] = R"({
Sam McCall6be38242018-07-09 10:05:41 +000094 "empty_array": [],
95 "empty_object": {},
96 "full_array": [
97 1,
98 null
99 ],
100 "full_object": {
101 "nested_array": [
102 {
103 "property": "value"
104 }
105 ]
106 }
107})";
108
Sam McCall41628752019-11-11 18:25:01 +0100109 EXPECT_EQ(Str, sp(Object{
Sam McCall6be38242018-07-09 10:05:41 +0000110 {"empty_object", Object{}},
111 {"empty_array", {}},
112 {"full_array", {1, nullptr}},
113 {"full_object",
114 Object{
115 {"nested_array",
116 {Object{
117 {"property", "value"},
118 }}},
119 }},
120 }));
121}
122
Sam McCall41628752019-11-11 18:25:01 +0100123TEST(JSONTest, Array) {
124 Array A{1, 2};
125 A.emplace_back(3);
126 A.emplace(++A.begin(), 0);
127 A.push_back(4);
128 A.insert(++++A.begin(), 99);
129
130 EXPECT_EQ(A.size(), 6u);
131 EXPECT_EQ(R"([1,0,99,2,3,4])", s(std::move(A)));
132}
133
134TEST(JSONTest, Object) {
135 Object O{{"a", 1}, {"b", 2}, {"c", 3}};
136 EXPECT_TRUE(O.try_emplace("d", 4).second);
137 EXPECT_FALSE(O.try_emplace("a", 4).second);
138
139 auto D = O.find("d");
140 EXPECT_FALSE(D == O.end());
141 auto E = O.find("e");
142 EXPECT_TRUE(E == O.end());
143
144 O.erase("b");
145 O.erase(D);
146 EXPECT_EQ(O.size(), 2u);
147 EXPECT_EQ(R"({"a":1,"c":3})", s(std::move(O)));
148}
149
Sam McCall6be38242018-07-09 10:05:41 +0000150TEST(JSONTest, Parse) {
151 auto Compare = [](llvm::StringRef S, Value Expected) {
152 if (auto E = parse(S)) {
153 // Compare both string forms and with operator==, in case we have bugs.
154 EXPECT_EQ(*E, Expected);
155 EXPECT_EQ(sp(*E), sp(Expected));
156 } else {
157 handleAllErrors(E.takeError(), [S](const llvm::ErrorInfoBase &E) {
158 FAIL() << "Failed to parse JSON >>> " << S << " <<<: " << E.message();
159 });
160 }
161 };
162
163 Compare(R"(true)", true);
164 Compare(R"(false)", false);
165 Compare(R"(null)", nullptr);
166
167 Compare(R"(42)", 42);
168 Compare(R"(2.5)", 2.5);
169 Compare(R"(2e50)", 2e50);
170 Compare(R"(1.2e3456789)", std::numeric_limits<double>::infinity());
171
172 Compare(R"("foo")", "foo");
173 Compare(R"("\"\\\b\f\n\r\t")", "\"\\\b\f\n\r\t");
174 Compare(R"("\u0000")", llvm::StringRef("\0", 1));
175 Compare("\"\x7f\"", "\x7f");
176 Compare(R"("\ud801\udc37")", u8"\U00010437"); // UTF16 surrogate pair escape.
177 Compare("\"\xE2\x82\xAC\xF0\x9D\x84\x9E\"", u8"\u20ac\U0001d11e"); // UTF8
178 Compare(
179 R"("LoneLeading=\ud801, LoneTrailing=\udc01, LeadingLeadingTrailing=\ud801\ud801\udc37")",
180 u8"LoneLeading=\ufffd, LoneTrailing=\ufffd, "
181 u8"LeadingLeadingTrailing=\ufffd\U00010437"); // Invalid unicode.
182
183 Compare(R"({"":0,"":0})", Object{{"", 0}});
184 Compare(R"({"obj":{},"arr":[]})", Object{{"obj", Object{}}, {"arr", {}}});
185 Compare(R"({"\n":{"\u0000":[[[[]]]]}})",
186 Object{{"\n", Object{
187 {llvm::StringRef("\0", 1), {{{{}}}}},
188 }}});
189 Compare("\r[\n\t] ", {});
190}
191
192TEST(JSONTest, ParseErrors) {
193 auto ExpectErr = [](llvm::StringRef Msg, llvm::StringRef S) {
194 if (auto E = parse(S)) {
195 // Compare both string forms and with operator==, in case we have bugs.
196 FAIL() << "Parsed JSON >>> " << S << " <<< but wanted error: " << Msg;
197 } else {
198 handleAllErrors(E.takeError(), [S, Msg](const llvm::ErrorInfoBase &E) {
199 EXPECT_THAT(E.message(), testing::HasSubstr(Msg)) << S;
200 });
201 }
202 };
203 ExpectErr("Unexpected EOF", "");
204 ExpectErr("Unexpected EOF", "[");
205 ExpectErr("Text after end of document", "[][]");
206 ExpectErr("Invalid JSON value (false?)", "fuzzy");
207 ExpectErr("Expected , or ]", "[2?]");
208 ExpectErr("Expected object key", "{a:2}");
209 ExpectErr("Expected : after object key", R"({"a",2})");
210 ExpectErr("Expected , or } after object property", R"({"a":2 "b":3})");
211 ExpectErr("Invalid JSON value", R"([&%!])");
212 ExpectErr("Invalid JSON value (number?)", "1e1.0");
213 ExpectErr("Unterminated string", R"("abc\"def)");
214 ExpectErr("Control character in string", "\"abc\ndef\"");
215 ExpectErr("Invalid escape sequence", R"("\030")");
216 ExpectErr("Invalid \\u escape sequence", R"("\usuck")");
217 ExpectErr("[3:3, byte=19]", R"({
218 "valid": 1,
219 invalid: 2
220})");
Sam McCalle6057bc2018-07-10 11:51:26 +0000221 ExpectErr("Invalid UTF-8 sequence", "\"\xC0\x80\""); // WTF-8 null
222}
223
224// Direct tests of isUTF8 and fixUTF8. Internal uses are also tested elsewhere.
225TEST(JSONTest, UTF8) {
226 for (const char *Valid : {
227 "this is ASCII text",
228 "thïs tëxt häs BMP chäräctërs",
229 "𐌶𐌰L𐌾𐍈 C𐍈𐌼𐌴𐍃",
230 }) {
231 EXPECT_TRUE(isUTF8(Valid)) << Valid;
232 EXPECT_EQ(fixUTF8(Valid), Valid);
233 }
234 for (auto Invalid : std::vector<std::pair<const char *, const char *>>{
235 {"lone trailing \x81\x82 bytes", "lone trailing �� bytes"},
236 {"missing trailing \xD0 bytes", "missing trailing � bytes"},
237 {"truncated character \xD0", "truncated character �"},
238 {"not \xC1\x80 the \xE0\x9f\xBF shortest \xF0\x83\x83\x83 encoding",
239 "not �� the ��� shortest ���� encoding"},
240 {"too \xF9\x80\x80\x80\x80 long", "too ����� long"},
241 {"surrogate \xED\xA0\x80 invalid \xF4\x90\x80\x80",
242 "surrogate ��� invalid ����"}}) {
243 EXPECT_FALSE(isUTF8(Invalid.first)) << Invalid.first;
244 EXPECT_EQ(fixUTF8(Invalid.first), Invalid.second);
245 }
Sam McCall6be38242018-07-09 10:05:41 +0000246}
247
248TEST(JSONTest, Inspection) {
249 llvm::Expected<Value> Doc = parse(R"(
250 {
251 "null": null,
252 "boolean": false,
253 "number": 2.78,
254 "string": "json",
255 "array": [null, true, 3.14, "hello", [1,2,3], {"time": "arrow"}],
256 "object": {"fruit": "banana"}
257 }
258 )");
259 EXPECT_TRUE(!!Doc);
260
261 Object *O = Doc->getAsObject();
262 ASSERT_TRUE(O);
263
264 EXPECT_FALSE(O->getNull("missing"));
265 EXPECT_FALSE(O->getNull("boolean"));
266 EXPECT_TRUE(O->getNull("null"));
267
268 EXPECT_EQ(O->getNumber("number"), llvm::Optional<double>(2.78));
269 EXPECT_FALSE(O->getInteger("number"));
270 EXPECT_EQ(O->getString("string"), llvm::Optional<llvm::StringRef>("json"));
271 ASSERT_FALSE(O->getObject("missing"));
272 ASSERT_FALSE(O->getObject("array"));
273 ASSERT_TRUE(O->getObject("object"));
274 EXPECT_EQ(*O->getObject("object"), (Object{{"fruit", "banana"}}));
275
276 Array *A = O->getArray("array");
277 ASSERT_TRUE(A);
278 EXPECT_EQ((*A)[1].getAsBoolean(), llvm::Optional<bool>(true));
279 ASSERT_TRUE((*A)[4].getAsArray());
280 EXPECT_EQ(*(*A)[4].getAsArray(), (Array{1, 2, 3}));
281 EXPECT_EQ((*(*A)[4].getAsArray())[1].getAsInteger(),
282 llvm::Optional<int64_t>(2));
283 int I = 0;
284 for (Value &E : *A) {
285 if (I++ == 5) {
286 ASSERT_TRUE(E.getAsObject());
287 EXPECT_EQ(E.getAsObject()->getString("time"),
288 llvm::Optional<llvm::StringRef>("arrow"));
289 } else
290 EXPECT_FALSE(E.getAsObject());
291 }
292}
293
Sam McCalld93eaeb2018-07-09 12:16:40 +0000294// Verify special integer handling - we try to preserve exact int64 values.
295TEST(JSONTest, Integers) {
296 struct {
297 const char *Desc;
298 Value Val;
299 const char *Str;
300 llvm::Optional<int64_t> AsInt;
301 llvm::Optional<double> AsNumber;
302 } TestCases[] = {
303 {
304 "Non-integer. Stored as double, not convertible.",
305 double{1.5},
306 "1.5",
307 llvm::None,
308 1.5,
309 },
310
311 {
312 "Integer, not exact double. Stored as int64, convertible.",
313 int64_t{0x4000000000000001},
314 "4611686018427387905",
315 int64_t{0x4000000000000001},
316 double{0x4000000000000000},
317 },
318
319 {
320 "Negative integer, not exact double. Stored as int64, convertible.",
321 int64_t{-0x4000000000000001},
322 "-4611686018427387905",
323 int64_t{-0x4000000000000001},
324 double{-0x4000000000000000},
325 },
326
327 {
328 "Dynamically exact integer. Stored as double, convertible.",
329 double{0x6000000000000000},
330 "6.9175290276410819e+18",
331 int64_t{0x6000000000000000},
332 double{0x6000000000000000},
333 },
334
335 {
336 "Dynamically integer, >64 bits. Stored as double, not convertible.",
337 1.5 * double{0x8000000000000000},
338 "1.3835058055282164e+19",
339 llvm::None,
340 1.5 * double{0x8000000000000000},
341 },
342 };
343 for (const auto &T : TestCases) {
344 EXPECT_EQ(T.Str, s(T.Val)) << T.Desc;
345 llvm::Expected<Value> Doc = parse(T.Str);
346 EXPECT_TRUE(!!Doc) << T.Desc;
347 EXPECT_EQ(Doc->getAsInteger(), T.AsInt) << T.Desc;
348 EXPECT_EQ(Doc->getAsNumber(), T.AsNumber) << T.Desc;
349 EXPECT_EQ(T.Val, *Doc) << T.Desc;
350 EXPECT_EQ(T.Str, s(*Doc)) << T.Desc;
351 }
352}
353
Sam McCall6be38242018-07-09 10:05:41 +0000354// Sample struct with typical JSON-mapping rules.
355struct CustomStruct {
356 CustomStruct() : B(false) {}
357 CustomStruct(std::string S, llvm::Optional<int> I, bool B)
358 : S(S), I(I), B(B) {}
359 std::string S;
360 llvm::Optional<int> I;
361 bool B;
362};
363inline bool operator==(const CustomStruct &L, const CustomStruct &R) {
364 return L.S == R.S && L.I == R.I && L.B == R.B;
365}
366inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
367 const CustomStruct &S) {
368 return OS << "(" << S.S << ", " << (S.I ? std::to_string(*S.I) : "None")
369 << ", " << S.B << ")";
370}
371bool fromJSON(const Value &E, CustomStruct &R) {
372 ObjectMapper O(E);
373 if (!O || !O.map("str", R.S) || !O.map("int", R.I))
374 return false;
375 O.map("bool", R.B);
376 return true;
377}
378
379TEST(JSONTest, Deserialize) {
380 std::map<std::string, std::vector<CustomStruct>> R;
381 CustomStruct ExpectedStruct = {"foo", 42, true};
382 std::map<std::string, std::vector<CustomStruct>> Expected;
383 Value J = Object{
384 {"foo",
385 Array{
386 Object{
387 {"str", "foo"},
388 {"int", 42},
389 {"bool", true},
390 {"unknown", "ignored"},
391 },
392 Object{{"str", "bar"}},
393 Object{
394 {"str", "baz"}, {"bool", "string"}, // OK, deserialize ignores.
395 },
396 }}};
397 Expected["foo"] = {
398 CustomStruct("foo", 42, true),
399 CustomStruct("bar", llvm::None, false),
400 CustomStruct("baz", llvm::None, false),
401 };
402 ASSERT_TRUE(fromJSON(J, R));
403 EXPECT_EQ(R, Expected);
404
405 CustomStruct V;
406 EXPECT_FALSE(fromJSON(nullptr, V)) << "Not an object " << V;
407 EXPECT_FALSE(fromJSON(Object{}, V)) << "Missing required field " << V;
408 EXPECT_FALSE(fromJSON(Object{{"str", 1}}, V)) << "Wrong type " << V;
409 // Optional<T> must parse as the correct type if present.
410 EXPECT_FALSE(fromJSON(Object{{"str", 1}, {"int", "string"}}, V))
411 << "Wrong type for Optional<T> " << V;
412}
413
Sam McCalla7edcfb2019-04-25 12:51:42 +0000414TEST(JSONTest, Stream) {
415 auto StreamStuff = [](unsigned Indent) {
416 std::string S;
417 llvm::raw_string_ostream OS(S);
418 OStream J(OS, Indent);
419 J.object([&] {
420 J.attributeArray("foo", [&] {
421 J.value(nullptr);
422 J.value(42.5);
423 J.arrayBegin();
424 J.value(43);
425 J.arrayEnd();
426 });
427 J.attributeBegin("bar");
428 J.objectBegin();
429 J.objectEnd();
430 J.attributeEnd();
431 J.attribute("baz", "xyz");
432 });
433 return OS.str();
434 };
435
436 const char *Plain = R"({"foo":[null,42.5,[43]],"bar":{},"baz":"xyz"})";
437 EXPECT_EQ(Plain, StreamStuff(0));
438 const char *Pretty = R"({
439 "foo": [
440 null,
441 42.5,
442 [
443 43
444 ]
445 ],
446 "bar": {},
447 "baz": "xyz"
448})";
449 EXPECT_EQ(Pretty, StreamStuff(2));
450}
451
Sam McCall6be38242018-07-09 10:05:41 +0000452} // namespace
453} // namespace json
454} // namespace llvm