1
2
3
4
5 package xml
6
7 import (
8 "io"
9 "reflect"
10 "strings"
11 "testing"
12 "time"
13 )
14
15
16
17 func TestUnmarshalFeed(t *testing.T) {
18 var f Feed
19 if err := Unmarshal([]byte(atomFeedString), &f); err != nil {
20 t.Fatalf("Unmarshal: %s", err)
21 }
22 if !reflect.DeepEqual(f, atomFeed) {
23 t.Fatalf("have %#v\nwant %#v", f, atomFeed)
24 }
25 }
26
27
28 const atomFeedString = `
29 <?xml version="1.0" encoding="utf-8"?>
30 <feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-us" updated="2009-10-04T01:35:58+00:00"><title>Code Review - My issues</title><link href="http://codereview.appspot.com/" rel="alternate"></link><link href="http://codereview.appspot.com/rss/mine/rsc" rel="self"></link><id>http://codereview.appspot.com/</id><author><name>rietveld<></name></author><entry><title>rietveld: an attempt at pubsubhubbub
31 </title><link href="http://codereview.appspot.com/126085" rel="alternate"></link><updated>2009-10-04T01:35:58+00:00</updated><author><name>email-address-removed</name></author><id>urn:md5:134d9179c41f806be79b3a5f7877d19a</id><summary type="html">
32 An attempt at adding pubsubhubbub support to Rietveld.
33 http://code.google.com/p/pubsubhubbub
34 http://code.google.com/p/rietveld/issues/detail?id=155
35
36 The server side of the protocol is trivial:
37 1. add a &lt;link rel=&quot;hub&quot; href=&quot;hub-server&quot;&gt; tag to all
38 feeds that will be pubsubhubbubbed.
39 2. every time one of those feeds changes, tell the hub
40 with a simple POST request.
41
42 I have tested this by adding debug prints to a local hub
43 server and checking that the server got the right publish
44 requests.
45
46 I can&#39;t quite get the server to work, but I think the bug
47 is not in my code. I think that the server expects to be
48 able to grab the feed and see the feed&#39;s actual URL in
49 the link rel=&quot;self&quot;, but the default value for that drops
50 the :port from the URL, and I cannot for the life of me
51 figure out how to get the Atom generator deep inside
52 django not to do that, or even where it is doing that,
53 or even what code is running to generate the Atom feed.
54 (I thought I knew but I added some assert False statements
55 and it kept running!)
56
57 Ignoring that particular problem, I would appreciate
58 feedback on the right way to get the two values at
59 the top of feeds.py marked NOTE(rsc).
60
61
62 </summary></entry><entry><title>rietveld: correct tab handling
63 </title><link href="http://codereview.appspot.com/124106" rel="alternate"></link><updated>2009-10-03T23:02:17+00:00</updated><author><name>email-address-removed</name></author><id>urn:md5:0a2a4f19bb815101f0ba2904aed7c35a</id><summary type="html">
64 This fixes the buggy tab rendering that can be seen at
65 http://codereview.appspot.com/116075/diff/1/2
66
67 The fundamental problem was that the tab code was
68 not being told what column the text began in, so it
69 didn&#39;t know where to put the tab stops. Another problem
70 was that some of the code assumed that string byte
71 offsets were the same as column offsets, which is only
72 true if there are no tabs.
73
74 In the process of fixing this, I cleaned up the arguments
75 to Fold and ExpandTabs and renamed them Break and
76 _ExpandTabs so that I could be sure that I found all the
77 call sites. I also wanted to verify that ExpandTabs was
78 not being used from outside intra_region_diff.py.
79
80
81 </summary></entry></feed> `
82
83 type Feed struct {
84 XMLName Name `xml:"http://www.w3.org/2005/Atom feed"`
85 Title string `xml:"title"`
86 ID string `xml:"id"`
87 Link []Link `xml:"link"`
88 Updated time.Time `xml:"updated,attr"`
89 Author Person `xml:"author"`
90 Entry []Entry `xml:"entry"`
91 }
92
93 type Entry struct {
94 Title string `xml:"title"`
95 ID string `xml:"id"`
96 Link []Link `xml:"link"`
97 Updated time.Time `xml:"updated"`
98 Author Person `xml:"author"`
99 Summary Text `xml:"summary"`
100 }
101
102 type Link struct {
103 Rel string `xml:"rel,attr,omitempty"`
104 Href string `xml:"href,attr"`
105 }
106
107 type Person struct {
108 Name string `xml:"name"`
109 URI string `xml:"uri"`
110 Email string `xml:"email"`
111 InnerXML string `xml:",innerxml"`
112 }
113
114 type Text struct {
115 Type string `xml:"type,attr,omitempty"`
116 Body string `xml:",chardata"`
117 }
118
119 var atomFeed = Feed{
120 XMLName: Name{"http://www.w3.org/2005/Atom", "feed"},
121 Title: "Code Review - My issues",
122 Link: []Link{
123 {Rel: "alternate", Href: "http://codereview.appspot.com/"},
124 {Rel: "self", Href: "http://codereview.appspot.com/rss/mine/rsc"},
125 },
126 ID: "http://codereview.appspot.com/",
127 Updated: ParseTime("2009-10-04T01:35:58+00:00"),
128 Author: Person{
129 Name: "rietveld<>",
130 InnerXML: "<name>rietveld<></name>",
131 },
132 Entry: []Entry{
133 {
134 Title: "rietveld: an attempt at pubsubhubbub\n",
135 Link: []Link{
136 {Rel: "alternate", Href: "http://codereview.appspot.com/126085"},
137 },
138 Updated: ParseTime("2009-10-04T01:35:58+00:00"),
139 Author: Person{
140 Name: "email-address-removed",
141 InnerXML: "<name>email-address-removed</name>",
142 },
143 ID: "urn:md5:134d9179c41f806be79b3a5f7877d19a",
144 Summary: Text{
145 Type: "html",
146 Body: `
147 An attempt at adding pubsubhubbub support to Rietveld.
148 http://code.google.com/p/pubsubhubbub
149 http://code.google.com/p/rietveld/issues/detail?id=155
150
151 The server side of the protocol is trivial:
152 1. add a <link rel="hub" href="hub-server"> tag to all
153 feeds that will be pubsubhubbubbed.
154 2. every time one of those feeds changes, tell the hub
155 with a simple POST request.
156
157 I have tested this by adding debug prints to a local hub
158 server and checking that the server got the right publish
159 requests.
160
161 I can't quite get the server to work, but I think the bug
162 is not in my code. I think that the server expects to be
163 able to grab the feed and see the feed's actual URL in
164 the link rel="self", but the default value for that drops
165 the :port from the URL, and I cannot for the life of me
166 figure out how to get the Atom generator deep inside
167 django not to do that, or even where it is doing that,
168 or even what code is running to generate the Atom feed.
169 (I thought I knew but I added some assert False statements
170 and it kept running!)
171
172 Ignoring that particular problem, I would appreciate
173 feedback on the right way to get the two values at
174 the top of feeds.py marked NOTE(rsc).
175
176
177 `,
178 },
179 },
180 {
181 Title: "rietveld: correct tab handling\n",
182 Link: []Link{
183 {Rel: "alternate", Href: "http://codereview.appspot.com/124106"},
184 },
185 Updated: ParseTime("2009-10-03T23:02:17+00:00"),
186 Author: Person{
187 Name: "email-address-removed",
188 InnerXML: "<name>email-address-removed</name>",
189 },
190 ID: "urn:md5:0a2a4f19bb815101f0ba2904aed7c35a",
191 Summary: Text{
192 Type: "html",
193 Body: `
194 This fixes the buggy tab rendering that can be seen at
195 http://codereview.appspot.com/116075/diff/1/2
196
197 The fundamental problem was that the tab code was
198 not being told what column the text began in, so it
199 didn't know where to put the tab stops. Another problem
200 was that some of the code assumed that string byte
201 offsets were the same as column offsets, which is only
202 true if there are no tabs.
203
204 In the process of fixing this, I cleaned up the arguments
205 to Fold and ExpandTabs and renamed them Break and
206 _ExpandTabs so that I could be sure that I found all the
207 call sites. I also wanted to verify that ExpandTabs was
208 not being used from outside intra_region_diff.py.
209
210
211 `,
212 },
213 },
214 },
215 }
216
217 const pathTestString = `
218 <Result>
219 <Before>1</Before>
220 <Items>
221 <Item1>
222 <Value>A</Value>
223 </Item1>
224 <Item2>
225 <Value>B</Value>
226 </Item2>
227 <Item1>
228 <Value>C</Value>
229 <Value>D</Value>
230 </Item1>
231 <_>
232 <Value>E</Value>
233 </_>
234 </Items>
235 <After>2</After>
236 </Result>
237 `
238
239 type PathTestItem struct {
240 Value string
241 }
242
243 type PathTestA struct {
244 Items []PathTestItem `xml:">Item1"`
245 Before, After string
246 }
247
248 type PathTestB struct {
249 Other []PathTestItem `xml:"Items>Item1"`
250 Before, After string
251 }
252
253 type PathTestC struct {
254 Values1 []string `xml:"Items>Item1>Value"`
255 Values2 []string `xml:"Items>Item2>Value"`
256 Before, After string
257 }
258
259 type PathTestSet struct {
260 Item1 []PathTestItem
261 }
262
263 type PathTestD struct {
264 Other PathTestSet `xml:"Items"`
265 Before, After string
266 }
267
268 type PathTestE struct {
269 Underline string `xml:"Items>_>Value"`
270 Before, After string
271 }
272
273 var pathTests = []any{
274 &PathTestA{Items: []PathTestItem{{"A"}, {"D"}}, Before: "1", After: "2"},
275 &PathTestB{Other: []PathTestItem{{"A"}, {"D"}}, Before: "1", After: "2"},
276 &PathTestC{Values1: []string{"A", "C", "D"}, Values2: []string{"B"}, Before: "1", After: "2"},
277 &PathTestD{Other: PathTestSet{Item1: []PathTestItem{{"A"}, {"D"}}}, Before: "1", After: "2"},
278 &PathTestE{Underline: "E", Before: "1", After: "2"},
279 }
280
281 func TestUnmarshalPaths(t *testing.T) {
282 for _, pt := range pathTests {
283 v := reflect.New(reflect.TypeOf(pt).Elem()).Interface()
284 if err := Unmarshal([]byte(pathTestString), v); err != nil {
285 t.Fatalf("Unmarshal: %s", err)
286 }
287 if !reflect.DeepEqual(v, pt) {
288 t.Fatalf("have %#v\nwant %#v", v, pt)
289 }
290 }
291 }
292
293 type BadPathTestA struct {
294 First string `xml:"items>item1"`
295 Other string `xml:"items>item2"`
296 Second string `xml:"items"`
297 }
298
299 type BadPathTestB struct {
300 Other string `xml:"items>item2>value"`
301 First string `xml:"items>item1"`
302 Second string `xml:"items>item1>value"`
303 }
304
305 type BadPathTestC struct {
306 First string
307 Second string `xml:"First"`
308 }
309
310 type BadPathTestD struct {
311 BadPathEmbeddedA
312 BadPathEmbeddedB
313 }
314
315 type BadPathEmbeddedA struct {
316 First string
317 }
318
319 type BadPathEmbeddedB struct {
320 Second string `xml:"First"`
321 }
322
323 var badPathTests = []struct {
324 v, e any
325 }{
326 {&BadPathTestA{}, &TagPathError{reflect.TypeOf(BadPathTestA{}), "First", "items>item1", "Second", "items"}},
327 {&BadPathTestB{}, &TagPathError{reflect.TypeOf(BadPathTestB{}), "First", "items>item1", "Second", "items>item1>value"}},
328 {&BadPathTestC{}, &TagPathError{reflect.TypeOf(BadPathTestC{}), "First", "", "Second", "First"}},
329 {&BadPathTestD{}, &TagPathError{reflect.TypeOf(BadPathTestD{}), "First", "", "Second", "First"}},
330 }
331
332 func TestUnmarshalBadPaths(t *testing.T) {
333 for _, tt := range badPathTests {
334 err := Unmarshal([]byte(pathTestString), tt.v)
335 if !reflect.DeepEqual(err, tt.e) {
336 t.Fatalf("Unmarshal with %#v didn't fail properly:\nhave %#v,\nwant %#v", tt.v, err, tt.e)
337 }
338 }
339 }
340
341 const OK = "OK"
342 const withoutNameTypeData = `
343 <?xml version="1.0" charset="utf-8"?>
344 <Test3 Attr="OK" />`
345
346 type TestThree struct {
347 XMLName Name `xml:"Test3"`
348 Attr string `xml:",attr"`
349 }
350
351 func TestUnmarshalWithoutNameType(t *testing.T) {
352 var x TestThree
353 if err := Unmarshal([]byte(withoutNameTypeData), &x); err != nil {
354 t.Fatalf("Unmarshal: %s", err)
355 }
356 if x.Attr != OK {
357 t.Fatalf("have %v\nwant %v", x.Attr, OK)
358 }
359 }
360
361 func TestUnmarshalAttr(t *testing.T) {
362 type ParamVal struct {
363 Int int `xml:"int,attr"`
364 }
365
366 type ParamPtr struct {
367 Int *int `xml:"int,attr"`
368 }
369
370 type ParamStringPtr struct {
371 Int *string `xml:"int,attr"`
372 }
373
374 x := []byte(`<Param int="1" />`)
375
376 p1 := &ParamPtr{}
377 if err := Unmarshal(x, p1); err != nil {
378 t.Fatalf("Unmarshal: %s", err)
379 }
380 if p1.Int == nil {
381 t.Fatalf("Unmarshal failed in to *int field")
382 } else if *p1.Int != 1 {
383 t.Fatalf("Unmarshal with %s failed:\nhave %#v,\n want %#v", x, p1.Int, 1)
384 }
385
386 p2 := &ParamVal{}
387 if err := Unmarshal(x, p2); err != nil {
388 t.Fatalf("Unmarshal: %s", err)
389 }
390 if p2.Int != 1 {
391 t.Fatalf("Unmarshal with %s failed:\nhave %#v,\n want %#v", x, p2.Int, 1)
392 }
393
394 p3 := &ParamStringPtr{}
395 if err := Unmarshal(x, p3); err != nil {
396 t.Fatalf("Unmarshal: %s", err)
397 }
398 if p3.Int == nil {
399 t.Fatalf("Unmarshal failed in to *string field")
400 } else if *p3.Int != "1" {
401 t.Fatalf("Unmarshal with %s failed:\nhave %#v,\n want %#v", x, p3.Int, 1)
402 }
403 }
404
405 type Tables struct {
406 HTable string `xml:"http://www.w3.org/TR/html4/ table"`
407 FTable string `xml:"http://www.w3schools.com/furniture table"`
408 }
409
410 var tables = []struct {
411 xml string
412 tab Tables
413 ns string
414 }{
415 {
416 xml: `<Tables>` +
417 `<table xmlns="http://www.w3.org/TR/html4/">hello</table>` +
418 `<table xmlns="http://www.w3schools.com/furniture">world</table>` +
419 `</Tables>`,
420 tab: Tables{"hello", "world"},
421 },
422 {
423 xml: `<Tables>` +
424 `<table xmlns="http://www.w3schools.com/furniture">world</table>` +
425 `<table xmlns="http://www.w3.org/TR/html4/">hello</table>` +
426 `</Tables>`,
427 tab: Tables{"hello", "world"},
428 },
429 {
430 xml: `<Tables xmlns:f="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/">` +
431 `<f:table>world</f:table>` +
432 `<h:table>hello</h:table>` +
433 `</Tables>`,
434 tab: Tables{"hello", "world"},
435 },
436 {
437 xml: `<Tables>` +
438 `<table>bogus</table>` +
439 `</Tables>`,
440 tab: Tables{},
441 },
442 {
443 xml: `<Tables>` +
444 `<table>only</table>` +
445 `</Tables>`,
446 tab: Tables{HTable: "only"},
447 ns: "http://www.w3.org/TR/html4/",
448 },
449 {
450 xml: `<Tables>` +
451 `<table>only</table>` +
452 `</Tables>`,
453 tab: Tables{FTable: "only"},
454 ns: "http://www.w3schools.com/furniture",
455 },
456 {
457 xml: `<Tables>` +
458 `<table>only</table>` +
459 `</Tables>`,
460 tab: Tables{},
461 ns: "something else entirely",
462 },
463 }
464
465 func TestUnmarshalNS(t *testing.T) {
466 for i, tt := range tables {
467 var dst Tables
468 var err error
469 if tt.ns != "" {
470 d := NewDecoder(strings.NewReader(tt.xml))
471 d.DefaultSpace = tt.ns
472 err = d.Decode(&dst)
473 } else {
474 err = Unmarshal([]byte(tt.xml), &dst)
475 }
476 if err != nil {
477 t.Errorf("#%d: Unmarshal: %v", i, err)
478 continue
479 }
480 want := tt.tab
481 if dst != want {
482 t.Errorf("#%d: dst=%+v, want %+v", i, dst, want)
483 }
484 }
485 }
486
487 func TestMarshalNS(t *testing.T) {
488 dst := Tables{"hello", "world"}
489 data, err := Marshal(&dst)
490 if err != nil {
491 t.Fatalf("Marshal: %v", err)
492 }
493 want := `<Tables><table xmlns="http://www.w3.org/TR/html4/">hello</table><table xmlns="http://www.w3schools.com/furniture">world</table></Tables>`
494 str := string(data)
495 if str != want {
496 t.Errorf("have: %q\nwant: %q\n", str, want)
497 }
498 }
499
500 type TableAttrs struct {
501 TAttr TAttr
502 }
503
504 type TAttr struct {
505 HTable string `xml:"http://www.w3.org/TR/html4/ table,attr"`
506 FTable string `xml:"http://www.w3schools.com/furniture table,attr"`
507 Lang string `xml:"http://www.w3.org/XML/1998/namespace lang,attr,omitempty"`
508 Other1 string `xml:"http://golang.org/xml/ other,attr,omitempty"`
509 Other2 string `xml:"http://golang.org/xmlfoo/ other,attr,omitempty"`
510 Other3 string `xml:"http://golang.org/json/ other,attr,omitempty"`
511 Other4 string `xml:"http://golang.org/2/json/ other,attr,omitempty"`
512 }
513
514 var tableAttrs = []struct {
515 xml string
516 tab TableAttrs
517 ns string
518 }{
519 {
520 xml: `<TableAttrs xmlns:f="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/"><TAttr ` +
521 `h:table="hello" f:table="world" ` +
522 `/></TableAttrs>`,
523 tab: TableAttrs{TAttr{HTable: "hello", FTable: "world"}},
524 },
525 {
526 xml: `<TableAttrs><TAttr xmlns:f="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/" ` +
527 `h:table="hello" f:table="world" ` +
528 `/></TableAttrs>`,
529 tab: TableAttrs{TAttr{HTable: "hello", FTable: "world"}},
530 },
531 {
532 xml: `<TableAttrs><TAttr ` +
533 `h:table="hello" f:table="world" xmlns:f="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/" ` +
534 `/></TableAttrs>`,
535 tab: TableAttrs{TAttr{HTable: "hello", FTable: "world"}},
536 },
537 {
538
539 xml: `<TableAttrs xmlns="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/"><TAttr ` +
540 `h:table="hello" table="world" ` +
541 `/></TableAttrs>`,
542 tab: TableAttrs{TAttr{HTable: "hello", FTable: ""}},
543 },
544 {
545
546 xml: `<TableAttrs xmlns:f="http://www.w3schools.com/furniture"><TAttr xmlns="http://www.w3.org/TR/html4/" ` +
547 `table="hello" f:table="world" ` +
548 `/></TableAttrs>`,
549 tab: TableAttrs{TAttr{HTable: "", FTable: "world"}},
550 },
551 {
552 xml: `<TableAttrs><TAttr ` +
553 `table="bogus" ` +
554 `/></TableAttrs>`,
555 tab: TableAttrs{},
556 },
557 {
558
559 xml: `<TableAttrs xmlns:h="http://www.w3.org/TR/html4/"><TAttr ` +
560 `h:table="hello" table="world" ` +
561 `/></TableAttrs>`,
562 tab: TableAttrs{TAttr{HTable: "hello", FTable: ""}},
563 ns: "http://www.w3schools.com/furniture",
564 },
565 {
566
567 xml: `<TableAttrs xmlns:f="http://www.w3schools.com/furniture"><TAttr ` +
568 `table="hello" f:table="world" ` +
569 `/></TableAttrs>`,
570 tab: TableAttrs{TAttr{HTable: "", FTable: "world"}},
571 ns: "http://www.w3.org/TR/html4/",
572 },
573 {
574 xml: `<TableAttrs><TAttr ` +
575 `table="bogus" ` +
576 `/></TableAttrs>`,
577 tab: TableAttrs{},
578 ns: "something else entirely",
579 },
580 }
581
582 func TestUnmarshalNSAttr(t *testing.T) {
583 for i, tt := range tableAttrs {
584 var dst TableAttrs
585 var err error
586 if tt.ns != "" {
587 d := NewDecoder(strings.NewReader(tt.xml))
588 d.DefaultSpace = tt.ns
589 err = d.Decode(&dst)
590 } else {
591 err = Unmarshal([]byte(tt.xml), &dst)
592 }
593 if err != nil {
594 t.Errorf("#%d: Unmarshal: %v", i, err)
595 continue
596 }
597 want := tt.tab
598 if dst != want {
599 t.Errorf("#%d: dst=%+v, want %+v", i, dst, want)
600 }
601 }
602 }
603
604 func TestMarshalNSAttr(t *testing.T) {
605 src := TableAttrs{TAttr{"hello", "world", "en_US", "other1", "other2", "other3", "other4"}}
606 data, err := Marshal(&src)
607 if err != nil {
608 t.Fatalf("Marshal: %v", err)
609 }
610 want := `<TableAttrs><TAttr xmlns:html4="http://www.w3.org/TR/html4/" html4:table="hello" xmlns:furniture="http://www.w3schools.com/furniture" furniture:table="world" xml:lang="en_US" xmlns:_xml="http://golang.org/xml/" _xml:other="other1" xmlns:_xmlfoo="http://golang.org/xmlfoo/" _xmlfoo:other="other2" xmlns:json="http://golang.org/json/" json:other="other3" xmlns:json_1="http://golang.org/2/json/" json_1:other="other4"></TAttr></TableAttrs>`
611 str := string(data)
612 if str != want {
613 t.Errorf("Marshal:\nhave: %#q\nwant: %#q\n", str, want)
614 }
615
616 var dst TableAttrs
617 if err := Unmarshal(data, &dst); err != nil {
618 t.Errorf("Unmarshal: %v", err)
619 }
620
621 if dst != src {
622 t.Errorf("Unmarshal = %q, want %q", dst, src)
623 }
624 }
625
626 type MyCharData struct {
627 body string
628 }
629
630 func (m *MyCharData) UnmarshalXML(d *Decoder, start StartElement) error {
631 for {
632 t, err := d.Token()
633 if err == io.EOF {
634 break
635 }
636 if err != nil {
637 return err
638 }
639 if char, ok := t.(CharData); ok {
640 m.body += string(char)
641 }
642 }
643 return nil
644 }
645
646 var _ Unmarshaler = (*MyCharData)(nil)
647
648 func (m *MyCharData) UnmarshalXMLAttr(attr Attr) error {
649 panic("must not call")
650 }
651
652 type MyAttr struct {
653 attr string
654 }
655
656 func (m *MyAttr) UnmarshalXMLAttr(attr Attr) error {
657 m.attr = attr.Value
658 return nil
659 }
660
661 var _ UnmarshalerAttr = (*MyAttr)(nil)
662
663 type MyStruct struct {
664 Data *MyCharData
665 Attr *MyAttr `xml:",attr"`
666
667 Data2 MyCharData
668 Attr2 MyAttr `xml:",attr"`
669 }
670
671 func TestUnmarshaler(t *testing.T) {
672 xml := `<?xml version="1.0" encoding="utf-8"?>
673 <MyStruct Attr="attr1" Attr2="attr2">
674 <Data>hello <!-- comment -->world</Data>
675 <Data2>howdy <!-- comment -->world</Data2>
676 </MyStruct>
677 `
678
679 var m MyStruct
680 if err := Unmarshal([]byte(xml), &m); err != nil {
681 t.Fatal(err)
682 }
683
684 if m.Data == nil || m.Attr == nil || m.Data.body != "hello world" || m.Attr.attr != "attr1" || m.Data2.body != "howdy world" || m.Attr2.attr != "attr2" {
685 t.Errorf("m=%#+v\n", m)
686 }
687 }
688
689 type Pea struct {
690 Cotelydon string
691 }
692
693 type Pod struct {
694 Pea any `xml:"Pea"`
695 }
696
697
698 func TestUnmarshalIntoInterface(t *testing.T) {
699 pod := new(Pod)
700 pod.Pea = new(Pea)
701 xml := `<Pod><Pea><Cotelydon>Green stuff</Cotelydon></Pea></Pod>`
702 err := Unmarshal([]byte(xml), pod)
703 if err != nil {
704 t.Fatalf("failed to unmarshal %q: %v", xml, err)
705 }
706 pea, ok := pod.Pea.(*Pea)
707 if !ok {
708 t.Fatalf("unmarshaled into wrong type: have %T want *Pea", pod.Pea)
709 }
710 have, want := pea.Cotelydon, "Green stuff"
711 if have != want {
712 t.Errorf("failed to unmarshal into interface, have %q want %q", have, want)
713 }
714 }
715
716 type X struct {
717 D string `xml:",comment"`
718 }
719
720
721 func TestMalformedComment(t *testing.T) {
722 testData := []string{
723 "<X><!-- a---></X>",
724 "<X><!-- -- --></X>",
725 "<X><!-- a--b --></X>",
726 "<X><!------></X>",
727 }
728 for i, test := range testData {
729 data := []byte(test)
730 v := new(X)
731 if err := Unmarshal(data, v); err == nil {
732 t.Errorf("%d: unmarshal should reject invalid comments", i)
733 }
734 }
735 }
736
737 type IXField struct {
738 Five int `xml:"five"`
739 NotInnerXML []string `xml:",innerxml"`
740 }
741
742
743 func TestInvalidInnerXMLType(t *testing.T) {
744 v := new(IXField)
745 if err := Unmarshal([]byte(`<tag><five>5</five><innertag/></tag>`), v); err != nil {
746 t.Errorf("Unmarshal failed: got %v", err)
747 }
748 if v.Five != 5 {
749 t.Errorf("Five = %v, want 5", v.Five)
750 }
751 if v.NotInnerXML != nil {
752 t.Errorf("NotInnerXML = %v, want nil", v.NotInnerXML)
753 }
754 }
755
756 type Child struct {
757 G struct {
758 I int
759 }
760 }
761
762 type ChildToEmbed struct {
763 X bool
764 }
765
766 type Parent struct {
767 I int
768 IPtr *int
769 Is []int
770 IPtrs []*int
771 F float32
772 FPtr *float32
773 Fs []float32
774 FPtrs []*float32
775 B bool
776 BPtr *bool
777 Bs []bool
778 BPtrs []*bool
779 Bytes []byte
780 BytesPtr *[]byte
781 S string
782 SPtr *string
783 Ss []string
784 SPtrs []*string
785 MyI MyInt
786 Child Child
787 Children []Child
788 ChildPtr *Child
789 ChildToEmbed
790 }
791
792 const (
793 emptyXML = `
794 <Parent>
795 <I></I>
796 <IPtr></IPtr>
797 <Is></Is>
798 <IPtrs></IPtrs>
799 <F></F>
800 <FPtr></FPtr>
801 <Fs></Fs>
802 <FPtrs></FPtrs>
803 <B></B>
804 <BPtr></BPtr>
805 <Bs></Bs>
806 <BPtrs></BPtrs>
807 <Bytes></Bytes>
808 <BytesPtr></BytesPtr>
809 <S></S>
810 <SPtr></SPtr>
811 <Ss></Ss>
812 <SPtrs></SPtrs>
813 <MyI></MyI>
814 <Child></Child>
815 <Children></Children>
816 <ChildPtr></ChildPtr>
817 <X></X>
818 </Parent>
819 `
820 )
821
822
823 func TestUnmarshalEmptyValues(t *testing.T) {
824
825 v := new(Parent)
826 if err := Unmarshal([]byte(emptyXML), v); err != nil {
827 t.Fatalf("zero: Unmarshal failed: got %v", err)
828 }
829
830 zBytes, zInt, zStr, zFloat, zBool := []byte{}, 0, "", float32(0), false
831 want := &Parent{
832 IPtr: &zInt,
833 Is: []int{zInt},
834 IPtrs: []*int{&zInt},
835 FPtr: &zFloat,
836 Fs: []float32{zFloat},
837 FPtrs: []*float32{&zFloat},
838 BPtr: &zBool,
839 Bs: []bool{zBool},
840 BPtrs: []*bool{&zBool},
841 Bytes: []byte{},
842 BytesPtr: &zBytes,
843 SPtr: &zStr,
844 Ss: []string{zStr},
845 SPtrs: []*string{&zStr},
846 Children: []Child{{}},
847 ChildPtr: new(Child),
848 ChildToEmbed: ChildToEmbed{},
849 }
850 if !reflect.DeepEqual(v, want) {
851 t.Fatalf("zero: Unmarshal:\nhave: %#+v\nwant: %#+v", v, want)
852 }
853
854
855
856 vBytes0, vInt0, vStr0, vFloat0, vBool0 := []byte("x"), 1, "x", float32(1), true
857 vBytes1, vInt1, vStr1, vFloat1, vBool1 := []byte("x"), 1, "x", float32(1), true
858 vInt2, vStr2, vFloat2, vBool2 := 1, "x", float32(1), true
859 v = &Parent{
860 I: vInt0,
861 IPtr: &vInt1,
862 Is: []int{vInt0},
863 IPtrs: []*int{&vInt2},
864 F: vFloat0,
865 FPtr: &vFloat1,
866 Fs: []float32{vFloat0},
867 FPtrs: []*float32{&vFloat2},
868 B: vBool0,
869 BPtr: &vBool1,
870 Bs: []bool{vBool0},
871 BPtrs: []*bool{&vBool2},
872 Bytes: vBytes0,
873 BytesPtr: &vBytes1,
874 S: vStr0,
875 SPtr: &vStr1,
876 Ss: []string{vStr0},
877 SPtrs: []*string{&vStr2},
878 MyI: MyInt(vInt0),
879 Child: Child{G: struct{ I int }{I: vInt0}},
880 Children: []Child{{G: struct{ I int }{I: vInt0}}},
881 ChildPtr: &Child{G: struct{ I int }{I: vInt0}},
882 ChildToEmbed: ChildToEmbed{X: vBool0},
883 }
884 if err := Unmarshal([]byte(emptyXML), v); err != nil {
885 t.Fatalf("populated: Unmarshal failed: got %v", err)
886 }
887
888 want = &Parent{
889 IPtr: &zInt,
890 Is: []int{vInt0, zInt},
891 IPtrs: []*int{&vInt0, &zInt},
892 FPtr: &zFloat,
893 Fs: []float32{vFloat0, zFloat},
894 FPtrs: []*float32{&vFloat0, &zFloat},
895 BPtr: &zBool,
896 Bs: []bool{vBool0, zBool},
897 BPtrs: []*bool{&vBool0, &zBool},
898 Bytes: []byte{},
899 BytesPtr: &zBytes,
900 SPtr: &zStr,
901 Ss: []string{vStr0, zStr},
902 SPtrs: []*string{&vStr0, &zStr},
903 Child: Child{G: struct{ I int }{I: vInt0}},
904 Children: []Child{{G: struct{ I int }{I: vInt0}}, {}},
905 ChildPtr: &Child{G: struct{ I int }{I: vInt0}},
906 }
907 if !reflect.DeepEqual(v, want) {
908 t.Fatalf("populated: Unmarshal:\nhave: %#+v\nwant: %#+v", v, want)
909 }
910 }
911
912 type WhitespaceValuesParent struct {
913 BFalse bool
914 BTrue bool
915 I int
916 INeg int
917 I8 int8
918 I8Neg int8
919 I16 int16
920 I16Neg int16
921 I32 int32
922 I32Neg int32
923 I64 int64
924 I64Neg int64
925 UI uint
926 UI8 uint8
927 UI16 uint16
928 UI32 uint32
929 UI64 uint64
930 F32 float32
931 F32Neg float32
932 F64 float64
933 F64Neg float64
934 }
935
936 const whitespaceValuesXML = `
937 <WhitespaceValuesParent>
938 <BFalse> false </BFalse>
939 <BTrue> true </BTrue>
940 <I> 266703 </I>
941 <INeg> -266703 </INeg>
942 <I8> 112 </I8>
943 <I8Neg> -112 </I8Neg>
944 <I16> 6703 </I16>
945 <I16Neg> -6703 </I16Neg>
946 <I32> 266703 </I32>
947 <I32Neg> -266703 </I32Neg>
948 <I64> 266703 </I64>
949 <I64Neg> -266703 </I64Neg>
950 <UI> 266703 </UI>
951 <UI8> 112 </UI8>
952 <UI16> 6703 </UI16>
953 <UI32> 266703 </UI32>
954 <UI64> 266703 </UI64>
955 <F32> 266.703 </F32>
956 <F32Neg> -266.703 </F32Neg>
957 <F64> 266.703 </F64>
958 <F64Neg> -266.703 </F64Neg>
959 </WhitespaceValuesParent>
960 `
961
962
963 func TestUnmarshalWhitespaceValues(t *testing.T) {
964 v := WhitespaceValuesParent{}
965 if err := Unmarshal([]byte(whitespaceValuesXML), &v); err != nil {
966 t.Fatalf("whitespace values: Unmarshal failed: got %v", err)
967 }
968
969 want := WhitespaceValuesParent{
970 BFalse: false,
971 BTrue: true,
972 I: 266703,
973 INeg: -266703,
974 I8: 112,
975 I8Neg: -112,
976 I16: 6703,
977 I16Neg: -6703,
978 I32: 266703,
979 I32Neg: -266703,
980 I64: 266703,
981 I64Neg: -266703,
982 UI: 266703,
983 UI8: 112,
984 UI16: 6703,
985 UI32: 266703,
986 UI64: 266703,
987 F32: 266.703,
988 F32Neg: -266.703,
989 F64: 266.703,
990 F64Neg: -266.703,
991 }
992 if v != want {
993 t.Fatalf("whitespace values: Unmarshal:\nhave: %#+v\nwant: %#+v", v, want)
994 }
995 }
996
997 type WhitespaceAttrsParent struct {
998 BFalse bool `xml:",attr"`
999 BTrue bool `xml:",attr"`
1000 I int `xml:",attr"`
1001 INeg int `xml:",attr"`
1002 I8 int8 `xml:",attr"`
1003 I8Neg int8 `xml:",attr"`
1004 I16 int16 `xml:",attr"`
1005 I16Neg int16 `xml:",attr"`
1006 I32 int32 `xml:",attr"`
1007 I32Neg int32 `xml:",attr"`
1008 I64 int64 `xml:",attr"`
1009 I64Neg int64 `xml:",attr"`
1010 UI uint `xml:",attr"`
1011 UI8 uint8 `xml:",attr"`
1012 UI16 uint16 `xml:",attr"`
1013 UI32 uint32 `xml:",attr"`
1014 UI64 uint64 `xml:",attr"`
1015 F32 float32 `xml:",attr"`
1016 F32Neg float32 `xml:",attr"`
1017 F64 float64 `xml:",attr"`
1018 F64Neg float64 `xml:",attr"`
1019 }
1020
1021 const whitespaceAttrsXML = `
1022 <WhitespaceAttrsParent
1023 BFalse=" false "
1024 BTrue=" true "
1025 I=" 266703 "
1026 INeg=" -266703 "
1027 I8=" 112 "
1028 I8Neg=" -112 "
1029 I16=" 6703 "
1030 I16Neg=" -6703 "
1031 I32=" 266703 "
1032 I32Neg=" -266703 "
1033 I64=" 266703 "
1034 I64Neg=" -266703 "
1035 UI=" 266703 "
1036 UI8=" 112 "
1037 UI16=" 6703 "
1038 UI32=" 266703 "
1039 UI64=" 266703 "
1040 F32=" 266.703 "
1041 F32Neg=" -266.703 "
1042 F64=" 266.703 "
1043 F64Neg=" -266.703 "
1044 >
1045 </WhitespaceAttrsParent>
1046 `
1047
1048
1049 func TestUnmarshalWhitespaceAttrs(t *testing.T) {
1050 v := WhitespaceAttrsParent{}
1051 if err := Unmarshal([]byte(whitespaceAttrsXML), &v); err != nil {
1052 t.Fatalf("whitespace attrs: Unmarshal failed: got %v", err)
1053 }
1054
1055 want := WhitespaceAttrsParent{
1056 BFalse: false,
1057 BTrue: true,
1058 I: 266703,
1059 INeg: -266703,
1060 I8: 112,
1061 I8Neg: -112,
1062 I16: 6703,
1063 I16Neg: -6703,
1064 I32: 266703,
1065 I32Neg: -266703,
1066 I64: 266703,
1067 I64Neg: -266703,
1068 UI: 266703,
1069 UI8: 112,
1070 UI16: 6703,
1071 UI32: 266703,
1072 UI64: 266703,
1073 F32: 266.703,
1074 F32Neg: -266.703,
1075 F64: 266.703,
1076 F64Neg: -266.703,
1077 }
1078 if v != want {
1079 t.Fatalf("whitespace attrs: Unmarshal:\nhave: %#+v\nwant: %#+v", v, want)
1080 }
1081 }
1082
View as plain text