1 | //===- unittest/Support/YAMLIOTest.cpp ------------------------------------===// |
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/ADT/BitmaskEnum.h" |
10 | #include "llvm/ADT/StringMap.h" |
11 | #include "llvm/ADT/StringRef.h" |
12 | #include "llvm/ADT/StringSwitch.h" |
13 | #include "llvm/ADT/Twine.h" |
14 | #include "llvm/Support/Casting.h" |
15 | #include "llvm/Support/Endian.h" |
16 | #include "llvm/Support/Format.h" |
17 | #include "llvm/Support/YAMLTraits.h" |
18 | #include "gmock/gmock.h" |
19 | #include "gtest/gtest.h" |
20 | |
21 | using llvm::yaml::Hex16; |
22 | using llvm::yaml::Hex32; |
23 | using llvm::yaml::Hex64; |
24 | using llvm::yaml::Hex8; |
25 | using llvm::yaml::Input; |
26 | using llvm::yaml::isNumeric; |
27 | using llvm::yaml::MappingNormalization; |
28 | using llvm::yaml::MappingTraits; |
29 | using llvm::yaml::Output; |
30 | using llvm::yaml::ScalarTraits; |
31 | using ::testing::StartsWith; |
32 | |
33 | |
34 | |
35 | |
36 | static void suppressErrorMessages(const llvm::SMDiagnostic &, void *) { |
37 | } |
38 | |
39 | |
40 | |
41 | //===----------------------------------------------------------------------===// |
42 | // Test MappingTraits |
43 | //===----------------------------------------------------------------------===// |
44 | |
45 | struct FooBar { |
46 | int foo; |
47 | int bar; |
48 | }; |
49 | typedef std::vector<FooBar> FooBarSequence; |
50 | |
51 | LLVM_YAML_IS_SEQUENCE_VECTOR(FooBar) |
52 | |
53 | struct FooBarContainer { |
54 | FooBarSequence fbs; |
55 | }; |
56 | |
57 | namespace llvm { |
58 | namespace yaml { |
59 | template <> |
60 | struct MappingTraits<FooBar> { |
61 | static void mapping(IO &io, FooBar& fb) { |
62 | io.mapRequired(Key: "foo" , Val&: fb.foo); |
63 | io.mapRequired(Key: "bar" , Val&: fb.bar); |
64 | } |
65 | }; |
66 | |
67 | template <> struct MappingTraits<FooBarContainer> { |
68 | static void mapping(IO &io, FooBarContainer &fb) { |
69 | io.mapRequired(Key: "fbs" , Val&: fb.fbs); |
70 | } |
71 | }; |
72 | } |
73 | } |
74 | |
75 | |
76 | // |
77 | // Test the reading of a yaml mapping |
78 | // |
79 | TEST(YAMLIO, TestMapRead) { |
80 | FooBar doc; |
81 | { |
82 | Input yin("---\nfoo: 3\nbar: 5\n...\n" ); |
83 | yin >> doc; |
84 | |
85 | EXPECT_FALSE(yin.error()); |
86 | EXPECT_EQ(doc.foo, 3); |
87 | EXPECT_EQ(doc.bar, 5); |
88 | } |
89 | |
90 | { |
91 | Input yin("{foo: 3, bar: 5}" ); |
92 | yin >> doc; |
93 | |
94 | EXPECT_FALSE(yin.error()); |
95 | EXPECT_EQ(doc.foo, 3); |
96 | EXPECT_EQ(doc.bar, 5); |
97 | } |
98 | |
99 | { |
100 | Input yin("{\"foo\": 3\n, \"bar\": 5}" ); |
101 | yin >> doc; |
102 | |
103 | EXPECT_FALSE(yin.error()); |
104 | EXPECT_EQ(doc.foo, 3); |
105 | EXPECT_EQ(doc.bar, 5); |
106 | } |
107 | } |
108 | |
109 | TEST(YAMLIO, TestMalformedMapRead) { |
110 | FooBar doc; |
111 | Input yin("{foo: 3; bar: 5}" , nullptr, suppressErrorMessages); |
112 | yin >> doc; |
113 | EXPECT_TRUE(!!yin.error()); |
114 | } |
115 | |
116 | TEST(YAMLIO, TestMapDuplicatedKeysRead) { |
117 | auto testDiagnostic = [](const llvm::SMDiagnostic &Error, void *) { |
118 | EXPECT_EQ(Error.getMessage(), "duplicated mapping key 'foo'" ); |
119 | }; |
120 | FooBar doc; |
121 | Input yin("{foo: 3, bar: 5, foo: 4}" , nullptr, testDiagnostic); |
122 | yin >> doc; |
123 | EXPECT_TRUE(!!yin.error()); |
124 | } |
125 | |
126 | // |
127 | // Test the reading of a yaml sequence of mappings |
128 | // |
129 | TEST(YAMLIO, TestSequenceMapRead) { |
130 | FooBarSequence seq; |
131 | Input yin("---\n - foo: 3\n bar: 5\n - foo: 7\n bar: 9\n...\n" ); |
132 | yin >> seq; |
133 | |
134 | EXPECT_FALSE(yin.error()); |
135 | EXPECT_EQ(seq.size(), 2UL); |
136 | FooBar& map1 = seq[0]; |
137 | FooBar& map2 = seq[1]; |
138 | EXPECT_EQ(map1.foo, 3); |
139 | EXPECT_EQ(map1.bar, 5); |
140 | EXPECT_EQ(map2.foo, 7); |
141 | EXPECT_EQ(map2.bar, 9); |
142 | } |
143 | |
144 | // |
145 | // Test the reading of a map containing a yaml sequence of mappings |
146 | // |
147 | TEST(YAMLIO, TestContainerSequenceMapRead) { |
148 | { |
149 | FooBarContainer cont; |
150 | Input yin2("---\nfbs:\n - foo: 3\n bar: 5\n - foo: 7\n bar: 9\n...\n" ); |
151 | yin2 >> cont; |
152 | |
153 | EXPECT_FALSE(yin2.error()); |
154 | EXPECT_EQ(cont.fbs.size(), 2UL); |
155 | EXPECT_EQ(cont.fbs[0].foo, 3); |
156 | EXPECT_EQ(cont.fbs[0].bar, 5); |
157 | EXPECT_EQ(cont.fbs[1].foo, 7); |
158 | EXPECT_EQ(cont.fbs[1].bar, 9); |
159 | } |
160 | |
161 | { |
162 | FooBarContainer cont; |
163 | Input yin("---\nfbs:\n...\n" ); |
164 | yin >> cont; |
165 | // Okay: Empty node represents an empty array. |
166 | EXPECT_FALSE(yin.error()); |
167 | EXPECT_EQ(cont.fbs.size(), 0UL); |
168 | } |
169 | |
170 | { |
171 | FooBarContainer cont; |
172 | Input yin("---\nfbs: !!null null\n...\n" ); |
173 | yin >> cont; |
174 | // Okay: null represents an empty array. |
175 | EXPECT_FALSE(yin.error()); |
176 | EXPECT_EQ(cont.fbs.size(), 0UL); |
177 | } |
178 | |
179 | { |
180 | FooBarContainer cont; |
181 | Input yin("---\nfbs: ~\n...\n" ); |
182 | yin >> cont; |
183 | // Okay: null represents an empty array. |
184 | EXPECT_FALSE(yin.error()); |
185 | EXPECT_EQ(cont.fbs.size(), 0UL); |
186 | } |
187 | |
188 | { |
189 | FooBarContainer cont; |
190 | Input yin("---\nfbs: null\n...\n" ); |
191 | yin >> cont; |
192 | // Okay: null represents an empty array. |
193 | EXPECT_FALSE(yin.error()); |
194 | EXPECT_EQ(cont.fbs.size(), 0UL); |
195 | } |
196 | } |
197 | |
198 | // |
199 | // Test the reading of a map containing a malformed yaml sequence |
200 | // |
201 | TEST(YAMLIO, TestMalformedContainerSequenceMapRead) { |
202 | { |
203 | FooBarContainer cont; |
204 | Input yin("---\nfbs:\n foo: 3\n bar: 5\n...\n" , nullptr, |
205 | suppressErrorMessages); |
206 | yin >> cont; |
207 | // Error: fbs is not a sequence. |
208 | EXPECT_TRUE(!!yin.error()); |
209 | EXPECT_EQ(cont.fbs.size(), 0UL); |
210 | } |
211 | |
212 | { |
213 | FooBarContainer cont; |
214 | Input yin("---\nfbs: 'scalar'\n...\n" , nullptr, suppressErrorMessages); |
215 | yin >> cont; |
216 | // This should be an error. |
217 | EXPECT_TRUE(!!yin.error()); |
218 | EXPECT_EQ(cont.fbs.size(), 0UL); |
219 | } |
220 | } |
221 | |
222 | // |
223 | // Test writing then reading back a sequence of mappings |
224 | // |
225 | TEST(YAMLIO, TestSequenceMapWriteAndRead) { |
226 | std::string intermediate; |
227 | { |
228 | FooBar entry1; |
229 | entry1.foo = 10; |
230 | entry1.bar = -3; |
231 | FooBar entry2; |
232 | entry2.foo = 257; |
233 | entry2.bar = 0; |
234 | FooBarSequence seq; |
235 | seq.push_back(x: entry1); |
236 | seq.push_back(x: entry2); |
237 | |
238 | llvm::raw_string_ostream ostr(intermediate); |
239 | Output yout(ostr); |
240 | yout << seq; |
241 | } |
242 | |
243 | { |
244 | Input yin(intermediate); |
245 | FooBarSequence seq2; |
246 | yin >> seq2; |
247 | |
248 | EXPECT_FALSE(yin.error()); |
249 | EXPECT_EQ(seq2.size(), 2UL); |
250 | FooBar& map1 = seq2[0]; |
251 | FooBar& map2 = seq2[1]; |
252 | EXPECT_EQ(map1.foo, 10); |
253 | EXPECT_EQ(map1.bar, -3); |
254 | EXPECT_EQ(map2.foo, 257); |
255 | EXPECT_EQ(map2.bar, 0); |
256 | } |
257 | } |
258 | |
259 | // |
260 | // Test reading the entire struct as an enum. |
261 | // |
262 | |
263 | struct FooBarEnum { |
264 | int Foo; |
265 | int Bar; |
266 | bool operator==(const FooBarEnum &R) const { |
267 | return Foo == R.Foo && Bar == R.Bar; |
268 | } |
269 | }; |
270 | |
271 | namespace llvm { |
272 | namespace yaml { |
273 | template <> struct MappingTraits<FooBarEnum> { |
274 | static void enumInput(IO &io, FooBarEnum &Val) { |
275 | io.enumCase(Val, Str: "OnlyFoo" , ConstVal: FooBarEnum({.Foo: 1, .Bar: 0})); |
276 | io.enumCase(Val, Str: "OnlyBar" , ConstVal: FooBarEnum({.Foo: 0, .Bar: 1})); |
277 | } |
278 | static void mapping(IO &io, FooBarEnum &Val) { |
279 | io.mapOptional(Key: "Foo" , Val&: Val.Foo); |
280 | io.mapOptional(Key: "Bar" , Val&: Val.Bar); |
281 | } |
282 | }; |
283 | } // namespace yaml |
284 | } // namespace llvm |
285 | |
286 | TEST(YAMLIO, TestMapEnumRead) { |
287 | FooBarEnum Doc; |
288 | { |
289 | Input Yin("OnlyFoo" ); |
290 | Yin >> Doc; |
291 | EXPECT_FALSE(Yin.error()); |
292 | EXPECT_EQ(Doc.Foo, 1); |
293 | EXPECT_EQ(Doc.Bar, 0); |
294 | } |
295 | { |
296 | Input Yin("OnlyBar" ); |
297 | Yin >> Doc; |
298 | EXPECT_FALSE(Yin.error()); |
299 | EXPECT_EQ(Doc.Foo, 0); |
300 | EXPECT_EQ(Doc.Bar, 1); |
301 | } |
302 | { |
303 | Input Yin("{Foo: 3, Bar: 5}" ); |
304 | Yin >> Doc; |
305 | EXPECT_FALSE(Yin.error()); |
306 | EXPECT_EQ(Doc.Foo, 3); |
307 | EXPECT_EQ(Doc.Bar, 5); |
308 | } |
309 | } |
310 | |
311 | // |
312 | // Test YAML filename handling. |
313 | // |
314 | static void testErrorFilename(const llvm::SMDiagnostic &Error, void *) { |
315 | EXPECT_EQ(Error.getFilename(), "foo.yaml" ); |
316 | } |
317 | |
318 | TEST(YAMLIO, TestGivenFilename) { |
319 | auto Buffer = llvm::MemoryBuffer::getMemBuffer(InputData: "{ x: 42 }" , BufferName: "foo.yaml" ); |
320 | Input yin(*Buffer, nullptr, testErrorFilename); |
321 | FooBar Value; |
322 | yin >> Value; |
323 | |
324 | EXPECT_TRUE(!!yin.error()); |
325 | } |
326 | |
327 | struct WithStringField { |
328 | std::string str1; |
329 | std::string str2; |
330 | std::string str3; |
331 | }; |
332 | |
333 | namespace llvm { |
334 | namespace yaml { |
335 | template <> struct MappingTraits<WithStringField> { |
336 | static void mapping(IO &io, WithStringField &fb) { |
337 | io.mapRequired(Key: "str1" , Val&: fb.str1); |
338 | io.mapRequired(Key: "str2" , Val&: fb.str2); |
339 | io.mapRequired(Key: "str3" , Val&: fb.str3); |
340 | } |
341 | }; |
342 | } // namespace yaml |
343 | } // namespace llvm |
344 | |
345 | TEST(YAMLIO, MultilineStrings) { |
346 | WithStringField Original; |
347 | Original.str1 = "a multiline string\nfoobarbaz" ; |
348 | Original.str2 = "another one\rfoobarbaz" ; |
349 | Original.str3 = "a one-line string" ; |
350 | |
351 | std::string Serialized; |
352 | { |
353 | llvm::raw_string_ostream OS(Serialized); |
354 | Output YOut(OS); |
355 | YOut << Original; |
356 | } |
357 | auto Expected = "---\n" |
358 | "str1: \"a multiline string\\nfoobarbaz\"\n" |
359 | "str2: \"another one\\rfoobarbaz\"\n" |
360 | "str3: a one-line string\n" |
361 | "...\n" ; |
362 | ASSERT_EQ(Serialized, Expected); |
363 | |
364 | // Also check it parses back without the errors. |
365 | WithStringField Deserialized; |
366 | { |
367 | Input YIn(Serialized); |
368 | YIn >> Deserialized; |
369 | ASSERT_FALSE(YIn.error()) |
370 | << "Parsing error occurred during deserialization. Serialized string:\n" |
371 | << Serialized; |
372 | } |
373 | EXPECT_EQ(Original.str1, Deserialized.str1); |
374 | EXPECT_EQ(Original.str2, Deserialized.str2); |
375 | EXPECT_EQ(Original.str3, Deserialized.str3); |
376 | } |
377 | |
378 | TEST(YAMLIO, NoQuotesForTab) { |
379 | WithStringField WithTab; |
380 | WithTab.str1 = "aba\tcaba" ; |
381 | std::string Serialized; |
382 | { |
383 | llvm::raw_string_ostream OS(Serialized); |
384 | Output YOut(OS); |
385 | YOut << WithTab; |
386 | } |
387 | auto ExpectedPrefix = "---\n" |
388 | "str1: aba\tcaba\n" ; |
389 | EXPECT_THAT(Serialized, StartsWith(ExpectedPrefix)); |
390 | } |
391 | |
392 | //===----------------------------------------------------------------------===// |
393 | // Test built-in types |
394 | //===----------------------------------------------------------------------===// |
395 | |
396 | struct BuiltInTypes { |
397 | llvm::StringRef str; |
398 | std::string stdstr; |
399 | uint64_t u64; |
400 | uint32_t u32; |
401 | uint16_t u16; |
402 | uint8_t u8; |
403 | bool b; |
404 | int64_t s64; |
405 | int32_t s32; |
406 | int16_t s16; |
407 | int8_t s8; |
408 | float f; |
409 | double d; |
410 | Hex8 h8; |
411 | Hex16 h16; |
412 | Hex32 h32; |
413 | Hex64 h64; |
414 | }; |
415 | |
416 | namespace llvm { |
417 | namespace yaml { |
418 | template <> |
419 | struct MappingTraits<BuiltInTypes> { |
420 | static void mapping(IO &io, BuiltInTypes& bt) { |
421 | io.mapRequired(Key: "str" , Val&: bt.str); |
422 | io.mapRequired(Key: "stdstr" , Val&: bt.stdstr); |
423 | io.mapRequired(Key: "u64" , Val&: bt.u64); |
424 | io.mapRequired(Key: "u32" , Val&: bt.u32); |
425 | io.mapRequired(Key: "u16" , Val&: bt.u16); |
426 | io.mapRequired(Key: "u8" , Val&: bt.u8); |
427 | io.mapRequired(Key: "b" , Val&: bt.b); |
428 | io.mapRequired(Key: "s64" , Val&: bt.s64); |
429 | io.mapRequired(Key: "s32" , Val&: bt.s32); |
430 | io.mapRequired(Key: "s16" , Val&: bt.s16); |
431 | io.mapRequired(Key: "s8" , Val&: bt.s8); |
432 | io.mapRequired(Key: "f" , Val&: bt.f); |
433 | io.mapRequired(Key: "d" , Val&: bt.d); |
434 | io.mapRequired(Key: "h8" , Val&: bt.h8); |
435 | io.mapRequired(Key: "h16" , Val&: bt.h16); |
436 | io.mapRequired(Key: "h32" , Val&: bt.h32); |
437 | io.mapRequired(Key: "h64" , Val&: bt.h64); |
438 | } |
439 | }; |
440 | } |
441 | } |
442 | |
443 | |
444 | // |
445 | // Test the reading of all built-in scalar conversions |
446 | // |
447 | TEST(YAMLIO, TestReadBuiltInTypes) { |
448 | BuiltInTypes map; |
449 | Input yin("---\n" |
450 | "str: hello there\n" |
451 | "stdstr: hello where?\n" |
452 | "u64: 5000000000\n" |
453 | "u32: 4000000000\n" |
454 | "u16: 65000\n" |
455 | "u8: 255\n" |
456 | "b: false\n" |
457 | "s64: -5000000000\n" |
458 | "s32: -2000000000\n" |
459 | "s16: -32000\n" |
460 | "s8: -127\n" |
461 | "f: 137.125\n" |
462 | "d: -2.8625\n" |
463 | "h8: 0xFF\n" |
464 | "h16: 0x8765\n" |
465 | "h32: 0xFEDCBA98\n" |
466 | "h64: 0xFEDCBA9876543210\n" |
467 | "...\n" ); |
468 | yin >> map; |
469 | |
470 | EXPECT_FALSE(yin.error()); |
471 | EXPECT_EQ(map.str, "hello there" ); |
472 | EXPECT_EQ(map.stdstr, "hello where?" ); |
473 | EXPECT_EQ(map.u64, 5000000000ULL); |
474 | EXPECT_EQ(map.u32, 4000000000U); |
475 | EXPECT_EQ(map.u16, 65000); |
476 | EXPECT_EQ(map.u8, 255); |
477 | EXPECT_EQ(map.b, false); |
478 | EXPECT_EQ(map.s64, -5000000000LL); |
479 | EXPECT_EQ(map.s32, -2000000000L); |
480 | EXPECT_EQ(map.s16, -32000); |
481 | EXPECT_EQ(map.s8, -127); |
482 | EXPECT_EQ(map.f, 137.125); |
483 | EXPECT_EQ(map.d, -2.8625); |
484 | EXPECT_EQ(map.h8, Hex8(255)); |
485 | EXPECT_EQ(map.h16, Hex16(0x8765)); |
486 | EXPECT_EQ(map.h32, Hex32(0xFEDCBA98)); |
487 | EXPECT_EQ(map.h64, Hex64(0xFEDCBA9876543210LL)); |
488 | } |
489 | |
490 | |
491 | // |
492 | // Test writing then reading back all built-in scalar types |
493 | // |
494 | TEST(YAMLIO, TestReadWriteBuiltInTypes) { |
495 | std::string intermediate; |
496 | { |
497 | BuiltInTypes map; |
498 | map.str = "one two" ; |
499 | map.stdstr = "three four" ; |
500 | map.u64 = 6000000000ULL; |
501 | map.u32 = 3000000000U; |
502 | map.u16 = 50000; |
503 | map.u8 = 254; |
504 | map.b = true; |
505 | map.s64 = -6000000000LL; |
506 | map.s32 = -2000000000; |
507 | map.s16 = -32000; |
508 | map.s8 = -128; |
509 | map.f = 3.25; |
510 | map.d = -2.8625; |
511 | map.h8 = 254; |
512 | map.h16 = 50000; |
513 | map.h32 = 3000000000U; |
514 | map.h64 = 6000000000LL; |
515 | |
516 | llvm::raw_string_ostream ostr(intermediate); |
517 | Output yout(ostr); |
518 | yout << map; |
519 | } |
520 | |
521 | { |
522 | Input yin(intermediate); |
523 | BuiltInTypes map; |
524 | yin >> map; |
525 | |
526 | EXPECT_FALSE(yin.error()); |
527 | EXPECT_EQ(map.str, "one two" ); |
528 | EXPECT_EQ(map.stdstr, "three four" ); |
529 | EXPECT_EQ(map.u64, 6000000000ULL); |
530 | EXPECT_EQ(map.u32, 3000000000U); |
531 | EXPECT_EQ(map.u16, 50000); |
532 | EXPECT_EQ(map.u8, 254); |
533 | EXPECT_EQ(map.b, true); |
534 | EXPECT_EQ(map.s64, -6000000000LL); |
535 | EXPECT_EQ(map.s32, -2000000000L); |
536 | EXPECT_EQ(map.s16, -32000); |
537 | EXPECT_EQ(map.s8, -128); |
538 | EXPECT_EQ(map.f, 3.25); |
539 | EXPECT_EQ(map.d, -2.8625); |
540 | EXPECT_EQ(map.h8, Hex8(254)); |
541 | EXPECT_EQ(map.h16, Hex16(50000)); |
542 | EXPECT_EQ(map.h32, Hex32(3000000000U)); |
543 | EXPECT_EQ(map.h64, Hex64(6000000000LL)); |
544 | } |
545 | } |
546 | |
547 | //===----------------------------------------------------------------------===// |
548 | // Test endian-aware types |
549 | //===----------------------------------------------------------------------===// |
550 | |
551 | struct EndianTypes { |
552 | typedef llvm::support::detail::packed_endian_specific_integral< |
553 | float, llvm::endianness::little, llvm::support::unaligned> |
554 | ulittle_float; |
555 | typedef llvm::support::detail::packed_endian_specific_integral< |
556 | double, llvm::endianness::little, llvm::support::unaligned> |
557 | ulittle_double; |
558 | |
559 | llvm::support::ulittle64_t u64; |
560 | llvm::support::ulittle32_t u32; |
561 | llvm::support::ulittle16_t u16; |
562 | llvm::support::little64_t s64; |
563 | llvm::support::little32_t s32; |
564 | llvm::support::little16_t s16; |
565 | ulittle_float f; |
566 | ulittle_double d; |
567 | }; |
568 | |
569 | namespace llvm { |
570 | namespace yaml { |
571 | template <> struct MappingTraits<EndianTypes> { |
572 | static void mapping(IO &io, EndianTypes &et) { |
573 | io.mapRequired(Key: "u64" , Val&: et.u64); |
574 | io.mapRequired(Key: "u32" , Val&: et.u32); |
575 | io.mapRequired(Key: "u16" , Val&: et.u16); |
576 | io.mapRequired(Key: "s64" , Val&: et.s64); |
577 | io.mapRequired(Key: "s32" , Val&: et.s32); |
578 | io.mapRequired(Key: "s16" , Val&: et.s16); |
579 | io.mapRequired(Key: "f" , Val&: et.f); |
580 | io.mapRequired(Key: "d" , Val&: et.d); |
581 | } |
582 | }; |
583 | } |
584 | } |
585 | |
586 | // |
587 | // Test the reading of all endian scalar conversions |
588 | // |
589 | TEST(YAMLIO, TestReadEndianTypes) { |
590 | EndianTypes map; |
591 | Input yin("---\n" |
592 | "u64: 5000000000\n" |
593 | "u32: 4000000000\n" |
594 | "u16: 65000\n" |
595 | "s64: -5000000000\n" |
596 | "s32: -2000000000\n" |
597 | "s16: -32000\n" |
598 | "f: 3.25\n" |
599 | "d: -2.8625\n" |
600 | "...\n" ); |
601 | yin >> map; |
602 | |
603 | EXPECT_FALSE(yin.error()); |
604 | EXPECT_EQ(map.u64, 5000000000ULL); |
605 | EXPECT_EQ(map.u32, 4000000000U); |
606 | EXPECT_EQ(map.u16, 65000); |
607 | EXPECT_EQ(map.s64, -5000000000LL); |
608 | EXPECT_EQ(map.s32, -2000000000L); |
609 | EXPECT_EQ(map.s16, -32000); |
610 | EXPECT_EQ(map.f, 3.25f); |
611 | EXPECT_EQ(map.d, -2.8625); |
612 | } |
613 | |
614 | // |
615 | // Test writing then reading back all endian-aware scalar types |
616 | // |
617 | TEST(YAMLIO, TestReadWriteEndianTypes) { |
618 | std::string intermediate; |
619 | { |
620 | EndianTypes map; |
621 | map.u64 = 6000000000ULL; |
622 | map.u32 = 3000000000U; |
623 | map.u16 = 50000; |
624 | map.s64 = -6000000000LL; |
625 | map.s32 = -2000000000; |
626 | map.s16 = -32000; |
627 | map.f = 3.25f; |
628 | map.d = -2.8625; |
629 | |
630 | llvm::raw_string_ostream ostr(intermediate); |
631 | Output yout(ostr); |
632 | yout << map; |
633 | } |
634 | |
635 | { |
636 | Input yin(intermediate); |
637 | EndianTypes map; |
638 | yin >> map; |
639 | |
640 | EXPECT_FALSE(yin.error()); |
641 | EXPECT_EQ(map.u64, 6000000000ULL); |
642 | EXPECT_EQ(map.u32, 3000000000U); |
643 | EXPECT_EQ(map.u16, 50000); |
644 | EXPECT_EQ(map.s64, -6000000000LL); |
645 | EXPECT_EQ(map.s32, -2000000000L); |
646 | EXPECT_EQ(map.s16, -32000); |
647 | EXPECT_EQ(map.f, 3.25f); |
648 | EXPECT_EQ(map.d, -2.8625); |
649 | } |
650 | } |
651 | |
652 | enum class Enum : uint16_t { One, Two }; |
653 | enum class BitsetEnum : uint16_t { |
654 | ZeroOne = 0x01, |
655 | OneZero = 0x10, |
656 | LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue*/ OneZero), |
657 | }; |
658 | LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); |
659 | struct EndianEnums { |
660 | llvm::support::little_t<Enum> LittleEnum; |
661 | llvm::support::big_t<Enum> BigEnum; |
662 | llvm::support::little_t<BitsetEnum> LittleBitset; |
663 | llvm::support::big_t<BitsetEnum> BigBitset; |
664 | }; |
665 | namespace llvm { |
666 | namespace yaml { |
667 | template <> struct ScalarEnumerationTraits<Enum> { |
668 | static void enumeration(IO &io, Enum &E) { |
669 | io.enumCase(Val&: E, Str: "One" , ConstVal: Enum::One); |
670 | io.enumCase(Val&: E, Str: "Two" , ConstVal: Enum::Two); |
671 | } |
672 | }; |
673 | |
674 | template <> struct ScalarBitSetTraits<BitsetEnum> { |
675 | static void bitset(IO &io, BitsetEnum &E) { |
676 | io.bitSetCase(Val&: E, Str: "ZeroOne" , ConstVal: BitsetEnum::ZeroOne); |
677 | io.bitSetCase(Val&: E, Str: "OneZero" , ConstVal: BitsetEnum::OneZero); |
678 | } |
679 | }; |
680 | |
681 | template <> struct MappingTraits<EndianEnums> { |
682 | static void mapping(IO &io, EndianEnums &EE) { |
683 | io.mapRequired(Key: "LittleEnum" , Val&: EE.LittleEnum); |
684 | io.mapRequired(Key: "BigEnum" , Val&: EE.BigEnum); |
685 | io.mapRequired(Key: "LittleBitset" , Val&: EE.LittleBitset); |
686 | io.mapRequired(Key: "BigBitset" , Val&: EE.BigBitset); |
687 | } |
688 | }; |
689 | } // namespace yaml |
690 | } // namespace llvm |
691 | |
692 | TEST(YAMLIO, TestReadEndianEnums) { |
693 | EndianEnums map; |
694 | Input yin("---\n" |
695 | "LittleEnum: One\n" |
696 | "BigEnum: Two\n" |
697 | "LittleBitset: [ ZeroOne ]\n" |
698 | "BigBitset: [ ZeroOne, OneZero ]\n" |
699 | "...\n" ); |
700 | yin >> map; |
701 | |
702 | EXPECT_FALSE(yin.error()); |
703 | EXPECT_EQ(Enum::One, map.LittleEnum); |
704 | EXPECT_EQ(Enum::Two, map.BigEnum); |
705 | EXPECT_EQ(BitsetEnum::ZeroOne, map.LittleBitset); |
706 | EXPECT_EQ(BitsetEnum::ZeroOne | BitsetEnum::OneZero, map.BigBitset); |
707 | } |
708 | |
709 | TEST(YAMLIO, TestReadWriteEndianEnums) { |
710 | std::string intermediate; |
711 | { |
712 | EndianEnums map; |
713 | map.LittleEnum = Enum::Two; |
714 | map.BigEnum = Enum::One; |
715 | map.LittleBitset = BitsetEnum::OneZero | BitsetEnum::ZeroOne; |
716 | map.BigBitset = BitsetEnum::OneZero; |
717 | |
718 | llvm::raw_string_ostream ostr(intermediate); |
719 | Output yout(ostr); |
720 | yout << map; |
721 | } |
722 | |
723 | { |
724 | Input yin(intermediate); |
725 | EndianEnums map; |
726 | yin >> map; |
727 | |
728 | EXPECT_FALSE(yin.error()); |
729 | EXPECT_EQ(Enum::Two, map.LittleEnum); |
730 | EXPECT_EQ(Enum::One, map.BigEnum); |
731 | EXPECT_EQ(BitsetEnum::OneZero | BitsetEnum::ZeroOne, map.LittleBitset); |
732 | EXPECT_EQ(BitsetEnum::OneZero, map.BigBitset); |
733 | } |
734 | } |
735 | |
736 | struct StringTypes { |
737 | llvm::StringRef str1; |
738 | llvm::StringRef str2; |
739 | llvm::StringRef str3; |
740 | llvm::StringRef str4; |
741 | llvm::StringRef str5; |
742 | llvm::StringRef str6; |
743 | llvm::StringRef str7; |
744 | llvm::StringRef str8; |
745 | llvm::StringRef str9; |
746 | llvm::StringRef str10; |
747 | llvm::StringRef str11; |
748 | std::string stdstr1; |
749 | std::string stdstr2; |
750 | std::string stdstr3; |
751 | std::string stdstr4; |
752 | std::string stdstr5; |
753 | std::string stdstr6; |
754 | std::string stdstr7; |
755 | std::string stdstr8; |
756 | std::string stdstr9; |
757 | std::string stdstr10; |
758 | std::string stdstr11; |
759 | std::string stdstr12; |
760 | std::string stdstr13; |
761 | }; |
762 | |
763 | namespace llvm { |
764 | namespace yaml { |
765 | template <> |
766 | struct MappingTraits<StringTypes> { |
767 | static void mapping(IO &io, StringTypes& st) { |
768 | io.mapRequired(Key: "str1" , Val&: st.str1); |
769 | io.mapRequired(Key: "str2" , Val&: st.str2); |
770 | io.mapRequired(Key: "str3" , Val&: st.str3); |
771 | io.mapRequired(Key: "str4" , Val&: st.str4); |
772 | io.mapRequired(Key: "str5" , Val&: st.str5); |
773 | io.mapRequired(Key: "str6" , Val&: st.str6); |
774 | io.mapRequired(Key: "str7" , Val&: st.str7); |
775 | io.mapRequired(Key: "str8" , Val&: st.str8); |
776 | io.mapRequired(Key: "str9" , Val&: st.str9); |
777 | io.mapRequired(Key: "str10" , Val&: st.str10); |
778 | io.mapRequired(Key: "str11" , Val&: st.str11); |
779 | io.mapRequired(Key: "stdstr1" , Val&: st.stdstr1); |
780 | io.mapRequired(Key: "stdstr2" , Val&: st.stdstr2); |
781 | io.mapRequired(Key: "stdstr3" , Val&: st.stdstr3); |
782 | io.mapRequired(Key: "stdstr4" , Val&: st.stdstr4); |
783 | io.mapRequired(Key: "stdstr5" , Val&: st.stdstr5); |
784 | io.mapRequired(Key: "stdstr6" , Val&: st.stdstr6); |
785 | io.mapRequired(Key: "stdstr7" , Val&: st.stdstr7); |
786 | io.mapRequired(Key: "stdstr8" , Val&: st.stdstr8); |
787 | io.mapRequired(Key: "stdstr9" , Val&: st.stdstr9); |
788 | io.mapRequired(Key: "stdstr10" , Val&: st.stdstr10); |
789 | io.mapRequired(Key: "stdstr11" , Val&: st.stdstr11); |
790 | io.mapRequired(Key: "stdstr12" , Val&: st.stdstr12); |
791 | io.mapRequired(Key: "stdstr13" , Val&: st.stdstr13); |
792 | } |
793 | }; |
794 | } |
795 | } |
796 | |
797 | TEST(YAMLIO, TestReadWriteStringTypes) { |
798 | std::string intermediate; |
799 | { |
800 | StringTypes map; |
801 | map.str1 = "'aaa" ; |
802 | map.str2 = "\"bbb" ; |
803 | map.str3 = "`ccc" ; |
804 | map.str4 = "@ddd" ; |
805 | map.str5 = "" ; |
806 | map.str6 = "0000000004000000" ; |
807 | map.str7 = "true" ; |
808 | map.str8 = "FALSE" ; |
809 | map.str9 = "~" ; |
810 | map.str10 = "0.2e20" ; |
811 | map.str11 = "0x30" ; |
812 | map.stdstr1 = "'eee" ; |
813 | map.stdstr2 = "\"fff" ; |
814 | map.stdstr3 = "`ggg" ; |
815 | map.stdstr4 = "@hhh" ; |
816 | map.stdstr5 = "" ; |
817 | map.stdstr6 = "0000000004000000" ; |
818 | map.stdstr7 = "true" ; |
819 | map.stdstr8 = "FALSE" ; |
820 | map.stdstr9 = "~" ; |
821 | map.stdstr10 = "0.2e20" ; |
822 | map.stdstr11 = "0x30" ; |
823 | map.stdstr12 = "- match" ; |
824 | map.stdstr13.assign(s: "\0a\0b\0" , n: 5); |
825 | |
826 | llvm::raw_string_ostream ostr(intermediate); |
827 | Output yout(ostr); |
828 | yout << map; |
829 | } |
830 | |
831 | llvm::StringRef flowOut(intermediate); |
832 | EXPECT_NE(llvm::StringRef::npos, flowOut.find("'''aaa" )); |
833 | EXPECT_NE(llvm::StringRef::npos, flowOut.find("'\"bbb'" )); |
834 | EXPECT_NE(llvm::StringRef::npos, flowOut.find("'`ccc'" )); |
835 | EXPECT_NE(llvm::StringRef::npos, flowOut.find("'@ddd'" )); |
836 | EXPECT_NE(llvm::StringRef::npos, flowOut.find("''\n" )); |
837 | EXPECT_NE(llvm::StringRef::npos, flowOut.find("'0000000004000000'\n" )); |
838 | EXPECT_NE(llvm::StringRef::npos, flowOut.find("'true'\n" )); |
839 | EXPECT_NE(llvm::StringRef::npos, flowOut.find("'FALSE'\n" )); |
840 | EXPECT_NE(llvm::StringRef::npos, flowOut.find("'~'\n" )); |
841 | EXPECT_NE(llvm::StringRef::npos, flowOut.find("'0.2e20'\n" )); |
842 | EXPECT_NE(llvm::StringRef::npos, flowOut.find("'0x30'\n" )); |
843 | EXPECT_NE(llvm::StringRef::npos, flowOut.find("'- match'\n" )); |
844 | EXPECT_NE(std::string::npos, flowOut.find("'''eee" )); |
845 | EXPECT_NE(std::string::npos, flowOut.find("'\"fff'" )); |
846 | EXPECT_NE(std::string::npos, flowOut.find("'`ggg'" )); |
847 | EXPECT_NE(std::string::npos, flowOut.find("'@hhh'" )); |
848 | EXPECT_NE(std::string::npos, flowOut.find("''\n" )); |
849 | EXPECT_NE(std::string::npos, flowOut.find("'0000000004000000'\n" )); |
850 | EXPECT_NE(std::string::npos, flowOut.find("\"\\0a\\0b\\0\"" )); |
851 | |
852 | { |
853 | Input yin(intermediate); |
854 | StringTypes map; |
855 | yin >> map; |
856 | |
857 | EXPECT_FALSE(yin.error()); |
858 | EXPECT_EQ(map.str1, "'aaa" ); |
859 | EXPECT_EQ(map.str2, "\"bbb" ); |
860 | EXPECT_EQ(map.str3, "`ccc" ); |
861 | EXPECT_EQ(map.str4, "@ddd" ); |
862 | EXPECT_EQ(map.str5, "" ); |
863 | EXPECT_EQ(map.str6, "0000000004000000" ); |
864 | EXPECT_EQ(map.stdstr1, "'eee" ); |
865 | EXPECT_EQ(map.stdstr2, "\"fff" ); |
866 | EXPECT_EQ(map.stdstr3, "`ggg" ); |
867 | EXPECT_EQ(map.stdstr4, "@hhh" ); |
868 | EXPECT_EQ(map.stdstr5, "" ); |
869 | EXPECT_EQ(map.stdstr6, "0000000004000000" ); |
870 | EXPECT_EQ(std::string("\0a\0b\0" , 5), map.stdstr13); |
871 | } |
872 | } |
873 | |
874 | //===----------------------------------------------------------------------===// |
875 | // Test ScalarEnumerationTraits |
876 | //===----------------------------------------------------------------------===// |
877 | |
878 | enum Colors { |
879 | cRed, |
880 | cBlue, |
881 | cGreen, |
882 | cYellow |
883 | }; |
884 | |
885 | struct ColorMap { |
886 | Colors c1; |
887 | Colors c2; |
888 | Colors c3; |
889 | Colors c4; |
890 | Colors c5; |
891 | Colors c6; |
892 | }; |
893 | |
894 | namespace llvm { |
895 | namespace yaml { |
896 | template <> |
897 | struct ScalarEnumerationTraits<Colors> { |
898 | static void enumeration(IO &io, Colors &value) { |
899 | io.enumCase(Val&: value, Str: "red" , ConstVal: cRed); |
900 | io.enumCase(Val&: value, Str: "blue" , ConstVal: cBlue); |
901 | io.enumCase(Val&: value, Str: "green" , ConstVal: cGreen); |
902 | io.enumCase(Val&: value, Str: "yellow" ,ConstVal: cYellow); |
903 | } |
904 | }; |
905 | template <> |
906 | struct MappingTraits<ColorMap> { |
907 | static void mapping(IO &io, ColorMap& c) { |
908 | io.mapRequired(Key: "c1" , Val&: c.c1); |
909 | io.mapRequired(Key: "c2" , Val&: c.c2); |
910 | io.mapRequired(Key: "c3" , Val&: c.c3); |
911 | io.mapOptional(Key: "c4" , Val&: c.c4, Default: cBlue); // supplies default |
912 | io.mapOptional(Key: "c5" , Val&: c.c5, Default: cYellow); // supplies default |
913 | io.mapOptional(Key: "c6" , Val&: c.c6, Default: cRed); // supplies default |
914 | } |
915 | }; |
916 | } |
917 | } |
918 | |
919 | |
920 | // |
921 | // Test reading enumerated scalars |
922 | // |
923 | TEST(YAMLIO, TestEnumRead) { |
924 | ColorMap map; |
925 | Input yin("---\n" |
926 | "c1: blue\n" |
927 | "c2: red\n" |
928 | "c3: green\n" |
929 | "c5: yellow\n" |
930 | "...\n" ); |
931 | yin >> map; |
932 | |
933 | EXPECT_FALSE(yin.error()); |
934 | EXPECT_EQ(cBlue, map.c1); |
935 | EXPECT_EQ(cRed, map.c2); |
936 | EXPECT_EQ(cGreen, map.c3); |
937 | EXPECT_EQ(cBlue, map.c4); // tests default |
938 | EXPECT_EQ(cYellow,map.c5); // tests overridden |
939 | EXPECT_EQ(cRed, map.c6); // tests default |
940 | } |
941 | |
942 | |
943 | |
944 | //===----------------------------------------------------------------------===// |
945 | // Test ScalarBitSetTraits |
946 | //===----------------------------------------------------------------------===// |
947 | |
948 | enum MyFlags { |
949 | flagNone = 0, |
950 | flagBig = 1 << 0, |
951 | flagFlat = 1 << 1, |
952 | flagRound = 1 << 2, |
953 | flagPointy = 1 << 3 |
954 | }; |
955 | inline MyFlags operator|(MyFlags a, MyFlags b) { |
956 | return static_cast<MyFlags>( |
957 | static_cast<uint32_t>(a) | static_cast<uint32_t>(b)); |
958 | } |
959 | |
960 | struct FlagsMap { |
961 | MyFlags f1; |
962 | MyFlags f2; |
963 | MyFlags f3; |
964 | MyFlags f4; |
965 | }; |
966 | |
967 | |
968 | namespace llvm { |
969 | namespace yaml { |
970 | template <> |
971 | struct ScalarBitSetTraits<MyFlags> { |
972 | static void bitset(IO &io, MyFlags &value) { |
973 | io.bitSetCase(Val&: value, Str: "big" , ConstVal: flagBig); |
974 | io.bitSetCase(Val&: value, Str: "flat" , ConstVal: flagFlat); |
975 | io.bitSetCase(Val&: value, Str: "round" , ConstVal: flagRound); |
976 | io.bitSetCase(Val&: value, Str: "pointy" ,ConstVal: flagPointy); |
977 | } |
978 | }; |
979 | template <> |
980 | struct MappingTraits<FlagsMap> { |
981 | static void mapping(IO &io, FlagsMap& c) { |
982 | io.mapRequired(Key: "f1" , Val&: c.f1); |
983 | io.mapRequired(Key: "f2" , Val&: c.f2); |
984 | io.mapRequired(Key: "f3" , Val&: c.f3); |
985 | io.mapOptional(Key: "f4" , Val&: c.f4, Default: flagRound); |
986 | } |
987 | }; |
988 | } |
989 | } |
990 | |
991 | |
992 | // |
993 | // Test reading flow sequence representing bit-mask values |
994 | // |
995 | TEST(YAMLIO, TestFlagsRead) { |
996 | FlagsMap map; |
997 | Input yin("---\n" |
998 | "f1: [ big ]\n" |
999 | "f2: [ round, flat ]\n" |
1000 | "f3: []\n" |
1001 | "...\n" ); |
1002 | yin >> map; |
1003 | |
1004 | EXPECT_FALSE(yin.error()); |
1005 | EXPECT_EQ(flagBig, map.f1); |
1006 | EXPECT_EQ(flagRound|flagFlat, map.f2); |
1007 | EXPECT_EQ(flagNone, map.f3); // check empty set |
1008 | EXPECT_EQ(flagRound, map.f4); // check optional key |
1009 | } |
1010 | |
1011 | |
1012 | // |
1013 | // Test writing then reading back bit-mask values |
1014 | // |
1015 | TEST(YAMLIO, TestReadWriteFlags) { |
1016 | std::string intermediate; |
1017 | { |
1018 | FlagsMap map; |
1019 | map.f1 = flagBig; |
1020 | map.f2 = flagRound | flagFlat; |
1021 | map.f3 = flagNone; |
1022 | map.f4 = flagNone; |
1023 | |
1024 | llvm::raw_string_ostream ostr(intermediate); |
1025 | Output yout(ostr); |
1026 | yout << map; |
1027 | } |
1028 | |
1029 | { |
1030 | Input yin(intermediate); |
1031 | FlagsMap map2; |
1032 | yin >> map2; |
1033 | |
1034 | EXPECT_FALSE(yin.error()); |
1035 | EXPECT_EQ(flagBig, map2.f1); |
1036 | EXPECT_EQ(flagRound|flagFlat, map2.f2); |
1037 | EXPECT_EQ(flagNone, map2.f3); |
1038 | //EXPECT_EQ(flagRound, map2.f4); // check optional key |
1039 | } |
1040 | } |
1041 | |
1042 | |
1043 | |
1044 | //===----------------------------------------------------------------------===// |
1045 | // Test ScalarTraits |
1046 | //===----------------------------------------------------------------------===// |
1047 | |
1048 | struct MyCustomType { |
1049 | int length; |
1050 | int width; |
1051 | }; |
1052 | |
1053 | struct MyCustomTypeMap { |
1054 | MyCustomType f1; |
1055 | MyCustomType f2; |
1056 | int f3; |
1057 | }; |
1058 | |
1059 | |
1060 | namespace llvm { |
1061 | namespace yaml { |
1062 | template <> |
1063 | struct MappingTraits<MyCustomTypeMap> { |
1064 | static void mapping(IO &io, MyCustomTypeMap& s) { |
1065 | io.mapRequired(Key: "f1" , Val&: s.f1); |
1066 | io.mapRequired(Key: "f2" , Val&: s.f2); |
1067 | io.mapRequired(Key: "f3" , Val&: s.f3); |
1068 | } |
1069 | }; |
1070 | // MyCustomType is formatted as a yaml scalar. A value of |
1071 | // {length=3, width=4} would be represented in yaml as "3 by 4". |
1072 | template<> |
1073 | struct ScalarTraits<MyCustomType> { |
1074 | static void output(const MyCustomType &value, void* ctxt, llvm::raw_ostream &out) { |
1075 | out << llvm::format(Fmt: "%d by %d" , Vals: value.length, Vals: value.width); |
1076 | } |
1077 | static StringRef input(StringRef scalar, void* ctxt, MyCustomType &value) { |
1078 | size_t byStart = scalar.find(Str: "by" ); |
1079 | if ( byStart != StringRef::npos ) { |
1080 | StringRef lenStr = scalar.slice(Start: 0, End: byStart); |
1081 | lenStr = lenStr.rtrim(); |
1082 | if ( lenStr.getAsInteger(Radix: 0, Result&: value.length) ) { |
1083 | return "malformed length" ; |
1084 | } |
1085 | StringRef widthStr = scalar.drop_front(N: byStart+2); |
1086 | widthStr = widthStr.ltrim(); |
1087 | if ( widthStr.getAsInteger(Radix: 0, Result&: value.width) ) { |
1088 | return "malformed width" ; |
1089 | } |
1090 | return StringRef(); |
1091 | } |
1092 | else { |
1093 | return "malformed by" ; |
1094 | } |
1095 | } |
1096 | static QuotingType mustQuote(StringRef) { return QuotingType::Single; } |
1097 | }; |
1098 | } |
1099 | } |
1100 | |
1101 | |
1102 | // |
1103 | // Test writing then reading back custom values |
1104 | // |
1105 | TEST(YAMLIO, TestReadWriteMyCustomType) { |
1106 | std::string intermediate; |
1107 | { |
1108 | MyCustomTypeMap map; |
1109 | map.f1.length = 1; |
1110 | map.f1.width = 4; |
1111 | map.f2.length = 100; |
1112 | map.f2.width = 400; |
1113 | map.f3 = 10; |
1114 | |
1115 | llvm::raw_string_ostream ostr(intermediate); |
1116 | Output yout(ostr); |
1117 | yout << map; |
1118 | } |
1119 | |
1120 | { |
1121 | Input yin(intermediate); |
1122 | MyCustomTypeMap map2; |
1123 | yin >> map2; |
1124 | |
1125 | EXPECT_FALSE(yin.error()); |
1126 | EXPECT_EQ(1, map2.f1.length); |
1127 | EXPECT_EQ(4, map2.f1.width); |
1128 | EXPECT_EQ(100, map2.f2.length); |
1129 | EXPECT_EQ(400, map2.f2.width); |
1130 | EXPECT_EQ(10, map2.f3); |
1131 | } |
1132 | } |
1133 | |
1134 | |
1135 | //===----------------------------------------------------------------------===// |
1136 | // Test BlockScalarTraits |
1137 | //===----------------------------------------------------------------------===// |
1138 | |
1139 | struct MultilineStringType { |
1140 | std::string str; |
1141 | }; |
1142 | |
1143 | struct MultilineStringTypeMap { |
1144 | MultilineStringType name; |
1145 | MultilineStringType description; |
1146 | MultilineStringType ingredients; |
1147 | MultilineStringType recipes; |
1148 | MultilineStringType warningLabels; |
1149 | MultilineStringType documentation; |
1150 | int price; |
1151 | }; |
1152 | |
1153 | namespace llvm { |
1154 | namespace yaml { |
1155 | template <> |
1156 | struct MappingTraits<MultilineStringTypeMap> { |
1157 | static void mapping(IO &io, MultilineStringTypeMap& s) { |
1158 | io.mapRequired(Key: "name" , Val&: s.name); |
1159 | io.mapRequired(Key: "description" , Val&: s.description); |
1160 | io.mapRequired(Key: "ingredients" , Val&: s.ingredients); |
1161 | io.mapRequired(Key: "recipes" , Val&: s.recipes); |
1162 | io.mapRequired(Key: "warningLabels" , Val&: s.warningLabels); |
1163 | io.mapRequired(Key: "documentation" , Val&: s.documentation); |
1164 | io.mapRequired(Key: "price" , Val&: s.price); |
1165 | } |
1166 | }; |
1167 | |
1168 | // MultilineStringType is formatted as a yaml block literal scalar. A value of |
1169 | // "Hello\nWorld" would be represented in yaml as |
1170 | // | |
1171 | // Hello |
1172 | // World |
1173 | template <> |
1174 | struct BlockScalarTraits<MultilineStringType> { |
1175 | static void output(const MultilineStringType &value, void *ctxt, |
1176 | llvm::raw_ostream &out) { |
1177 | out << value.str; |
1178 | } |
1179 | static StringRef input(StringRef scalar, void *ctxt, |
1180 | MultilineStringType &value) { |
1181 | value.str = scalar.str(); |
1182 | return StringRef(); |
1183 | } |
1184 | }; |
1185 | } |
1186 | } |
1187 | |
1188 | LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(MultilineStringType) |
1189 | |
1190 | // |
1191 | // Test writing then reading back custom values |
1192 | // |
1193 | TEST(YAMLIO, TestReadWriteMultilineStringType) { |
1194 | std::string intermediate; |
1195 | { |
1196 | MultilineStringTypeMap map; |
1197 | map.name.str = "An Item" ; |
1198 | map.description.str = "Hello\nWorld" ; |
1199 | map.ingredients.str = "SubItem 1\nSub Item 2\n\nSub Item 3\n" ; |
1200 | map.recipes.str = "\n\nTest 1\n\n\n" ; |
1201 | map.warningLabels.str = "" ; |
1202 | map.documentation.str = "\n\n" ; |
1203 | map.price = 350; |
1204 | |
1205 | llvm::raw_string_ostream ostr(intermediate); |
1206 | Output yout(ostr); |
1207 | yout << map; |
1208 | } |
1209 | { |
1210 | Input yin(intermediate); |
1211 | MultilineStringTypeMap map2; |
1212 | yin >> map2; |
1213 | |
1214 | EXPECT_FALSE(yin.error()); |
1215 | EXPECT_EQ(map2.name.str, "An Item\n" ); |
1216 | EXPECT_EQ(map2.description.str, "Hello\nWorld\n" ); |
1217 | EXPECT_EQ(map2.ingredients.str, "SubItem 1\nSub Item 2\n\nSub Item 3\n" ); |
1218 | EXPECT_EQ(map2.recipes.str, "\n\nTest 1\n" ); |
1219 | EXPECT_TRUE(map2.warningLabels.str.empty()); |
1220 | EXPECT_TRUE(map2.documentation.str.empty()); |
1221 | EXPECT_EQ(map2.price, 350); |
1222 | } |
1223 | } |
1224 | |
1225 | // |
1226 | // Test writing then reading back custom values |
1227 | // |
1228 | TEST(YAMLIO, TestReadWriteBlockScalarDocuments) { |
1229 | std::string intermediate; |
1230 | { |
1231 | std::vector<MultilineStringType> documents; |
1232 | MultilineStringType doc; |
1233 | doc.str = "Hello\nWorld" ; |
1234 | documents.push_back(x: doc); |
1235 | |
1236 | llvm::raw_string_ostream ostr(intermediate); |
1237 | Output yout(ostr); |
1238 | yout << documents; |
1239 | |
1240 | // Verify that the block scalar header was written out on the same line |
1241 | // as the document marker. |
1242 | EXPECT_NE(llvm::StringRef::npos, llvm::StringRef(ostr.str()).find("--- |" )); |
1243 | } |
1244 | { |
1245 | Input yin(intermediate); |
1246 | std::vector<MultilineStringType> documents2; |
1247 | yin >> documents2; |
1248 | |
1249 | EXPECT_FALSE(yin.error()); |
1250 | EXPECT_EQ(documents2.size(), size_t(1)); |
1251 | EXPECT_EQ(documents2[0].str, "Hello\nWorld\n" ); |
1252 | } |
1253 | } |
1254 | |
1255 | TEST(YAMLIO, TestReadWriteBlockScalarValue) { |
1256 | std::string intermediate; |
1257 | { |
1258 | MultilineStringType doc; |
1259 | doc.str = "Just a block\nscalar doc" ; |
1260 | |
1261 | llvm::raw_string_ostream ostr(intermediate); |
1262 | Output yout(ostr); |
1263 | yout << doc; |
1264 | } |
1265 | { |
1266 | Input yin(intermediate); |
1267 | MultilineStringType doc; |
1268 | yin >> doc; |
1269 | |
1270 | EXPECT_FALSE(yin.error()); |
1271 | EXPECT_EQ(doc.str, "Just a block\nscalar doc\n" ); |
1272 | } |
1273 | } |
1274 | |
1275 | //===----------------------------------------------------------------------===// |
1276 | // Test flow sequences |
1277 | //===----------------------------------------------------------------------===// |
1278 | |
1279 | LLVM_YAML_STRONG_TYPEDEF(int, MyNumber) |
1280 | LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(MyNumber) |
1281 | LLVM_YAML_STRONG_TYPEDEF(llvm::StringRef, MyString) |
1282 | LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(MyString) |
1283 | |
1284 | namespace llvm { |
1285 | namespace yaml { |
1286 | template<> |
1287 | struct ScalarTraits<MyNumber> { |
1288 | static void output(const MyNumber &value, void *, llvm::raw_ostream &out) { |
1289 | out << value; |
1290 | } |
1291 | |
1292 | static StringRef input(StringRef scalar, void *, MyNumber &value) { |
1293 | long long n; |
1294 | if ( getAsSignedInteger(Str: scalar, Radix: 0, Result&: n) ) |
1295 | return "invalid number" ; |
1296 | value = n; |
1297 | return StringRef(); |
1298 | } |
1299 | |
1300 | static QuotingType mustQuote(StringRef) { return QuotingType::None; } |
1301 | }; |
1302 | |
1303 | template <> struct ScalarTraits<MyString> { |
1304 | using Impl = ScalarTraits<StringRef>; |
1305 | static void output(const MyString &V, void *Ctx, raw_ostream &OS) { |
1306 | Impl::output(V, Ctx, OS); |
1307 | } |
1308 | static StringRef input(StringRef S, void *Ctx, MyString &V) { |
1309 | return Impl::input(S, Ctx, V.value); |
1310 | } |
1311 | static QuotingType mustQuote(StringRef S) { |
1312 | return Impl::mustQuote(S); |
1313 | } |
1314 | }; |
1315 | } |
1316 | } |
1317 | |
1318 | struct NameAndNumbers { |
1319 | llvm::StringRef name; |
1320 | std::vector<MyString> strings; |
1321 | std::vector<MyNumber> single; |
1322 | std::vector<MyNumber> numbers; |
1323 | }; |
1324 | |
1325 | namespace llvm { |
1326 | namespace yaml { |
1327 | template <> |
1328 | struct MappingTraits<NameAndNumbers> { |
1329 | static void mapping(IO &io, NameAndNumbers& nn) { |
1330 | io.mapRequired(Key: "name" , Val&: nn.name); |
1331 | io.mapRequired(Key: "strings" , Val&: nn.strings); |
1332 | io.mapRequired(Key: "single" , Val&: nn.single); |
1333 | io.mapRequired(Key: "numbers" , Val&: nn.numbers); |
1334 | } |
1335 | }; |
1336 | } |
1337 | } |
1338 | |
1339 | typedef std::vector<MyNumber> MyNumberFlowSequence; |
1340 | |
1341 | LLVM_YAML_IS_SEQUENCE_VECTOR(MyNumberFlowSequence) |
1342 | |
1343 | struct NameAndNumbersFlow { |
1344 | llvm::StringRef name; |
1345 | std::vector<MyNumberFlowSequence> sequenceOfNumbers; |
1346 | }; |
1347 | |
1348 | namespace llvm { |
1349 | namespace yaml { |
1350 | template <> |
1351 | struct MappingTraits<NameAndNumbersFlow> { |
1352 | static void mapping(IO &io, NameAndNumbersFlow& nn) { |
1353 | io.mapRequired(Key: "name" , Val&: nn.name); |
1354 | io.mapRequired(Key: "sequenceOfNumbers" , Val&: nn.sequenceOfNumbers); |
1355 | } |
1356 | }; |
1357 | } |
1358 | } |
1359 | |
1360 | // |
1361 | // Test writing then reading back custom values |
1362 | // |
1363 | TEST(YAMLIO, TestReadWriteMyFlowSequence) { |
1364 | std::string intermediate; |
1365 | { |
1366 | NameAndNumbers map; |
1367 | map.name = "hello" ; |
1368 | map.strings.push_back(x: llvm::StringRef("one" )); |
1369 | map.strings.push_back(x: llvm::StringRef("two" )); |
1370 | map.single.push_back(x: 1); |
1371 | map.numbers.push_back(x: 10); |
1372 | map.numbers.push_back(x: -30); |
1373 | map.numbers.push_back(x: 1024); |
1374 | |
1375 | llvm::raw_string_ostream ostr(intermediate); |
1376 | Output yout(ostr); |
1377 | yout << map; |
1378 | |
1379 | // Verify sequences were written in flow style |
1380 | ostr.flush(); |
1381 | llvm::StringRef flowOut(intermediate); |
1382 | EXPECT_NE(llvm::StringRef::npos, flowOut.find("one, two" )); |
1383 | EXPECT_NE(llvm::StringRef::npos, flowOut.find("10, -30, 1024" )); |
1384 | } |
1385 | |
1386 | { |
1387 | Input yin(intermediate); |
1388 | NameAndNumbers map2; |
1389 | yin >> map2; |
1390 | |
1391 | EXPECT_FALSE(yin.error()); |
1392 | EXPECT_TRUE(map2.name.equals("hello" )); |
1393 | EXPECT_EQ(map2.strings.size(), 2UL); |
1394 | EXPECT_TRUE(map2.strings[0].value.equals("one" )); |
1395 | EXPECT_TRUE(map2.strings[1].value.equals("two" )); |
1396 | EXPECT_EQ(map2.single.size(), 1UL); |
1397 | EXPECT_EQ(1, map2.single[0]); |
1398 | EXPECT_EQ(map2.numbers.size(), 3UL); |
1399 | EXPECT_EQ(10, map2.numbers[0]); |
1400 | EXPECT_EQ(-30, map2.numbers[1]); |
1401 | EXPECT_EQ(1024, map2.numbers[2]); |
1402 | } |
1403 | } |
1404 | |
1405 | |
1406 | // |
1407 | // Test writing then reading back a sequence of flow sequences. |
1408 | // |
1409 | TEST(YAMLIO, TestReadWriteSequenceOfMyFlowSequence) { |
1410 | std::string intermediate; |
1411 | { |
1412 | NameAndNumbersFlow map; |
1413 | map.name = "hello" ; |
1414 | MyNumberFlowSequence single = { 0 }; |
1415 | MyNumberFlowSequence numbers = { 12, 1, -512 }; |
1416 | map.sequenceOfNumbers.push_back(x: single); |
1417 | map.sequenceOfNumbers.push_back(x: numbers); |
1418 | map.sequenceOfNumbers.push_back(x: MyNumberFlowSequence()); |
1419 | |
1420 | llvm::raw_string_ostream ostr(intermediate); |
1421 | Output yout(ostr); |
1422 | yout << map; |
1423 | |
1424 | // Verify sequences were written in flow style |
1425 | // and that the parent sequence used '-'. |
1426 | ostr.flush(); |
1427 | llvm::StringRef flowOut(intermediate); |
1428 | EXPECT_NE(llvm::StringRef::npos, flowOut.find("- [ 0 ]" )); |
1429 | EXPECT_NE(llvm::StringRef::npos, flowOut.find("- [ 12, 1, -512 ]" )); |
1430 | EXPECT_NE(llvm::StringRef::npos, flowOut.find("- [ ]" )); |
1431 | } |
1432 | |
1433 | { |
1434 | Input yin(intermediate); |
1435 | NameAndNumbersFlow map2; |
1436 | yin >> map2; |
1437 | |
1438 | EXPECT_FALSE(yin.error()); |
1439 | EXPECT_TRUE(map2.name.equals("hello" )); |
1440 | EXPECT_EQ(map2.sequenceOfNumbers.size(), 3UL); |
1441 | EXPECT_EQ(map2.sequenceOfNumbers[0].size(), 1UL); |
1442 | EXPECT_EQ(0, map2.sequenceOfNumbers[0][0]); |
1443 | EXPECT_EQ(map2.sequenceOfNumbers[1].size(), 3UL); |
1444 | EXPECT_EQ(12, map2.sequenceOfNumbers[1][0]); |
1445 | EXPECT_EQ(1, map2.sequenceOfNumbers[1][1]); |
1446 | EXPECT_EQ(-512, map2.sequenceOfNumbers[1][2]); |
1447 | EXPECT_TRUE(map2.sequenceOfNumbers[2].empty()); |
1448 | } |
1449 | } |
1450 | |
1451 | //===----------------------------------------------------------------------===// |
1452 | // Test normalizing/denormalizing |
1453 | //===----------------------------------------------------------------------===// |
1454 | |
1455 | LLVM_YAML_STRONG_TYPEDEF(uint32_t, TotalSeconds) |
1456 | |
1457 | typedef std::vector<TotalSeconds> SecondsSequence; |
1458 | |
1459 | LLVM_YAML_IS_SEQUENCE_VECTOR(TotalSeconds) |
1460 | |
1461 | |
1462 | namespace llvm { |
1463 | namespace yaml { |
1464 | template <> |
1465 | struct MappingTraits<TotalSeconds> { |
1466 | |
1467 | class NormalizedSeconds { |
1468 | public: |
1469 | NormalizedSeconds(IO &io) |
1470 | : hours(0), minutes(0), seconds(0) { |
1471 | } |
1472 | NormalizedSeconds(IO &, TotalSeconds &secs) |
1473 | : hours(secs/3600), |
1474 | minutes((secs - (hours*3600))/60), |
1475 | seconds(secs % 60) { |
1476 | } |
1477 | TotalSeconds denormalize(IO &) { |
1478 | return TotalSeconds(hours*3600 + minutes*60 + seconds); |
1479 | } |
1480 | |
1481 | uint32_t hours; |
1482 | uint8_t minutes; |
1483 | uint8_t seconds; |
1484 | }; |
1485 | |
1486 | static void mapping(IO &io, TotalSeconds &secs) { |
1487 | MappingNormalization<NormalizedSeconds, TotalSeconds> keys(io, secs); |
1488 | |
1489 | io.mapOptional(Key: "hours" , Val&: keys->hours, Default: 0); |
1490 | io.mapOptional(Key: "minutes" , Val&: keys->minutes, Default: 0); |
1491 | io.mapRequired(Key: "seconds" , Val&: keys->seconds); |
1492 | } |
1493 | }; |
1494 | } |
1495 | } |
1496 | |
1497 | |
1498 | // |
1499 | // Test the reading of a yaml sequence of mappings |
1500 | // |
1501 | TEST(YAMLIO, TestReadMySecondsSequence) { |
1502 | SecondsSequence seq; |
1503 | Input yin("---\n - hours: 1\n seconds: 5\n - seconds: 59\n...\n" ); |
1504 | yin >> seq; |
1505 | |
1506 | EXPECT_FALSE(yin.error()); |
1507 | EXPECT_EQ(seq.size(), 2UL); |
1508 | EXPECT_EQ(seq[0], 3605U); |
1509 | EXPECT_EQ(seq[1], 59U); |
1510 | } |
1511 | |
1512 | |
1513 | // |
1514 | // Test writing then reading back custom values |
1515 | // |
1516 | TEST(YAMLIO, TestReadWriteMySecondsSequence) { |
1517 | std::string intermediate; |
1518 | { |
1519 | SecondsSequence seq; |
1520 | seq.push_back(x: 4000); |
1521 | seq.push_back(x: 500); |
1522 | seq.push_back(x: 59); |
1523 | |
1524 | llvm::raw_string_ostream ostr(intermediate); |
1525 | Output yout(ostr); |
1526 | yout << seq; |
1527 | } |
1528 | { |
1529 | Input yin(intermediate); |
1530 | SecondsSequence seq2; |
1531 | yin >> seq2; |
1532 | |
1533 | EXPECT_FALSE(yin.error()); |
1534 | EXPECT_EQ(seq2.size(), 3UL); |
1535 | EXPECT_EQ(seq2[0], 4000U); |
1536 | EXPECT_EQ(seq2[1], 500U); |
1537 | EXPECT_EQ(seq2[2], 59U); |
1538 | } |
1539 | } |
1540 | |
1541 | |
1542 | //===----------------------------------------------------------------------===// |
1543 | // Test dynamic typing |
1544 | //===----------------------------------------------------------------------===// |
1545 | |
1546 | enum AFlags { |
1547 | a1, |
1548 | a2, |
1549 | a3 |
1550 | }; |
1551 | |
1552 | enum BFlags { |
1553 | b1, |
1554 | b2, |
1555 | b3 |
1556 | }; |
1557 | |
1558 | enum Kind { |
1559 | kindA, |
1560 | kindB |
1561 | }; |
1562 | |
1563 | struct KindAndFlags { |
1564 | KindAndFlags() : kind(kindA), flags(0) { } |
1565 | KindAndFlags(Kind k, uint32_t f) : kind(k), flags(f) { } |
1566 | Kind kind; |
1567 | uint32_t flags; |
1568 | }; |
1569 | |
1570 | typedef std::vector<KindAndFlags> KindAndFlagsSequence; |
1571 | |
1572 | LLVM_YAML_IS_SEQUENCE_VECTOR(KindAndFlags) |
1573 | |
1574 | namespace llvm { |
1575 | namespace yaml { |
1576 | template <> |
1577 | struct ScalarEnumerationTraits<AFlags> { |
1578 | static void enumeration(IO &io, AFlags &value) { |
1579 | io.enumCase(Val&: value, Str: "a1" , ConstVal: a1); |
1580 | io.enumCase(Val&: value, Str: "a2" , ConstVal: a2); |
1581 | io.enumCase(Val&: value, Str: "a3" , ConstVal: a3); |
1582 | } |
1583 | }; |
1584 | template <> |
1585 | struct ScalarEnumerationTraits<BFlags> { |
1586 | static void enumeration(IO &io, BFlags &value) { |
1587 | io.enumCase(Val&: value, Str: "b1" , ConstVal: b1); |
1588 | io.enumCase(Val&: value, Str: "b2" , ConstVal: b2); |
1589 | io.enumCase(Val&: value, Str: "b3" , ConstVal: b3); |
1590 | } |
1591 | }; |
1592 | template <> |
1593 | struct ScalarEnumerationTraits<Kind> { |
1594 | static void enumeration(IO &io, Kind &value) { |
1595 | io.enumCase(Val&: value, Str: "A" , ConstVal: kindA); |
1596 | io.enumCase(Val&: value, Str: "B" , ConstVal: kindB); |
1597 | } |
1598 | }; |
1599 | template <> |
1600 | struct MappingTraits<KindAndFlags> { |
1601 | static void mapping(IO &io, KindAndFlags& kf) { |
1602 | io.mapRequired(Key: "kind" , Val&: kf.kind); |
1603 | // Type of "flags" field varies depending on "kind" field. |
1604 | // Use memcpy here to avoid breaking strict aliasing rules. |
1605 | if (kf.kind == kindA) { |
1606 | AFlags aflags = static_cast<AFlags>(kf.flags); |
1607 | io.mapRequired(Key: "flags" , Val&: aflags); |
1608 | kf.flags = aflags; |
1609 | } else { |
1610 | BFlags bflags = static_cast<BFlags>(kf.flags); |
1611 | io.mapRequired(Key: "flags" , Val&: bflags); |
1612 | kf.flags = bflags; |
1613 | } |
1614 | } |
1615 | }; |
1616 | } |
1617 | } |
1618 | |
1619 | |
1620 | // |
1621 | // Test the reading of a yaml sequence dynamic types |
1622 | // |
1623 | TEST(YAMLIO, TestReadKindAndFlagsSequence) { |
1624 | KindAndFlagsSequence seq; |
1625 | Input yin("---\n - kind: A\n flags: a2\n - kind: B\n flags: b1\n...\n" ); |
1626 | yin >> seq; |
1627 | |
1628 | EXPECT_FALSE(yin.error()); |
1629 | EXPECT_EQ(seq.size(), 2UL); |
1630 | EXPECT_EQ(seq[0].kind, kindA); |
1631 | EXPECT_EQ(seq[0].flags, (uint32_t)a2); |
1632 | EXPECT_EQ(seq[1].kind, kindB); |
1633 | EXPECT_EQ(seq[1].flags, (uint32_t)b1); |
1634 | } |
1635 | |
1636 | // |
1637 | // Test writing then reading back dynamic types |
1638 | // |
1639 | TEST(YAMLIO, TestReadWriteKindAndFlagsSequence) { |
1640 | std::string intermediate; |
1641 | { |
1642 | KindAndFlagsSequence seq; |
1643 | seq.push_back(x: KindAndFlags(kindA,a1)); |
1644 | seq.push_back(x: KindAndFlags(kindB,b1)); |
1645 | seq.push_back(x: KindAndFlags(kindA,a2)); |
1646 | seq.push_back(x: KindAndFlags(kindB,b2)); |
1647 | seq.push_back(x: KindAndFlags(kindA,a3)); |
1648 | |
1649 | llvm::raw_string_ostream ostr(intermediate); |
1650 | Output yout(ostr); |
1651 | yout << seq; |
1652 | } |
1653 | { |
1654 | Input yin(intermediate); |
1655 | KindAndFlagsSequence seq2; |
1656 | yin >> seq2; |
1657 | |
1658 | EXPECT_FALSE(yin.error()); |
1659 | EXPECT_EQ(seq2.size(), 5UL); |
1660 | EXPECT_EQ(seq2[0].kind, kindA); |
1661 | EXPECT_EQ(seq2[0].flags, (uint32_t)a1); |
1662 | EXPECT_EQ(seq2[1].kind, kindB); |
1663 | EXPECT_EQ(seq2[1].flags, (uint32_t)b1); |
1664 | EXPECT_EQ(seq2[2].kind, kindA); |
1665 | EXPECT_EQ(seq2[2].flags, (uint32_t)a2); |
1666 | EXPECT_EQ(seq2[3].kind, kindB); |
1667 | EXPECT_EQ(seq2[3].flags, (uint32_t)b2); |
1668 | EXPECT_EQ(seq2[4].kind, kindA); |
1669 | EXPECT_EQ(seq2[4].flags, (uint32_t)a3); |
1670 | } |
1671 | } |
1672 | |
1673 | |
1674 | //===----------------------------------------------------------------------===// |
1675 | // Test document list |
1676 | //===----------------------------------------------------------------------===// |
1677 | |
1678 | struct FooBarMap { |
1679 | int foo; |
1680 | int bar; |
1681 | }; |
1682 | typedef std::vector<FooBarMap> FooBarMapDocumentList; |
1683 | |
1684 | LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(FooBarMap) |
1685 | |
1686 | |
1687 | namespace llvm { |
1688 | namespace yaml { |
1689 | template <> |
1690 | struct MappingTraits<FooBarMap> { |
1691 | static void mapping(IO &io, FooBarMap& fb) { |
1692 | io.mapRequired(Key: "foo" , Val&: fb.foo); |
1693 | io.mapRequired(Key: "bar" , Val&: fb.bar); |
1694 | } |
1695 | }; |
1696 | } |
1697 | } |
1698 | |
1699 | |
1700 | // |
1701 | // Test the reading of a yaml mapping |
1702 | // |
1703 | TEST(YAMLIO, TestDocRead) { |
1704 | FooBarMap doc; |
1705 | Input yin("---\nfoo: 3\nbar: 5\n...\n" ); |
1706 | yin >> doc; |
1707 | |
1708 | EXPECT_FALSE(yin.error()); |
1709 | EXPECT_EQ(doc.foo, 3); |
1710 | EXPECT_EQ(doc.bar,5); |
1711 | } |
1712 | |
1713 | |
1714 | |
1715 | // |
1716 | // Test writing then reading back a sequence of mappings |
1717 | // |
1718 | TEST(YAMLIO, TestSequenceDocListWriteAndRead) { |
1719 | std::string intermediate; |
1720 | { |
1721 | FooBarMap doc1; |
1722 | doc1.foo = 10; |
1723 | doc1.bar = -3; |
1724 | FooBarMap doc2; |
1725 | doc2.foo = 257; |
1726 | doc2.bar = 0; |
1727 | std::vector<FooBarMap> docList; |
1728 | docList.push_back(x: doc1); |
1729 | docList.push_back(x: doc2); |
1730 | |
1731 | llvm::raw_string_ostream ostr(intermediate); |
1732 | Output yout(ostr); |
1733 | yout << docList; |
1734 | } |
1735 | |
1736 | |
1737 | { |
1738 | Input yin(intermediate); |
1739 | std::vector<FooBarMap> docList2; |
1740 | yin >> docList2; |
1741 | |
1742 | EXPECT_FALSE(yin.error()); |
1743 | EXPECT_EQ(docList2.size(), 2UL); |
1744 | FooBarMap& map1 = docList2[0]; |
1745 | FooBarMap& map2 = docList2[1]; |
1746 | EXPECT_EQ(map1.foo, 10); |
1747 | EXPECT_EQ(map1.bar, -3); |
1748 | EXPECT_EQ(map2.foo, 257); |
1749 | EXPECT_EQ(map2.bar, 0); |
1750 | } |
1751 | } |
1752 | |
1753 | //===----------------------------------------------------------------------===// |
1754 | // Test document tags |
1755 | //===----------------------------------------------------------------------===// |
1756 | |
1757 | struct MyDouble { |
1758 | MyDouble() : value(0.0) { } |
1759 | MyDouble(double x) : value(x) { } |
1760 | double value; |
1761 | }; |
1762 | |
1763 | LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(MyDouble) |
1764 | |
1765 | |
1766 | namespace llvm { |
1767 | namespace yaml { |
1768 | template <> |
1769 | struct MappingTraits<MyDouble> { |
1770 | static void mapping(IO &io, MyDouble &d) { |
1771 | if (io.mapTag(Tag: "!decimal" , Default: true)) { |
1772 | mappingDecimal(io, d); |
1773 | } else if (io.mapTag(Tag: "!fraction" )) { |
1774 | mappingFraction(io, d); |
1775 | } |
1776 | } |
1777 | static void mappingDecimal(IO &io, MyDouble &d) { |
1778 | io.mapRequired(Key: "value" , Val&: d.value); |
1779 | } |
1780 | static void mappingFraction(IO &io, MyDouble &d) { |
1781 | double num, denom; |
1782 | io.mapRequired(Key: "numerator" , Val&: num); |
1783 | io.mapRequired(Key: "denominator" , Val&: denom); |
1784 | // convert fraction to double |
1785 | d.value = num/denom; |
1786 | } |
1787 | }; |
1788 | } |
1789 | } |
1790 | |
1791 | |
1792 | // |
1793 | // Test the reading of two different tagged yaml documents. |
1794 | // |
1795 | TEST(YAMLIO, TestTaggedDocuments) { |
1796 | std::vector<MyDouble> docList; |
1797 | Input yin("--- !decimal\nvalue: 3.0\n" |
1798 | "--- !fraction\nnumerator: 9.0\ndenominator: 2\n...\n" ); |
1799 | yin >> docList; |
1800 | EXPECT_FALSE(yin.error()); |
1801 | EXPECT_EQ(docList.size(), 2UL); |
1802 | EXPECT_EQ(docList[0].value, 3.0); |
1803 | EXPECT_EQ(docList[1].value, 4.5); |
1804 | } |
1805 | |
1806 | |
1807 | |
1808 | // |
1809 | // Test writing then reading back tagged documents |
1810 | // |
1811 | TEST(YAMLIO, TestTaggedDocumentsWriteAndRead) { |
1812 | std::string intermediate; |
1813 | { |
1814 | MyDouble a(10.25); |
1815 | MyDouble b(-3.75); |
1816 | std::vector<MyDouble> docList; |
1817 | docList.push_back(x: a); |
1818 | docList.push_back(x: b); |
1819 | |
1820 | llvm::raw_string_ostream ostr(intermediate); |
1821 | Output yout(ostr); |
1822 | yout << docList; |
1823 | } |
1824 | |
1825 | { |
1826 | Input yin(intermediate); |
1827 | std::vector<MyDouble> docList2; |
1828 | yin >> docList2; |
1829 | |
1830 | EXPECT_FALSE(yin.error()); |
1831 | EXPECT_EQ(docList2.size(), 2UL); |
1832 | EXPECT_EQ(docList2[0].value, 10.25); |
1833 | EXPECT_EQ(docList2[1].value, -3.75); |
1834 | } |
1835 | } |
1836 | |
1837 | |
1838 | //===----------------------------------------------------------------------===// |
1839 | // Test mapping validation |
1840 | //===----------------------------------------------------------------------===// |
1841 | |
1842 | struct MyValidation { |
1843 | double value; |
1844 | }; |
1845 | |
1846 | LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(MyValidation) |
1847 | |
1848 | namespace llvm { |
1849 | namespace yaml { |
1850 | template <> |
1851 | struct MappingTraits<MyValidation> { |
1852 | static void mapping(IO &io, MyValidation &d) { |
1853 | io.mapRequired(Key: "value" , Val&: d.value); |
1854 | } |
1855 | static std::string validate(IO &io, MyValidation &d) { |
1856 | if (d.value < 0) |
1857 | return "negative value" ; |
1858 | return {}; |
1859 | } |
1860 | }; |
1861 | } |
1862 | } |
1863 | |
1864 | |
1865 | // |
1866 | // Test that validate() is called and complains about the negative value. |
1867 | // |
1868 | TEST(YAMLIO, TestValidatingInput) { |
1869 | std::vector<MyValidation> docList; |
1870 | Input yin("--- \nvalue: 3.0\n" |
1871 | "--- \nvalue: -1.0\n...\n" , |
1872 | nullptr, suppressErrorMessages); |
1873 | yin >> docList; |
1874 | EXPECT_TRUE(!!yin.error()); |
1875 | } |
1876 | |
1877 | //===----------------------------------------------------------------------===// |
1878 | // Test flow mapping |
1879 | //===----------------------------------------------------------------------===// |
1880 | |
1881 | struct FlowFooBar { |
1882 | int foo; |
1883 | int bar; |
1884 | |
1885 | FlowFooBar() : foo(0), bar(0) {} |
1886 | FlowFooBar(int foo, int bar) : foo(foo), bar(bar) {} |
1887 | }; |
1888 | |
1889 | typedef std::vector<FlowFooBar> FlowFooBarSequence; |
1890 | |
1891 | LLVM_YAML_IS_SEQUENCE_VECTOR(FlowFooBar) |
1892 | |
1893 | struct FlowFooBarDoc { |
1894 | FlowFooBar attribute; |
1895 | FlowFooBarSequence seq; |
1896 | }; |
1897 | |
1898 | namespace llvm { |
1899 | namespace yaml { |
1900 | template <> |
1901 | struct MappingTraits<FlowFooBar> { |
1902 | static void mapping(IO &io, FlowFooBar &fb) { |
1903 | io.mapRequired(Key: "foo" , Val&: fb.foo); |
1904 | io.mapRequired(Key: "bar" , Val&: fb.bar); |
1905 | } |
1906 | |
1907 | static const bool flow = true; |
1908 | }; |
1909 | |
1910 | template <> |
1911 | struct MappingTraits<FlowFooBarDoc> { |
1912 | static void mapping(IO &io, FlowFooBarDoc &fb) { |
1913 | io.mapRequired(Key: "attribute" , Val&: fb.attribute); |
1914 | io.mapRequired(Key: "seq" , Val&: fb.seq); |
1915 | } |
1916 | }; |
1917 | } |
1918 | } |
1919 | |
1920 | // |
1921 | // Test writing then reading back custom mappings |
1922 | // |
1923 | TEST(YAMLIO, TestReadWriteMyFlowMapping) { |
1924 | std::string intermediate; |
1925 | { |
1926 | FlowFooBarDoc doc; |
1927 | doc.attribute = FlowFooBar(42, 907); |
1928 | doc.seq.push_back(x: FlowFooBar(1, 2)); |
1929 | doc.seq.push_back(x: FlowFooBar(0, 0)); |
1930 | doc.seq.push_back(x: FlowFooBar(-1, 1024)); |
1931 | |
1932 | llvm::raw_string_ostream ostr(intermediate); |
1933 | Output yout(ostr); |
1934 | yout << doc; |
1935 | |
1936 | // Verify that mappings were written in flow style |
1937 | ostr.flush(); |
1938 | llvm::StringRef flowOut(intermediate); |
1939 | EXPECT_NE(llvm::StringRef::npos, flowOut.find("{ foo: 42, bar: 907 }" )); |
1940 | EXPECT_NE(llvm::StringRef::npos, flowOut.find("- { foo: 1, bar: 2 }" )); |
1941 | EXPECT_NE(llvm::StringRef::npos, flowOut.find("- { foo: 0, bar: 0 }" )); |
1942 | EXPECT_NE(llvm::StringRef::npos, flowOut.find("- { foo: -1, bar: 1024 }" )); |
1943 | } |
1944 | |
1945 | { |
1946 | Input yin(intermediate); |
1947 | FlowFooBarDoc doc2; |
1948 | yin >> doc2; |
1949 | |
1950 | EXPECT_FALSE(yin.error()); |
1951 | EXPECT_EQ(doc2.attribute.foo, 42); |
1952 | EXPECT_EQ(doc2.attribute.bar, 907); |
1953 | EXPECT_EQ(doc2.seq.size(), 3UL); |
1954 | EXPECT_EQ(doc2.seq[0].foo, 1); |
1955 | EXPECT_EQ(doc2.seq[0].bar, 2); |
1956 | EXPECT_EQ(doc2.seq[1].foo, 0); |
1957 | EXPECT_EQ(doc2.seq[1].bar, 0); |
1958 | EXPECT_EQ(doc2.seq[2].foo, -1); |
1959 | EXPECT_EQ(doc2.seq[2].bar, 1024); |
1960 | } |
1961 | } |
1962 | |
1963 | //===----------------------------------------------------------------------===// |
1964 | // Test error handling |
1965 | //===----------------------------------------------------------------------===// |
1966 | |
1967 | // |
1968 | // Test error handling of unknown enumerated scalar |
1969 | // |
1970 | TEST(YAMLIO, TestColorsReadError) { |
1971 | ColorMap map; |
1972 | Input yin("---\n" |
1973 | "c1: blue\n" |
1974 | "c2: purple\n" |
1975 | "c3: green\n" |
1976 | "...\n" , |
1977 | /*Ctxt=*/nullptr, |
1978 | suppressErrorMessages); |
1979 | yin >> map; |
1980 | EXPECT_TRUE(!!yin.error()); |
1981 | } |
1982 | |
1983 | |
1984 | // |
1985 | // Test error handling of flow sequence with unknown value |
1986 | // |
1987 | TEST(YAMLIO, TestFlagsReadError) { |
1988 | FlagsMap map; |
1989 | Input yin("---\n" |
1990 | "f1: [ big ]\n" |
1991 | "f2: [ round, hollow ]\n" |
1992 | "f3: []\n" |
1993 | "...\n" , |
1994 | /*Ctxt=*/nullptr, |
1995 | suppressErrorMessages); |
1996 | yin >> map; |
1997 | |
1998 | EXPECT_TRUE(!!yin.error()); |
1999 | } |
2000 | |
2001 | |
2002 | // |
2003 | // Test error handling reading built-in uint8_t type |
2004 | // |
2005 | TEST(YAMLIO, TestReadBuiltInTypesUint8Error) { |
2006 | std::vector<uint8_t> seq; |
2007 | Input yin("---\n" |
2008 | "- 255\n" |
2009 | "- 0\n" |
2010 | "- 257\n" |
2011 | "...\n" , |
2012 | /*Ctxt=*/nullptr, |
2013 | suppressErrorMessages); |
2014 | yin >> seq; |
2015 | |
2016 | EXPECT_TRUE(!!yin.error()); |
2017 | } |
2018 | |
2019 | |
2020 | // |
2021 | // Test error handling reading built-in uint16_t type |
2022 | // |
2023 | TEST(YAMLIO, TestReadBuiltInTypesUint16Error) { |
2024 | std::vector<uint16_t> seq; |
2025 | Input yin("---\n" |
2026 | "- 65535\n" |
2027 | "- 0\n" |
2028 | "- 66000\n" |
2029 | "...\n" , |
2030 | /*Ctxt=*/nullptr, |
2031 | suppressErrorMessages); |
2032 | yin >> seq; |
2033 | |
2034 | EXPECT_TRUE(!!yin.error()); |
2035 | } |
2036 | |
2037 | |
2038 | // |
2039 | // Test error handling reading built-in uint32_t type |
2040 | // |
2041 | TEST(YAMLIO, TestReadBuiltInTypesUint32Error) { |
2042 | std::vector<uint32_t> seq; |
2043 | Input yin("---\n" |
2044 | "- 4000000000\n" |
2045 | "- 0\n" |
2046 | "- 5000000000\n" |
2047 | "...\n" , |
2048 | /*Ctxt=*/nullptr, |
2049 | suppressErrorMessages); |
2050 | yin >> seq; |
2051 | |
2052 | EXPECT_TRUE(!!yin.error()); |
2053 | } |
2054 | |
2055 | |
2056 | // |
2057 | // Test error handling reading built-in uint64_t type |
2058 | // |
2059 | TEST(YAMLIO, TestReadBuiltInTypesUint64Error) { |
2060 | std::vector<uint64_t> seq; |
2061 | Input yin("---\n" |
2062 | "- 18446744073709551615\n" |
2063 | "- 0\n" |
2064 | "- 19446744073709551615\n" |
2065 | "...\n" , |
2066 | /*Ctxt=*/nullptr, |
2067 | suppressErrorMessages); |
2068 | yin >> seq; |
2069 | |
2070 | EXPECT_TRUE(!!yin.error()); |
2071 | } |
2072 | |
2073 | |
2074 | // |
2075 | // Test error handling reading built-in int8_t type |
2076 | // |
2077 | TEST(YAMLIO, TestReadBuiltInTypesint8OverError) { |
2078 | std::vector<int8_t> seq; |
2079 | Input yin("---\n" |
2080 | "- -128\n" |
2081 | "- 0\n" |
2082 | "- 127\n" |
2083 | "- 128\n" |
2084 | "...\n" , |
2085 | /*Ctxt=*/nullptr, |
2086 | suppressErrorMessages); |
2087 | yin >> seq; |
2088 | |
2089 | EXPECT_TRUE(!!yin.error()); |
2090 | } |
2091 | |
2092 | // |
2093 | // Test error handling reading built-in int8_t type |
2094 | // |
2095 | TEST(YAMLIO, TestReadBuiltInTypesint8UnderError) { |
2096 | std::vector<int8_t> seq; |
2097 | Input yin("---\n" |
2098 | "- -128\n" |
2099 | "- 0\n" |
2100 | "- 127\n" |
2101 | "- -129\n" |
2102 | "...\n" , |
2103 | /*Ctxt=*/nullptr, |
2104 | suppressErrorMessages); |
2105 | yin >> seq; |
2106 | |
2107 | EXPECT_TRUE(!!yin.error()); |
2108 | } |
2109 | |
2110 | |
2111 | // |
2112 | // Test error handling reading built-in int16_t type |
2113 | // |
2114 | TEST(YAMLIO, TestReadBuiltInTypesint16UnderError) { |
2115 | std::vector<int16_t> seq; |
2116 | Input yin("---\n" |
2117 | "- 32767\n" |
2118 | "- 0\n" |
2119 | "- -32768\n" |
2120 | "- -32769\n" |
2121 | "...\n" , |
2122 | /*Ctxt=*/nullptr, |
2123 | suppressErrorMessages); |
2124 | yin >> seq; |
2125 | |
2126 | EXPECT_TRUE(!!yin.error()); |
2127 | } |
2128 | |
2129 | |
2130 | // |
2131 | // Test error handling reading built-in int16_t type |
2132 | // |
2133 | TEST(YAMLIO, TestReadBuiltInTypesint16OverError) { |
2134 | std::vector<int16_t> seq; |
2135 | Input yin("---\n" |
2136 | "- 32767\n" |
2137 | "- 0\n" |
2138 | "- -32768\n" |
2139 | "- 32768\n" |
2140 | "...\n" , |
2141 | /*Ctxt=*/nullptr, |
2142 | suppressErrorMessages); |
2143 | yin >> seq; |
2144 | |
2145 | EXPECT_TRUE(!!yin.error()); |
2146 | } |
2147 | |
2148 | |
2149 | // |
2150 | // Test error handling reading built-in int32_t type |
2151 | // |
2152 | TEST(YAMLIO, TestReadBuiltInTypesint32UnderError) { |
2153 | std::vector<int32_t> seq; |
2154 | Input yin("---\n" |
2155 | "- 2147483647\n" |
2156 | "- 0\n" |
2157 | "- -2147483648\n" |
2158 | "- -2147483649\n" |
2159 | "...\n" , |
2160 | /*Ctxt=*/nullptr, |
2161 | suppressErrorMessages); |
2162 | yin >> seq; |
2163 | |
2164 | EXPECT_TRUE(!!yin.error()); |
2165 | } |
2166 | |
2167 | // |
2168 | // Test error handling reading built-in int32_t type |
2169 | // |
2170 | TEST(YAMLIO, TestReadBuiltInTypesint32OverError) { |
2171 | std::vector<int32_t> seq; |
2172 | Input yin("---\n" |
2173 | "- 2147483647\n" |
2174 | "- 0\n" |
2175 | "- -2147483648\n" |
2176 | "- 2147483649\n" |
2177 | "...\n" , |
2178 | /*Ctxt=*/nullptr, |
2179 | suppressErrorMessages); |
2180 | yin >> seq; |
2181 | |
2182 | EXPECT_TRUE(!!yin.error()); |
2183 | } |
2184 | |
2185 | |
2186 | // |
2187 | // Test error handling reading built-in int64_t type |
2188 | // |
2189 | TEST(YAMLIO, TestReadBuiltInTypesint64UnderError) { |
2190 | std::vector<int64_t> seq; |
2191 | Input yin("---\n" |
2192 | "- -9223372036854775808\n" |
2193 | "- 0\n" |
2194 | "- 9223372036854775807\n" |
2195 | "- -9223372036854775809\n" |
2196 | "...\n" , |
2197 | /*Ctxt=*/nullptr, |
2198 | suppressErrorMessages); |
2199 | yin >> seq; |
2200 | |
2201 | EXPECT_TRUE(!!yin.error()); |
2202 | } |
2203 | |
2204 | // |
2205 | // Test error handling reading built-in int64_t type |
2206 | // |
2207 | TEST(YAMLIO, TestReadBuiltInTypesint64OverError) { |
2208 | std::vector<int64_t> seq; |
2209 | Input yin("---\n" |
2210 | "- -9223372036854775808\n" |
2211 | "- 0\n" |
2212 | "- 9223372036854775807\n" |
2213 | "- 9223372036854775809\n" |
2214 | "...\n" , |
2215 | /*Ctxt=*/nullptr, |
2216 | suppressErrorMessages); |
2217 | yin >> seq; |
2218 | |
2219 | EXPECT_TRUE(!!yin.error()); |
2220 | } |
2221 | |
2222 | // |
2223 | // Test error handling reading built-in float type |
2224 | // |
2225 | TEST(YAMLIO, TestReadBuiltInTypesFloatError) { |
2226 | std::vector<float> seq; |
2227 | Input yin("---\n" |
2228 | "- 0.0\n" |
2229 | "- 1000.1\n" |
2230 | "- -123.456\n" |
2231 | "- 1.2.3\n" |
2232 | "...\n" , |
2233 | /*Ctxt=*/nullptr, |
2234 | suppressErrorMessages); |
2235 | yin >> seq; |
2236 | |
2237 | EXPECT_TRUE(!!yin.error()); |
2238 | } |
2239 | |
2240 | // |
2241 | // Test error handling reading built-in float type |
2242 | // |
2243 | TEST(YAMLIO, TestReadBuiltInTypesDoubleError) { |
2244 | std::vector<double> seq; |
2245 | Input yin("---\n" |
2246 | "- 0.0\n" |
2247 | "- 1000.1\n" |
2248 | "- -123.456\n" |
2249 | "- 1.2.3\n" |
2250 | "...\n" , |
2251 | /*Ctxt=*/nullptr, |
2252 | suppressErrorMessages); |
2253 | yin >> seq; |
2254 | |
2255 | EXPECT_TRUE(!!yin.error()); |
2256 | } |
2257 | |
2258 | // |
2259 | // Test error handling reading built-in Hex8 type |
2260 | // |
2261 | TEST(YAMLIO, TestReadBuiltInTypesHex8Error) { |
2262 | std::vector<Hex8> seq; |
2263 | Input yin("---\n" |
2264 | "- 0x12\n" |
2265 | "- 0xFE\n" |
2266 | "- 0x123\n" |
2267 | "...\n" , |
2268 | /*Ctxt=*/nullptr, |
2269 | suppressErrorMessages); |
2270 | yin >> seq; |
2271 | EXPECT_TRUE(!!yin.error()); |
2272 | |
2273 | std::vector<Hex8> seq2; |
2274 | Input yin2("---\n" |
2275 | "[ 0x12, 0xFE, 0x123 ]\n" |
2276 | "...\n" , |
2277 | /*Ctxt=*/nullptr, suppressErrorMessages); |
2278 | yin2 >> seq2; |
2279 | EXPECT_TRUE(!!yin2.error()); |
2280 | |
2281 | EXPECT_EQ(seq.size(), 3u); |
2282 | EXPECT_EQ(seq.size(), seq2.size()); |
2283 | for (size_t i = 0; i < seq.size(); ++i) |
2284 | EXPECT_EQ(seq[i], seq2[i]); |
2285 | } |
2286 | |
2287 | |
2288 | // |
2289 | // Test error handling reading built-in Hex16 type |
2290 | // |
2291 | TEST(YAMLIO, TestReadBuiltInTypesHex16Error) { |
2292 | std::vector<Hex16> seq; |
2293 | Input yin("---\n" |
2294 | "- 0x0012\n" |
2295 | "- 0xFEFF\n" |
2296 | "- 0x12345\n" |
2297 | "...\n" , |
2298 | /*Ctxt=*/nullptr, |
2299 | suppressErrorMessages); |
2300 | yin >> seq; |
2301 | EXPECT_TRUE(!!yin.error()); |
2302 | |
2303 | std::vector<Hex16> seq2; |
2304 | Input yin2("---\n" |
2305 | "[ 0x0012, 0xFEFF, 0x12345 ]\n" |
2306 | "...\n" , |
2307 | /*Ctxt=*/nullptr, suppressErrorMessages); |
2308 | yin2 >> seq2; |
2309 | EXPECT_TRUE(!!yin2.error()); |
2310 | |
2311 | EXPECT_EQ(seq.size(), 3u); |
2312 | EXPECT_EQ(seq.size(), seq2.size()); |
2313 | for (size_t i = 0; i < seq.size(); ++i) |
2314 | EXPECT_EQ(seq[i], seq2[i]); |
2315 | } |
2316 | |
2317 | // |
2318 | // Test error handling reading built-in Hex32 type |
2319 | // |
2320 | TEST(YAMLIO, TestReadBuiltInTypesHex32Error) { |
2321 | std::vector<Hex32> seq; |
2322 | Input yin("---\n" |
2323 | "- 0x0012\n" |
2324 | "- 0xFEFF0000\n" |
2325 | "- 0x1234556789\n" |
2326 | "...\n" , |
2327 | /*Ctxt=*/nullptr, |
2328 | suppressErrorMessages); |
2329 | yin >> seq; |
2330 | |
2331 | EXPECT_TRUE(!!yin.error()); |
2332 | |
2333 | std::vector<Hex32> seq2; |
2334 | Input yin2("---\n" |
2335 | "[ 0x0012, 0xFEFF0000, 0x1234556789 ]\n" |
2336 | "...\n" , |
2337 | /*Ctxt=*/nullptr, suppressErrorMessages); |
2338 | yin2 >> seq2; |
2339 | EXPECT_TRUE(!!yin2.error()); |
2340 | |
2341 | EXPECT_EQ(seq.size(), 3u); |
2342 | EXPECT_EQ(seq.size(), seq2.size()); |
2343 | for (size_t i = 0; i < seq.size(); ++i) |
2344 | EXPECT_EQ(seq[i], seq2[i]); |
2345 | } |
2346 | |
2347 | // |
2348 | // Test error handling reading built-in Hex64 type |
2349 | // |
2350 | TEST(YAMLIO, TestReadBuiltInTypesHex64Error) { |
2351 | std::vector<Hex64> seq; |
2352 | Input yin("---\n" |
2353 | "- 0x0012\n" |
2354 | "- 0xFFEEDDCCBBAA9988\n" |
2355 | "- 0x12345567890ABCDEF0\n" |
2356 | "...\n" , |
2357 | /*Ctxt=*/nullptr, |
2358 | suppressErrorMessages); |
2359 | yin >> seq; |
2360 | EXPECT_TRUE(!!yin.error()); |
2361 | |
2362 | std::vector<Hex64> seq2; |
2363 | Input yin2("---\n" |
2364 | "[ 0x0012, 0xFFEEDDCCBBAA9988, 0x12345567890ABCDEF0 ]\n" |
2365 | "...\n" , |
2366 | /*Ctxt=*/nullptr, suppressErrorMessages); |
2367 | yin2 >> seq2; |
2368 | EXPECT_TRUE(!!yin2.error()); |
2369 | |
2370 | EXPECT_EQ(seq.size(), 3u); |
2371 | EXPECT_EQ(seq.size(), seq2.size()); |
2372 | for (size_t i = 0; i < seq.size(); ++i) |
2373 | EXPECT_EQ(seq[i], seq2[i]); |
2374 | } |
2375 | |
2376 | TEST(YAMLIO, TestMalformedMapFailsGracefully) { |
2377 | FooBar doc; |
2378 | { |
2379 | // We pass the suppressErrorMessages handler to handle the error |
2380 | // message generated in the constructor of Input. |
2381 | Input yin("{foo:3, bar: 5}" , /*Ctxt=*/nullptr, suppressErrorMessages); |
2382 | yin >> doc; |
2383 | EXPECT_TRUE(!!yin.error()); |
2384 | } |
2385 | |
2386 | { |
2387 | Input yin("---\nfoo:3\nbar: 5\n...\n" , /*Ctxt=*/nullptr, suppressErrorMessages); |
2388 | yin >> doc; |
2389 | EXPECT_TRUE(!!yin.error()); |
2390 | } |
2391 | } |
2392 | |
2393 | struct OptionalTest { |
2394 | std::vector<int> Numbers; |
2395 | std::optional<int> MaybeNumber; |
2396 | }; |
2397 | |
2398 | struct OptionalTestSeq { |
2399 | std::vector<OptionalTest> Tests; |
2400 | }; |
2401 | |
2402 | LLVM_YAML_IS_SEQUENCE_VECTOR(OptionalTest) |
2403 | namespace llvm { |
2404 | namespace yaml { |
2405 | template <> |
2406 | struct MappingTraits<OptionalTest> { |
2407 | static void mapping(IO& IO, OptionalTest &OT) { |
2408 | IO.mapOptional(Key: "Numbers" , Val&: OT.Numbers); |
2409 | IO.mapOptional(Key: "MaybeNumber" , Val&: OT.MaybeNumber); |
2410 | } |
2411 | }; |
2412 | |
2413 | template <> |
2414 | struct MappingTraits<OptionalTestSeq> { |
2415 | static void mapping(IO &IO, OptionalTestSeq &OTS) { |
2416 | IO.mapOptional(Key: "Tests" , Val&: OTS.Tests); |
2417 | } |
2418 | }; |
2419 | } |
2420 | } |
2421 | |
2422 | TEST(YAMLIO, SequenceElideTest) { |
2423 | // Test that writing out a purely optional structure with its fields set to |
2424 | // default followed by other data is properly read back in. |
2425 | OptionalTestSeq Seq; |
2426 | OptionalTest One, Two, Three, Four; |
2427 | int N[] = {1, 2, 3}; |
2428 | Three.Numbers.assign(first: N, last: N + 3); |
2429 | Seq.Tests.push_back(x: One); |
2430 | Seq.Tests.push_back(x: Two); |
2431 | Seq.Tests.push_back(x: Three); |
2432 | Seq.Tests.push_back(x: Four); |
2433 | |
2434 | std::string intermediate; |
2435 | { |
2436 | llvm::raw_string_ostream ostr(intermediate); |
2437 | Output yout(ostr); |
2438 | yout << Seq; |
2439 | } |
2440 | |
2441 | Input yin(intermediate); |
2442 | OptionalTestSeq Seq2; |
2443 | yin >> Seq2; |
2444 | |
2445 | EXPECT_FALSE(yin.error()); |
2446 | |
2447 | EXPECT_EQ(4UL, Seq2.Tests.size()); |
2448 | |
2449 | EXPECT_TRUE(Seq2.Tests[0].Numbers.empty()); |
2450 | EXPECT_TRUE(Seq2.Tests[1].Numbers.empty()); |
2451 | |
2452 | EXPECT_EQ(1, Seq2.Tests[2].Numbers[0]); |
2453 | EXPECT_EQ(2, Seq2.Tests[2].Numbers[1]); |
2454 | EXPECT_EQ(3, Seq2.Tests[2].Numbers[2]); |
2455 | |
2456 | EXPECT_TRUE(Seq2.Tests[3].Numbers.empty()); |
2457 | } |
2458 | |
2459 | TEST(YAMLIO, TestEmptyStringFailsForMapWithRequiredFields) { |
2460 | FooBar doc; |
2461 | Input yin("" ); |
2462 | yin >> doc; |
2463 | EXPECT_TRUE(!!yin.error()); |
2464 | } |
2465 | |
2466 | TEST(YAMLIO, TestEmptyStringSucceedsForMapWithOptionalFields) { |
2467 | OptionalTest doc; |
2468 | Input yin("" ); |
2469 | yin >> doc; |
2470 | EXPECT_FALSE(yin.error()); |
2471 | EXPECT_FALSE(doc.MaybeNumber.has_value()); |
2472 | } |
2473 | |
2474 | TEST(YAMLIO, TestEmptyStringSucceedsForSequence) { |
2475 | std::vector<uint8_t> seq; |
2476 | Input yin("" , /*Ctxt=*/nullptr, suppressErrorMessages); |
2477 | yin >> seq; |
2478 | |
2479 | EXPECT_FALSE(yin.error()); |
2480 | EXPECT_TRUE(seq.empty()); |
2481 | } |
2482 | |
2483 | struct FlowMap { |
2484 | llvm::StringRef str1, str2, str3; |
2485 | FlowMap(llvm::StringRef str1, llvm::StringRef str2, llvm::StringRef str3) |
2486 | : str1(str1), str2(str2), str3(str3) {} |
2487 | }; |
2488 | |
2489 | struct FlowSeq { |
2490 | llvm::StringRef str; |
2491 | FlowSeq(llvm::StringRef S) : str(S) {} |
2492 | FlowSeq() = default; |
2493 | }; |
2494 | |
2495 | namespace llvm { |
2496 | namespace yaml { |
2497 | template <> |
2498 | struct MappingTraits<FlowMap> { |
2499 | static void mapping(IO &io, FlowMap &fm) { |
2500 | io.mapRequired(Key: "str1" , Val&: fm.str1); |
2501 | io.mapRequired(Key: "str2" , Val&: fm.str2); |
2502 | io.mapRequired(Key: "str3" , Val&: fm.str3); |
2503 | } |
2504 | |
2505 | static const bool flow = true; |
2506 | }; |
2507 | |
2508 | template <> |
2509 | struct ScalarTraits<FlowSeq> { |
2510 | static void output(const FlowSeq &value, void*, llvm::raw_ostream &out) { |
2511 | out << value.str; |
2512 | } |
2513 | static StringRef input(StringRef scalar, void*, FlowSeq &value) { |
2514 | value.str = scalar; |
2515 | return "" ; |
2516 | } |
2517 | |
2518 | static QuotingType mustQuote(StringRef S) { return QuotingType::None; } |
2519 | }; |
2520 | } |
2521 | } |
2522 | |
2523 | LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FlowSeq) |
2524 | |
2525 | TEST(YAMLIO, TestWrapFlow) { |
2526 | std::string out; |
2527 | llvm::raw_string_ostream ostr(out); |
2528 | FlowMap Map("This is str1" , "This is str2" , "This is str3" ); |
2529 | std::vector<FlowSeq> Seq; |
2530 | Seq.emplace_back(args: "This is str1" ); |
2531 | Seq.emplace_back(args: "This is str2" ); |
2532 | Seq.emplace_back(args: "This is str3" ); |
2533 | |
2534 | { |
2535 | // 20 is just bellow the total length of the first mapping field. |
2536 | // We should wreap at every element. |
2537 | Output yout(ostr, nullptr, 15); |
2538 | |
2539 | yout << Map; |
2540 | ostr.flush(); |
2541 | EXPECT_EQ(out, |
2542 | "---\n" |
2543 | "{ str1: This is str1, \n" |
2544 | " str2: This is str2, \n" |
2545 | " str3: This is str3 }\n" |
2546 | "...\n" ); |
2547 | out.clear(); |
2548 | |
2549 | yout << Seq; |
2550 | ostr.flush(); |
2551 | EXPECT_EQ(out, |
2552 | "---\n" |
2553 | "[ This is str1, \n" |
2554 | " This is str2, \n" |
2555 | " This is str3 ]\n" |
2556 | "...\n" ); |
2557 | out.clear(); |
2558 | } |
2559 | { |
2560 | // 25 will allow the second field to be output on the first line. |
2561 | Output yout(ostr, nullptr, 25); |
2562 | |
2563 | yout << Map; |
2564 | ostr.flush(); |
2565 | EXPECT_EQ(out, |
2566 | "---\n" |
2567 | "{ str1: This is str1, str2: This is str2, \n" |
2568 | " str3: This is str3 }\n" |
2569 | "...\n" ); |
2570 | out.clear(); |
2571 | |
2572 | yout << Seq; |
2573 | ostr.flush(); |
2574 | EXPECT_EQ(out, |
2575 | "---\n" |
2576 | "[ This is str1, This is str2, \n" |
2577 | " This is str3 ]\n" |
2578 | "...\n" ); |
2579 | out.clear(); |
2580 | } |
2581 | { |
2582 | // 0 means no wrapping. |
2583 | Output yout(ostr, nullptr, 0); |
2584 | |
2585 | yout << Map; |
2586 | ostr.flush(); |
2587 | EXPECT_EQ(out, |
2588 | "---\n" |
2589 | "{ str1: This is str1, str2: This is str2, str3: This is str3 }\n" |
2590 | "...\n" ); |
2591 | out.clear(); |
2592 | |
2593 | yout << Seq; |
2594 | ostr.flush(); |
2595 | EXPECT_EQ(out, |
2596 | "---\n" |
2597 | "[ This is str1, This is str2, This is str3 ]\n" |
2598 | "...\n" ); |
2599 | out.clear(); |
2600 | } |
2601 | } |
2602 | |
2603 | struct MappingContext { |
2604 | int A = 0; |
2605 | }; |
2606 | struct SimpleMap { |
2607 | int B = 0; |
2608 | int C = 0; |
2609 | }; |
2610 | |
2611 | struct NestedMap { |
2612 | NestedMap(MappingContext &Context) : Context(Context) {} |
2613 | SimpleMap Simple; |
2614 | MappingContext &Context; |
2615 | }; |
2616 | |
2617 | namespace llvm { |
2618 | namespace yaml { |
2619 | template <> struct MappingContextTraits<SimpleMap, MappingContext> { |
2620 | static void mapping(IO &io, SimpleMap &sm, MappingContext &Context) { |
2621 | io.mapRequired(Key: "B" , Val&: sm.B); |
2622 | io.mapRequired(Key: "C" , Val&: sm.C); |
2623 | ++Context.A; |
2624 | io.mapRequired(Key: "Context" , Val&: Context.A); |
2625 | } |
2626 | static std::string validate(IO &io, SimpleMap &sm, MappingContext &Context) { |
2627 | return "" ; |
2628 | } |
2629 | }; |
2630 | |
2631 | template <> struct MappingTraits<NestedMap> { |
2632 | static void mapping(IO &io, NestedMap &nm) { |
2633 | io.mapRequired(Key: "Simple" , Val&: nm.Simple, Ctx&: nm.Context); |
2634 | } |
2635 | }; |
2636 | } |
2637 | } |
2638 | |
2639 | TEST(YAMLIO, TestMapWithContext) { |
2640 | MappingContext Context; |
2641 | NestedMap Nested(Context); |
2642 | std::string out; |
2643 | llvm::raw_string_ostream ostr(out); |
2644 | |
2645 | Output yout(ostr, nullptr, 15); |
2646 | |
2647 | yout << Nested; |
2648 | ostr.flush(); |
2649 | EXPECT_EQ(1, Context.A); |
2650 | EXPECT_EQ("---\n" |
2651 | "Simple:\n" |
2652 | " B: 0\n" |
2653 | " C: 0\n" |
2654 | " Context: 1\n" |
2655 | "...\n" , |
2656 | out); |
2657 | |
2658 | out.clear(); |
2659 | |
2660 | Nested.Simple.B = 2; |
2661 | Nested.Simple.C = 3; |
2662 | yout << Nested; |
2663 | ostr.flush(); |
2664 | EXPECT_EQ(2, Context.A); |
2665 | EXPECT_EQ("---\n" |
2666 | "Simple:\n" |
2667 | " B: 2\n" |
2668 | " C: 3\n" |
2669 | " Context: 2\n" |
2670 | "...\n" , |
2671 | out); |
2672 | out.clear(); |
2673 | } |
2674 | |
2675 | LLVM_YAML_IS_STRING_MAP(int) |
2676 | |
2677 | TEST(YAMLIO, TestCustomMapping) { |
2678 | std::map<std::string, int> x; |
2679 | |
2680 | std::string out; |
2681 | llvm::raw_string_ostream ostr(out); |
2682 | Output xout(ostr, nullptr, 0); |
2683 | |
2684 | xout << x; |
2685 | ostr.flush(); |
2686 | EXPECT_EQ("---\n" |
2687 | "{}\n" |
2688 | "...\n" , |
2689 | out); |
2690 | |
2691 | x["foo" ] = 1; |
2692 | x["bar" ] = 2; |
2693 | |
2694 | out.clear(); |
2695 | xout << x; |
2696 | ostr.flush(); |
2697 | EXPECT_EQ("---\n" |
2698 | "bar: 2\n" |
2699 | "foo: 1\n" |
2700 | "...\n" , |
2701 | out); |
2702 | |
2703 | Input yin(out); |
2704 | std::map<std::string, int> y; |
2705 | yin >> y; |
2706 | EXPECT_EQ(2ul, y.size()); |
2707 | EXPECT_EQ(1, y["foo" ]); |
2708 | EXPECT_EQ(2, y["bar" ]); |
2709 | } |
2710 | |
2711 | LLVM_YAML_IS_STRING_MAP(FooBar) |
2712 | |
2713 | TEST(YAMLIO, TestCustomMappingStruct) { |
2714 | std::map<std::string, FooBar> x; |
2715 | x["foo" ].foo = 1; |
2716 | x["foo" ].bar = 2; |
2717 | x["bar" ].foo = 3; |
2718 | x["bar" ].bar = 4; |
2719 | |
2720 | std::string out; |
2721 | llvm::raw_string_ostream ostr(out); |
2722 | Output xout(ostr, nullptr, 0); |
2723 | |
2724 | xout << x; |
2725 | ostr.flush(); |
2726 | EXPECT_EQ("---\n" |
2727 | "bar:\n" |
2728 | " foo: 3\n" |
2729 | " bar: 4\n" |
2730 | "foo:\n" |
2731 | " foo: 1\n" |
2732 | " bar: 2\n" |
2733 | "...\n" , |
2734 | out); |
2735 | |
2736 | Input yin(out); |
2737 | std::map<std::string, FooBar> y; |
2738 | yin >> y; |
2739 | EXPECT_EQ(2ul, y.size()); |
2740 | EXPECT_EQ(1, y["foo" ].foo); |
2741 | EXPECT_EQ(2, y["foo" ].bar); |
2742 | EXPECT_EQ(3, y["bar" ].foo); |
2743 | EXPECT_EQ(4, y["bar" ].bar); |
2744 | } |
2745 | |
2746 | struct FooBarMapMap { |
2747 | std::map<std::string, FooBar> fbm; |
2748 | }; |
2749 | |
2750 | namespace llvm { |
2751 | namespace yaml { |
2752 | template <> struct MappingTraits<FooBarMapMap> { |
2753 | static void mapping(IO &io, FooBarMapMap &x) { |
2754 | io.mapRequired(Key: "fbm" , Val&: x.fbm); |
2755 | } |
2756 | }; |
2757 | } |
2758 | } |
2759 | |
2760 | TEST(YAMLIO, TestEmptyMapWrite) { |
2761 | FooBarMapMap cont; |
2762 | std::string str; |
2763 | llvm::raw_string_ostream OS(str); |
2764 | Output yout(OS); |
2765 | yout << cont; |
2766 | EXPECT_EQ(OS.str(), "---\nfbm: {}\n...\n" ); |
2767 | } |
2768 | |
2769 | TEST(YAMLIO, TestEmptySequenceWrite) { |
2770 | { |
2771 | FooBarContainer cont; |
2772 | std::string str; |
2773 | llvm::raw_string_ostream OS(str); |
2774 | Output yout(OS); |
2775 | yout << cont; |
2776 | EXPECT_EQ(OS.str(), "---\nfbs: []\n...\n" ); |
2777 | } |
2778 | |
2779 | { |
2780 | FooBarSequence seq; |
2781 | std::string str; |
2782 | llvm::raw_string_ostream OS(str); |
2783 | Output yout(OS); |
2784 | yout << seq; |
2785 | EXPECT_EQ(OS.str(), "---\n[]\n...\n" ); |
2786 | } |
2787 | } |
2788 | |
2789 | static void TestEscaped(llvm::StringRef Input, llvm::StringRef Expected) { |
2790 | std::string out; |
2791 | llvm::raw_string_ostream ostr(out); |
2792 | Output xout(ostr, nullptr, 0); |
2793 | |
2794 | llvm::yaml::EmptyContext Ctx; |
2795 | yamlize(io&: xout, Val&: Input, true, Ctx); |
2796 | |
2797 | ostr.flush(); |
2798 | |
2799 | // Make a separate StringRef so we get nice byte-by-byte output. |
2800 | llvm::StringRef Got(out); |
2801 | EXPECT_EQ(Expected, Got); |
2802 | } |
2803 | |
2804 | TEST(YAMLIO, TestEscaped) { |
2805 | // Single quote |
2806 | TestEscaped(Input: "@abc@" , Expected: "'@abc@'" ); |
2807 | // No quote |
2808 | TestEscaped(Input: "abc" , Expected: "abc" ); |
2809 | // Forward slash quoted |
2810 | TestEscaped(Input: "abc/" , Expected: "'abc/'" ); |
2811 | // Double quote non-printable |
2812 | TestEscaped(Input: "\01@abc@" , Expected: "\"\\x01@abc@\"" ); |
2813 | // Double quote inside single quote |
2814 | TestEscaped(Input: "abc\"fdf" , Expected: "'abc\"fdf'" ); |
2815 | // Double quote inside double quote |
2816 | TestEscaped(Input: "\01bc\"fdf" , Expected: "\"\\x01bc\\\"fdf\"" ); |
2817 | // Single quote inside single quote |
2818 | TestEscaped(Input: "abc'fdf" , Expected: "'abc''fdf'" ); |
2819 | // UTF8 |
2820 | TestEscaped(Input: "/*параметр*/" , Expected: "\"/*параметр*/\"" ); |
2821 | // UTF8 with single quote inside double quote |
2822 | TestEscaped(Input: "parameter 'параметр' is unused" , |
2823 | Expected: "\"parameter 'параметр' is unused\"" ); |
2824 | |
2825 | // String with embedded non-printable multibyte UTF-8 sequence (U+200B |
2826 | // zero-width space). The thing to test here is that we emit a |
2827 | // unicode-scalar level escape like \uNNNN (at the YAML level), and don't |
2828 | // just pass the UTF-8 byte sequence through as with quoted printables. |
2829 | { |
2830 | const unsigned char foobar[10] = {'f', 'o', 'o', |
2831 | 0xE2, 0x80, 0x8B, // UTF-8 of U+200B |
2832 | 'b', 'a', 'r', |
2833 | 0x0}; |
2834 | TestEscaped(Input: (char const *)foobar, Expected: "\"foo\\u200Bbar\"" ); |
2835 | } |
2836 | } |
2837 | |
2838 | TEST(YAMLIO, Numeric) { |
2839 | EXPECT_TRUE(isNumeric(".inf" )); |
2840 | EXPECT_TRUE(isNumeric(".INF" )); |
2841 | EXPECT_TRUE(isNumeric(".Inf" )); |
2842 | EXPECT_TRUE(isNumeric("-.inf" )); |
2843 | EXPECT_TRUE(isNumeric("+.inf" )); |
2844 | |
2845 | EXPECT_TRUE(isNumeric(".nan" )); |
2846 | EXPECT_TRUE(isNumeric(".NaN" )); |
2847 | EXPECT_TRUE(isNumeric(".NAN" )); |
2848 | |
2849 | EXPECT_TRUE(isNumeric("0" )); |
2850 | EXPECT_TRUE(isNumeric("0." )); |
2851 | EXPECT_TRUE(isNumeric("0.0" )); |
2852 | EXPECT_TRUE(isNumeric("-0.0" )); |
2853 | EXPECT_TRUE(isNumeric("+0.0" )); |
2854 | |
2855 | EXPECT_TRUE(isNumeric("12345" )); |
2856 | EXPECT_TRUE(isNumeric("012345" )); |
2857 | EXPECT_TRUE(isNumeric("+12.0" )); |
2858 | EXPECT_TRUE(isNumeric(".5" )); |
2859 | EXPECT_TRUE(isNumeric("+.5" )); |
2860 | EXPECT_TRUE(isNumeric("-1.0" )); |
2861 | |
2862 | EXPECT_TRUE(isNumeric("2.3e4" )); |
2863 | EXPECT_TRUE(isNumeric("-2E+05" )); |
2864 | EXPECT_TRUE(isNumeric("+12e03" )); |
2865 | EXPECT_TRUE(isNumeric("6.8523015e+5" )); |
2866 | |
2867 | EXPECT_TRUE(isNumeric("1.e+1" )); |
2868 | EXPECT_TRUE(isNumeric(".0e+1" )); |
2869 | |
2870 | EXPECT_TRUE(isNumeric("0x2aF3" )); |
2871 | EXPECT_TRUE(isNumeric("0o01234567" )); |
2872 | |
2873 | EXPECT_FALSE(isNumeric("not a number" )); |
2874 | EXPECT_FALSE(isNumeric("." )); |
2875 | EXPECT_FALSE(isNumeric(".e+1" )); |
2876 | EXPECT_FALSE(isNumeric(".1e" )); |
2877 | EXPECT_FALSE(isNumeric(".1e+" )); |
2878 | EXPECT_FALSE(isNumeric(".1e++1" )); |
2879 | |
2880 | EXPECT_FALSE(isNumeric("ABCD" )); |
2881 | EXPECT_FALSE(isNumeric("+0x2AF3" )); |
2882 | EXPECT_FALSE(isNumeric("-0x2AF3" )); |
2883 | EXPECT_FALSE(isNumeric("0x2AF3Z" )); |
2884 | EXPECT_FALSE(isNumeric("0o012345678" )); |
2885 | EXPECT_FALSE(isNumeric("0xZ" )); |
2886 | EXPECT_FALSE(isNumeric("-0o012345678" )); |
2887 | EXPECT_FALSE(isNumeric("000003A8229434B839616A25C16B0291F77A438B" )); |
2888 | |
2889 | EXPECT_FALSE(isNumeric("" )); |
2890 | EXPECT_FALSE(isNumeric("." )); |
2891 | EXPECT_FALSE(isNumeric(".e+1" )); |
2892 | EXPECT_FALSE(isNumeric(".e+" )); |
2893 | EXPECT_FALSE(isNumeric(".e" )); |
2894 | EXPECT_FALSE(isNumeric("e1" )); |
2895 | |
2896 | // Deprecated formats: as for YAML 1.2 specification, the following are not |
2897 | // valid numbers anymore: |
2898 | // |
2899 | // * Sexagecimal numbers |
2900 | // * Decimal numbers with comma s the delimiter |
2901 | // * "inf", "nan" without '.' prefix |
2902 | EXPECT_FALSE(isNumeric("3:25:45" )); |
2903 | EXPECT_FALSE(isNumeric("+12,345" )); |
2904 | EXPECT_FALSE(isNumeric("-inf" )); |
2905 | EXPECT_FALSE(isNumeric("1,230.15" )); |
2906 | } |
2907 | |
2908 | //===----------------------------------------------------------------------===// |
2909 | // Test PolymorphicTraits and TaggedScalarTraits |
2910 | //===----------------------------------------------------------------------===// |
2911 | |
2912 | struct Poly { |
2913 | enum NodeKind { |
2914 | NK_Scalar, |
2915 | NK_Seq, |
2916 | NK_Map, |
2917 | } Kind; |
2918 | |
2919 | Poly(NodeKind Kind) : Kind(Kind) {} |
2920 | |
2921 | virtual ~Poly() = default; |
2922 | |
2923 | NodeKind getKind() const { return Kind; } |
2924 | }; |
2925 | |
2926 | struct Scalar : Poly { |
2927 | enum ScalarKind { |
2928 | SK_Unknown, |
2929 | SK_Double, |
2930 | SK_Bool, |
2931 | } SKind; |
2932 | |
2933 | union { |
2934 | double DoubleValue; |
2935 | bool BoolValue; |
2936 | }; |
2937 | |
2938 | Scalar() : Poly(NK_Scalar), SKind(SK_Unknown) {} |
2939 | Scalar(double DoubleValue) |
2940 | : Poly(NK_Scalar), SKind(SK_Double), DoubleValue(DoubleValue) {} |
2941 | Scalar(bool BoolValue) |
2942 | : Poly(NK_Scalar), SKind(SK_Bool), BoolValue(BoolValue) {} |
2943 | |
2944 | static bool classof(const Poly *N) { return N->getKind() == NK_Scalar; } |
2945 | }; |
2946 | |
2947 | struct Seq : Poly, std::vector<std::unique_ptr<Poly>> { |
2948 | Seq() : Poly(NK_Seq) {} |
2949 | |
2950 | static bool classof(const Poly *N) { return N->getKind() == NK_Seq; } |
2951 | }; |
2952 | |
2953 | struct Map : Poly, llvm::StringMap<std::unique_ptr<Poly>> { |
2954 | Map() : Poly(NK_Map) {} |
2955 | |
2956 | static bool classof(const Poly *N) { return N->getKind() == NK_Map; } |
2957 | }; |
2958 | |
2959 | namespace llvm { |
2960 | namespace yaml { |
2961 | |
2962 | template <> struct PolymorphicTraits<std::unique_ptr<Poly>> { |
2963 | static NodeKind getKind(const std::unique_ptr<Poly> &N) { |
2964 | if (isa<Scalar>(Val: *N)) |
2965 | return NodeKind::Scalar; |
2966 | if (isa<Seq>(Val: *N)) |
2967 | return NodeKind::Sequence; |
2968 | if (isa<Map>(Val: *N)) |
2969 | return NodeKind::Map; |
2970 | llvm_unreachable("unsupported node type" ); |
2971 | } |
2972 | |
2973 | static Scalar &getAsScalar(std::unique_ptr<Poly> &N) { |
2974 | if (!N || !isa<Scalar>(Val: *N)) |
2975 | N = std::make_unique<Scalar>(); |
2976 | return *cast<Scalar>(Val: N.get()); |
2977 | } |
2978 | |
2979 | static Seq &getAsSequence(std::unique_ptr<Poly> &N) { |
2980 | if (!N || !isa<Seq>(Val: *N)) |
2981 | N = std::make_unique<Seq>(); |
2982 | return *cast<Seq>(Val: N.get()); |
2983 | } |
2984 | |
2985 | static Map &getAsMap(std::unique_ptr<Poly> &N) { |
2986 | if (!N || !isa<Map>(Val: *N)) |
2987 | N = std::make_unique<Map>(); |
2988 | return *cast<Map>(Val: N.get()); |
2989 | } |
2990 | }; |
2991 | |
2992 | template <> struct TaggedScalarTraits<Scalar> { |
2993 | static void output(const Scalar &S, void *Ctxt, raw_ostream &ScalarOS, |
2994 | raw_ostream &TagOS) { |
2995 | switch (S.SKind) { |
2996 | case Scalar::SK_Unknown: |
2997 | report_fatal_error(reason: "output unknown scalar" ); |
2998 | break; |
2999 | case Scalar::SK_Double: |
3000 | TagOS << "!double" ; |
3001 | ScalarTraits<double>::output(S.DoubleValue, Ctxt, ScalarOS); |
3002 | break; |
3003 | case Scalar::SK_Bool: |
3004 | TagOS << "!bool" ; |
3005 | ScalarTraits<bool>::output(S.BoolValue, Ctxt, ScalarOS); |
3006 | break; |
3007 | } |
3008 | } |
3009 | |
3010 | static StringRef input(StringRef ScalarStr, StringRef Tag, void *Ctxt, |
3011 | Scalar &S) { |
3012 | S.SKind = StringSwitch<Scalar::ScalarKind>(Tag) |
3013 | .Case(S: "!double" , Value: Scalar::SK_Double) |
3014 | .Case(S: "!bool" , Value: Scalar::SK_Bool) |
3015 | .Default(Value: Scalar::SK_Unknown); |
3016 | switch (S.SKind) { |
3017 | case Scalar::SK_Unknown: |
3018 | return StringRef("unknown scalar tag" ); |
3019 | case Scalar::SK_Double: |
3020 | return ScalarTraits<double>::input(ScalarStr, Ctxt, S.DoubleValue); |
3021 | case Scalar::SK_Bool: |
3022 | return ScalarTraits<bool>::input(ScalarStr, Ctxt, S.BoolValue); |
3023 | } |
3024 | llvm_unreachable("unknown scalar kind" ); |
3025 | } |
3026 | |
3027 | static QuotingType mustQuote(const Scalar &S, StringRef Str) { |
3028 | switch (S.SKind) { |
3029 | case Scalar::SK_Unknown: |
3030 | report_fatal_error(reason: "quote unknown scalar" ); |
3031 | case Scalar::SK_Double: |
3032 | return ScalarTraits<double>::mustQuote(Str); |
3033 | case Scalar::SK_Bool: |
3034 | return ScalarTraits<bool>::mustQuote(Str); |
3035 | } |
3036 | llvm_unreachable("unknown scalar kind" ); |
3037 | } |
3038 | }; |
3039 | |
3040 | template <> struct CustomMappingTraits<Map> { |
3041 | static void inputOne(IO &IO, StringRef Key, Map &M) { |
3042 | IO.mapRequired(Key: Key.str().c_str(), Val&: M[Key]); |
3043 | } |
3044 | |
3045 | static void output(IO &IO, Map &M) { |
3046 | for (auto &N : M) |
3047 | IO.mapRequired(Key: N.getKey().str().c_str(), Val&: N.getValue()); |
3048 | } |
3049 | }; |
3050 | |
3051 | template <> struct SequenceTraits<Seq> { |
3052 | static size_t size(IO &IO, Seq &A) { return A.size(); } |
3053 | |
3054 | static std::unique_ptr<Poly> &element(IO &IO, Seq &A, size_t Index) { |
3055 | if (Index >= A.size()) |
3056 | A.resize(new_size: Index + 1); |
3057 | return A[Index]; |
3058 | } |
3059 | }; |
3060 | |
3061 | } // namespace yaml |
3062 | } // namespace llvm |
3063 | |
3064 | TEST(YAMLIO, TestReadWritePolymorphicScalar) { |
3065 | std::string intermediate; |
3066 | std::unique_ptr<Poly> node = std::make_unique<Scalar>(args: true); |
3067 | |
3068 | llvm::raw_string_ostream ostr(intermediate); |
3069 | Output yout(ostr); |
3070 | #ifdef GTEST_HAS_DEATH_TEST |
3071 | #ifndef NDEBUG |
3072 | EXPECT_DEATH(yout << node, "plain scalar documents are not supported" ); |
3073 | #endif |
3074 | #endif |
3075 | } |
3076 | |
3077 | TEST(YAMLIO, TestReadWritePolymorphicSeq) { |
3078 | std::string intermediate; |
3079 | { |
3080 | auto seq = std::make_unique<Seq>(); |
3081 | seq->push_back(x: std::make_unique<Scalar>(args: true)); |
3082 | seq->push_back(x: std::make_unique<Scalar>(args: 1.0)); |
3083 | auto node = llvm::unique_dyn_cast<Poly>(Val&: seq); |
3084 | |
3085 | llvm::raw_string_ostream ostr(intermediate); |
3086 | Output yout(ostr); |
3087 | yout << node; |
3088 | } |
3089 | { |
3090 | Input yin(intermediate); |
3091 | std::unique_ptr<Poly> node; |
3092 | yin >> node; |
3093 | |
3094 | EXPECT_FALSE(yin.error()); |
3095 | auto seq = llvm::dyn_cast<Seq>(Val: node.get()); |
3096 | ASSERT_TRUE(seq); |
3097 | ASSERT_EQ(seq->size(), 2u); |
3098 | auto first = llvm::dyn_cast<Scalar>(Val: (*seq)[0].get()); |
3099 | ASSERT_TRUE(first); |
3100 | EXPECT_EQ(first->SKind, Scalar::SK_Bool); |
3101 | EXPECT_TRUE(first->BoolValue); |
3102 | auto second = llvm::dyn_cast<Scalar>(Val: (*seq)[1].get()); |
3103 | ASSERT_TRUE(second); |
3104 | EXPECT_EQ(second->SKind, Scalar::SK_Double); |
3105 | EXPECT_EQ(second->DoubleValue, 1.0); |
3106 | } |
3107 | } |
3108 | |
3109 | TEST(YAMLIO, TestReadWritePolymorphicMap) { |
3110 | std::string intermediate; |
3111 | { |
3112 | auto map = std::make_unique<Map>(); |
3113 | (*map)["foo" ] = std::make_unique<Scalar>(args: false); |
3114 | (*map)["bar" ] = std::make_unique<Scalar>(args: 2.0); |
3115 | std::unique_ptr<Poly> node = llvm::unique_dyn_cast<Poly>(Val&: map); |
3116 | |
3117 | llvm::raw_string_ostream ostr(intermediate); |
3118 | Output yout(ostr); |
3119 | yout << node; |
3120 | } |
3121 | { |
3122 | Input yin(intermediate); |
3123 | std::unique_ptr<Poly> node; |
3124 | yin >> node; |
3125 | |
3126 | EXPECT_FALSE(yin.error()); |
3127 | auto map = llvm::dyn_cast<Map>(Val: node.get()); |
3128 | ASSERT_TRUE(map); |
3129 | auto foo = llvm::dyn_cast<Scalar>(Val: (*map)["foo" ].get()); |
3130 | ASSERT_TRUE(foo); |
3131 | EXPECT_EQ(foo->SKind, Scalar::SK_Bool); |
3132 | EXPECT_FALSE(foo->BoolValue); |
3133 | auto bar = llvm::dyn_cast<Scalar>(Val: (*map)["bar" ].get()); |
3134 | ASSERT_TRUE(bar); |
3135 | EXPECT_EQ(bar->SKind, Scalar::SK_Double); |
3136 | EXPECT_EQ(bar->DoubleValue, 2.0); |
3137 | } |
3138 | } |
3139 | |
3140 | TEST(YAMLIO, TestAnchorMapError) { |
3141 | Input yin("& & &: " ); |
3142 | yin.setCurrentDocument(); |
3143 | EXPECT_TRUE(yin.error()); |
3144 | } |
3145 | |
3146 | TEST(YAMLIO, TestFlowSequenceTokenErrors) { |
3147 | Input yin("," ); |
3148 | EXPECT_FALSE(yin.setCurrentDocument()); |
3149 | EXPECT_TRUE(yin.error()); |
3150 | |
3151 | Input yin2("]" ); |
3152 | EXPECT_FALSE(yin2.setCurrentDocument()); |
3153 | EXPECT_TRUE(yin2.error()); |
3154 | |
3155 | Input yin3("}" ); |
3156 | EXPECT_FALSE(yin3.setCurrentDocument()); |
3157 | EXPECT_TRUE(yin3.error()); |
3158 | } |
3159 | |
3160 | TEST(YAMLIO, TestDirectiveMappingNoValue) { |
3161 | Input yin("%YAML\n{5:" ); |
3162 | yin.setCurrentDocument(); |
3163 | EXPECT_TRUE(yin.error()); |
3164 | |
3165 | Input yin2("%TAG\n'\x98!< :\n" ); |
3166 | yin2.setCurrentDocument(); |
3167 | EXPECT_TRUE(yin2.error()); |
3168 | } |
3169 | |
3170 | TEST(YAMLIO, TestUnescapeInfiniteLoop) { |
3171 | Input yin("\"\\u\\^#\\\\\"" ); |
3172 | yin.setCurrentDocument(); |
3173 | EXPECT_TRUE(yin.error()); |
3174 | } |
3175 | |
3176 | TEST(YAMLIO, TestScannerUnexpectedCharacter) { |
3177 | Input yin("!<$\x9F." ); |
3178 | EXPECT_FALSE(yin.setCurrentDocument()); |
3179 | EXPECT_TRUE(yin.error()); |
3180 | } |
3181 | |
3182 | TEST(YAMLIO, TestUnknownDirective) { |
3183 | Input yin("%" ); |
3184 | EXPECT_FALSE(yin.setCurrentDocument()); |
3185 | EXPECT_TRUE(yin.error()); |
3186 | |
3187 | Input yin2("%)" ); |
3188 | EXPECT_FALSE(yin2.setCurrentDocument()); |
3189 | EXPECT_TRUE(yin2.error()); |
3190 | } |
3191 | |
3192 | TEST(YAMLIO, TestEmptyAlias) { |
3193 | Input yin("&" ); |
3194 | EXPECT_FALSE(yin.setCurrentDocument()); |
3195 | EXPECT_TRUE(yin.error()); |
3196 | } |
3197 | |
3198 | TEST(YAMLIO, TestEmptyAnchor) { |
3199 | Input yin("*" ); |
3200 | EXPECT_FALSE(yin.setCurrentDocument()); |
3201 | } |
3202 | |
3203 | TEST(YAMLIO, TestScannerNoNullEmpty) { |
3204 | std::vector<char> str{}; |
3205 | Input yin(llvm::StringRef(str.data(), str.size())); |
3206 | yin.setCurrentDocument(); |
3207 | EXPECT_FALSE(yin.error()); |
3208 | } |
3209 | |
3210 | TEST(YAMLIO, TestScannerNoNullSequenceOfNull) { |
3211 | std::vector<char> str{'-'}; |
3212 | Input yin(llvm::StringRef(str.data(), str.size())); |
3213 | yin.setCurrentDocument(); |
3214 | EXPECT_FALSE(yin.error()); |
3215 | } |
3216 | |
3217 | TEST(YAMLIO, TestScannerNoNullSimpleSequence) { |
3218 | std::vector<char> str{'-', ' ', 'a'}; |
3219 | Input yin(llvm::StringRef(str.data(), str.size())); |
3220 | yin.setCurrentDocument(); |
3221 | EXPECT_FALSE(yin.error()); |
3222 | } |
3223 | |
3224 | TEST(YAMLIO, TestScannerNoNullUnbalancedMap) { |
3225 | std::vector<char> str{'{'}; |
3226 | Input yin(llvm::StringRef(str.data(), str.size())); |
3227 | yin.setCurrentDocument(); |
3228 | EXPECT_TRUE(yin.error()); |
3229 | } |
3230 | |
3231 | TEST(YAMLIO, TestScannerNoNullEmptyMap) { |
3232 | std::vector<char> str{'{', '}'}; |
3233 | Input yin(llvm::StringRef(str.data(), str.size())); |
3234 | yin.setCurrentDocument(); |
3235 | EXPECT_FALSE(yin.error()); |
3236 | } |
3237 | |
3238 | TEST(YAMLIO, TestScannerNoNullUnbalancedSequence) { |
3239 | std::vector<char> str{'['}; |
3240 | Input yin(llvm::StringRef(str.data(), str.size())); |
3241 | yin.setCurrentDocument(); |
3242 | EXPECT_TRUE(yin.error()); |
3243 | } |
3244 | |
3245 | TEST(YAMLIO, TestScannerNoNullEmptySequence) { |
3246 | std::vector<char> str{'[', ']'}; |
3247 | Input yin(llvm::StringRef(str.data(), str.size())); |
3248 | yin.setCurrentDocument(); |
3249 | EXPECT_FALSE(yin.error()); |
3250 | } |
3251 | |
3252 | TEST(YAMLIO, TestScannerNoNullScalarUnbalancedDoubleQuote) { |
3253 | std::vector<char> str{'"'}; |
3254 | Input yin(llvm::StringRef(str.data(), str.size())); |
3255 | yin.setCurrentDocument(); |
3256 | EXPECT_TRUE(yin.error()); |
3257 | } |
3258 | |
3259 | TEST(YAMLIO, TestScannerNoNullScalarUnbalancedSingleQuote) { |
3260 | std::vector<char> str{'\''}; |
3261 | Input yin(llvm::StringRef(str.data(), str.size())); |
3262 | yin.setCurrentDocument(); |
3263 | EXPECT_TRUE(yin.error()); |
3264 | } |
3265 | |
3266 | TEST(YAMLIO, TestScannerNoNullEmptyAlias) { |
3267 | std::vector<char> str{'&'}; |
3268 | Input yin(llvm::StringRef(str.data(), str.size())); |
3269 | yin.setCurrentDocument(); |
3270 | EXPECT_TRUE(yin.error()); |
3271 | } |
3272 | |
3273 | TEST(YAMLIO, TestScannerNoNullEmptyAnchor) { |
3274 | std::vector<char> str{'*'}; |
3275 | Input yin(llvm::StringRef(str.data(), str.size())); |
3276 | yin.setCurrentDocument(); |
3277 | EXPECT_TRUE(yin.error()); |
3278 | } |
3279 | |
3280 | TEST(YAMLIO, TestScannerNoNullDecodeInvalidUTF8) { |
3281 | std::vector<char> str{'\xef'}; |
3282 | Input yin(llvm::StringRef(str.data(), str.size())); |
3283 | yin.setCurrentDocument(); |
3284 | EXPECT_TRUE(yin.error()); |
3285 | } |
3286 | |
3287 | TEST(YAMLIO, TestScannerNoNullScanPlainScalarInFlow) { |
3288 | std::vector<char> str{'{', 'a', ':'}; |
3289 | Input yin(llvm::StringRef(str.data(), str.size())); |
3290 | yin.setCurrentDocument(); |
3291 | EXPECT_TRUE(yin.error()); |
3292 | } |
3293 | |
3294 | struct FixedArray { |
3295 | FixedArray() { |
3296 | // Initialize to int max as a sentinel value. |
3297 | for (auto &v : values) |
3298 | v = std::numeric_limits<int>::max(); |
3299 | } |
3300 | int values[4]; |
3301 | }; |
3302 | |
3303 | namespace llvm { |
3304 | namespace yaml { |
3305 | template <> |
3306 | struct MappingTraits<FixedArray> { |
3307 | static void mapping(IO &io, FixedArray& st) { |
3308 | MutableArrayRef<int> array = st.values; |
3309 | io.mapRequired(Key: "Values" , Val&: array); |
3310 | } |
3311 | }; |
3312 | } |
3313 | } |
3314 | |
3315 | TEST(YAMLIO, FixedSizeArray) { |
3316 | FixedArray faval; |
3317 | Input yin("---\nValues: [ 1, 2, 3, 4 ]\n...\n" ); |
3318 | yin >> faval; |
3319 | |
3320 | EXPECT_FALSE(yin.error()); |
3321 | EXPECT_EQ(faval.values[0], 1); |
3322 | EXPECT_EQ(faval.values[1], 2); |
3323 | EXPECT_EQ(faval.values[2], 3); |
3324 | EXPECT_EQ(faval.values[3], 4); |
3325 | |
3326 | std::string serialized; |
3327 | { |
3328 | llvm::raw_string_ostream os(serialized); |
3329 | Output yout(os); |
3330 | yout << faval; |
3331 | } |
3332 | auto expected = "---\n" |
3333 | "Values: [ 1, 2, 3, 4 ]\n" |
3334 | "...\n" ; |
3335 | ASSERT_EQ(serialized, expected); |
3336 | } |
3337 | |
3338 | TEST(YAMLIO, FixedSizeArrayMismatch) { |
3339 | { |
3340 | FixedArray faval; |
3341 | Input yin("---\nValues: [ 1, 2, 3 ]\n...\n" ); |
3342 | yin >> faval; |
3343 | |
3344 | // No error for too small, leaves the default initialized value |
3345 | EXPECT_FALSE(yin.error()); |
3346 | EXPECT_EQ(faval.values[0], 1); |
3347 | EXPECT_EQ(faval.values[1], 2); |
3348 | EXPECT_EQ(faval.values[2], 3); |
3349 | EXPECT_EQ(faval.values[3], std::numeric_limits<int>::max()); |
3350 | } |
3351 | |
3352 | { |
3353 | FixedArray faval; |
3354 | Input yin("---\nValues: [ 1, 2, 3, 4, 5 ]\n...\n" ); |
3355 | yin >> faval; |
3356 | |
3357 | // Error for too many elements. |
3358 | EXPECT_TRUE(!!yin.error()); |
3359 | } |
3360 | |
3361 | } |
3362 | |