Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(640)

Delta Between Two Patch Sets: src/pkg/encoding/xml/marshal_test.go

Issue 6868044: code review 6868044: encoding/xml: namespace handling
Left Patch Set: diff -r d05272f402ec https://code.google.com/p/go Created 11 years, 3 months ago
Right Patch Set: diff -r 8ea98ec93704 https://code.google.com/p/go Created 11 years, 2 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « src/pkg/encoding/xml/marshal.go ('k') | src/pkg/encoding/xml/namespace.go » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
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 ( 1 import (
8 "bytes" 2 "bytes"
9 "errors" 3 "errors"
10 "io" 4 "io"
11 "reflect" 5 "reflect"
12 "strconv" 6 "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 type NsRoot struct {
227 HTable HtmlTable `xml:"h=http://www.w3.org/TR/html4/ table"`
228 FTable FurnTable `xml:"http://www.w3schools.com/furniture table"`
229 }
230
231 type HtmlTable struct {
232 Rows []HtmlTr `xml:"tr"`
233 }
234
235 type HtmlTr struct {
236 Td []string `xml:"td"`
237 }
238
239 type FurnTable struct {
240 Name string `xml:"name"`
241 Width int `xml:"width"`
242 Length int `xml:"length"`
243 }
244
245 // Unless explicitly stated as such (or *Plain), all of the
246 // tests below are two-way tests. When introducing new tests,
247 // please try to make them two-way as well to ensure that
248 // marshalling and unmarshalling are as symmetrical as feasible.
249 var marshalTests = []struct {
250 Value interface{}
251 ExpectXML string
252 MarshalOnly bool
253 UnmarshalOnly bool
254 Namespaces map[string]string
255 Xmlns string
256 }{
257 // Test nil marshals to nothing
258 {Value: nil, ExpectXML: ``, MarshalOnly: true},
259 {Value: nilStruct, ExpectXML: ``, MarshalOnly: true},
260
261 // Test value types
262 {Value: &Plain{true}, ExpectXML: `<Plain><V>true</V></Plain>`},
263 {Value: &Plain{false}, ExpectXML: `<Plain><V>false</V></Plain>`},
264 {Value: &Plain{int(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
265 {Value: &Plain{int8(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
266 {Value: &Plain{int16(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
267 {Value: &Plain{int32(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
268 {Value: &Plain{uint(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
269 {Value: &Plain{uint8(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
270 {Value: &Plain{uint16(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
271 {Value: &Plain{uint32(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
272 {Value: &Plain{float32(1.25)}, ExpectXML: `<Plain><V>1.25</V></Plain>`},
273 {Value: &Plain{float64(1.25)}, ExpectXML: `<Plain><V>1.25</V></Plain>`},
274 {Value: &Plain{uintptr(0xFFDD)}, ExpectXML: `<Plain><V>65501</V></Plain> `},
275 {Value: &Plain{"gopher"}, ExpectXML: `<Plain><V>gopher</V></Plain>`},
276 {Value: &Plain{[]byte("gopher")}, ExpectXML: `<Plain><V>gopher</V></Plai n>`},
277 {Value: &Plain{"</>"}, ExpectXML: `<Plain><V>&lt;/&gt;</V></Plain>`},
278 {Value: &Plain{[]byte("</>")}, ExpectXML: `<Plain><V>&lt;/&gt;</V></Plai n>`},
279 {Value: &Plain{[3]byte{'<', '/', '>'}}, ExpectXML: `<Plain><V>&lt;/&gt;< /V></Plain>`},
280 {Value: &Plain{NamedType("potato")}, ExpectXML: `<Plain><V>potato</V></P lain>`},
281 {Value: &Plain{[]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3< /V></Plain>`},
282 {Value: &Plain{[3]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3 </V></Plain>`},
283
284 // Test time.
285 {
286 Value: &Plain{time.Unix(1e9, 123456789).UTC()},
287 ExpectXML: `<Plain><V>2001-09-09T01:46:40.123456789Z</V></Plain> `,
288 },
289
290 // A pointer to struct{} may be used to test for an element's presence.
291 {
292 Value: &PresenceTest{new(struct{})},
293 ExpectXML: `<PresenceTest><Exists></Exists></PresenceTest>`,
294 },
295 {
296 Value: &PresenceTest{},
297 ExpectXML: `<PresenceTest></PresenceTest>`,
298 },
299
300 // A pointer to struct{} may be used to test for an element's presence.
301 {
302 Value: &PresenceTest{new(struct{})},
303 ExpectXML: `<PresenceTest><Exists></Exists></PresenceTest>`,
304 },
305 {
306 Value: &PresenceTest{},
307 ExpectXML: `<PresenceTest></PresenceTest>`,
308 },
309
310 // A []byte field is only nil if the element was not found.
311 {
312 Value: &Data{},
313 ExpectXML: `<Data></Data>`,
314 UnmarshalOnly: true,
315 },
316 {
317 Value: &Data{Bytes: []byte{}, Custom: MyBytes{}, Attr: [ ]byte{}},
318 ExpectXML: `<Data Attr=""><Bytes></Bytes><Custom></Custom></ Data>`,
319 UnmarshalOnly: true,
320 },
321
322 // Check that []byte works, including named []byte types.
323 {
324 Value: &Data{Bytes: []byte("ab"), Custom: MyBytes("cd"), Att r: []byte{'v'}},
325 ExpectXML: `<Data Attr="v"><Bytes>ab</Bytes><Custom>cd</Custom>< /Data>`,
326 },
327
328 // Test innerxml
329 {
330 Value: &SecretAgent{
331 Handle: "007",
332 Identity: "James Bond",
333 Obfuscate: "<redacted/>",
334 },
335 ExpectXML: `<agent handle="007"><Identity>James Bond</Identity ><redacted/></agent>`,
336 MarshalOnly: true,
337 },
338 {
339 Value: &SecretAgent{
340 Handle: "007",
341 Identity: "James Bond",
342 Obfuscate: "<Identity>James Bond</Identity><redacted/>",
343 },
344 ExpectXML: `<agent handle="007"><Identity>James Bond</Identi ty><redacted/></agent>`,
345 UnmarshalOnly: true,
346 },
347
348 // Test structs
349 {Value: &Port{Type: "ssl", Number: "443"}, ExpectXML: `<port type="ssl"> 443</port>`},
350 {Value: &Port{Number: "443"}, ExpectXML: `<port>443</port>`},
351 {Value: &Port{Type: "<unix>"}, ExpectXML: `<port type="&lt;unix&gt;"></p ort>`},
352 {Value: &Port{Number: "443", Comment: "https"}, ExpectXML: `<port><!--ht tps-->443</port>`},
353 {Value: &Port{Number: "443", Comment: "add space-"}, ExpectXML: `<port>< !--add space- -->443</port>`, MarshalOnly: true},
354 {Value: &Domain{Name: []byte("google.com&friends")}, ExpectXML: `<domain >google.com&amp;friends</domain>`},
355 {Value: &Domain{Name: []byte("google.com"), Comment: []byte(" &friends " )}, ExpectXML: `<domain>google.com<!-- &friends --></domain>`},
356 {Value: &Book{Title: "Pride & Prejudice"}, ExpectXML: `<book>Pride &amp; Prejudice</book>`},
357 {Value: atomValue, ExpectXML: atomXml},
358 {
359 Value: &Ship{
360 Name: "Heart of Gold",
361 Pilot: "Computer",
362 Age: 1,
363 Drive: ImprobabilityDrive,
364 Passenger: []*Passenger{
365 {
366 Name: []string{"Zaphod", "Beeblebrox"} ,
367 Weight: 7.25,
368 },
369 {
370 Name: []string{"Trisha", "McMillen"},
371 Weight: 5.5,
372 },
373 {
374 Name: []string{"Ford", "Prefect"},
375 Weight: 7,
376 },
377 {
378 Name: []string{"Arthur", "Dent"},
379 Weight: 6.75,
380 },
381 },
382 },
383 ExpectXML: `<spaceship name="Heart of Gold" pilot="Computer">` +
384 `<drive>` + strconv.Itoa(int(ImprobabilityDrive)) + `</d rive>` +
385 `<age>1</age>` +
386 `<passenger>` +
387 `<name>Zaphod</name>` +
388 `<name>Beeblebrox</name>` +
389 `<weight>7.25</weight>` +
390 `</passenger>` +
391 `<passenger>` +
392 `<name>Trisha</name>` +
393 `<name>McMillen</name>` +
394 `<weight>5.5</weight>` +
395 `</passenger>` +
396 `<passenger>` +
397 `<name>Ford</name>` +
398 `<name>Prefect</name>` +
399 `<weight>7</weight>` +
400 `</passenger>` +
401 `<passenger>` +
402 `<name>Arthur</name>` +
403 `<name>Dent</name>` +
404 `<weight>6.75</weight>` +
405 `</passenger>` +
406 `</spaceship>`,
407 },
408
409 // Test a>b
410 {
411 Value: &NestedItems{Items: nil, Item1: nil},
412 ExpectXML: `<result>` +
413 `<Items>` +
414 `</Items>` +
415 `</result>`,
416 },
417 {
418 Value: &NestedItems{Items: []string{}, Item1: []string{}},
419 ExpectXML: `<result>` +
420 `<Items>` +
421 `</Items>` +
422 `</result>`,
423 MarshalOnly: true,
424 },
425 {
426 Value: &NestedItems{Items: nil, Item1: []string{"A"}},
427 ExpectXML: `<result>` +
428 `<Items>` +
429 `<item1>A</item1>` +
430 `</Items>` +
431 `</result>`,
432 },
433 {
434 Value: &NestedItems{Items: []string{"A", "B"}, Item1: nil},
435 ExpectXML: `<result>` +
436 `<Items>` +
437 `<item>A</item>` +
438 `<item>B</item>` +
439 `</Items>` +
440 `</result>`,
441 },
442 {
443 Value: &NestedItems{Items: []string{"A", "B"}, Item1: []string{" C"}},
444 ExpectXML: `<result>` +
445 `<Items>` +
446 `<item>A</item>` +
447 `<item>B</item>` +
448 `<item1>C</item1>` +
449 `</Items>` +
450 `</result>`,
451 },
452 {
453 Value: &NestedOrder{Field1: "C", Field2: "B", Field3: "A"},
454 ExpectXML: `<result>` +
455 `<parent>` +
456 `<c>C</c>` +
457 `<b>B</b>` +
458 `<a>A</a>` +
459 `</parent>` +
460 `</result>`,
461 },
462 {
463 Value: &NilTest{A: "A", B: nil, C: "C"},
464 ExpectXML: `<NilTest>` +
465 `<parent1>` +
466 `<parent2><a>A</a></parent2>` +
467 `<parent2><c>C</c></parent2>` +
468 `</parent1>` +
469 `</NilTest>`,
470 MarshalOnly: true, // Uses interface{}
471 },
472 {
473 Value: &MixedNested{A: "A", B: "B", C: "C", D: "D"},
474 ExpectXML: `<result>` +
475 `<parent1><a>A</a></parent1>` +
476 `<b>B</b>` +
477 `<parent1>` +
478 `<parent2><c>C</c></parent2>` +
479 `<d>D</d>` +
480 `</parent1>` +
481 `</result>`,
482 },
483 {
484 Value: &Service{Port: &Port{Number: "80"}},
485 ExpectXML: `<service><host><port>80</port></host></service>`,
486 },
487 {
488 Value: &Service{},
489 ExpectXML: `<service></service>`,
490 },
491 {
492 Value: &Service{Port: &Port{Number: "80"}, Extra1: "A", Extra2: "B"},
493 ExpectXML: `<service>` +
494 `<host><port>80</port></host>` +
495 `<Extra1>A</Extra1>` +
496 `<host><extra2>B</extra2></host>` +
497 `</service>`,
498 MarshalOnly: true,
499 },
500 {
501 Value: &Service{Port: &Port{Number: "80"}, Extra2: "example"},
502 ExpectXML: `<service>` +
503 `<host><port>80</port></host>` +
504 `<host><extra2>example</extra2></host>` +
505 `</service>`,
506 MarshalOnly: true,
507 },
508
509 // Test struct embedding
510 {
511 Value: &EmbedA{
512 EmbedC: EmbedC{
513 FieldA1: "", // Shadowed by A.A
514 FieldA2: "", // Shadowed by A.A
515 FieldB: "A.C.B",
516 FieldC: "A.C.C",
517 },
518 EmbedB: EmbedB{
519 FieldB: "A.B.B",
520 EmbedC: &EmbedC{
521 FieldA1: "A.B.C.A1",
522 FieldA2: "A.B.C.A2",
523 FieldB: "", // Shadowed by A.B.B
524 FieldC: "A.B.C.C",
525 },
526 },
527 FieldA: "A.A",
528 },
529 ExpectXML: `<EmbedA>` +
530 `<FieldB>A.C.B</FieldB>` +
531 `<FieldC>A.C.C</FieldC>` +
532 `<EmbedB>` +
533 `<FieldB>A.B.B</FieldB>` +
534 `<FieldA>` +
535 `<A1>A.B.C.A1</A1>` +
536 `<A2>A.B.C.A2</A2>` +
537 `</FieldA>` +
538 `<FieldC>A.B.C.C</FieldC>` +
539 `</EmbedB>` +
540 `<FieldA>A.A</FieldA>` +
541 `</EmbedA>`,
542 },
543
544 // Test that name casing matters
545 {
546 Value: &NameCasing{Xy: "mixed", XY: "upper", XyA: "mixedA", XYA: "upperA"},
547 ExpectXML: `<casing Xy="mixedA" XY="upperA"><Xy>mixed</Xy><XY>up per</XY></casing>`,
548 },
549
550 // Test the order in which the XML element name is chosen
551 {
552 Value: &NamePrecedence{
553 FromTag: XMLNameWithoutTag{Value: "A"},
554 FromNameVal: XMLNameWithoutTag{XMLName: Name{Local: "InX MLName"}, Value: "B"},
555 FromNameTag: XMLNameWithTag{Value: "C"},
556 InFieldName: "D",
557 },
558 ExpectXML: `<Parent>` +
559 `<InTag>A</InTag>` +
560 `<InXMLName>B</InXMLName>` +
561 `<InXMLNameTag>C</InXMLNameTag>` +
562 `<InFieldName>D</InFieldName>` +
563 `</Parent>`,
564 MarshalOnly: true,
565 },
566 {
567 Value: &NamePrecedence{
568 XMLName: Name{Local: "Parent"},
569 FromTag: XMLNameWithoutTag{XMLName: Name{Local: "InT ag"}, Value: "A"},
570 FromNameVal: XMLNameWithoutTag{XMLName: Name{Local: "Fro mNameVal"}, Value: "B"},
571 FromNameTag: XMLNameWithTag{XMLName: Name{Local: "InXMLN ameTag"}, Value: "C"},
572 InFieldName: "D",
573 },
574 ExpectXML: `<Parent>` +
575 `<InTag>A</InTag>` +
576 `<FromNameVal>B</FromNameVal>` +
577 `<InXMLNameTag>C</InXMLNameTag>` +
578 `<InFieldName>D</InFieldName>` +
579 `</Parent>`,
580 UnmarshalOnly: true,
581 },
582
583 // xml.Name works in a plain field as well.
584 {
585 Value: &NameInField{Name{Space: "ns", Local: "foo"}},
586 ExpectXML: `<NameInField><foo xmlns="ns"></foo></NameInField>`,
587 },
588 {
589 Value: &NameInField{Name{Space: "ns", Local: "foo"}},
590 ExpectXML: `<NameInField><foo xmlns="ns"><ignore></ignore></ foo></NameInField>`,
591 UnmarshalOnly: true,
592 },
593
594 // Marshaling zero xml.Name uses the tag or field name.
595 {
596 Value: &NameInField{},
597 ExpectXML: `<NameInField><foo xmlns="ns"></foo></NameInField>` ,
598 MarshalOnly: true,
599 },
600
601 // Test attributes
602 {
603 Value: &AttrTest{
604 Int: 8,
605 Named: 9,
606 Float: 23.5,
607 Uint8: 255,
608 Bool: true,
609 Str: "str",
610 Bytes: []byte("byt"),
611 },
612 ExpectXML: `<AttrTest Int="8" int="9" Float="23.5" Uint8="255"` +
613 ` Bool="true" Str="str" Bytes="byt"></AttrTest>`,
614 },
615 {
616 Value: &AttrTest{Bytes: []byte{}},
617 ExpectXML: `<AttrTest Int="0" int="0" Float="0" Uint8="0"` +
618 ` Bool="false" Str="" Bytes=""></AttrTest>`,
619 },
620 {
621 Value: &OmitAttrTest{
622 Int: 8,
623 Named: 9,
624 Float: 23.5,
625 Uint8: 255,
626 Bool: true,
627 Str: "str",
628 Bytes: []byte("byt"),
629 },
630 ExpectXML: `<OmitAttrTest Int="8" int="9" Float="23.5" Uint8="25 5"` +
631 ` Bool="true" Str="str" Bytes="byt"></OmitAttrTest>`,
632 },
633 {
634 Value: &OmitAttrTest{},
635 ExpectXML: `<OmitAttrTest></OmitAttrTest>`,
636 },
637
638 // omitempty on fields
639 {
640 Value: &OmitFieldTest{
641 Int: 8,
642 Named: 9,
643 Float: 23.5,
644 Uint8: 255,
645 Bool: true,
646 Str: "str",
647 Bytes: []byte("byt"),
648 Ptr: &PresenceTest{},
649 },
650 ExpectXML: `<OmitFieldTest>` +
651 `<Int>8</Int>` +
652 `<int>9</int>` +
653 `<Float>23.5</Float>` +
654 `<Uint8>255</Uint8>` +
655 `<Bool>true</Bool>` +
656 `<Str>str</Str>` +
657 `<Bytes>byt</Bytes>` +
658 `<Ptr></Ptr>` +
659 `</OmitFieldTest>`,
660 },
661 {
662 Value: &OmitFieldTest{},
663 ExpectXML: `<OmitFieldTest></OmitFieldTest>`,
664 },
665
666 // Test ",any"
667 {
668 ExpectXML: `<a><nested><value>known</value></nested><other><sub> unknown</sub></other></a>`,
669 Value: &AnyTest{
670 Nested: "known",
671 AnyField: AnyHolder{
672 XMLName: Name{Local: "other"},
673 XML: "<sub>unknown</sub>",
674 },
675 },
676 UnmarshalOnly: true,
677 },
678 {
679 Value: &AnyTest{Nested: "known", AnyField: AnyHolder{XML: "<unknown/>"}},
680 ExpectXML: `<a><nested><value>known</value></nested></a>`,
681 MarshalOnly: true,
682 },
683
684 // Test recursive types.
685 {
686 Value: &RecurseA{
687 A: "a1",
688 B: &RecurseB{
689 A: &RecurseA{"a2", nil},
690 B: "b1",
691 },
692 },
693 ExpectXML: `<RecurseA><A>a1</A><B><A><A>a2</A></A><B>b1</B></B>< /RecurseA>`,
694 },
695
696 // Test ignoring fields via "-" tag
697 {
698 ExpectXML: `<IgnoreTest></IgnoreTest>`,
699 Value: &IgnoreTest{},
700 },
701 {
702 ExpectXML: `<IgnoreTest></IgnoreTest>`,
703 Value: &IgnoreTest{PublicSecret: "can't tell"},
704 MarshalOnly: true,
705 },
706 {
707 ExpectXML: `<IgnoreTest><PublicSecret>ignore me</PublicSecre t></IgnoreTest>`,
708 Value: &IgnoreTest{},
709 UnmarshalOnly: true,
710 },
711
712 // Test XML namespaces.
713 {
714 ExpectXML: `<NsRoot>` +
715 `<h:table xmlns:h="http://www.w3.org/TR/html4/">` +
716 `<h:tr>` +
717 `<h:td>Apples</h:td>` +
718 `<h:td>Bananas</h:td>` +
719 `</h:tr>` +
720 `</h:table>` +
721 `<table xmlns="http://www.w3schools.com/furniture">` +
722 `<name>African Coffee Table</name>` +
723 `<width>80</width>` +
724 `<length>120</length>` +
725 `</table>` +
726 `</NsRoot>`,
727 Value: &NsRoot{HTable: HtmlTable{Rows: []HtmlTr{HtmlTr{Td: []str ing{"Apples", "Bananas"}}}},
728 FTable: FurnTable{Name: "African Coffee Table",
729 Width: 80, Length: 120},
730 },
731 },
732 {
733 ExpectXML: `<NsRoot>` +
734 `<h:table>` +
735 `<h:tr>` +
736 `<h:td>Apples</h:td>` +
737 `<h:td>Bananas</h:td>` +
738 `</h:tr>` +
739 `</h:table>` +
740 `<f:table>` +
741 `<f:name>African Coffee Table</f:name>` +
742 `<f:width>80</f:width>` +
743 `<f:length>120</f:length>` +
744 `</f:table>` +
745 `</NsRoot>`,
746 Value: &NsRoot{HTable: HtmlTable{Rows: []HtmlTr{HtmlTr{Td: []str ing{"Apples", "Bananas"}}}},
747 FTable: FurnTable{Name: "African Coffee Table",
748 Width: 80, Length: 120},
749 },
750 Namespaces: map[string]string{"h": "http://www.w3.org/TR/html4/" ,
751 "f": "http://www.w3schools.com/furniture"},
752 },
753
754 // Test escaping.
755 {
756 ExpectXML: `<a><nested><value>dquote: &#34;; squote: &#39;; ampe rsand: &amp;; less: &lt;; greater: &gt;;</value></nested></a>`,
757 Value: &AnyTest{
758 Nested: `dquote: "; squote: '; ampersand: &; less: <; gr eater: >;`,
759 },
760 },
761 {
762 ExpectXML: `<a><nested><value>newline: &#xA;; cr: &#xD;; tab: &# x9;;</value></nested></a>`,
763 Value: &AnyTest{
764 Nested: "newline: \n; cr: \r; tab: \t;",
765 },
766 },
767 {
768 ExpectXML: "<a><nested><value>1\r2\r\n3\n\r4\n5</value></nested> </a>",
769 Value: &AnyTest{
770 Nested: "1\n2\n3\n\n4\n5",
771 },
772 UnmarshalOnly: true,
773 },
774 }
775
776 func TestMarshal(t *testing.T) {
777 for idx, test := range marshalTests {
778 if test.UnmarshalOnly {
779 continue
780 }
781 var data []byte
782 var err error
783 if test.Namespaces == nil && test.Xmlns == "" {
784 data, err = Marshal(test.Value)
785 } else {
786 var w bytes.Buffer
787 enc := NewEncoder(&w)
788 enc.Context.Xmlns = test.Xmlns
789 for k, v := range test.Namespaces {
790 enc.Context.Map[v] = k
791 }
792 err = enc.Encode(test.Value)
793 data = w.Bytes()
794 }
795 if err != nil {
796 t.Errorf("#%d: Error: %s", idx, err)
797 continue
798 }
799 if got, want := string(data), test.ExpectXML; got != want {
800 if strings.Contains(want, "\n") {
801 t.Errorf("#%d: marshal(%#v):\nHAVE:\n%s\nWANT:\n %s", idx, test.Value, got, want)
802 } else {
803 t.Errorf("#%d: marshal(%#v):\nhave %#q\nwant %#q ", idx, test.Value, got, want)
804 }
805 }
806 }
807 }
808
809 var marshalErrorTests = []struct {
810 Value interface{}
811 Err string
812 Kind reflect.Kind
813 }{
814 {
815 Value: make(chan bool),
816 Err: "xml: unsupported type: chan bool",
817 Kind: reflect.Chan,
818 },
819 {
820 Value: map[string]string{
821 "question": "What do you get when you multiply six by ni ne?",
822 "answer": "42",
823 },
824 Err: "xml: unsupported type: map[string]string",
825 Kind: reflect.Map,
826 },
827 {
828 Value: map[*Ship]bool{nil: false},
829 Err: "xml: unsupported type: map[*xml.Ship]bool",
830 Kind: reflect.Map,
831 },
832 {
833 Value: &Domain{Comment: []byte("f--bar")},
834 Err: `xml: comments must not contain "--"`,
835 },
836 }
837
838 func TestMarshalErrors(t *testing.T) {
839 for idx, test := range marshalErrorTests {
840 _, err := Marshal(test.Value)
841 if err == nil || err.Error() != test.Err {
842 t.Errorf("#%d: marshal(%#v) = [error] %v, want %v", idx, test.Value, err, test.Err)
843 }
844 if test.Kind != reflect.Invalid {
845 if kind := err.(*UnsupportedTypeError).Type.Kind(); kind != test.Kind {
846 t.Errorf("#%d: marshal(%#v) = [error kind] %s, w ant %s", idx, test.Value, kind, test.Kind)
847 }
848 }
849 }
850 }
851
852 // Do invertibility testing on the various structures that we test
853 func TestUnmarshal(t *testing.T) {
854 for i, test := range marshalTests {
855 if test.MarshalOnly {
856 continue
857 }
858 if _, ok := test.Value.(*Plain); ok {
859 continue
860 }
861
862 vt := reflect.TypeOf(test.Value)
863 dest := reflect.New(vt.Elem()).Interface()
864 var err error
865 if test.Namespaces == nil && test.Xmlns == "" {
866 err = Unmarshal([]byte(test.ExpectXML), dest)
867 } else {
868 buf := bytes.NewBuffer([]byte(test.ExpectXML))
869 dec := NewDecoder(buf)
870 dec.Context.Xmlns = test.Xmlns
871 for k, v := range test.Namespaces {
872 dec.Context.Map[k] = v
873 }
874 err = dec.Decode(dest)
875 }
876
877 switch fix := dest.(type) {
878 case *Feed:
879 fix.Author.InnerXML = ""
880 for i := range fix.Entry {
881 fix.Entry[i].Author.InnerXML = ""
882 }
883 }
884
885 if err != nil {
886 t.Errorf("#%d: unexpected error: %#v", i, err)
887 } else if got, want := dest, test.Value; !reflect.DeepEqual(got, want) {
888 t.Errorf("#%d: unmarshal(%q):\nhave %#v\nwant %#v", i, t est.ExpectXML, got, want)
889 }
890 }
891 }
892
893 type limitedBytesWriter struct {
894 w io.Writer
895 remain int // until writes fail
896 }
897
898 func (lw *limitedBytesWriter) Write(p []byte) (n int, err error) {
899 if lw.remain <= 0 {
900 println("error")
901 return 0, errors.New("write limit hit")
902 }
903 if len(p) > lw.remain {
904 p = p[:lw.remain]
905 n, _ = lw.w.Write(p)
906 lw.remain = 0
907 return n, errors.New("write limit hit")
908 }
909 n, err = lw.w.Write(p)
910 lw.remain -= n
911 return n, err
912 }
913
914 func TestMarshalWriteErrors(t *testing.T) {
915 var buf bytes.Buffer
916 const writeCap = 1024
917 w := &limitedBytesWriter{&buf, writeCap}
918 enc := NewEncoder(w)
919 var err error
920 var i int
921 const n = 4000
922 for i = 1; i <= n; i++ {
923 err = enc.Encode(&Passenger{
924 Name: []string{"Alice", "Bob"},
925 Weight: 5,
926 })
927 if err != nil {
928 break
929 }
930 }
931 if err == nil {
932 t.Error("expected an error")
933 }
934 if i == n {
935 t.Errorf("expected to fail before the end")
936 }
937 if buf.Len() != writeCap {
938 t.Errorf("buf.Len() = %d; want %d", buf.Len(), writeCap)
939 }
940 }
941
942 func BenchmarkMarshal(b *testing.B) {
943 for i := 0; i < b.N; i++ {
944 Marshal(atomValue)
945 }
946 }
947
948 func BenchmarkUnmarshal(b *testing.B) {
949 xml := []byte(atomXml)
950 for i := 0; i < b.N; i++ {
951 Unmarshal(xml, &Feed{})
952 }
953 }
LEFTRIGHT

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b