re PR go/66138 (json decoder Decode function fails for some structure return values)
[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 "fmt"
11 "io"
12 "reflect"
13 "strconv"
14 "strings"
15 "sync"
16 "testing"
17 "time"
18 )
19
20 type DriveType int
21
22 const (
23 HyperDrive DriveType = iota
24 ImprobabilityDrive
25 )
26
27 type Passenger struct {
28 Name []string `xml:"name"`
29 Weight float32 `xml:"weight"`
30 }
31
32 type Ship struct {
33 XMLName struct{} `xml:"spaceship"`
34
35 Name string `xml:"name,attr"`
36 Pilot string `xml:"pilot,attr"`
37 Drive DriveType `xml:"drive"`
38 Age uint `xml:"age"`
39 Passenger []*Passenger `xml:"passenger"`
40 secret string
41 }
42
43 type NamedType string
44
45 type Port struct {
46 XMLName struct{} `xml:"port"`
47 Type string `xml:"type,attr,omitempty"`
48 Comment string `xml:",comment"`
49 Number string `xml:",chardata"`
50 }
51
52 type Domain struct {
53 XMLName struct{} `xml:"domain"`
54 Country string `xml:",attr,omitempty"`
55 Name []byte `xml:",chardata"`
56 Comment []byte `xml:",comment"`
57 }
58
59 type Book struct {
60 XMLName struct{} `xml:"book"`
61 Title string `xml:",chardata"`
62 }
63
64 type Event struct {
65 XMLName struct{} `xml:"event"`
66 Year int `xml:",chardata"`
67 }
68
69 type Movie struct {
70 XMLName struct{} `xml:"movie"`
71 Length uint `xml:",chardata"`
72 }
73
74 type Pi struct {
75 XMLName struct{} `xml:"pi"`
76 Approximation float32 `xml:",chardata"`
77 }
78
79 type Universe struct {
80 XMLName struct{} `xml:"universe"`
81 Visible float64 `xml:",chardata"`
82 }
83
84 type Particle struct {
85 XMLName struct{} `xml:"particle"`
86 HasMass bool `xml:",chardata"`
87 }
88
89 type Departure struct {
90 XMLName struct{} `xml:"departure"`
91 When time.Time `xml:",chardata"`
92 }
93
94 type SecretAgent struct {
95 XMLName struct{} `xml:"agent"`
96 Handle string `xml:"handle,attr"`
97 Identity string
98 Obfuscate string `xml:",innerxml"`
99 }
100
101 type NestedItems struct {
102 XMLName struct{} `xml:"result"`
103 Items []string `xml:">item"`
104 Item1 []string `xml:"Items>item1"`
105 }
106
107 type NestedOrder struct {
108 XMLName struct{} `xml:"result"`
109 Field1 string `xml:"parent>c"`
110 Field2 string `xml:"parent>b"`
111 Field3 string `xml:"parent>a"`
112 }
113
114 type MixedNested struct {
115 XMLName struct{} `xml:"result"`
116 A string `xml:"parent1>a"`
117 B string `xml:"b"`
118 C string `xml:"parent1>parent2>c"`
119 D string `xml:"parent1>d"`
120 }
121
122 type NilTest struct {
123 A interface{} `xml:"parent1>parent2>a"`
124 B interface{} `xml:"parent1>b"`
125 C interface{} `xml:"parent1>parent2>c"`
126 }
127
128 type Service struct {
129 XMLName struct{} `xml:"service"`
130 Domain *Domain `xml:"host>domain"`
131 Port *Port `xml:"host>port"`
132 Extra1 interface{}
133 Extra2 interface{} `xml:"host>extra2"`
134 }
135
136 var nilStruct *Ship
137
138 type EmbedA struct {
139 EmbedC
140 EmbedB EmbedB
141 FieldA string
142 embedD
143 }
144
145 type EmbedB struct {
146 FieldB string
147 *EmbedC
148 }
149
150 type EmbedC struct {
151 FieldA1 string `xml:"FieldA>A1"`
152 FieldA2 string `xml:"FieldA>A2"`
153 FieldB string
154 FieldC string
155 }
156
157 type embedD struct {
158 fieldD string
159 FieldE string // Promoted and visible when embedD is embedded.
160 }
161
162 type NameCasing struct {
163 XMLName struct{} `xml:"casing"`
164 Xy string
165 XY string
166 XyA string `xml:"Xy,attr"`
167 XYA string `xml:"XY,attr"`
168 }
169
170 type NamePrecedence struct {
171 XMLName Name `xml:"Parent"`
172 FromTag XMLNameWithoutTag `xml:"InTag"`
173 FromNameVal XMLNameWithoutTag
174 FromNameTag XMLNameWithTag
175 InFieldName string
176 }
177
178 type XMLNameWithTag struct {
179 XMLName Name `xml:"InXMLNameTag"`
180 Value string `xml:",chardata"`
181 }
182
183 type XMLNameWithoutTag struct {
184 XMLName Name
185 Value string `xml:",chardata"`
186 }
187
188 type NameInField struct {
189 Foo Name `xml:"ns foo"`
190 }
191
192 type AttrTest struct {
193 Int int `xml:",attr"`
194 Named int `xml:"int,attr"`
195 Float float64 `xml:",attr"`
196 Uint8 uint8 `xml:",attr"`
197 Bool bool `xml:",attr"`
198 Str string `xml:",attr"`
199 Bytes []byte `xml:",attr"`
200 }
201
202 type OmitAttrTest struct {
203 Int int `xml:",attr,omitempty"`
204 Named int `xml:"int,attr,omitempty"`
205 Float float64 `xml:",attr,omitempty"`
206 Uint8 uint8 `xml:",attr,omitempty"`
207 Bool bool `xml:",attr,omitempty"`
208 Str string `xml:",attr,omitempty"`
209 Bytes []byte `xml:",attr,omitempty"`
210 }
211
212 type OmitFieldTest struct {
213 Int int `xml:",omitempty"`
214 Named int `xml:"int,omitempty"`
215 Float float64 `xml:",omitempty"`
216 Uint8 uint8 `xml:",omitempty"`
217 Bool bool `xml:",omitempty"`
218 Str string `xml:",omitempty"`
219 Bytes []byte `xml:",omitempty"`
220 Ptr *PresenceTest `xml:",omitempty"`
221 }
222
223 type AnyTest struct {
224 XMLName struct{} `xml:"a"`
225 Nested string `xml:"nested>value"`
226 AnyField AnyHolder `xml:",any"`
227 }
228
229 type AnyOmitTest struct {
230 XMLName struct{} `xml:"a"`
231 Nested string `xml:"nested>value"`
232 AnyField *AnyHolder `xml:",any,omitempty"`
233 }
234
235 type AnySliceTest struct {
236 XMLName struct{} `xml:"a"`
237 Nested string `xml:"nested>value"`
238 AnyField []AnyHolder `xml:",any"`
239 }
240
241 type AnyHolder struct {
242 XMLName Name
243 XML string `xml:",innerxml"`
244 }
245
246 type RecurseA struct {
247 A string
248 B *RecurseB
249 }
250
251 type RecurseB struct {
252 A *RecurseA
253 B string
254 }
255
256 type PresenceTest struct {
257 Exists *struct{}
258 }
259
260 type IgnoreTest struct {
261 PublicSecret string `xml:"-"`
262 }
263
264 type MyBytes []byte
265
266 type Data struct {
267 Bytes []byte
268 Attr []byte `xml:",attr"`
269 Custom MyBytes
270 }
271
272 type Plain struct {
273 V interface{}
274 }
275
276 type MyInt int
277
278 type EmbedInt struct {
279 MyInt
280 }
281
282 type Strings struct {
283 X []string `xml:"A>B,omitempty"`
284 }
285
286 type PointerFieldsTest struct {
287 XMLName Name `xml:"dummy"`
288 Name *string `xml:"name,attr"`
289 Age *uint `xml:"age,attr"`
290 Empty *string `xml:"empty,attr"`
291 Contents *string `xml:",chardata"`
292 }
293
294 type ChardataEmptyTest struct {
295 XMLName Name `xml:"test"`
296 Contents *string `xml:",chardata"`
297 }
298
299 type MyMarshalerTest struct {
300 }
301
302 var _ Marshaler = (*MyMarshalerTest)(nil)
303
304 func (m *MyMarshalerTest) MarshalXML(e *Encoder, start StartElement) error {
305 e.EncodeToken(start)
306 e.EncodeToken(CharData([]byte("hello world")))
307 e.EncodeToken(EndElement{start.Name})
308 return nil
309 }
310
311 type MyMarshalerAttrTest struct {
312 }
313
314 var _ MarshalerAttr = (*MyMarshalerAttrTest)(nil)
315
316 func (m *MyMarshalerAttrTest) MarshalXMLAttr(name Name) (Attr, error) {
317 return Attr{name, "hello world"}, nil
318 }
319
320 type MarshalerStruct struct {
321 Foo MyMarshalerAttrTest `xml:",attr"`
322 }
323
324 type InnerStruct struct {
325 XMLName Name `xml:"testns outer"`
326 }
327
328 type OuterStruct struct {
329 InnerStruct
330 IntAttr int `xml:"int,attr"`
331 }
332
333 type OuterNamedStruct struct {
334 InnerStruct
335 XMLName Name `xml:"outerns test"`
336 IntAttr int `xml:"int,attr"`
337 }
338
339 type OuterNamedOrderedStruct struct {
340 XMLName Name `xml:"outerns test"`
341 InnerStruct
342 IntAttr int `xml:"int,attr"`
343 }
344
345 type OuterOuterStruct struct {
346 OuterStruct
347 }
348
349 type NestedAndChardata struct {
350 AB []string `xml:"A>B"`
351 Chardata string `xml:",chardata"`
352 }
353
354 type NestedAndComment struct {
355 AB []string `xml:"A>B"`
356 Comment string `xml:",comment"`
357 }
358
359 func ifaceptr(x interface{}) interface{} {
360 return &x
361 }
362
363 var (
364 nameAttr = "Sarah"
365 ageAttr = uint(12)
366 contentsAttr = "lorem ipsum"
367 )
368
369 // Unless explicitly stated as such (or *Plain), all of the
370 // tests below are two-way tests. When introducing new tests,
371 // please try to make them two-way as well to ensure that
372 // marshalling and unmarshalling are as symmetrical as feasible.
373 var marshalTests = []struct {
374 Value interface{}
375 ExpectXML string
376 MarshalOnly bool
377 UnmarshalOnly bool
378 }{
379 // Test nil marshals to nothing
380 {Value: nil, ExpectXML: ``, MarshalOnly: true},
381 {Value: nilStruct, ExpectXML: ``, MarshalOnly: true},
382
383 // Test value types
384 {Value: &Plain{true}, ExpectXML: `<Plain><V>true</V></Plain>`},
385 {Value: &Plain{false}, ExpectXML: `<Plain><V>false</V></Plain>`},
386 {Value: &Plain{int(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
387 {Value: &Plain{int8(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
388 {Value: &Plain{int16(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
389 {Value: &Plain{int32(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
390 {Value: &Plain{uint(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
391 {Value: &Plain{uint8(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
392 {Value: &Plain{uint16(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
393 {Value: &Plain{uint32(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
394 {Value: &Plain{float32(1.25)}, ExpectXML: `<Plain><V>1.25</V></Plain>`},
395 {Value: &Plain{float64(1.25)}, ExpectXML: `<Plain><V>1.25</V></Plain>`},
396 {Value: &Plain{uintptr(0xFFDD)}, ExpectXML: `<Plain><V>65501</V></Plain>`},
397 {Value: &Plain{"gopher"}, ExpectXML: `<Plain><V>gopher</V></Plain>`},
398 {Value: &Plain{[]byte("gopher")}, ExpectXML: `<Plain><V>gopher</V></Plain>`},
399 {Value: &Plain{"</>"}, ExpectXML: `<Plain><V>&lt;/&gt;</V></Plain>`},
400 {Value: &Plain{[]byte("</>")}, ExpectXML: `<Plain><V>&lt;/&gt;</V></Plain>`},
401 {Value: &Plain{[3]byte{'<', '/', '>'}}, ExpectXML: `<Plain><V>&lt;/&gt;</V></Plain>`},
402 {Value: &Plain{NamedType("potato")}, ExpectXML: `<Plain><V>potato</V></Plain>`},
403 {Value: &Plain{[]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3</V></Plain>`},
404 {Value: &Plain{[3]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3</V></Plain>`},
405 {Value: ifaceptr(true), MarshalOnly: true, ExpectXML: `<bool>true</bool>`},
406
407 // Test time.
408 {
409 Value: &Plain{time.Unix(1e9, 123456789).UTC()},
410 ExpectXML: `<Plain><V>2001-09-09T01:46:40.123456789Z</V></Plain>`,
411 },
412
413 // A pointer to struct{} may be used to test for an element's presence.
414 {
415 Value: &PresenceTest{new(struct{})},
416 ExpectXML: `<PresenceTest><Exists></Exists></PresenceTest>`,
417 },
418 {
419 Value: &PresenceTest{},
420 ExpectXML: `<PresenceTest></PresenceTest>`,
421 },
422
423 // A pointer to struct{} may be used to test for an element's presence.
424 {
425 Value: &PresenceTest{new(struct{})},
426 ExpectXML: `<PresenceTest><Exists></Exists></PresenceTest>`,
427 },
428 {
429 Value: &PresenceTest{},
430 ExpectXML: `<PresenceTest></PresenceTest>`,
431 },
432
433 // A []byte field is only nil if the element was not found.
434 {
435 Value: &Data{},
436 ExpectXML: `<Data></Data>`,
437 UnmarshalOnly: true,
438 },
439 {
440 Value: &Data{Bytes: []byte{}, Custom: MyBytes{}, Attr: []byte{}},
441 ExpectXML: `<Data Attr=""><Bytes></Bytes><Custom></Custom></Data>`,
442 UnmarshalOnly: true,
443 },
444
445 // Check that []byte works, including named []byte types.
446 {
447 Value: &Data{Bytes: []byte("ab"), Custom: MyBytes("cd"), Attr: []byte{'v'}},
448 ExpectXML: `<Data Attr="v"><Bytes>ab</Bytes><Custom>cd</Custom></Data>`,
449 },
450
451 // Test innerxml
452 {
453 Value: &SecretAgent{
454 Handle: "007",
455 Identity: "James Bond",
456 Obfuscate: "<redacted/>",
457 },
458 ExpectXML: `<agent handle="007"><Identity>James Bond</Identity><redacted/></agent>`,
459 MarshalOnly: true,
460 },
461 {
462 Value: &SecretAgent{
463 Handle: "007",
464 Identity: "James Bond",
465 Obfuscate: "<Identity>James Bond</Identity><redacted/>",
466 },
467 ExpectXML: `<agent handle="007"><Identity>James Bond</Identity><redacted/></agent>`,
468 UnmarshalOnly: true,
469 },
470
471 // Test structs
472 {Value: &Port{Type: "ssl", Number: "443"}, ExpectXML: `<port type="ssl">443</port>`},
473 {Value: &Port{Number: "443"}, ExpectXML: `<port>443</port>`},
474 {Value: &Port{Type: "<unix>"}, ExpectXML: `<port type="&lt;unix&gt;"></port>`},
475 {Value: &Port{Number: "443", Comment: "https"}, ExpectXML: `<port><!--https-->443</port>`},
476 {Value: &Port{Number: "443", Comment: "add space-"}, ExpectXML: `<port><!--add space- -->443</port>`, MarshalOnly: true},
477 {Value: &Domain{Name: []byte("google.com&friends")}, ExpectXML: `<domain>google.com&amp;friends</domain>`},
478 {Value: &Domain{Name: []byte("google.com"), Comment: []byte(" &friends ")}, ExpectXML: `<domain>google.com<!-- &friends --></domain>`},
479 {Value: &Book{Title: "Pride & Prejudice"}, ExpectXML: `<book>Pride &amp; Prejudice</book>`},
480 {Value: &Event{Year: -3114}, ExpectXML: `<event>-3114</event>`},
481 {Value: &Movie{Length: 13440}, ExpectXML: `<movie>13440</movie>`},
482 {Value: &Pi{Approximation: 3.14159265}, ExpectXML: `<pi>3.1415927</pi>`},
483 {Value: &Universe{Visible: 9.3e13}, ExpectXML: `<universe>9.3e+13</universe>`},
484 {Value: &Particle{HasMass: true}, ExpectXML: `<particle>true</particle>`},
485 {Value: &Departure{When: ParseTime("2013-01-09T00:15:00-09:00")}, ExpectXML: `<departure>2013-01-09T00:15:00-09:00</departure>`},
486 {Value: atomValue, ExpectXML: atomXml},
487 {
488 Value: &Ship{
489 Name: "Heart of Gold",
490 Pilot: "Computer",
491 Age: 1,
492 Drive: ImprobabilityDrive,
493 Passenger: []*Passenger{
494 {
495 Name: []string{"Zaphod", "Beeblebrox"},
496 Weight: 7.25,
497 },
498 {
499 Name: []string{"Trisha", "McMillen"},
500 Weight: 5.5,
501 },
502 {
503 Name: []string{"Ford", "Prefect"},
504 Weight: 7,
505 },
506 {
507 Name: []string{"Arthur", "Dent"},
508 Weight: 6.75,
509 },
510 },
511 },
512 ExpectXML: `<spaceship name="Heart of Gold" pilot="Computer">` +
513 `<drive>` + strconv.Itoa(int(ImprobabilityDrive)) + `</drive>` +
514 `<age>1</age>` +
515 `<passenger>` +
516 `<name>Zaphod</name>` +
517 `<name>Beeblebrox</name>` +
518 `<weight>7.25</weight>` +
519 `</passenger>` +
520 `<passenger>` +
521 `<name>Trisha</name>` +
522 `<name>McMillen</name>` +
523 `<weight>5.5</weight>` +
524 `</passenger>` +
525 `<passenger>` +
526 `<name>Ford</name>` +
527 `<name>Prefect</name>` +
528 `<weight>7</weight>` +
529 `</passenger>` +
530 `<passenger>` +
531 `<name>Arthur</name>` +
532 `<name>Dent</name>` +
533 `<weight>6.75</weight>` +
534 `</passenger>` +
535 `</spaceship>`,
536 },
537
538 // Test a>b
539 {
540 Value: &NestedItems{Items: nil, Item1: nil},
541 ExpectXML: `<result>` +
542 `<Items>` +
543 `</Items>` +
544 `</result>`,
545 },
546 {
547 Value: &NestedItems{Items: []string{}, Item1: []string{}},
548 ExpectXML: `<result>` +
549 `<Items>` +
550 `</Items>` +
551 `</result>`,
552 MarshalOnly: true,
553 },
554 {
555 Value: &NestedItems{Items: nil, Item1: []string{"A"}},
556 ExpectXML: `<result>` +
557 `<Items>` +
558 `<item1>A</item1>` +
559 `</Items>` +
560 `</result>`,
561 },
562 {
563 Value: &NestedItems{Items: []string{"A", "B"}, Item1: nil},
564 ExpectXML: `<result>` +
565 `<Items>` +
566 `<item>A</item>` +
567 `<item>B</item>` +
568 `</Items>` +
569 `</result>`,
570 },
571 {
572 Value: &NestedItems{Items: []string{"A", "B"}, Item1: []string{"C"}},
573 ExpectXML: `<result>` +
574 `<Items>` +
575 `<item>A</item>` +
576 `<item>B</item>` +
577 `<item1>C</item1>` +
578 `</Items>` +
579 `</result>`,
580 },
581 {
582 Value: &NestedOrder{Field1: "C", Field2: "B", Field3: "A"},
583 ExpectXML: `<result>` +
584 `<parent>` +
585 `<c>C</c>` +
586 `<b>B</b>` +
587 `<a>A</a>` +
588 `</parent>` +
589 `</result>`,
590 },
591 {
592 Value: &NilTest{A: "A", B: nil, C: "C"},
593 ExpectXML: `<NilTest>` +
594 `<parent1>` +
595 `<parent2><a>A</a></parent2>` +
596 `<parent2><c>C</c></parent2>` +
597 `</parent1>` +
598 `</NilTest>`,
599 MarshalOnly: true, // Uses interface{}
600 },
601 {
602 Value: &MixedNested{A: "A", B: "B", C: "C", D: "D"},
603 ExpectXML: `<result>` +
604 `<parent1><a>A</a></parent1>` +
605 `<b>B</b>` +
606 `<parent1>` +
607 `<parent2><c>C</c></parent2>` +
608 `<d>D</d>` +
609 `</parent1>` +
610 `</result>`,
611 },
612 {
613 Value: &Service{Port: &Port{Number: "80"}},
614 ExpectXML: `<service><host><port>80</port></host></service>`,
615 },
616 {
617 Value: &Service{},
618 ExpectXML: `<service></service>`,
619 },
620 {
621 Value: &Service{Port: &Port{Number: "80"}, Extra1: "A", Extra2: "B"},
622 ExpectXML: `<service>` +
623 `<host><port>80</port></host>` +
624 `<Extra1>A</Extra1>` +
625 `<host><extra2>B</extra2></host>` +
626 `</service>`,
627 MarshalOnly: true,
628 },
629 {
630 Value: &Service{Port: &Port{Number: "80"}, Extra2: "example"},
631 ExpectXML: `<service>` +
632 `<host><port>80</port></host>` +
633 `<host><extra2>example</extra2></host>` +
634 `</service>`,
635 MarshalOnly: true,
636 },
637 {
638 Value: &struct {
639 XMLName struct{} `xml:"space top"`
640 A string `xml:"x>a"`
641 B string `xml:"x>b"`
642 C string `xml:"space x>c"`
643 C1 string `xml:"space1 x>c"`
644 D1 string `xml:"space1 x>d"`
645 }{
646 A: "a",
647 B: "b",
648 C: "c",
649 C1: "c1",
650 D1: "d1",
651 },
652 ExpectXML: `<top xmlns="space">` +
653 `<x><a>a</a><b>b</b><c xmlns="space">c</c>` +
654 `<c xmlns="space1">c1</c>` +
655 `<d xmlns="space1">d1</d>` +
656 `</x>` +
657 `</top>`,
658 },
659 {
660 Value: &struct {
661 XMLName Name
662 A string `xml:"x>a"`
663 B string `xml:"x>b"`
664 C string `xml:"space x>c"`
665 C1 string `xml:"space1 x>c"`
666 D1 string `xml:"space1 x>d"`
667 }{
668 XMLName: Name{
669 Space: "space0",
670 Local: "top",
671 },
672 A: "a",
673 B: "b",
674 C: "c",
675 C1: "c1",
676 D1: "d1",
677 },
678 ExpectXML: `<top xmlns="space0">` +
679 `<x><a>a</a><b>b</b>` +
680 `<c xmlns="space">c</c>` +
681 `<c xmlns="space1">c1</c>` +
682 `<d xmlns="space1">d1</d>` +
683 `</x>` +
684 `</top>`,
685 },
686 {
687 Value: &struct {
688 XMLName struct{} `xml:"top"`
689 B string `xml:"space x>b"`
690 B1 string `xml:"space1 x>b"`
691 }{
692 B: "b",
693 B1: "b1",
694 },
695 ExpectXML: `<top>` +
696 `<x><b xmlns="space">b</b>` +
697 `<b xmlns="space1">b1</b></x>` +
698 `</top>`,
699 },
700
701 // Test struct embedding
702 {
703 Value: &EmbedA{
704 EmbedC: EmbedC{
705 FieldA1: "", // Shadowed by A.A
706 FieldA2: "", // Shadowed by A.A
707 FieldB: "A.C.B",
708 FieldC: "A.C.C",
709 },
710 EmbedB: EmbedB{
711 FieldB: "A.B.B",
712 EmbedC: &EmbedC{
713 FieldA1: "A.B.C.A1",
714 FieldA2: "A.B.C.A2",
715 FieldB: "", // Shadowed by A.B.B
716 FieldC: "A.B.C.C",
717 },
718 },
719 FieldA: "A.A",
720 embedD: embedD{
721 FieldE: "A.D.E",
722 },
723 },
724 ExpectXML: `<EmbedA>` +
725 `<FieldB>A.C.B</FieldB>` +
726 `<FieldC>A.C.C</FieldC>` +
727 `<EmbedB>` +
728 `<FieldB>A.B.B</FieldB>` +
729 `<FieldA>` +
730 `<A1>A.B.C.A1</A1>` +
731 `<A2>A.B.C.A2</A2>` +
732 `</FieldA>` +
733 `<FieldC>A.B.C.C</FieldC>` +
734 `</EmbedB>` +
735 `<FieldA>A.A</FieldA>` +
736 `<FieldE>A.D.E</FieldE>` +
737 `</EmbedA>`,
738 },
739
740 // Test that name casing matters
741 {
742 Value: &NameCasing{Xy: "mixed", XY: "upper", XyA: "mixedA", XYA: "upperA"},
743 ExpectXML: `<casing Xy="mixedA" XY="upperA"><Xy>mixed</Xy><XY>upper</XY></casing>`,
744 },
745
746 // Test the order in which the XML element name is chosen
747 {
748 Value: &NamePrecedence{
749 FromTag: XMLNameWithoutTag{Value: "A"},
750 FromNameVal: XMLNameWithoutTag{XMLName: Name{Local: "InXMLName"}, Value: "B"},
751 FromNameTag: XMLNameWithTag{Value: "C"},
752 InFieldName: "D",
753 },
754 ExpectXML: `<Parent>` +
755 `<InTag>A</InTag>` +
756 `<InXMLName>B</InXMLName>` +
757 `<InXMLNameTag>C</InXMLNameTag>` +
758 `<InFieldName>D</InFieldName>` +
759 `</Parent>`,
760 MarshalOnly: true,
761 },
762 {
763 Value: &NamePrecedence{
764 XMLName: Name{Local: "Parent"},
765 FromTag: XMLNameWithoutTag{XMLName: Name{Local: "InTag"}, Value: "A"},
766 FromNameVal: XMLNameWithoutTag{XMLName: Name{Local: "FromNameVal"}, Value: "B"},
767 FromNameTag: XMLNameWithTag{XMLName: Name{Local: "InXMLNameTag"}, Value: "C"},
768 InFieldName: "D",
769 },
770 ExpectXML: `<Parent>` +
771 `<InTag>A</InTag>` +
772 `<FromNameVal>B</FromNameVal>` +
773 `<InXMLNameTag>C</InXMLNameTag>` +
774 `<InFieldName>D</InFieldName>` +
775 `</Parent>`,
776 UnmarshalOnly: true,
777 },
778
779 // xml.Name works in a plain field as well.
780 {
781 Value: &NameInField{Name{Space: "ns", Local: "foo"}},
782 ExpectXML: `<NameInField><foo xmlns="ns"></foo></NameInField>`,
783 },
784 {
785 Value: &NameInField{Name{Space: "ns", Local: "foo"}},
786 ExpectXML: `<NameInField><foo xmlns="ns"><ignore></ignore></foo></NameInField>`,
787 UnmarshalOnly: true,
788 },
789
790 // Marshaling zero xml.Name uses the tag or field name.
791 {
792 Value: &NameInField{},
793 ExpectXML: `<NameInField><foo xmlns="ns"></foo></NameInField>`,
794 MarshalOnly: true,
795 },
796
797 // Test attributes
798 {
799 Value: &AttrTest{
800 Int: 8,
801 Named: 9,
802 Float: 23.5,
803 Uint8: 255,
804 Bool: true,
805 Str: "str",
806 Bytes: []byte("byt"),
807 },
808 ExpectXML: `<AttrTest Int="8" int="9" Float="23.5" Uint8="255"` +
809 ` Bool="true" Str="str" Bytes="byt"></AttrTest>`,
810 },
811 {
812 Value: &AttrTest{Bytes: []byte{}},
813 ExpectXML: `<AttrTest Int="0" int="0" Float="0" Uint8="0"` +
814 ` Bool="false" Str="" Bytes=""></AttrTest>`,
815 },
816 {
817 Value: &OmitAttrTest{
818 Int: 8,
819 Named: 9,
820 Float: 23.5,
821 Uint8: 255,
822 Bool: true,
823 Str: "str",
824 Bytes: []byte("byt"),
825 },
826 ExpectXML: `<OmitAttrTest Int="8" int="9" Float="23.5" Uint8="255"` +
827 ` Bool="true" Str="str" Bytes="byt"></OmitAttrTest>`,
828 },
829 {
830 Value: &OmitAttrTest{},
831 ExpectXML: `<OmitAttrTest></OmitAttrTest>`,
832 },
833
834 // pointer fields
835 {
836 Value: &PointerFieldsTest{Name: &nameAttr, Age: &ageAttr, Contents: &contentsAttr},
837 ExpectXML: `<dummy name="Sarah" age="12">lorem ipsum</dummy>`,
838 MarshalOnly: true,
839 },
840
841 // empty chardata pointer field
842 {
843 Value: &ChardataEmptyTest{},
844 ExpectXML: `<test></test>`,
845 MarshalOnly: true,
846 },
847
848 // omitempty on fields
849 {
850 Value: &OmitFieldTest{
851 Int: 8,
852 Named: 9,
853 Float: 23.5,
854 Uint8: 255,
855 Bool: true,
856 Str: "str",
857 Bytes: []byte("byt"),
858 Ptr: &PresenceTest{},
859 },
860 ExpectXML: `<OmitFieldTest>` +
861 `<Int>8</Int>` +
862 `<int>9</int>` +
863 `<Float>23.5</Float>` +
864 `<Uint8>255</Uint8>` +
865 `<Bool>true</Bool>` +
866 `<Str>str</Str>` +
867 `<Bytes>byt</Bytes>` +
868 `<Ptr></Ptr>` +
869 `</OmitFieldTest>`,
870 },
871 {
872 Value: &OmitFieldTest{},
873 ExpectXML: `<OmitFieldTest></OmitFieldTest>`,
874 },
875
876 // Test ",any"
877 {
878 ExpectXML: `<a><nested><value>known</value></nested><other><sub>unknown</sub></other></a>`,
879 Value: &AnyTest{
880 Nested: "known",
881 AnyField: AnyHolder{
882 XMLName: Name{Local: "other"},
883 XML: "<sub>unknown</sub>",
884 },
885 },
886 },
887 {
888 Value: &AnyTest{Nested: "known",
889 AnyField: AnyHolder{
890 XML: "<unknown/>",
891 XMLName: Name{Local: "AnyField"},
892 },
893 },
894 ExpectXML: `<a><nested><value>known</value></nested><AnyField><unknown/></AnyField></a>`,
895 },
896 {
897 ExpectXML: `<a><nested><value>b</value></nested></a>`,
898 Value: &AnyOmitTest{
899 Nested: "b",
900 },
901 },
902 {
903 ExpectXML: `<a><nested><value>b</value></nested><c><d>e</d></c><g xmlns="f"><h>i</h></g></a>`,
904 Value: &AnySliceTest{
905 Nested: "b",
906 AnyField: []AnyHolder{
907 {
908 XMLName: Name{Local: "c"},
909 XML: "<d>e</d>",
910 },
911 {
912 XMLName: Name{Space: "f", Local: "g"},
913 XML: "<h>i</h>",
914 },
915 },
916 },
917 },
918 {
919 ExpectXML: `<a><nested><value>b</value></nested></a>`,
920 Value: &AnySliceTest{
921 Nested: "b",
922 },
923 },
924
925 // Test recursive types.
926 {
927 Value: &RecurseA{
928 A: "a1",
929 B: &RecurseB{
930 A: &RecurseA{"a2", nil},
931 B: "b1",
932 },
933 },
934 ExpectXML: `<RecurseA><A>a1</A><B><A><A>a2</A></A><B>b1</B></B></RecurseA>`,
935 },
936
937 // Test ignoring fields via "-" tag
938 {
939 ExpectXML: `<IgnoreTest></IgnoreTest>`,
940 Value: &IgnoreTest{},
941 },
942 {
943 ExpectXML: `<IgnoreTest></IgnoreTest>`,
944 Value: &IgnoreTest{PublicSecret: "can't tell"},
945 MarshalOnly: true,
946 },
947 {
948 ExpectXML: `<IgnoreTest><PublicSecret>ignore me</PublicSecret></IgnoreTest>`,
949 Value: &IgnoreTest{},
950 UnmarshalOnly: true,
951 },
952
953 // Test escaping.
954 {
955 ExpectXML: `<a><nested><value>dquote: &#34;; squote: &#39;; ampersand: &amp;; less: &lt;; greater: &gt;;</value></nested><empty></empty></a>`,
956 Value: &AnyTest{
957 Nested: `dquote: "; squote: '; ampersand: &; less: <; greater: >;`,
958 AnyField: AnyHolder{XMLName: Name{Local: "empty"}},
959 },
960 },
961 {
962 ExpectXML: `<a><nested><value>newline: &#xA;; cr: &#xD;; tab: &#x9;;</value></nested><AnyField></AnyField></a>`,
963 Value: &AnyTest{
964 Nested: "newline: \n; cr: \r; tab: \t;",
965 AnyField: AnyHolder{XMLName: Name{Local: "AnyField"}},
966 },
967 },
968 {
969 ExpectXML: "<a><nested><value>1\r2\r\n3\n\r4\n5</value></nested></a>",
970 Value: &AnyTest{
971 Nested: "1\n2\n3\n\n4\n5",
972 },
973 UnmarshalOnly: true,
974 },
975 {
976 ExpectXML: `<EmbedInt><MyInt>42</MyInt></EmbedInt>`,
977 Value: &EmbedInt{
978 MyInt: 42,
979 },
980 },
981 // Test omitempty with parent chain; see golang.org/issue/4168.
982 {
983 ExpectXML: `<Strings><A></A></Strings>`,
984 Value: &Strings{},
985 },
986 // Custom marshalers.
987 {
988 ExpectXML: `<MyMarshalerTest>hello world</MyMarshalerTest>`,
989 Value: &MyMarshalerTest{},
990 },
991 {
992 ExpectXML: `<MarshalerStruct Foo="hello world"></MarshalerStruct>`,
993 Value: &MarshalerStruct{},
994 },
995 {
996 ExpectXML: `<outer xmlns="testns" int="10"></outer>`,
997 Value: &OuterStruct{IntAttr: 10},
998 },
999 {
1000 ExpectXML: `<test xmlns="outerns" int="10"></test>`,
1001 Value: &OuterNamedStruct{XMLName: Name{Space: "outerns", Local: "test"}, IntAttr: 10},
1002 },
1003 {
1004 ExpectXML: `<test xmlns="outerns" int="10"></test>`,
1005 Value: &OuterNamedOrderedStruct{XMLName: Name{Space: "outerns", Local: "test"}, IntAttr: 10},
1006 },
1007 {
1008 ExpectXML: `<outer xmlns="testns" int="10"></outer>`,
1009 Value: &OuterOuterStruct{OuterStruct{IntAttr: 10}},
1010 },
1011 {
1012 ExpectXML: `<NestedAndChardata><A><B></B><B></B></A>test</NestedAndChardata>`,
1013 Value: &NestedAndChardata{AB: make([]string, 2), Chardata: "test"},
1014 },
1015 {
1016 ExpectXML: `<NestedAndComment><A><B></B><B></B></A><!--test--></NestedAndComment>`,
1017 Value: &NestedAndComment{AB: make([]string, 2), Comment: "test"},
1018 },
1019 }
1020
1021 func TestMarshal(t *testing.T) {
1022 for idx, test := range marshalTests {
1023 if test.UnmarshalOnly {
1024 continue
1025 }
1026 data, err := Marshal(test.Value)
1027 if err != nil {
1028 t.Errorf("#%d: marshal(%#v): %s", idx, test.Value, err)
1029 continue
1030 }
1031 if got, want := string(data), test.ExpectXML; got != want {
1032 if strings.Contains(want, "\n") {
1033 t.Errorf("#%d: marshal(%#v):\nHAVE:\n%s\nWANT:\n%s", idx, test.Value, got, want)
1034 } else {
1035 t.Errorf("#%d: marshal(%#v):\nhave %#q\nwant %#q", idx, test.Value, got, want)
1036 }
1037 }
1038 }
1039 }
1040
1041 type AttrParent struct {
1042 X string `xml:"X>Y,attr"`
1043 }
1044
1045 type BadAttr struct {
1046 Name []string `xml:"name,attr"`
1047 }
1048
1049 var marshalErrorTests = []struct {
1050 Value interface{}
1051 Err string
1052 Kind reflect.Kind
1053 }{
1054 {
1055 Value: make(chan bool),
1056 Err: "xml: unsupported type: chan bool",
1057 Kind: reflect.Chan,
1058 },
1059 {
1060 Value: map[string]string{
1061 "question": "What do you get when you multiply six by nine?",
1062 "answer": "42",
1063 },
1064 Err: "xml: unsupported type: map[string]string",
1065 Kind: reflect.Map,
1066 },
1067 {
1068 Value: map[*Ship]bool{nil: false},
1069 Err: "xml: unsupported type: map[*xml.Ship]bool",
1070 Kind: reflect.Map,
1071 },
1072 {
1073 Value: &Domain{Comment: []byte("f--bar")},
1074 Err: `xml: comments must not contain "--"`,
1075 },
1076 // Reject parent chain with attr, never worked; see golang.org/issue/5033.
1077 {
1078 Value: &AttrParent{},
1079 Err: `xml: X>Y chain not valid with attr flag`,
1080 },
1081 {
1082 Value: BadAttr{[]string{"X", "Y"}},
1083 Err: `xml: unsupported type: []string`,
1084 },
1085 }
1086
1087 var marshalIndentTests = []struct {
1088 Value interface{}
1089 Prefix string
1090 Indent string
1091 ExpectXML string
1092 }{
1093 {
1094 Value: &SecretAgent{
1095 Handle: "007",
1096 Identity: "James Bond",
1097 Obfuscate: "<redacted/>",
1098 },
1099 Prefix: "",
1100 Indent: "\t",
1101 ExpectXML: fmt.Sprintf("<agent handle=\"007\">\n\t<Identity>James Bond</Identity><redacted/>\n</agent>"),
1102 },
1103 }
1104
1105 func TestMarshalErrors(t *testing.T) {
1106 for idx, test := range marshalErrorTests {
1107 data, err := Marshal(test.Value)
1108 if err == nil {
1109 t.Errorf("#%d: marshal(%#v) = [success] %q, want error %v", idx, test.Value, data, test.Err)
1110 continue
1111 }
1112 if err.Error() != test.Err {
1113 t.Errorf("#%d: marshal(%#v) = [error] %v, want %v", idx, test.Value, err, test.Err)
1114 }
1115 if test.Kind != reflect.Invalid {
1116 if kind := err.(*UnsupportedTypeError).Type.Kind(); kind != test.Kind {
1117 t.Errorf("#%d: marshal(%#v) = [error kind] %s, want %s", idx, test.Value, kind, test.Kind)
1118 }
1119 }
1120 }
1121 }
1122
1123 // Do invertibility testing on the various structures that we test
1124 func TestUnmarshal(t *testing.T) {
1125 for i, test := range marshalTests {
1126 if test.MarshalOnly {
1127 continue
1128 }
1129 if _, ok := test.Value.(*Plain); ok {
1130 continue
1131 }
1132 if test.ExpectXML == `<top>`+
1133 `<x><b xmlns="space">b</b>`+
1134 `<b xmlns="space1">b1</b></x>`+
1135 `</top>` {
1136 // TODO(rogpeppe): re-enable this test in
1137 // https://go-review.googlesource.com/#/c/5910/
1138 continue
1139 }
1140
1141 vt := reflect.TypeOf(test.Value)
1142 dest := reflect.New(vt.Elem()).Interface()
1143 err := Unmarshal([]byte(test.ExpectXML), dest)
1144
1145 switch fix := dest.(type) {
1146 case *Feed:
1147 fix.Author.InnerXML = ""
1148 for i := range fix.Entry {
1149 fix.Entry[i].Author.InnerXML = ""
1150 }
1151 }
1152
1153 if err != nil {
1154 t.Errorf("#%d: unexpected error: %#v", i, err)
1155 } else if got, want := dest, test.Value; !reflect.DeepEqual(got, want) {
1156 t.Errorf("#%d: unmarshal(%q):\nhave %#v\nwant %#v", i, test.ExpectXML, got, want)
1157 }
1158 }
1159 }
1160
1161 func TestMarshalIndent(t *testing.T) {
1162 for i, test := range marshalIndentTests {
1163 data, err := MarshalIndent(test.Value, test.Prefix, test.Indent)
1164 if err != nil {
1165 t.Errorf("#%d: Error: %s", i, err)
1166 continue
1167 }
1168 if got, want := string(data), test.ExpectXML; got != want {
1169 t.Errorf("#%d: MarshalIndent:\nGot:%s\nWant:\n%s", i, got, want)
1170 }
1171 }
1172 }
1173
1174 type limitedBytesWriter struct {
1175 w io.Writer
1176 remain int // until writes fail
1177 }
1178
1179 func (lw *limitedBytesWriter) Write(p []byte) (n int, err error) {
1180 if lw.remain <= 0 {
1181 println("error")
1182 return 0, errors.New("write limit hit")
1183 }
1184 if len(p) > lw.remain {
1185 p = p[:lw.remain]
1186 n, _ = lw.w.Write(p)
1187 lw.remain = 0
1188 return n, errors.New("write limit hit")
1189 }
1190 n, err = lw.w.Write(p)
1191 lw.remain -= n
1192 return n, err
1193 }
1194
1195 func TestMarshalWriteErrors(t *testing.T) {
1196 var buf bytes.Buffer
1197 const writeCap = 1024
1198 w := &limitedBytesWriter{&buf, writeCap}
1199 enc := NewEncoder(w)
1200 var err error
1201 var i int
1202 const n = 4000
1203 for i = 1; i <= n; i++ {
1204 err = enc.Encode(&Passenger{
1205 Name: []string{"Alice", "Bob"},
1206 Weight: 5,
1207 })
1208 if err != nil {
1209 break
1210 }
1211 }
1212 if err == nil {
1213 t.Error("expected an error")
1214 }
1215 if i == n {
1216 t.Errorf("expected to fail before the end")
1217 }
1218 if buf.Len() != writeCap {
1219 t.Errorf("buf.Len() = %d; want %d", buf.Len(), writeCap)
1220 }
1221 }
1222
1223 func TestMarshalWriteIOErrors(t *testing.T) {
1224 enc := NewEncoder(errWriter{})
1225
1226 expectErr := "unwritable"
1227 err := enc.Encode(&Passenger{})
1228 if err == nil || err.Error() != expectErr {
1229 t.Errorf("EscapeTest = [error] %v, want %v", err, expectErr)
1230 }
1231 }
1232
1233 func TestMarshalFlush(t *testing.T) {
1234 var buf bytes.Buffer
1235 enc := NewEncoder(&buf)
1236 if err := enc.EncodeToken(CharData("hello world")); err != nil {
1237 t.Fatalf("enc.EncodeToken: %v", err)
1238 }
1239 if buf.Len() > 0 {
1240 t.Fatalf("enc.EncodeToken caused actual write: %q", buf.Bytes())
1241 }
1242 if err := enc.Flush(); err != nil {
1243 t.Fatalf("enc.Flush: %v", err)
1244 }
1245 if buf.String() != "hello world" {
1246 t.Fatalf("after enc.Flush, buf.String() = %q, want %q", buf.String(), "hello world")
1247 }
1248 }
1249
1250 func BenchmarkMarshal(b *testing.B) {
1251 b.ReportAllocs()
1252 for i := 0; i < b.N; i++ {
1253 Marshal(atomValue)
1254 }
1255 }
1256
1257 func BenchmarkUnmarshal(b *testing.B) {
1258 b.ReportAllocs()
1259 xml := []byte(atomXml)
1260 for i := 0; i < b.N; i++ {
1261 Unmarshal(xml, &Feed{})
1262 }
1263 }
1264
1265 // golang.org/issue/6556
1266 func TestStructPointerMarshal(t *testing.T) {
1267 type A struct {
1268 XMLName string `xml:"a"`
1269 B []interface{}
1270 }
1271 type C struct {
1272 XMLName Name
1273 Value string `xml:"value"`
1274 }
1275
1276 a := new(A)
1277 a.B = append(a.B, &C{
1278 XMLName: Name{Local: "c"},
1279 Value: "x",
1280 })
1281
1282 b, err := Marshal(a)
1283 if err != nil {
1284 t.Fatal(err)
1285 }
1286 if x := string(b); x != "<a><c><value>x</value></c></a>" {
1287 t.Fatal(x)
1288 }
1289 var v A
1290 err = Unmarshal(b, &v)
1291 if err != nil {
1292 t.Fatal(err)
1293 }
1294 }
1295
1296 var encodeTokenTests = []struct {
1297 desc string
1298 toks []Token
1299 want string
1300 err string
1301 }{{
1302 desc: "start element with name space",
1303 toks: []Token{
1304 StartElement{Name{"space", "local"}, nil},
1305 },
1306 want: `<local xmlns="space">`,
1307 }, {
1308 desc: "start element with no name",
1309 toks: []Token{
1310 StartElement{Name{"space", ""}, nil},
1311 },
1312 err: "xml: start tag with no name",
1313 }, {
1314 desc: "end element with no name",
1315 toks: []Token{
1316 EndElement{Name{"space", ""}},
1317 },
1318 err: "xml: end tag with no name",
1319 }, {
1320 desc: "char data",
1321 toks: []Token{
1322 CharData("foo"),
1323 },
1324 want: `foo`,
1325 }, {
1326 desc: "char data with escaped chars",
1327 toks: []Token{
1328 CharData(" \t\n"),
1329 },
1330 want: " &#x9;\n",
1331 }, {
1332 desc: "comment",
1333 toks: []Token{
1334 Comment("foo"),
1335 },
1336 want: `<!--foo-->`,
1337 }, {
1338 desc: "comment with invalid content",
1339 toks: []Token{
1340 Comment("foo-->"),
1341 },
1342 err: "xml: EncodeToken of Comment containing --> marker",
1343 }, {
1344 desc: "proc instruction",
1345 toks: []Token{
1346 ProcInst{"Target", []byte("Instruction")},
1347 },
1348 want: `<?Target Instruction?>`,
1349 }, {
1350 desc: "proc instruction with empty target",
1351 toks: []Token{
1352 ProcInst{"", []byte("Instruction")},
1353 },
1354 err: "xml: EncodeToken of ProcInst with invalid Target",
1355 }, {
1356 desc: "proc instruction with bad content",
1357 toks: []Token{
1358 ProcInst{"", []byte("Instruction?>")},
1359 },
1360 err: "xml: EncodeToken of ProcInst with invalid Target",
1361 }, {
1362 desc: "directive",
1363 toks: []Token{
1364 Directive("foo"),
1365 },
1366 want: `<!foo>`,
1367 }, {
1368 desc: "more complex directive",
1369 toks: []Token{
1370 Directive("DOCTYPE doc [ <!ELEMENT doc '>'> <!-- com>ment --> ]"),
1371 },
1372 want: `<!DOCTYPE doc [ <!ELEMENT doc '>'> <!-- com>ment --> ]>`,
1373 }, {
1374 desc: "directive instruction with bad name",
1375 toks: []Token{
1376 Directive("foo>"),
1377 },
1378 err: "xml: EncodeToken of Directive containing wrong < or > markers",
1379 }, {
1380 desc: "end tag without start tag",
1381 toks: []Token{
1382 EndElement{Name{"foo", "bar"}},
1383 },
1384 err: "xml: end tag </bar> without start tag",
1385 }, {
1386 desc: "mismatching end tag local name",
1387 toks: []Token{
1388 StartElement{Name{"", "foo"}, nil},
1389 EndElement{Name{"", "bar"}},
1390 },
1391 err: "xml: end tag </bar> does not match start tag <foo>",
1392 want: `<foo>`,
1393 }, {
1394 desc: "mismatching end tag namespace",
1395 toks: []Token{
1396 StartElement{Name{"space", "foo"}, nil},
1397 EndElement{Name{"another", "foo"}},
1398 },
1399 err: "xml: end tag </foo> in namespace another does not match start tag <foo> in namespace space",
1400 want: `<foo xmlns="space">`,
1401 }, {
1402 desc: "start element with explicit namespace",
1403 toks: []Token{
1404 StartElement{Name{"space", "local"}, []Attr{
1405 {Name{"xmlns", "x"}, "space"},
1406 {Name{"space", "foo"}, "value"},
1407 }},
1408 },
1409 want: `<local xmlns="space" xmlns:_xmlns="xmlns" _xmlns:x="space" xmlns:space="space" space:foo="value">`,
1410 }, {
1411 desc: "start element with explicit namespace and colliding prefix",
1412 toks: []Token{
1413 StartElement{Name{"space", "local"}, []Attr{
1414 {Name{"xmlns", "x"}, "space"},
1415 {Name{"space", "foo"}, "value"},
1416 {Name{"x", "bar"}, "other"},
1417 }},
1418 },
1419 want: `<local xmlns="space" xmlns:_xmlns="xmlns" _xmlns:x="space" xmlns:space="space" space:foo="value" xmlns:x="x" x:bar="other">`,
1420 }, {
1421 desc: "start element using previously defined namespace",
1422 toks: []Token{
1423 StartElement{Name{"", "local"}, []Attr{
1424 {Name{"xmlns", "x"}, "space"},
1425 }},
1426 StartElement{Name{"space", "foo"}, []Attr{
1427 {Name{"space", "x"}, "y"},
1428 }},
1429 },
1430 want: `<local xmlns:_xmlns="xmlns" _xmlns:x="space"><foo xmlns="space" xmlns:space="space" space:x="y">`,
1431 }, {
1432 desc: "nested name space with same prefix",
1433 toks: []Token{
1434 StartElement{Name{"", "foo"}, []Attr{
1435 {Name{"xmlns", "x"}, "space1"},
1436 }},
1437 StartElement{Name{"", "foo"}, []Attr{
1438 {Name{"xmlns", "x"}, "space2"},
1439 }},
1440 StartElement{Name{"", "foo"}, []Attr{
1441 {Name{"space1", "a"}, "space1 value"},
1442 {Name{"space2", "b"}, "space2 value"},
1443 }},
1444 EndElement{Name{"", "foo"}},
1445 EndElement{Name{"", "foo"}},
1446 StartElement{Name{"", "foo"}, []Attr{
1447 {Name{"space1", "a"}, "space1 value"},
1448 {Name{"space2", "b"}, "space2 value"},
1449 }},
1450 },
1451 want: `<foo xmlns:_xmlns="xmlns" _xmlns:x="space1"><foo _xmlns:x="space2"><foo xmlns:space1="space1" space1:a="space1 value" xmlns:space2="space2" space2:b="space2 value"></foo></foo><foo xmlns:space1="space1" space1:a="space1 value" xmlns:space2="space2" space2:b="space2 value">`,
1452 }, {
1453 desc: "start element defining several prefixes for the same name space",
1454 toks: []Token{
1455 StartElement{Name{"space", "foo"}, []Attr{
1456 {Name{"xmlns", "a"}, "space"},
1457 {Name{"xmlns", "b"}, "space"},
1458 {Name{"space", "x"}, "value"},
1459 }},
1460 },
1461 want: `<foo xmlns="space" xmlns:_xmlns="xmlns" _xmlns:a="space" _xmlns:b="space" xmlns:space="space" space:x="value">`,
1462 }, {
1463 desc: "nested element redefines name space",
1464 toks: []Token{
1465 StartElement{Name{"", "foo"}, []Attr{
1466 {Name{"xmlns", "x"}, "space"},
1467 }},
1468 StartElement{Name{"space", "foo"}, []Attr{
1469 {Name{"xmlns", "y"}, "space"},
1470 {Name{"space", "a"}, "value"},
1471 }},
1472 },
1473 want: `<foo xmlns:_xmlns="xmlns" _xmlns:x="space"><foo xmlns="space" _xmlns:y="space" xmlns:space="space" space:a="value">`,
1474 }, {
1475 desc: "nested element creates alias for default name space",
1476 toks: []Token{
1477 StartElement{Name{"space", "foo"}, []Attr{
1478 {Name{"", "xmlns"}, "space"},
1479 }},
1480 StartElement{Name{"space", "foo"}, []Attr{
1481 {Name{"xmlns", "y"}, "space"},
1482 {Name{"space", "a"}, "value"},
1483 }},
1484 },
1485 want: `<foo xmlns="space" xmlns="space"><foo xmlns="space" xmlns:_xmlns="xmlns" _xmlns:y="space" xmlns:space="space" space:a="value">`,
1486 }, {
1487 desc: "nested element defines default name space with existing prefix",
1488 toks: []Token{
1489 StartElement{Name{"", "foo"}, []Attr{
1490 {Name{"xmlns", "x"}, "space"},
1491 }},
1492 StartElement{Name{"space", "foo"}, []Attr{
1493 {Name{"", "xmlns"}, "space"},
1494 {Name{"space", "a"}, "value"},
1495 }},
1496 },
1497 want: `<foo xmlns:_xmlns="xmlns" _xmlns:x="space"><foo xmlns="space" xmlns="space" xmlns:space="space" space:a="value">`,
1498 }, {
1499 desc: "nested element uses empty attribute name space when default ns defined",
1500 toks: []Token{
1501 StartElement{Name{"space", "foo"}, []Attr{
1502 {Name{"", "xmlns"}, "space"},
1503 }},
1504 StartElement{Name{"space", "foo"}, []Attr{
1505 {Name{"", "attr"}, "value"},
1506 }},
1507 },
1508 want: `<foo xmlns="space" xmlns="space"><foo xmlns="space" attr="value">`,
1509 }, {
1510 desc: "redefine xmlns",
1511 toks: []Token{
1512 StartElement{Name{"", "foo"}, []Attr{
1513 {Name{"foo", "xmlns"}, "space"},
1514 }},
1515 },
1516 want: `<foo xmlns:foo="foo" foo:xmlns="space">`,
1517 }, {
1518 desc: "xmlns with explicit name space #1",
1519 toks: []Token{
1520 StartElement{Name{"space", "foo"}, []Attr{
1521 {Name{"xml", "xmlns"}, "space"},
1522 }},
1523 },
1524 want: `<foo xmlns="space" xmlns:_xml="xml" _xml:xmlns="space">`,
1525 }, {
1526 desc: "xmlns with explicit name space #2",
1527 toks: []Token{
1528 StartElement{Name{"space", "foo"}, []Attr{
1529 {Name{xmlURL, "xmlns"}, "space"},
1530 }},
1531 },
1532 want: `<foo xmlns="space" xml:xmlns="space">`,
1533 }, {
1534 desc: "empty name space declaration is ignored",
1535 toks: []Token{
1536 StartElement{Name{"", "foo"}, []Attr{
1537 {Name{"xmlns", "foo"}, ""},
1538 }},
1539 },
1540 want: `<foo xmlns:_xmlns="xmlns" _xmlns:foo="">`,
1541 }, {
1542 desc: "attribute with no name is ignored",
1543 toks: []Token{
1544 StartElement{Name{"", "foo"}, []Attr{
1545 {Name{"", ""}, "value"},
1546 }},
1547 },
1548 want: `<foo>`,
1549 }, {
1550 desc: "namespace URL with non-valid name",
1551 toks: []Token{
1552 StartElement{Name{"/34", "foo"}, []Attr{
1553 {Name{"/34", "x"}, "value"},
1554 }},
1555 },
1556 want: `<foo xmlns="/34" xmlns:_="/34" _:x="value">`,
1557 }, {
1558 desc: "nested element resets default namespace to empty",
1559 toks: []Token{
1560 StartElement{Name{"space", "foo"}, []Attr{
1561 {Name{"", "xmlns"}, "space"},
1562 }},
1563 StartElement{Name{"", "foo"}, []Attr{
1564 {Name{"", "xmlns"}, ""},
1565 {Name{"", "x"}, "value"},
1566 {Name{"space", "x"}, "value"},
1567 }},
1568 },
1569 want: `<foo xmlns="space" xmlns="space"><foo xmlns="" x="value" xmlns:space="space" space:x="value">`,
1570 }, {
1571 desc: "nested element requires empty default name space",
1572 toks: []Token{
1573 StartElement{Name{"space", "foo"}, []Attr{
1574 {Name{"", "xmlns"}, "space"},
1575 }},
1576 StartElement{Name{"", "foo"}, nil},
1577 },
1578 want: `<foo xmlns="space" xmlns="space"><foo>`,
1579 }, {
1580 desc: "attribute uses name space from xmlns",
1581 toks: []Token{
1582 StartElement{Name{"some/space", "foo"}, []Attr{
1583 {Name{"", "attr"}, "value"},
1584 {Name{"some/space", "other"}, "other value"},
1585 }},
1586 },
1587 want: `<foo xmlns="some/space" attr="value" xmlns:space="some/space" space:other="other value">`,
1588 }, {
1589 desc: "default name space should not be used by attributes",
1590 toks: []Token{
1591 StartElement{Name{"space", "foo"}, []Attr{
1592 {Name{"", "xmlns"}, "space"},
1593 {Name{"xmlns", "bar"}, "space"},
1594 {Name{"space", "baz"}, "foo"},
1595 }},
1596 StartElement{Name{"space", "baz"}, nil},
1597 EndElement{Name{"space", "baz"}},
1598 EndElement{Name{"space", "foo"}},
1599 },
1600 want: `<foo xmlns="space" xmlns="space" xmlns:_xmlns="xmlns" _xmlns:bar="space" xmlns:space="space" space:baz="foo"><baz xmlns="space"></baz></foo>`,
1601 }, {
1602 desc: "default name space not used by attributes, not explicitly defined",
1603 toks: []Token{
1604 StartElement{Name{"space", "foo"}, []Attr{
1605 {Name{"", "xmlns"}, "space"},
1606 {Name{"space", "baz"}, "foo"},
1607 }},
1608 StartElement{Name{"space", "baz"}, nil},
1609 EndElement{Name{"space", "baz"}},
1610 EndElement{Name{"space", "foo"}},
1611 },
1612 want: `<foo xmlns="space" xmlns="space" xmlns:space="space" space:baz="foo"><baz xmlns="space"></baz></foo>`,
1613 }, {
1614 desc: "impossible xmlns declaration",
1615 toks: []Token{
1616 StartElement{Name{"", "foo"}, []Attr{
1617 {Name{"", "xmlns"}, "space"},
1618 }},
1619 StartElement{Name{"space", "bar"}, []Attr{
1620 {Name{"space", "attr"}, "value"},
1621 }},
1622 },
1623 want: `<foo xmlns="space"><bar xmlns="space" xmlns:space="space" space:attr="value">`,
1624 }}
1625
1626 func TestEncodeToken(t *testing.T) {
1627 loop:
1628 for i, tt := range encodeTokenTests {
1629 var buf bytes.Buffer
1630 enc := NewEncoder(&buf)
1631 var err error
1632 for j, tok := range tt.toks {
1633 err = enc.EncodeToken(tok)
1634 if err != nil && j < len(tt.toks)-1 {
1635 t.Errorf("#%d %s token #%d: %v", i, tt.desc, j, err)
1636 continue loop
1637 }
1638 }
1639 errorf := func(f string, a ...interface{}) {
1640 t.Errorf("#%d %s token #%d:%s", i, tt.desc, len(tt.toks)-1, fmt.Sprintf(f, a...))
1641 }
1642 switch {
1643 case tt.err != "" && err == nil:
1644 errorf(" expected error; got none")
1645 continue
1646 case tt.err == "" && err != nil:
1647 errorf(" got error: %v", err)
1648 continue
1649 case tt.err != "" && err != nil && tt.err != err.Error():
1650 errorf(" error mismatch; got %v, want %v", err, tt.err)
1651 continue
1652 }
1653 if err := enc.Flush(); err != nil {
1654 errorf(" %v", err)
1655 continue
1656 }
1657 if got := buf.String(); got != tt.want {
1658 errorf("\ngot %v\nwant %v", got, tt.want)
1659 continue
1660 }
1661 }
1662 }
1663
1664 func TestProcInstEncodeToken(t *testing.T) {
1665 var buf bytes.Buffer
1666 enc := NewEncoder(&buf)
1667
1668 if err := enc.EncodeToken(ProcInst{"xml", []byte("Instruction")}); err != nil {
1669 t.Fatalf("enc.EncodeToken: expected to be able to encode xml target ProcInst as first token, %s", err)
1670 }
1671
1672 if err := enc.EncodeToken(ProcInst{"Target", []byte("Instruction")}); err != nil {
1673 t.Fatalf("enc.EncodeToken: expected to be able to add non-xml target ProcInst")
1674 }
1675
1676 if err := enc.EncodeToken(ProcInst{"xml", []byte("Instruction")}); err == nil {
1677 t.Fatalf("enc.EncodeToken: expected to not be allowed to encode xml target ProcInst when not first token")
1678 }
1679 }
1680
1681 func TestDecodeEncode(t *testing.T) {
1682 var in, out bytes.Buffer
1683 in.WriteString(`<?xml version="1.0" encoding="UTF-8"?>
1684 <?Target Instruction?>
1685 <root>
1686 </root>
1687 `)
1688 dec := NewDecoder(&in)
1689 enc := NewEncoder(&out)
1690 for tok, err := dec.Token(); err == nil; tok, err = dec.Token() {
1691 err = enc.EncodeToken(tok)
1692 if err != nil {
1693 t.Fatalf("enc.EncodeToken: Unable to encode token (%#v), %v", tok, err)
1694 }
1695 }
1696 }
1697
1698 // Issue 9796. Used to fail with GORACE="halt_on_error=1" -race.
1699 func TestRace9796(t *testing.T) {
1700 type A struct{}
1701 type B struct {
1702 C []A `xml:"X>Y"`
1703 }
1704 var wg sync.WaitGroup
1705 for i := 0; i < 2; i++ {
1706 wg.Add(1)
1707 go func() {
1708 Marshal(B{[]A{A{}}})
1709 wg.Done()
1710 }()
1711 }
1712 wg.Wait()
1713 }
1714
1715 func TestIsValidDirective(t *testing.T) {
1716 testOK := []string{
1717 "<>",
1718 "< < > >",
1719 "<!DOCTYPE '<' '>' '>' <!--nothing-->>",
1720 "<!DOCTYPE doc [ <!ELEMENT doc ANY> <!ELEMENT doc ANY> ]>",
1721 "<!DOCTYPE doc [ <!ELEMENT doc \"ANY> '<' <!E\" LEMENT '>' doc ANY> ]>",
1722 "<!DOCTYPE doc <!-- just>>>> a < comment --> [ <!ITEM anything> ] >",
1723 }
1724 testKO := []string{
1725 "<",
1726 ">",
1727 "<!--",
1728 "-->",
1729 "< > > < < >",
1730 "<!dummy <!-- > -->",
1731 "<!DOCTYPE doc '>",
1732 "<!DOCTYPE doc '>'",
1733 "<!DOCTYPE doc <!--comment>",
1734 }
1735 for _, s := range testOK {
1736 if !isValidDirective(Directive(s)) {
1737 t.Errorf("Directive %q is expected to be valid", s)
1738 }
1739 }
1740 for _, s := range testKO {
1741 if isValidDirective(Directive(s)) {
1742 t.Errorf("Directive %q is expected to be invalid", s)
1743 }
1744 }
1745 }
1746
1747 // Issue 11719. EncodeToken used to silently eat tokens with an invalid type.
1748 func TestSimpleUseOfEncodeToken(t *testing.T) {
1749 var buf bytes.Buffer
1750 enc := NewEncoder(&buf)
1751 if err := enc.EncodeToken(&StartElement{Name: Name{"", "object1"}}); err == nil {
1752 t.Errorf("enc.EncodeToken: pointer type should be rejected")
1753 }
1754 if err := enc.EncodeToken(&EndElement{Name: Name{"", "object1"}}); err == nil {
1755 t.Errorf("enc.EncodeToken: pointer type should be rejected")
1756 }
1757 if err := enc.EncodeToken(StartElement{Name: Name{"", "object2"}}); err != nil {
1758 t.Errorf("enc.EncodeToken: StartElement %s", err)
1759 }
1760 if err := enc.EncodeToken(EndElement{Name: Name{"", "object2"}}); err != nil {
1761 t.Errorf("enc.EncodeToken: EndElement %s", err)
1762 }
1763 if err := enc.EncodeToken(Universe{}); err == nil {
1764 t.Errorf("enc.EncodeToken: invalid type not caught")
1765 }
1766 if err := enc.Flush(); err != nil {
1767 t.Errorf("enc.Flush: %s", err)
1768 }
1769 if buf.Len() == 0 {
1770 t.Errorf("enc.EncodeToken: empty buffer")
1771 }
1772 want := "<object2></object2>"
1773 if buf.String() != want {
1774 t.Errorf("enc.EncodeToken: expected %q; got %q", want, buf.String())
1775 }
1776 }