1 | //===- llvm/unittest/Support/ScopedPrinterTest.cpp - ScopedPrinter tests --===// |
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/ScopedPrinter.h" |
10 | #include "llvm/ADT/APSInt.h" |
11 | #include "llvm/Support/Format.h" |
12 | #include "gtest/gtest.h" |
13 | #include <cmath> |
14 | #include <vector> |
15 | |
16 | using namespace llvm; |
17 | |
18 | TEST(JSONScopedPrinterTest, PrettyPrintCtor) { |
19 | auto PrintFunc = [](ScopedPrinter &W) { |
20 | DictScope D(W); |
21 | W.printString(Label: "Key" , Value: "Value" ); |
22 | }; |
23 | std::string StreamBuffer; |
24 | raw_string_ostream OS(StreamBuffer); |
25 | JSONScopedPrinter PrettyPrintWriter(OS, /*PrettyPrint=*/true); |
26 | JSONScopedPrinter NoPrettyPrintWriter(OS, /*PrettyPrint=*/false); |
27 | |
28 | const char *PrettyPrintOut = R"({ |
29 | "Key": "Value" |
30 | })" ; |
31 | const char *NoPrettyPrintOut = R"({"Key":"Value"})" ; |
32 | PrintFunc(PrettyPrintWriter); |
33 | EXPECT_EQ(PrettyPrintOut, OS.str()); |
34 | StreamBuffer.clear(); |
35 | PrintFunc(NoPrettyPrintWriter); |
36 | EXPECT_EQ(NoPrettyPrintOut, OS.str()); |
37 | } |
38 | |
39 | TEST(JSONScopedPrinterTest, DelimitedScopeCtor) { |
40 | std::string StreamBuffer; |
41 | raw_string_ostream OS(StreamBuffer); |
42 | { |
43 | JSONScopedPrinter DictScopeWriter(OS, /*PrettyPrint=*/false, |
44 | std::make_unique<DictScope>()); |
45 | DictScopeWriter.printString(Label: "Label" , Value: "DictScope" ); |
46 | } |
47 | EXPECT_EQ(R"({"Label":"DictScope"})" , OS.str()); |
48 | StreamBuffer.clear(); |
49 | { |
50 | JSONScopedPrinter ListScopeWriter(OS, /*PrettyPrint=*/false, |
51 | std::make_unique<ListScope>()); |
52 | ListScopeWriter.printString(Value: "ListScope" ); |
53 | } |
54 | EXPECT_EQ(R"(["ListScope"])" , OS.str()); |
55 | StreamBuffer.clear(); |
56 | { |
57 | JSONScopedPrinter NoScopeWriter(OS, /*PrettyPrint=*/false); |
58 | NoScopeWriter.printString(Value: "NoScope" ); |
59 | } |
60 | EXPECT_EQ(R"("NoScope")" , OS.str()); |
61 | } |
62 | |
63 | class ScopedPrinterTest : public ::testing::Test { |
64 | protected: |
65 | std::string StreamBuffer; |
66 | raw_string_ostream OS; |
67 | ScopedPrinter Writer; |
68 | JSONScopedPrinter JSONWriter; |
69 | |
70 | bool HasPrintedToJSON; |
71 | |
72 | ScopedPrinterTest() |
73 | : OS(StreamBuffer), Writer(OS), JSONWriter(OS, /*PrettyPrint=*/true), |
74 | HasPrintedToJSON(false) {} |
75 | |
76 | using PrintFunc = function_ref<void(ScopedPrinter &)>; |
77 | |
78 | void verifyScopedPrinter(StringRef Expected, PrintFunc Func) { |
79 | Func(Writer); |
80 | Writer.flush(); |
81 | EXPECT_EQ(Expected.str(), OS.str()); |
82 | StreamBuffer.clear(); |
83 | } |
84 | |
85 | void verifyJSONScopedPrinter(StringRef Expected, PrintFunc Func) { |
86 | { |
87 | DictScope D(JSONWriter); |
88 | Func(JSONWriter); |
89 | } |
90 | JSONWriter.flush(); |
91 | EXPECT_EQ(Expected.str(), OS.str()); |
92 | StreamBuffer.clear(); |
93 | HasPrintedToJSON = true; |
94 | } |
95 | |
96 | void verifyAll(StringRef ExpectedOut, StringRef JSONExpectedOut, |
97 | PrintFunc Func) { |
98 | verifyScopedPrinter(Expected: ExpectedOut, Func); |
99 | verifyJSONScopedPrinter(Expected: JSONExpectedOut, Func); |
100 | } |
101 | |
102 | void TearDown() { |
103 | // JSONScopedPrinter fails an assert if nothing's been printed. |
104 | if (!HasPrintedToJSON) |
105 | JSONWriter.printString(Value: "" ); |
106 | } |
107 | }; |
108 | |
109 | TEST_F(ScopedPrinterTest, GetKind) { |
110 | EXPECT_EQ(ScopedPrinter::ScopedPrinterKind::Base, Writer.getKind()); |
111 | EXPECT_EQ(ScopedPrinter::ScopedPrinterKind::JSON, JSONWriter.getKind()); |
112 | } |
113 | |
114 | TEST_F(ScopedPrinterTest, ClassOf) { |
115 | EXPECT_TRUE(ScopedPrinter::classof(&Writer)); |
116 | EXPECT_TRUE(JSONScopedPrinter::classof(&JSONWriter)); |
117 | EXPECT_FALSE(ScopedPrinter::classof(&JSONWriter)); |
118 | EXPECT_FALSE(JSONScopedPrinter::classof(&Writer)); |
119 | } |
120 | |
121 | TEST_F(ScopedPrinterTest, Indent) { |
122 | auto PrintFunc = [](ScopedPrinter &W) { |
123 | W.printString(Value: "|" ); |
124 | W.indent(); |
125 | W.printString(Value: "|" ); |
126 | W.indent(Levels: 2); |
127 | W.printString(Value: "|" ); |
128 | }; |
129 | |
130 | const char *ExpectedOut = R"(| |
131 | | |
132 | | |
133 | )" ; |
134 | verifyScopedPrinter(Expected: ExpectedOut, Func: PrintFunc); |
135 | } |
136 | |
137 | TEST_F(ScopedPrinterTest, Unindent) { |
138 | auto PrintFunc = [](ScopedPrinter &W) { |
139 | W.indent(Levels: 3); |
140 | W.printString(Value: "|" ); |
141 | W.unindent(Levels: 2); |
142 | W.printString(Value: "|" ); |
143 | W.unindent(); |
144 | W.printString(Value: "|" ); |
145 | W.unindent(); |
146 | W.printString(Value: "|" ); |
147 | }; |
148 | |
149 | const char *ExpectedOut = R"( | |
150 | | |
151 | | |
152 | | |
153 | )" ; |
154 | verifyScopedPrinter(Expected: ExpectedOut, Func: PrintFunc); |
155 | } |
156 | |
157 | TEST_F(ScopedPrinterTest, ResetIndent) { |
158 | auto PrintFunc = [](ScopedPrinter &W) { |
159 | W.indent(Levels: 4); |
160 | W.printString(Value: "|" ); |
161 | W.resetIndent(); |
162 | W.printString(Value: "|" ); |
163 | }; |
164 | |
165 | const char *ExpectedOut = R"( | |
166 | | |
167 | )" ; |
168 | verifyScopedPrinter(Expected: ExpectedOut, Func: PrintFunc); |
169 | } |
170 | |
171 | TEST_F(ScopedPrinterTest, PrintIndent) { |
172 | auto PrintFunc = [](ScopedPrinter &W) { |
173 | W.printIndent(); |
174 | W.printString(Value: "|" ); |
175 | W.indent(); |
176 | W.printIndent(); |
177 | W.printString(Value: "|" ); |
178 | }; |
179 | |
180 | const char *ExpectedOut = R"(| |
181 | | |
182 | )" ; |
183 | verifyScopedPrinter(Expected: ExpectedOut, Func: PrintFunc); |
184 | } |
185 | |
186 | TEST_F(ScopedPrinterTest, GetIndentLevel) { |
187 | EXPECT_EQ(Writer.getIndentLevel(), 0); |
188 | Writer.indent(); |
189 | EXPECT_EQ(Writer.getIndentLevel(), 1); |
190 | Writer.indent(); |
191 | EXPECT_EQ(Writer.getIndentLevel(), 2); |
192 | Writer.unindent(); |
193 | EXPECT_EQ(Writer.getIndentLevel(), 1); |
194 | Writer.indent(); |
195 | Writer.resetIndent(); |
196 | EXPECT_EQ(Writer.getIndentLevel(), 0); |
197 | Writer.unindent(); |
198 | EXPECT_EQ(Writer.getIndentLevel(), 0); |
199 | Writer.indent(); |
200 | EXPECT_EQ(Writer.getIndentLevel(), 1); |
201 | } |
202 | |
203 | TEST_F(ScopedPrinterTest, SetPrefix) { |
204 | auto PrintFunc = [](ScopedPrinter &W) { |
205 | W.setPrefix("Prefix1" ); |
206 | W.indent(); |
207 | W.printIndent(); |
208 | W.printString(Value: "|" ); |
209 | W.unindent(); |
210 | W.printIndent(); |
211 | W.printString(Value: "|" ); |
212 | W.setPrefix("Prefix2" ); |
213 | W.printIndent(); |
214 | W.printString(Value: "|" ); |
215 | }; |
216 | |
217 | const char *ExpectedOut = R"(Prefix1 Prefix1 | |
218 | Prefix1Prefix1| |
219 | Prefix2Prefix2| |
220 | )" ; |
221 | verifyScopedPrinter(Expected: ExpectedOut, Func: PrintFunc); |
222 | } |
223 | |
224 | TEST_F(ScopedPrinterTest, PrintEnum) { |
225 | auto PrintFunc = [](ScopedPrinter &W) { |
226 | const EnumEntry<int> EnumList[] = {{"Name1" , "AltName1" , 1}, |
227 | {"Name2" , "AltName2" , 2}, |
228 | {"Name3" , "AltName3" , 3}, |
229 | {"Name4" , "AltName4" , 2}}; |
230 | EnumEntry<int> OtherEnum{"Name5" , "AltName5" , 5}; |
231 | W.printEnum(Label: "Exists" , Value: EnumList[1].Value, EnumValues: ArrayRef(EnumList)); |
232 | W.printEnum(Label: "DoesNotExist" , Value: OtherEnum.Value, EnumValues: ArrayRef(EnumList)); |
233 | }; |
234 | |
235 | const char *ExpectedOut = R"(Exists: Name2 (0x2) |
236 | DoesNotExist: 0x5 |
237 | )" ; |
238 | |
239 | const char *JSONExpectedOut = R"({ |
240 | "Exists": { |
241 | "Name": "Name2", |
242 | "Value": 2 |
243 | }, |
244 | "DoesNotExist": 5 |
245 | })" ; |
246 | verifyAll(ExpectedOut, JSONExpectedOut, Func: PrintFunc); |
247 | } |
248 | |
249 | TEST_F(ScopedPrinterTest, PrintFlag) { |
250 | auto PrintFunc = [](ScopedPrinter &W) { |
251 | const EnumEntry<uint16_t> SingleBitFlags[] = { |
252 | {"Name0" , "AltName0" , 0}, |
253 | {"Name1" , "AltName1" , 1}, |
254 | {"Name2" , "AltName2" , 1 << 1}, |
255 | {"Name3" , "AltName3" , 1 << 2}}; |
256 | const EnumEntry<uint16_t> UnsortedFlags[] = { |
257 | {"C" , "c" , 1}, {"B" , "b" , 1 << 1}, {"A" , "a" , 1 << 2}}; |
258 | const EnumEntry<uint16_t> EnumFlags[] = { |
259 | {"FirstByte1" , "First1" , 0x1u}, {"FirstByte2" , "First2" , 0x2u}, |
260 | {"FirstByte3" , "First3" , 0x3u}, {"SecondByte1" , "Second1" , 0x10u}, |
261 | {"SecondByte2" , "Second2" , 0x20u}, {"SecondByte3" , "Second3" , 0x30u}, |
262 | {"ThirdByte1" , "Third1" , 0x100u}, {"ThirdByte2" , "Third2" , 0x200u}, |
263 | {"ThirdByte3" , "Third3" , 0x300u}}; |
264 | W.printFlags(Label: "ZeroFlag" , Value: 0, Flags: ArrayRef(SingleBitFlags)); |
265 | W.printFlags(Label: "NoFlag" , Value: 1 << 3, Flags: ArrayRef(SingleBitFlags)); |
266 | W.printFlags(Label: "Flag1" , Value: SingleBitFlags[1].Value, Flags: ArrayRef(SingleBitFlags)); |
267 | W.printFlags(Label: "Flag1&3" , Value: (1 << 2) + 1, Flags: ArrayRef(SingleBitFlags)); |
268 | |
269 | W.printFlags(Label: "ZeroFlagRaw" , Value: 0); |
270 | W.printFlags(Label: "NoFlagRaw" , Value: 1 << 3); |
271 | W.printFlags(Label: "Flag1Raw" , Value: SingleBitFlags[1].Value); |
272 | W.printFlags(Label: "Flag1&3Raw" , Value: (1 << 2) + 1); |
273 | |
274 | W.printFlags(Label: "FlagSorted" , Value: (1 << 2) + (1 << 1) + 1, |
275 | Flags: ArrayRef(UnsortedFlags)); |
276 | |
277 | uint16_t NoBitMask = 0; |
278 | uint16_t FirstByteMask = 0xFu; |
279 | uint16_t SecondByteMask = 0xF0u; |
280 | uint16_t ThirdByteMask = 0xF00u; |
281 | W.printFlags(Label: "NoBitMask" , Value: 0xFFFu, Flags: ArrayRef(EnumFlags), EnumMask1: NoBitMask); |
282 | W.printFlags(Label: "FirstByteMask" , Value: 0x3u, Flags: ArrayRef(EnumFlags), EnumMask1: FirstByteMask); |
283 | W.printFlags(Label: "SecondByteMask" , Value: 0x30u, Flags: ArrayRef(EnumFlags), EnumMask1: SecondByteMask); |
284 | W.printFlags(Label: "ValueOutsideMask" , Value: 0x1u, Flags: ArrayRef(EnumFlags), EnumMask1: SecondByteMask); |
285 | W.printFlags(Label: "FirstSecondByteMask" , Value: 0xFFu, Flags: ArrayRef(EnumFlags), |
286 | EnumMask1: FirstByteMask, EnumMask2: SecondByteMask); |
287 | W.printFlags(Label: "FirstSecondThirdByteMask" , Value: 0x333u, Flags: ArrayRef(EnumFlags), |
288 | EnumMask1: FirstByteMask, EnumMask2: SecondByteMask, EnumMask3: ThirdByteMask); |
289 | }; |
290 | |
291 | const char *ExpectedOut = R"(ZeroFlag [ (0x0) |
292 | ] |
293 | NoFlag [ (0x8) |
294 | ] |
295 | Flag1 [ (0x1) |
296 | Name1 (0x1) |
297 | ] |
298 | Flag1&3 [ (0x5) |
299 | Name1 (0x1) |
300 | Name3 (0x4) |
301 | ] |
302 | ZeroFlagRaw [ (0x0) |
303 | ] |
304 | NoFlagRaw [ (0x8) |
305 | 0x8 |
306 | ] |
307 | Flag1Raw [ (0x1) |
308 | 0x1 |
309 | ] |
310 | Flag1&3Raw [ (0x5) |
311 | 0x1 |
312 | 0x4 |
313 | ] |
314 | FlagSorted [ (0x7) |
315 | A (0x4) |
316 | B (0x2) |
317 | C (0x1) |
318 | ] |
319 | NoBitMask [ (0xFFF) |
320 | FirstByte1 (0x1) |
321 | FirstByte2 (0x2) |
322 | FirstByte3 (0x3) |
323 | SecondByte1 (0x10) |
324 | SecondByte2 (0x20) |
325 | SecondByte3 (0x30) |
326 | ThirdByte1 (0x100) |
327 | ThirdByte2 (0x200) |
328 | ThirdByte3 (0x300) |
329 | ] |
330 | FirstByteMask [ (0x3) |
331 | FirstByte3 (0x3) |
332 | ] |
333 | SecondByteMask [ (0x30) |
334 | SecondByte3 (0x30) |
335 | ] |
336 | ValueOutsideMask [ (0x1) |
337 | FirstByte1 (0x1) |
338 | ] |
339 | FirstSecondByteMask [ (0xFF) |
340 | ] |
341 | FirstSecondThirdByteMask [ (0x333) |
342 | FirstByte3 (0x3) |
343 | SecondByte3 (0x30) |
344 | ThirdByte3 (0x300) |
345 | ] |
346 | )" ; |
347 | |
348 | const char *JSONExpectedOut = R"({ |
349 | "ZeroFlag": { |
350 | "Value": 0, |
351 | "Flags": [] |
352 | }, |
353 | "NoFlag": { |
354 | "Value": 8, |
355 | "Flags": [] |
356 | }, |
357 | "Flag1": { |
358 | "Value": 1, |
359 | "Flags": [ |
360 | { |
361 | "Name": "Name1", |
362 | "Value": 1 |
363 | } |
364 | ] |
365 | }, |
366 | "Flag1&3": { |
367 | "Value": 5, |
368 | "Flags": [ |
369 | { |
370 | "Name": "Name1", |
371 | "Value": 1 |
372 | }, |
373 | { |
374 | "Name": "Name3", |
375 | "Value": 4 |
376 | } |
377 | ] |
378 | }, |
379 | "ZeroFlagRaw": { |
380 | "Value": 0, |
381 | "Flags": [] |
382 | }, |
383 | "NoFlagRaw": { |
384 | "Value": 8, |
385 | "Flags": [ |
386 | 8 |
387 | ] |
388 | }, |
389 | "Flag1Raw": { |
390 | "Value": 1, |
391 | "Flags": [ |
392 | 1 |
393 | ] |
394 | }, |
395 | "Flag1&3Raw": { |
396 | "Value": 5, |
397 | "Flags": [ |
398 | 1, |
399 | 4 |
400 | ] |
401 | }, |
402 | "FlagSorted": { |
403 | "Value": 7, |
404 | "Flags": [ |
405 | { |
406 | "Name": "A", |
407 | "Value": 4 |
408 | }, |
409 | { |
410 | "Name": "B", |
411 | "Value": 2 |
412 | }, |
413 | { |
414 | "Name": "C", |
415 | "Value": 1 |
416 | } |
417 | ] |
418 | }, |
419 | "NoBitMask": { |
420 | "Value": 4095, |
421 | "Flags": [ |
422 | { |
423 | "Name": "FirstByte1", |
424 | "Value": 1 |
425 | }, |
426 | { |
427 | "Name": "FirstByte2", |
428 | "Value": 2 |
429 | }, |
430 | { |
431 | "Name": "FirstByte3", |
432 | "Value": 3 |
433 | }, |
434 | { |
435 | "Name": "SecondByte1", |
436 | "Value": 16 |
437 | }, |
438 | { |
439 | "Name": "SecondByte2", |
440 | "Value": 32 |
441 | }, |
442 | { |
443 | "Name": "SecondByte3", |
444 | "Value": 48 |
445 | }, |
446 | { |
447 | "Name": "ThirdByte1", |
448 | "Value": 256 |
449 | }, |
450 | { |
451 | "Name": "ThirdByte2", |
452 | "Value": 512 |
453 | }, |
454 | { |
455 | "Name": "ThirdByte3", |
456 | "Value": 768 |
457 | } |
458 | ] |
459 | }, |
460 | "FirstByteMask": { |
461 | "Value": 3, |
462 | "Flags": [ |
463 | { |
464 | "Name": "FirstByte3", |
465 | "Value": 3 |
466 | } |
467 | ] |
468 | }, |
469 | "SecondByteMask": { |
470 | "Value": 48, |
471 | "Flags": [ |
472 | { |
473 | "Name": "SecondByte3", |
474 | "Value": 48 |
475 | } |
476 | ] |
477 | }, |
478 | "ValueOutsideMask": { |
479 | "Value": 1, |
480 | "Flags": [ |
481 | { |
482 | "Name": "FirstByte1", |
483 | "Value": 1 |
484 | } |
485 | ] |
486 | }, |
487 | "FirstSecondByteMask": { |
488 | "Value": 255, |
489 | "Flags": [] |
490 | }, |
491 | "FirstSecondThirdByteMask": { |
492 | "Value": 819, |
493 | "Flags": [ |
494 | { |
495 | "Name": "FirstByte3", |
496 | "Value": 3 |
497 | }, |
498 | { |
499 | "Name": "SecondByte3", |
500 | "Value": 48 |
501 | }, |
502 | { |
503 | "Name": "ThirdByte3", |
504 | "Value": 768 |
505 | } |
506 | ] |
507 | } |
508 | })" ; |
509 | verifyAll(ExpectedOut, JSONExpectedOut, Func: PrintFunc); |
510 | } |
511 | |
512 | // Format floats using the same format string as PrintNumber, so we can check |
513 | // the output on all platforms. |
514 | template <typename T, |
515 | std::enable_if_t<std::is_floating_point_v<T>, bool> = true> |
516 | std::string formatFloatString(T Val) { |
517 | std::string Ret; |
518 | raw_string_ostream OS(Ret); |
519 | OS << format("%5.1f" , Val); |
520 | return Ret; |
521 | } |
522 | |
523 | // Format floats using the same format string used in JSON, so we can check the |
524 | // output on all platforms. |
525 | template <typename T, |
526 | std::enable_if_t<std::is_floating_point_v<T>, bool> = true> |
527 | std::string formatJsonFloatString(T Val) { |
528 | std::string Ret; |
529 | raw_string_ostream OS(Ret); |
530 | OS << format("%.*g" , std::numeric_limits<double>::max_digits10, Val); |
531 | return Ret; |
532 | } |
533 | |
534 | TEST_F(ScopedPrinterTest, PrintNumber) { |
535 | constexpr float MaxFloat = std::numeric_limits<float>::max(); |
536 | constexpr float MinFloat = std::numeric_limits<float>::min(); |
537 | constexpr float InfFloat = std::numeric_limits<float>::infinity(); |
538 | const float NaNFloat = std::nanf(tagb: "1" ); |
539 | constexpr double MaxDouble = std::numeric_limits<double>::max(); |
540 | constexpr double MinDouble = std::numeric_limits<double>::min(); |
541 | constexpr double InfDouble = std::numeric_limits<double>::infinity(); |
542 | const double NaNDouble = std::nan(tagb: "1" ); |
543 | |
544 | auto PrintFunc = [&](ScopedPrinter &W) { |
545 | uint64_t Unsigned64Max = std::numeric_limits<uint64_t>::max(); |
546 | uint64_t Unsigned64Min = std::numeric_limits<uint64_t>::min(); |
547 | W.printNumber(Label: "uint64_t-max" , Value: Unsigned64Max); |
548 | W.printNumber(Label: "uint64_t-min" , Value: Unsigned64Min); |
549 | |
550 | uint32_t Unsigned32Max = std::numeric_limits<uint32_t>::max(); |
551 | uint32_t Unsigned32Min = std::numeric_limits<uint32_t>::min(); |
552 | W.printNumber(Label: "uint32_t-max" , Value: Unsigned32Max); |
553 | W.printNumber(Label: "uint32_t-min" , Value: Unsigned32Min); |
554 | |
555 | uint16_t Unsigned16Max = std::numeric_limits<uint16_t>::max(); |
556 | uint16_t Unsigned16Min = std::numeric_limits<uint16_t>::min(); |
557 | W.printNumber(Label: "uint16_t-max" , Value: Unsigned16Max); |
558 | W.printNumber(Label: "uint16_t-min" , Value: Unsigned16Min); |
559 | |
560 | uint8_t Unsigned8Max = std::numeric_limits<uint8_t>::max(); |
561 | uint8_t Unsigned8Min = std::numeric_limits<uint8_t>::min(); |
562 | W.printNumber(Label: "uint8_t-max" , Value: Unsigned8Max); |
563 | W.printNumber(Label: "uint8_t-min" , Value: Unsigned8Min); |
564 | |
565 | int64_t Signed64Max = std::numeric_limits<int64_t>::max(); |
566 | int64_t Signed64Min = std::numeric_limits<int64_t>::min(); |
567 | W.printNumber(Label: "int64_t-max" , Value: Signed64Max); |
568 | W.printNumber(Label: "int64_t-min" , Value: Signed64Min); |
569 | |
570 | int32_t Signed32Max = std::numeric_limits<int32_t>::max(); |
571 | int32_t Signed32Min = std::numeric_limits<int32_t>::min(); |
572 | W.printNumber(Label: "int32_t-max" , Value: Signed32Max); |
573 | W.printNumber(Label: "int32_t-min" , Value: Signed32Min); |
574 | |
575 | int16_t Signed16Max = std::numeric_limits<int16_t>::max(); |
576 | int16_t Signed16Min = std::numeric_limits<int16_t>::min(); |
577 | W.printNumber(Label: "int16_t-max" , Value: Signed16Max); |
578 | W.printNumber(Label: "int16_t-min" , Value: Signed16Min); |
579 | |
580 | int8_t Signed8Max = std::numeric_limits<int8_t>::max(); |
581 | int8_t Signed8Min = std::numeric_limits<int8_t>::min(); |
582 | W.printNumber(Label: "int8_t-max" , Value: Signed8Max); |
583 | W.printNumber(Label: "int8_t-min" , Value: Signed8Min); |
584 | |
585 | APSInt LargeNum("9999999999999999999999" ); |
586 | W.printNumber(Label: "apsint" , Value: LargeNum); |
587 | |
588 | W.printNumber(Label: "label" , Str: "value" , Value: 0); |
589 | |
590 | W.printNumber(Label: "float-max" , Value: MaxFloat); |
591 | W.printNumber(Label: "float-min" , Value: MinFloat); |
592 | W.printNumber(Label: "float-inf" , Value: InfFloat); |
593 | W.printNumber(Label: "float-nan" , Value: NaNFloat); |
594 | W.printNumber(Label: "float-42.0" , Value: 42.0f); |
595 | W.printNumber(Label: "float-42.5625" , Value: 42.5625f); |
596 | |
597 | W.printNumber(Label: "double-max" , Value: MaxDouble); |
598 | W.printNumber(Label: "double-min" , Value: MinDouble); |
599 | W.printNumber(Label: "double-inf" , Value: InfDouble); |
600 | W.printNumber(Label: "double-nan" , Value: NaNDouble); |
601 | W.printNumber(Label: "double-42.0" , Value: 42.0); |
602 | W.printNumber(Label: "double-42.5625" , Value: 42.5625); |
603 | }; |
604 | |
605 | std::string ExpectedOut = Twine( |
606 | R"(uint64_t-max: 18446744073709551615 |
607 | uint64_t-min: 0 |
608 | uint32_t-max: 4294967295 |
609 | uint32_t-min: 0 |
610 | uint16_t-max: 65535 |
611 | uint16_t-min: 0 |
612 | uint8_t-max: 255 |
613 | uint8_t-min: 0 |
614 | int64_t-max: 9223372036854775807 |
615 | int64_t-min: -9223372036854775808 |
616 | int32_t-max: 2147483647 |
617 | int32_t-min: -2147483648 |
618 | int16_t-max: 32767 |
619 | int16_t-min: -32768 |
620 | int8_t-max: 127 |
621 | int8_t-min: -128 |
622 | apsint: 9999999999999999999999 |
623 | label: value (0) |
624 | float-max: )" + formatFloatString(Val: MaxFloat) + |
625 | R"( |
626 | float-min: 0.0 |
627 | float-inf: )" + formatFloatString(Val: InfFloat) + |
628 | R"( |
629 | float-nan: )" + formatFloatString(Val: NaNFloat) + |
630 | R"( |
631 | float-42.0: 42.0 |
632 | float-42.5625: 42.6 |
633 | double-max: )" + formatFloatString(Val: MaxDouble) + |
634 | R"( |
635 | double-min: 0.0 |
636 | double-inf: )" + formatFloatString(Val: InfDouble) + |
637 | R"( |
638 | double-nan: )" + formatFloatString(Val: NaNDouble) + |
639 | R"( |
640 | double-42.0: 42.0 |
641 | double-42.5625: 42.6 |
642 | )" ) |
643 | .str(); |
644 | |
645 | std::string JSONExpectedOut = Twine(R"({ |
646 | "uint64_t-max": 18446744073709551615, |
647 | "uint64_t-min": 0, |
648 | "uint32_t-max": 4294967295, |
649 | "uint32_t-min": 0, |
650 | "uint16_t-max": 65535, |
651 | "uint16_t-min": 0, |
652 | "uint8_t-max": 255, |
653 | "uint8_t-min": 0, |
654 | "int64_t-max": 9223372036854775807, |
655 | "int64_t-min": -9223372036854775808, |
656 | "int32_t-max": 2147483647, |
657 | "int32_t-min": -2147483648, |
658 | "int16_t-max": 32767, |
659 | "int16_t-min": -32768, |
660 | "int8_t-max": 127, |
661 | "int8_t-min": -128, |
662 | "apsint": 9999999999999999999999, |
663 | "label": { |
664 | "Name": "value", |
665 | "Value": 0 |
666 | }, |
667 | "float-max": 3.4028234663852886e+38, |
668 | "float-min": 1.1754943508222875e-38, |
669 | "float-inf": )" + formatJsonFloatString(Val: InfFloat) + |
670 | R"(, |
671 | "float-nan": )" + formatJsonFloatString(Val: NaNFloat) + |
672 | R"(, |
673 | "float-42.0": 42, |
674 | "float-42.5625": 42.5625, |
675 | "double-max": 1.7976931348623157e+308, |
676 | "double-min": 2.2250738585072014e-308, |
677 | "double-inf": )" + formatJsonFloatString(Val: InfDouble) + |
678 | R"(, |
679 | "double-nan": )" + formatJsonFloatString(Val: NaNDouble) + |
680 | R"(, |
681 | "double-42.0": 42, |
682 | "double-42.5625": 42.5625 |
683 | })" ) |
684 | .str(); |
685 | verifyAll(ExpectedOut, JSONExpectedOut, Func: PrintFunc); |
686 | } |
687 | |
688 | TEST_F(ScopedPrinterTest, PrintBoolean) { |
689 | auto PrintFunc = [](ScopedPrinter &W) { |
690 | W.printBoolean(Label: "True" , Value: true); |
691 | W.printBoolean(Label: "False" , Value: false); |
692 | }; |
693 | |
694 | const char *ExpectedOut = R"(True: Yes |
695 | False: No |
696 | )" ; |
697 | |
698 | const char *JSONExpectedOut = R"({ |
699 | "True": true, |
700 | "False": false |
701 | })" ; |
702 | verifyAll(ExpectedOut, JSONExpectedOut, Func: PrintFunc); |
703 | } |
704 | |
705 | TEST_F(ScopedPrinterTest, PrintVersion) { |
706 | auto PrintFunc = [](ScopedPrinter &W) { |
707 | W.printVersion(Label: "Version" , Version: "123" , Version: "456" , Version: "789" ); |
708 | }; |
709 | const char *ExpectedOut = R"(Version: 123.456.789 |
710 | )" ; |
711 | verifyScopedPrinter(Expected: ExpectedOut, Func: PrintFunc); |
712 | } |
713 | |
714 | TEST_F(ScopedPrinterTest, PrintList) { |
715 | auto PrintFunc = [](ScopedPrinter &W) { |
716 | const std::vector<uint64_t> EmptyList; |
717 | const std::vector<std::string> StringList = {"foo" , "bar" , "baz" }; |
718 | const bool BoolList[] = {true, false}; |
719 | const std::vector<uint64_t> Unsigned64List = { |
720 | std::numeric_limits<uint64_t>::max(), |
721 | std::numeric_limits<uint64_t>::min()}; |
722 | const std::vector<uint32_t> Unsigned32List = { |
723 | std::numeric_limits<uint32_t>::max(), |
724 | std::numeric_limits<uint32_t>::min()}; |
725 | const std::vector<uint16_t> Unsigned16List = { |
726 | std::numeric_limits<uint16_t>::max(), |
727 | std::numeric_limits<uint16_t>::min()}; |
728 | const std::vector<uint8_t> Unsigned8List = { |
729 | std::numeric_limits<uint8_t>::max(), |
730 | std::numeric_limits<uint8_t>::min()}; |
731 | const std::vector<int64_t> Signed64List = { |
732 | std::numeric_limits<int64_t>::max(), |
733 | std::numeric_limits<int64_t>::min()}; |
734 | const std::vector<int32_t> Signed32List = { |
735 | std::numeric_limits<int32_t>::max(), |
736 | std::numeric_limits<int32_t>::min()}; |
737 | const std::vector<int16_t> Signed16List = { |
738 | std::numeric_limits<int16_t>::max(), |
739 | std::numeric_limits<int16_t>::min()}; |
740 | const std::vector<int8_t> Signed8List = { |
741 | std::numeric_limits<int8_t>::max(), std::numeric_limits<int8_t>::min()}; |
742 | const std::vector<APSInt> APSIntList = {APSInt("9999999999999999999999" ), |
743 | APSInt("-9999999999999999999999" )}; |
744 | W.printList(Label: "EmptyList" , List: EmptyList); |
745 | W.printList(Label: "StringList" , List: StringList); |
746 | W.printList(Label: "BoolList" , List: ArrayRef(BoolList)); |
747 | W.printList(Label: "uint64List" , List: Unsigned64List); |
748 | W.printList(Label: "uint32List" , List: Unsigned32List); |
749 | W.printList(Label: "uint16List" , List: Unsigned16List); |
750 | W.printList(Label: "uint8List" , List: Unsigned8List); |
751 | W.printList(Label: "int64List" , List: Signed64List); |
752 | W.printList(Label: "int32List" , List: Signed32List); |
753 | W.printList(Label: "int16List" , List: Signed16List); |
754 | W.printList(Label: "int8List" , List: Signed8List); |
755 | W.printList(Label: "APSIntList" , List: APSIntList); |
756 | }; |
757 | |
758 | const char *ExpectedOut = R"(EmptyList: [] |
759 | StringList: [foo, bar, baz] |
760 | BoolList: [1, 0] |
761 | uint64List: [18446744073709551615, 0] |
762 | uint32List: [4294967295, 0] |
763 | uint16List: [65535, 0] |
764 | uint8List: [255, 0] |
765 | int64List: [9223372036854775807, -9223372036854775808] |
766 | int32List: [2147483647, -2147483648] |
767 | int16List: [32767, -32768] |
768 | int8List: [127, -128] |
769 | APSIntList: [9999999999999999999999, -9999999999999999999999] |
770 | )" ; |
771 | |
772 | const char *JSONExpectedOut = R"({ |
773 | "EmptyList": [], |
774 | "StringList": [ |
775 | "foo", |
776 | "bar", |
777 | "baz" |
778 | ], |
779 | "BoolList": [ |
780 | true, |
781 | false |
782 | ], |
783 | "uint64List": [ |
784 | 18446744073709551615, |
785 | 0 |
786 | ], |
787 | "uint32List": [ |
788 | 4294967295, |
789 | 0 |
790 | ], |
791 | "uint16List": [ |
792 | 65535, |
793 | 0 |
794 | ], |
795 | "uint8List": [ |
796 | 255, |
797 | 0 |
798 | ], |
799 | "int64List": [ |
800 | 9223372036854775807, |
801 | -9223372036854775808 |
802 | ], |
803 | "int32List": [ |
804 | 2147483647, |
805 | -2147483648 |
806 | ], |
807 | "int16List": [ |
808 | 32767, |
809 | -32768 |
810 | ], |
811 | "int8List": [ |
812 | 127, |
813 | -128 |
814 | ], |
815 | "APSIntList": [ |
816 | 9999999999999999999999, |
817 | -9999999999999999999999 |
818 | ] |
819 | })" ; |
820 | verifyAll(ExpectedOut, JSONExpectedOut, Func: PrintFunc); |
821 | } |
822 | |
823 | TEST_F(ScopedPrinterTest, PrintListPrinter) { |
824 | auto PrintFunc = [](ScopedPrinter &W) { |
825 | const std::string StringList[] = {"a" , "ab" , "abc" }; |
826 | W.printList(Label: "StringSizeList" , List: StringList, |
827 | Printer: [](raw_ostream &OS, StringRef Item) { OS << Item.size(); }); |
828 | }; |
829 | |
830 | const char *ExpectedOut = R"(StringSizeList: [1, 2, 3] |
831 | )" ; |
832 | verifyScopedPrinter(Expected: ExpectedOut, Func: PrintFunc); |
833 | } |
834 | |
835 | TEST_F(ScopedPrinterTest, PrintHex) { |
836 | auto PrintFunc = [](ScopedPrinter &W) { |
837 | W.printHex(Label: "HexNumber" , Value: 0x10); |
838 | W.printHex(Label: "HexLabel" , Str: "Name" , Value: 0x10); |
839 | }; |
840 | |
841 | const char *ExpectedOut = R"(HexNumber: 0x10 |
842 | HexLabel: Name (0x10) |
843 | )" ; |
844 | |
845 | const char *JSONExpectedOut = R"({ |
846 | "HexNumber": 16, |
847 | "HexLabel": { |
848 | "Name": "Name", |
849 | "Value": 16 |
850 | } |
851 | })" ; |
852 | verifyAll(ExpectedOut, JSONExpectedOut, Func: PrintFunc); |
853 | } |
854 | |
855 | TEST_F(ScopedPrinterTest, PrintHexList) { |
856 | auto PrintFunc = [](ScopedPrinter &W) { |
857 | const uint64_t HexList[] = {0x1, 0x10, 0x100}; |
858 | W.printHexList(Label: "HexList" , List: HexList); |
859 | }; |
860 | const char *ExpectedOut = R"(HexList: [0x1, 0x10, 0x100] |
861 | )" ; |
862 | |
863 | const char *JSONExpectedOut = R"({ |
864 | "HexList": [ |
865 | 1, |
866 | 16, |
867 | 256 |
868 | ] |
869 | })" ; |
870 | verifyAll(ExpectedOut, JSONExpectedOut, Func: PrintFunc); |
871 | } |
872 | |
873 | TEST_F(ScopedPrinterTest, PrintSymbolOffset) { |
874 | auto PrintFunc = [](ScopedPrinter &W) { |
875 | W.printSymbolOffset(Label: "SymbolOffset" , Symbol: "SymbolName" , Value: 0x10); |
876 | W.printSymbolOffset(Label: "NoSymbolOffset" , Symbol: "SymbolName" , Value: 0); |
877 | }; |
878 | const char *ExpectedOut = R"(SymbolOffset: SymbolName+0x10 |
879 | NoSymbolOffset: SymbolName+0x0 |
880 | )" ; |
881 | |
882 | const char *JSONExpectedOut = R"({ |
883 | "SymbolOffset": { |
884 | "SymName": "SymbolName", |
885 | "Offset": 16 |
886 | }, |
887 | "NoSymbolOffset": { |
888 | "SymName": "SymbolName", |
889 | "Offset": 0 |
890 | } |
891 | })" ; |
892 | verifyAll(ExpectedOut, JSONExpectedOut, Func: PrintFunc); |
893 | } |
894 | |
895 | TEST_F(ScopedPrinterTest, PrintString) { |
896 | auto PrintFunc = [](ScopedPrinter &W) { |
897 | const StringRef StringRefValue("Value" ); |
898 | const std::string StringValue = "Value" ; |
899 | const char *CharArrayValue = "Value" ; |
900 | W.printString(Label: "StringRef" , Value: StringRefValue); |
901 | W.printString(Label: "String" , Value: StringValue); |
902 | W.printString(Label: "CharArray" , Value: CharArrayValue); |
903 | ListScope L(W, "StringList" ); |
904 | W.printString(Value: StringRefValue); |
905 | }; |
906 | |
907 | const char *ExpectedOut = R"(StringRef: Value |
908 | String: Value |
909 | CharArray: Value |
910 | StringList [ |
911 | Value |
912 | ] |
913 | )" ; |
914 | |
915 | const char *JSONExpectedOut = R"({ |
916 | "StringRef": "Value", |
917 | "String": "Value", |
918 | "CharArray": "Value", |
919 | "StringList": [ |
920 | "Value" |
921 | ] |
922 | })" ; |
923 | verifyAll(ExpectedOut, JSONExpectedOut, Func: PrintFunc); |
924 | } |
925 | |
926 | TEST_F(ScopedPrinterTest, PrintBinary) { |
927 | auto PrintFunc = [](ScopedPrinter &W) { |
928 | std::vector<uint8_t> IntArray = {70, 111, 111, 66, 97, 114}; |
929 | std::vector<char> CharArray = {'F', 'o', 'o', 'B', 'a', 'r'}; |
930 | std::vector<uint8_t> InvalidChars = {255, 255}; |
931 | W.printBinary(Label: "Binary1" , Str: "FooBar" , Value: IntArray); |
932 | W.printBinary(Label: "Binary2" , Str: "FooBar" , Value: CharArray); |
933 | W.printBinary(Label: "Binary3" , Value: IntArray); |
934 | W.printBinary(Label: "Binary4" , Value: CharArray); |
935 | W.printBinary(Label: "Binary5" , Value: StringRef("FooBar" )); |
936 | W.printBinary(Label: "Binary6" , Value: StringRef("Multiple Line FooBar" )); |
937 | W.printBinaryBlock(Label: "Binary7" , Value: IntArray, StartOffset: 20); |
938 | W.printBinaryBlock(Label: "Binary8" , Value: IntArray); |
939 | W.printBinaryBlock(Label: "Binary9" , Value: "FooBar" ); |
940 | W.printBinaryBlock(Label: "Binary10" , Value: "Multiple Line FooBar" ); |
941 | W.printBinaryBlock(Label: "Binary11" , Value: InvalidChars); |
942 | }; |
943 | |
944 | const char *ExpectedOut = R"(Binary1: FooBar (46 6F 6F 42 61 72) |
945 | Binary2: FooBar (46 6F 6F 42 61 72) |
946 | Binary3: (46 6F 6F 42 61 72) |
947 | Binary4: (46 6F 6F 42 61 72) |
948 | Binary5: (46 6F 6F 42 61 72) |
949 | Binary6 ( |
950 | 0000: 4D756C74 69706C65 204C696E 6520466F |Multiple Line Fo| |
951 | 0010: 6F426172 |oBar| |
952 | ) |
953 | Binary7 ( |
954 | 0014: 466F6F42 6172 |FooBar| |
955 | ) |
956 | Binary8 ( |
957 | 0000: 466F6F42 6172 |FooBar| |
958 | ) |
959 | Binary9 ( |
960 | 0000: 466F6F42 6172 |FooBar| |
961 | ) |
962 | Binary10 ( |
963 | 0000: 4D756C74 69706C65 204C696E 6520466F |Multiple Line Fo| |
964 | 0010: 6F426172 |oBar| |
965 | ) |
966 | Binary11 ( |
967 | 0000: FFFF |..| |
968 | ) |
969 | )" ; |
970 | |
971 | const char *JSONExpectedOut = R"({ |
972 | "Binary1": { |
973 | "Value": "FooBar", |
974 | "Offset": 0, |
975 | "Bytes": [ |
976 | 70, |
977 | 111, |
978 | 111, |
979 | 66, |
980 | 97, |
981 | 114 |
982 | ] |
983 | }, |
984 | "Binary2": { |
985 | "Value": "FooBar", |
986 | "Offset": 0, |
987 | "Bytes": [ |
988 | 70, |
989 | 111, |
990 | 111, |
991 | 66, |
992 | 97, |
993 | 114 |
994 | ] |
995 | }, |
996 | "Binary3": { |
997 | "Offset": 0, |
998 | "Bytes": [ |
999 | 70, |
1000 | 111, |
1001 | 111, |
1002 | 66, |
1003 | 97, |
1004 | 114 |
1005 | ] |
1006 | }, |
1007 | "Binary4": { |
1008 | "Offset": 0, |
1009 | "Bytes": [ |
1010 | 70, |
1011 | 111, |
1012 | 111, |
1013 | 66, |
1014 | 97, |
1015 | 114 |
1016 | ] |
1017 | }, |
1018 | "Binary5": { |
1019 | "Offset": 0, |
1020 | "Bytes": [ |
1021 | 70, |
1022 | 111, |
1023 | 111, |
1024 | 66, |
1025 | 97, |
1026 | 114 |
1027 | ] |
1028 | }, |
1029 | "Binary6": { |
1030 | "Offset": 0, |
1031 | "Bytes": [ |
1032 | 77, |
1033 | 117, |
1034 | 108, |
1035 | 116, |
1036 | 105, |
1037 | 112, |
1038 | 108, |
1039 | 101, |
1040 | 32, |
1041 | 76, |
1042 | 105, |
1043 | 110, |
1044 | 101, |
1045 | 32, |
1046 | 70, |
1047 | 111, |
1048 | 111, |
1049 | 66, |
1050 | 97, |
1051 | 114 |
1052 | ] |
1053 | }, |
1054 | "Binary7": { |
1055 | "Offset": 20, |
1056 | "Bytes": [ |
1057 | 70, |
1058 | 111, |
1059 | 111, |
1060 | 66, |
1061 | 97, |
1062 | 114 |
1063 | ] |
1064 | }, |
1065 | "Binary8": { |
1066 | "Offset": 0, |
1067 | "Bytes": [ |
1068 | 70, |
1069 | 111, |
1070 | 111, |
1071 | 66, |
1072 | 97, |
1073 | 114 |
1074 | ] |
1075 | }, |
1076 | "Binary9": { |
1077 | "Offset": 0, |
1078 | "Bytes": [ |
1079 | 70, |
1080 | 111, |
1081 | 111, |
1082 | 66, |
1083 | 97, |
1084 | 114 |
1085 | ] |
1086 | }, |
1087 | "Binary10": { |
1088 | "Offset": 0, |
1089 | "Bytes": [ |
1090 | 77, |
1091 | 117, |
1092 | 108, |
1093 | 116, |
1094 | 105, |
1095 | 112, |
1096 | 108, |
1097 | 101, |
1098 | 32, |
1099 | 76, |
1100 | 105, |
1101 | 110, |
1102 | 101, |
1103 | 32, |
1104 | 70, |
1105 | 111, |
1106 | 111, |
1107 | 66, |
1108 | 97, |
1109 | 114 |
1110 | ] |
1111 | }, |
1112 | "Binary11": { |
1113 | "Offset": 0, |
1114 | "Bytes": [ |
1115 | 255, |
1116 | 255 |
1117 | ] |
1118 | } |
1119 | })" ; |
1120 | verifyAll(ExpectedOut, JSONExpectedOut, Func: PrintFunc); |
1121 | } |
1122 | |
1123 | TEST_F(ScopedPrinterTest, PrintObject) { |
1124 | auto PrintFunc = [](ScopedPrinter &W) { W.printObject(Label: "Object" , Value: "Value" ); }; |
1125 | |
1126 | const char *ExpectedOut = R"(Object: Value |
1127 | )" ; |
1128 | |
1129 | const char *JSONExpectedOut = R"({ |
1130 | "Object": "Value" |
1131 | })" ; |
1132 | verifyAll(ExpectedOut, JSONExpectedOut, Func: PrintFunc); |
1133 | } |
1134 | |
1135 | TEST_F(ScopedPrinterTest, StartLine) { |
1136 | auto PrintFunc = [](ScopedPrinter &W) { |
1137 | W.startLine() << "|" ; |
1138 | W.indent(Levels: 2); |
1139 | W.startLine() << "|" ; |
1140 | W.unindent(); |
1141 | W.startLine() << "|" ; |
1142 | }; |
1143 | |
1144 | const char *ExpectedOut = "| | |" ; |
1145 | verifyScopedPrinter(Expected: ExpectedOut, Func: PrintFunc); |
1146 | } |
1147 | |
1148 | TEST_F(ScopedPrinterTest, GetOStream) { |
1149 | auto PrintFunc = [](ScopedPrinter &W) { W.getOStream() << "Test" ; }; |
1150 | |
1151 | const char *ExpectedOut = "Test" ; |
1152 | verifyScopedPrinter(Expected: ExpectedOut, Func: PrintFunc); |
1153 | } |
1154 | |
1155 | TEST_F(ScopedPrinterTest, PrintScope) { |
1156 | auto PrintFunc = [](ScopedPrinter &W) { |
1157 | { |
1158 | DictScope O(W, "Object" ); |
1159 | { DictScope OO(W, "ObjectInObject" ); } |
1160 | { ListScope LO(W, "ListInObject" ); } |
1161 | } |
1162 | { |
1163 | ListScope L(W, "List" ); |
1164 | { DictScope OL(W, "ObjectInList" ); } |
1165 | { ListScope LL(W, "ListInList" ); } |
1166 | } |
1167 | }; |
1168 | |
1169 | const char *ExpectedOut = R"(Object { |
1170 | ObjectInObject { |
1171 | } |
1172 | ListInObject [ |
1173 | ] |
1174 | } |
1175 | List [ |
1176 | ObjectInList { |
1177 | } |
1178 | ListInList [ |
1179 | ] |
1180 | ] |
1181 | )" ; |
1182 | |
1183 | const char *JSONExpectedOut = R"({ |
1184 | "Object": { |
1185 | "ObjectInObject": {}, |
1186 | "ListInObject": [] |
1187 | }, |
1188 | "List": [ |
1189 | { |
1190 | "ObjectInList": {} |
1191 | }, |
1192 | { |
1193 | "ListInList": [] |
1194 | } |
1195 | ] |
1196 | })" ; |
1197 | verifyAll(ExpectedOut, JSONExpectedOut, Func: PrintFunc); |
1198 | } |
1199 | |