]> git.ipfire.org Git - thirdparty/gcc.git/blob - libgo/go/encoding/xml/marshal_test.go
libgo: Update to current sources.
[thirdparty/gcc.git] / libgo / go / encoding / xml / marshal_test.go
1 // Copyright 2011 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package xml
6
7 import (
8 "bytes"
9 "errors"
10 "io"
11 "reflect"
12 "strconv"
13 "strings"
14 "testing"
15 "time"
16 )
17
18 type DriveType int
19
20 const (
21 HyperDrive DriveType = iota
22 ImprobabilityDrive
23 )
24
25 type Passenger struct {
26 Name []string `xml:"name"`
27 Weight float32 `xml:"weight"`
28 }
29
30 type Ship struct {
31 XMLName struct{} `xml:"spaceship"`
32
33 Name string `xml:"name,attr"`
34 Pilot string `xml:"pilot,attr"`
35 Drive DriveType `xml:"drive"`
36 Age uint `xml:"age"`
37 Passenger []*Passenger `xml:"passenger"`
38 secret string
39 }
40
41 type NamedType string
42
43 type Port struct {
44 XMLName struct{} `xml:"port"`
45 Type string `xml:"type,attr,omitempty"`
46 Comment string `xml:",comment"`
47 Number string `xml:",chardata"`
48 }
49
50 type Domain struct {
51 XMLName struct{} `xml:"domain"`
52 Country string `xml:",attr,omitempty"`
53 Name []byte `xml:",chardata"`
54 Comment []byte `xml:",comment"`
55 }
56
57 type Book struct {
58 XMLName struct{} `xml:"book"`
59 Title string `xml:",chardata"`
60 }
61
62 type SecretAgent struct {
63 XMLName struct{} `xml:"agent"`
64 Handle string `xml:"handle,attr"`
65 Identity string
66 Obfuscate string `xml:",innerxml"`
67 }
68
69 type NestedItems struct {
70 XMLName struct{} `xml:"result"`
71 Items []string `xml:">item"`
72 Item1 []string `xml:"Items>item1"`
73 }
74
75 type NestedOrder struct {
76 XMLName struct{} `xml:"result"`
77 Field1 string `xml:"parent>c"`
78 Field2 string `xml:"parent>b"`
79 Field3 string `xml:"parent>a"`
80 }
81
82 type MixedNested struct {
83 XMLName struct{} `xml:"result"`
84 A string `xml:"parent1>a"`
85 B string `xml:"b"`
86 C string `xml:"parent1>parent2>c"`
87 D string `xml:"parent1>d"`
88 }
89
90 type NilTest struct {
91 A interface{} `xml:"parent1>parent2>a"`
92 B interface{} `xml:"parent1>b"`
93 C interface{} `xml:"parent1>parent2>c"`
94 }
95
96 type Service struct {
97 XMLName struct{} `xml:"service"`
98 Domain *Domain `xml:"host>domain"`
99 Port *Port `xml:"host>port"`
100 Extra1 interface{}
101 Extra2 interface{} `xml:"host>extra2"`
102 }
103
104 var nilStruct *Ship
105
106 type EmbedA struct {
107 EmbedC
108 EmbedB EmbedB
109 FieldA string
110 }
111
112 type EmbedB struct {
113 FieldB string
114 *EmbedC
115 }
116
117 type EmbedC struct {
118 FieldA1 string `xml:"FieldA>A1"`
119 FieldA2 string `xml:"FieldA>A2"`
120 FieldB string
121 FieldC string
122 }
123
124 type NameCasing struct {
125 XMLName struct{} `xml:"casing"`
126 Xy string
127 XY string
128 XyA string `xml:"Xy,attr"`
129 XYA string `xml:"XY,attr"`
130 }
131
132 type NamePrecedence struct {
133 XMLName Name `xml:"Parent"`
134 FromTag XMLNameWithoutTag `xml:"InTag"`
135 FromNameVal XMLNameWithoutTag
136 FromNameTag XMLNameWithTag
137 InFieldName string
138 }
139
140 type XMLNameWithTag struct {
141 XMLName Name `xml:"InXMLNameTag"`
142 Value string `xml:",chardata"`
143 }
144
145 type XMLNameWithoutTag struct {
146 XMLName Name
147 Value string `xml:",chardata"`
148 }
149
150 type NameInField struct {
151 Foo Name `xml:"ns foo"`
152 }
153
154 type AttrTest struct {
155 Int int `xml:",attr"`
156 Named int `xml:"int,attr"`
157 Float float64 `xml:",attr"`
158 Uint8 uint8 `xml:",attr"`
159 Bool bool `xml:",attr"`
160 Str string `xml:",attr"`
161 Bytes []byte `xml:",attr"`
162 }
163
164 type OmitAttrTest struct {
165 Int int `xml:",attr,omitempty"`
166 Named int `xml:"int,attr,omitempty"`
167 Float float64 `xml:",attr,omitempty"`
168 Uint8 uint8 `xml:",attr,omitempty"`
169 Bool bool `xml:",attr,omitempty"`
170 Str string `xml:",attr,omitempty"`
171 Bytes []byte `xml:",attr,omitempty"`
172 }
173
174 type OmitFieldTest struct {
175 Int int `xml:",omitempty"`
176 Named int `xml:"int,omitempty"`
177 Float float64 `xml:",omitempty"`
178 Uint8 uint8 `xml:",omitempty"`
179 Bool bool `xml:",omitempty"`
180 Str string `xml:",omitempty"`
181 Bytes []byte `xml:",omitempty"`
182 Ptr *PresenceTest `xml:",omitempty"`
183 }
184
185 type AnyTest struct {
186 XMLName struct{} `xml:"a"`
187 Nested string `xml:"nested>value"`
188 AnyField AnyHolder `xml:",any"`
189 }
190
191 type AnyHolder struct {
192 XMLName Name
193 XML string `xml:",innerxml"`
194 }
195
196 type RecurseA struct {
197 A string
198 B *RecurseB
199 }
200
201 type RecurseB struct {
202 A *RecurseA
203 B string
204 }
205
206 type PresenceTest struct {
207 Exists *struct{}
208 }
209
210 type IgnoreTest struct {
211 PublicSecret string `xml:"-"`
212 }
213
214 type MyBytes []byte
215
216 type Data struct {
217 Bytes []byte
218 Attr []byte `xml:",attr"`
219 Custom MyBytes
220 }
221
222 type Plain struct {
223 V interface{}
224 }
225
226 // Unless explicitly stated as such (or *Plain), all of the
227 // tests below are two-way tests. When introducing new tests,
228 // please try to make them two-way as well to ensure that
229 // marshalling and unmarshalling are as symmetrical as feasible.
230 var marshalTests = []struct {
231 Value interface{}
232 ExpectXML string
233 MarshalOnly bool
234 UnmarshalOnly bool
235 }{
236 // Test nil marshals to nothing
237 {Value: nil, ExpectXML: ``, MarshalOnly: true},
238 {Value: nilStruct, ExpectXML: ``, MarshalOnly: true},
239
240 // Test value types
241 {Value: &Plain{true}, ExpectXML: `<Plain><V>true</V></Plain>`},
242 {Value: &Plain{false}, ExpectXML: `<Plain><V>false</V></Plain>`},
243 {Value: &Plain{int(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
244 {Value: &Plain{int8(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
245 {Value: &Plain{int16(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
246 {Value: &Plain{int32(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
247 {Value: &Plain{uint(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
248 {Value: &Plain{uint8(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
249 {Value: &Plain{uint16(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
250 {Value: &Plain{uint32(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
251 {Value: &Plain{float32(1.25)}, ExpectXML: `<Plain><V>1.25</V></Plain>`},
252 {Value: &Plain{float64(1.25)}, ExpectXML: `<Plain><V>1.25</V></Plain>`},
253 {Value: &Plain{uintptr(0xFFDD)}, ExpectXML: `<Plain><V>65501</V></Plain>`},
254 {Value: &Plain{"gopher"}, ExpectXML: `<Plain><V>gopher</V></Plain>`},
255 {Value: &Plain{[]byte("gopher")}, ExpectXML: `<Plain><V>gopher</V></Plain>`},
256 {Value: &Plain{"</>"}, ExpectXML: `<Plain><V>&lt;/&gt;</V></Plain>`},
257 {Value: &Plain{[]byte("</>")}, ExpectXML: `<Plain><V>&lt;/&gt;</V></Plain>`},
258 {Value: &Plain{[3]byte{'<', '/', '>'}}, ExpectXML: `<Plain><V>&lt;/&gt;</V></Plain>`},
259 {Value: &Plain{NamedType("potato")}, ExpectXML: `<Plain><V>potato</V></Plain>`},
260 {Value: &Plain{[]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3</V></Plain>`},
261 {Value: &Plain{[3]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3</V></Plain>`},
262
263 // Test time.
264 {
265 Value: &Plain{time.Unix(1e9, 123456789).UTC()},
266 ExpectXML: `<Plain><V>2001-09-09T01:46:40.123456789Z</V></Plain>`,
267 },
268
269 // A pointer to struct{} may be used to test for an element's presence.
270 {
271 Value: &PresenceTest{new(struct{})},
272 ExpectXML: `<PresenceTest><Exists></Exists></PresenceTest>`,
273 },
274 {
275 Value: &PresenceTest{},
276 ExpectXML: `<PresenceTest></PresenceTest>`,
277 },
278
279 // A pointer to struct{} may be used to test for an element's presence.
280 {
281 Value: &PresenceTest{new(struct{})},
282 ExpectXML: `<PresenceTest><Exists></Exists></PresenceTest>`,
283 },
284 {
285 Value: &PresenceTest{},
286 ExpectXML: `<PresenceTest></PresenceTest>`,
287 },
288
289 // A []byte field is only nil if the element was not found.
290 {
291 Value: &Data{},
292 ExpectXML: `<Data></Data>`,
293 UnmarshalOnly: true,
294 },
295 {
296 Value: &Data{Bytes: []byte{}, Custom: MyBytes{}, Attr: []byte{}},
297 ExpectXML: `<Data Attr=""><Bytes></Bytes><Custom></Custom></Data>`,
298 UnmarshalOnly: true,
299 },
300
301 // Check that []byte works, including named []byte types.
302 {
303 Value: &Data{Bytes: []byte("ab"), Custom: MyBytes("cd"), Attr: []byte{'v'}},
304 ExpectXML: `<Data Attr="v"><Bytes>ab</Bytes><Custom>cd</Custom></Data>`,
305 },
306
307 // Test innerxml
308 {
309 Value: &SecretAgent{
310 Handle: "007",
311 Identity: "James Bond",
312 Obfuscate: "<redacted/>",
313 },
314 ExpectXML: `<agent handle="007"><Identity>James Bond</Identity><redacted/></agent>`,
315 MarshalOnly: true,
316 },
317 {
318 Value: &SecretAgent{
319 Handle: "007",
320 Identity: "James Bond",
321 Obfuscate: "<Identity>James Bond</Identity><redacted/>",
322 },
323 ExpectXML: `<agent handle="007"><Identity>James Bond</Identity><redacted/></agent>`,
324 UnmarshalOnly: true,
325 },
326
327 // Test structs
328 {Value: &Port{Type: "ssl", Number: "443"}, ExpectXML: `<port type="ssl">443</port>`},
329 {Value: &Port{Number: "443"}, ExpectXML: `<port>443</port>`},
330 {Value: &Port{Type: "<unix>"}, ExpectXML: `<port type="&lt;unix&gt;"></port>`},
331 {Value: &Port{Number: "443", Comment: "https"}, ExpectXML: `<port><!--https-->443</port>`},
332 {Value: &Port{Number: "443", Comment: "add space-"}, ExpectXML: `<port><!--add space- -->443</port>`, MarshalOnly: true},
333 {Value: &Domain{Name: []byte("google.com&friends")}, ExpectXML: `<domain>google.com&amp;friends</domain>`},
334 {Value: &Domain{Name: []byte("google.com"), Comment: []byte(" &friends ")}, ExpectXML: `<domain>google.com<!-- &friends --></domain>`},
335 {Value: &Book{Title: "Pride & Prejudice"}, ExpectXML: `<book>Pride &amp; Prejudice</book>`},
336 {Value: atomValue, ExpectXML: atomXml},
337 {
338 Value: &Ship{
339 Name: "Heart of Gold",
340 Pilot: "Computer",
341 Age: 1,
342 Drive: ImprobabilityDrive,
343 Passenger: []*Passenger{
344 {
345 Name: []string{"Zaphod", "Beeblebrox"},
346 Weight: 7.25,
347 },
348 {
349 Name: []string{"Trisha", "McMillen"},
350 Weight: 5.5,
351 },
352 {
353 Name: []string{"Ford", "Prefect"},
354 Weight: 7,
355 },
356 {
357 Name: []string{"Arthur", "Dent"},
358 Weight: 6.75,
359 },
360 },
361 },
362 ExpectXML: `<spaceship name="Heart of Gold" pilot="Computer">` +
363 `<drive>` + strconv.Itoa(int(ImprobabilityDrive)) + `</drive>` +
364 `<age>1</age>` +
365 `<passenger>` +
366 `<name>Zaphod</name>` +
367 `<name>Beeblebrox</name>` +
368 `<weight>7.25</weight>` +
369 `</passenger>` +
370 `<passenger>` +
371 `<name>Trisha</name>` +
372 `<name>McMillen</name>` +
373 `<weight>5.5</weight>` +
374 `</passenger>` +
375 `<passenger>` +
376 `<name>Ford</name>` +
377 `<name>Prefect</name>` +
378 `<weight>7</weight>` +
379 `</passenger>` +
380 `<passenger>` +
381 `<name>Arthur</name>` +
382 `<name>Dent</name>` +
383 `<weight>6.75</weight>` +
384 `</passenger>` +
385 `</spaceship>`,
386 },
387
388 // Test a>b
389 {
390 Value: &NestedItems{Items: nil, Item1: nil},
391 ExpectXML: `<result>` +
392 `<Items>` +
393 `</Items>` +
394 `</result>`,
395 },
396 {
397 Value: &NestedItems{Items: []string{}, Item1: []string{}},
398 ExpectXML: `<result>` +
399 `<Items>` +
400 `</Items>` +
401 `</result>`,
402 MarshalOnly: true,
403 },
404 {
405 Value: &NestedItems{Items: nil, Item1: []string{"A"}},
406 ExpectXML: `<result>` +
407 `<Items>` +
408 `<item1>A</item1>` +
409 `</Items>` +
410 `</result>`,
411 },
412 {
413 Value: &NestedItems{Items: []string{"A", "B"}, Item1: nil},
414 ExpectXML: `<result>` +
415 `<Items>` +
416 `<item>A</item>` +
417 `<item>B</item>` +
418 `</Items>` +
419 `</result>`,
420 },
421 {
422 Value: &NestedItems{Items: []string{"A", "B"}, Item1: []string{"C"}},
423 ExpectXML: `<result>` +
424 `<Items>` +
425 `<item>A</item>` +
426 `<item>B</item>` +
427 `<item1>C</item1>` +
428 `</Items>` +
429 `</result>`,
430 },
431 {
432 Value: &NestedOrder{Field1: "C", Field2: "B", Field3: "A"},
433 ExpectXML: `<result>` +
434 `<parent>` +
435 `<c>C</c>` +
436 `<b>B</b>` +
437 `<a>A</a>` +
438 `</parent>` +
439 `</result>`,
440 },
441 {
442 Value: &NilTest{A: "A", B: nil, C: "C"},
443 ExpectXML: `<NilTest>` +
444 `<parent1>` +
445 `<parent2><a>A</a></parent2>` +
446 `<parent2><c>C</c></parent2>` +
447 `</parent1>` +
448 `</NilTest>`,
449 MarshalOnly: true, // Uses interface{}
450 },
451 {
452 Value: &MixedNested{A: "A", B: "B", C: "C", D: "D"},
453 ExpectXML: `<result>` +
454 `<parent1><a>A</a></parent1>` +
455 `<b>B</b>` +
456 `<parent1>` +
457 `<parent2><c>C</c></parent2>` +
458 `<d>D</d>` +
459 `</parent1>` +
460 `</result>`,
461 },
462 {
463 Value: &Service{Port: &Port{Number: "80"}},
464 ExpectXML: `<service><host><port>80</port></host></service>`,
465 },
466 {
467 Value: &Service{},
468 ExpectXML: `<service></service>`,
469 },
470 {
471 Value: &Service{Port: &Port{Number: "80"}, Extra1: "A", Extra2: "B"},
472 ExpectXML: `<service>` +
473 `<host><port>80</port></host>` +
474 `<Extra1>A</Extra1>` +
475 `<host><extra2>B</extra2></host>` +
476 `</service>`,
477 MarshalOnly: true,
478 },
479 {
480 Value: &Service{Port: &Port{Number: "80"}, Extra2: "example"},
481 ExpectXML: `<service>` +
482 `<host><port>80</port></host>` +
483 `<host><extra2>example</extra2></host>` +
484 `</service>`,
485 MarshalOnly: true,
486 },
487
488 // Test struct embedding
489 {
490 Value: &EmbedA{
491 EmbedC: EmbedC{
492 FieldA1: "", // Shadowed by A.A
493 FieldA2: "", // Shadowed by A.A
494 FieldB: "A.C.B",
495 FieldC: "A.C.C",
496 },
497 EmbedB: EmbedB{
498 FieldB: "A.B.B",
499 EmbedC: &EmbedC{
500 FieldA1: "A.B.C.A1",
501 FieldA2: "A.B.C.A2",
502 FieldB: "", // Shadowed by A.B.B
503 FieldC: "A.B.C.C",
504 },
505 },
506 FieldA: "A.A",
507 },
508 ExpectXML: `<EmbedA>` +
509 `<FieldB>A.C.B</FieldB>` +
510 `<FieldC>A.C.C</FieldC>` +
511 `<EmbedB>` +
512 `<FieldB>A.B.B</FieldB>` +
513 `<FieldA>` +
514 `<A1>A.B.C.A1</A1>` +
515 `<A2>A.B.C.A2</A2>` +
516 `</FieldA>` +
517 `<FieldC>A.B.C.C</FieldC>` +
518 `</EmbedB>` +
519 `<FieldA>A.A</FieldA>` +
520 `</EmbedA>`,
521 },
522
523 // Test that name casing matters
524 {
525 Value: &NameCasing{Xy: "mixed", XY: "upper", XyA: "mixedA", XYA: "upperA"},
526 ExpectXML: `<casing Xy="mixedA" XY="upperA"><Xy>mixed</Xy><XY>upper</XY></casing>`,
527 },
528
529 // Test the order in which the XML element name is chosen
530 {
531 Value: &NamePrecedence{
532 FromTag: XMLNameWithoutTag{Value: "A"},
533 FromNameVal: XMLNameWithoutTag{XMLName: Name{Local: "InXMLName"}, Value: "B"},
534 FromNameTag: XMLNameWithTag{Value: "C"},
535 InFieldName: "D",
536 },
537 ExpectXML: `<Parent>` +
538 `<InTag>A</InTag>` +
539 `<InXMLName>B</InXMLName>` +
540 `<InXMLNameTag>C</InXMLNameTag>` +
541 `<InFieldName>D</InFieldName>` +
542 `</Parent>`,
543 MarshalOnly: true,
544 },
545 {
546 Value: &NamePrecedence{
547 XMLName: Name{Local: "Parent"},
548 FromTag: XMLNameWithoutTag{XMLName: Name{Local: "InTag"}, Value: "A"},
549 FromNameVal: XMLNameWithoutTag{XMLName: Name{Local: "FromNameVal"}, Value: "B"},
550 FromNameTag: XMLNameWithTag{XMLName: Name{Local: "InXMLNameTag"}, Value: "C"},
551 InFieldName: "D",
552 },
553 ExpectXML: `<Parent>` +
554 `<InTag>A</InTag>` +
555 `<FromNameVal>B</FromNameVal>` +
556 `<InXMLNameTag>C</InXMLNameTag>` +
557 `<InFieldName>D</InFieldName>` +
558 `</Parent>`,
559 UnmarshalOnly: true,
560 },
561
562 // xml.Name works in a plain field as well.
563 {
564 Value: &NameInField{Name{Space: "ns", Local: "foo"}},
565 ExpectXML: `<NameInField><foo xmlns="ns"></foo></NameInField>`,
566 },
567 {
568 Value: &NameInField{Name{Space: "ns", Local: "foo"}},
569 ExpectXML: `<NameInField><foo xmlns="ns"><ignore></ignore></foo></NameInField>`,
570 UnmarshalOnly: true,
571 },
572
573 // Marshaling zero xml.Name uses the tag or field name.
574 {
575 Value: &NameInField{},
576 ExpectXML: `<NameInField><foo xmlns="ns"></foo></NameInField>`,
577 MarshalOnly: true,
578 },
579
580 // Test attributes
581 {
582 Value: &AttrTest{
583 Int: 8,
584 Named: 9,
585 Float: 23.5,
586 Uint8: 255,
587 Bool: true,
588 Str: "str",
589 Bytes: []byte("byt"),
590 },
591 ExpectXML: `<AttrTest Int="8" int="9" Float="23.5" Uint8="255"` +
592 ` Bool="true" Str="str" Bytes="byt"></AttrTest>`,
593 },
594 {
595 Value: &AttrTest{Bytes: []byte{}},
596 ExpectXML: `<AttrTest Int="0" int="0" Float="0" Uint8="0"` +
597 ` Bool="false" Str="" Bytes=""></AttrTest>`,
598 },
599 {
600 Value: &OmitAttrTest{
601 Int: 8,
602 Named: 9,
603 Float: 23.5,
604 Uint8: 255,
605 Bool: true,
606 Str: "str",
607 Bytes: []byte("byt"),
608 },
609 ExpectXML: `<OmitAttrTest Int="8" int="9" Float="23.5" Uint8="255"` +
610 ` Bool="true" Str="str" Bytes="byt"></OmitAttrTest>`,
611 },
612 {
613 Value: &OmitAttrTest{},
614 ExpectXML: `<OmitAttrTest></OmitAttrTest>`,
615 },
616
617 // omitempty on fields
618 {
619 Value: &OmitFieldTest{
620 Int: 8,
621 Named: 9,
622 Float: 23.5,
623 Uint8: 255,
624 Bool: true,
625 Str: "str",
626 Bytes: []byte("byt"),
627 Ptr: &PresenceTest{},
628 },
629 ExpectXML: `<OmitFieldTest>` +
630 `<Int>8</Int>` +
631 `<int>9</int>` +
632 `<Float>23.5</Float>` +
633 `<Uint8>255</Uint8>` +
634 `<Bool>true</Bool>` +
635 `<Str>str</Str>` +
636 `<Bytes>byt</Bytes>` +
637 `<Ptr></Ptr>` +
638 `</OmitFieldTest>`,
639 },
640 {
641 Value: &OmitFieldTest{},
642 ExpectXML: `<OmitFieldTest></OmitFieldTest>`,
643 },
644
645 // Test ",any"
646 {
647 ExpectXML: `<a><nested><value>known</value></nested><other><sub>unknown</sub></other></a>`,
648 Value: &AnyTest{
649 Nested: "known",
650 AnyField: AnyHolder{
651 XMLName: Name{Local: "other"},
652 XML: "<sub>unknown</sub>",
653 },
654 },
655 UnmarshalOnly: true,
656 },
657 {
658 Value: &AnyTest{Nested: "known", AnyField: AnyHolder{XML: "<unknown/>"}},
659 ExpectXML: `<a><nested><value>known</value></nested></a>`,
660 MarshalOnly: true,
661 },
662
663 // Test recursive types.
664 {
665 Value: &RecurseA{
666 A: "a1",
667 B: &RecurseB{
668 A: &RecurseA{"a2", nil},
669 B: "b1",
670 },
671 },
672 ExpectXML: `<RecurseA><A>a1</A><B><A><A>a2</A></A><B>b1</B></B></RecurseA>`,
673 },
674
675 // Test ignoring fields via "-" tag
676 {
677 ExpectXML: `<IgnoreTest></IgnoreTest>`,
678 Value: &IgnoreTest{},
679 },
680 {
681 ExpectXML: `<IgnoreTest></IgnoreTest>`,
682 Value: &IgnoreTest{PublicSecret: "can't tell"},
683 MarshalOnly: true,
684 },
685 {
686 ExpectXML: `<IgnoreTest><PublicSecret>ignore me</PublicSecret></IgnoreTest>`,
687 Value: &IgnoreTest{},
688 UnmarshalOnly: true,
689 },
690 }
691
692 func TestMarshal(t *testing.T) {
693 for idx, test := range marshalTests {
694 if test.UnmarshalOnly {
695 continue
696 }
697 data, err := Marshal(test.Value)
698 if err != nil {
699 t.Errorf("#%d: Error: %s", idx, err)
700 continue
701 }
702 if got, want := string(data), test.ExpectXML; got != want {
703 if strings.Contains(want, "\n") {
704 t.Errorf("#%d: marshal(%#v):\nHAVE:\n%s\nWANT:\n%s", idx, test.Value, got, want)
705 } else {
706 t.Errorf("#%d: marshal(%#v):\nhave %#q\nwant %#q", idx, test.Value, got, want)
707 }
708 }
709 }
710 }
711
712 var marshalErrorTests = []struct {
713 Value interface{}
714 Err string
715 Kind reflect.Kind
716 }{
717 {
718 Value: make(chan bool),
719 Err: "xml: unsupported type: chan bool",
720 Kind: reflect.Chan,
721 },
722 {
723 Value: map[string]string{
724 "question": "What do you get when you multiply six by nine?",
725 "answer": "42",
726 },
727 Err: "xml: unsupported type: map[string]string",
728 Kind: reflect.Map,
729 },
730 {
731 Value: map[*Ship]bool{nil: false},
732 Err: "xml: unsupported type: map[*xml.Ship]bool",
733 Kind: reflect.Map,
734 },
735 {
736 Value: &Domain{Comment: []byte("f--bar")},
737 Err: `xml: comments must not contain "--"`,
738 },
739 }
740
741 func TestMarshalErrors(t *testing.T) {
742 for idx, test := range marshalErrorTests {
743 _, err := Marshal(test.Value)
744 if err == nil || err.Error() != test.Err {
745 t.Errorf("#%d: marshal(%#v) = [error] %v, want %v", idx, test.Value, err, test.Err)
746 }
747 if test.Kind != reflect.Invalid {
748 if kind := err.(*UnsupportedTypeError).Type.Kind(); kind != test.Kind {
749 t.Errorf("#%d: marshal(%#v) = [error kind] %s, want %s", idx, test.Value, kind, test.Kind)
750 }
751 }
752 }
753 }
754
755 // Do invertibility testing on the various structures that we test
756 func TestUnmarshal(t *testing.T) {
757 for i, test := range marshalTests {
758 if test.MarshalOnly {
759 continue
760 }
761 if _, ok := test.Value.(*Plain); ok {
762 continue
763 }
764
765 vt := reflect.TypeOf(test.Value)
766 dest := reflect.New(vt.Elem()).Interface()
767 err := Unmarshal([]byte(test.ExpectXML), dest)
768
769 switch fix := dest.(type) {
770 case *Feed:
771 fix.Author.InnerXML = ""
772 for i := range fix.Entry {
773 fix.Entry[i].Author.InnerXML = ""
774 }
775 }
776
777 if err != nil {
778 t.Errorf("#%d: unexpected error: %#v", i, err)
779 } else if got, want := dest, test.Value; !reflect.DeepEqual(got, want) {
780 t.Errorf("#%d: unmarshal(%q):\nhave %#v\nwant %#v", i, test.ExpectXML, got, want)
781 }
782 }
783 }
784
785 type limitedBytesWriter struct {
786 w io.Writer
787 remain int // until writes fail
788 }
789
790 func (lw *limitedBytesWriter) Write(p []byte) (n int, err error) {
791 if lw.remain <= 0 {
792 println("error")
793 return 0, errors.New("write limit hit")
794 }
795 if len(p) > lw.remain {
796 p = p[:lw.remain]
797 n, _ = lw.w.Write(p)
798 lw.remain = 0
799 return n, errors.New("write limit hit")
800 }
801 n, err = lw.w.Write(p)
802 lw.remain -= n
803 return n, err
804 }
805
806 func TestMarshalWriteErrors(t *testing.T) {
807 var buf bytes.Buffer
808 const writeCap = 1024
809 w := &limitedBytesWriter{&buf, writeCap}
810 enc := NewEncoder(w)
811 var err error
812 var i int
813 const n = 4000
814 for i = 1; i <= n; i++ {
815 err = enc.Encode(&Passenger{
816 Name: []string{"Alice", "Bob"},
817 Weight: 5,
818 })
819 if err != nil {
820 break
821 }
822 }
823 if err == nil {
824 t.Error("expected an error")
825 }
826 if i == n {
827 t.Errorf("expected to fail before the end")
828 }
829 if buf.Len() != writeCap {
830 t.Errorf("buf.Len() = %d; want %d", buf.Len(), writeCap)
831 }
832 }
833
834 func BenchmarkMarshal(b *testing.B) {
835 for i := 0; i < b.N; i++ {
836 Marshal(atomValue)
837 }
838 }
839
840 func BenchmarkUnmarshal(b *testing.B) {
841 xml := []byte(atomXml)
842 for i := 0; i < b.N; i++ {
843 Unmarshal(xml, &Feed{})
844 }
845 }