1
2
3
4
5 package xml
6
7 import (
8 "bytes"
9 "fmt"
10 "io"
11 "reflect"
12 "strings"
13 "testing"
14 "time"
15 )
16
17
18
19 func TestUnmarshalFeed(t *testing.T) {
20 var f Feed
21 if err := Unmarshal([]byte(atomFeedString), &f); err != nil {
22 t.Fatalf("Unmarshal: %s", err)
23 }
24 if !reflect.DeepEqual(f, atomFeed) {
25 t.Fatalf("have %#v\nwant %#v", f, atomFeed)
26 }
27 }
28
29
30 const atomFeedString = `
31 <?xml version="1.0" encoding="utf-8"?>
32 <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
33 </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">
34 An attempt at adding pubsubhubbub support to Rietveld.
35 http://code.google.com/p/pubsubhubbub
36 http://code.google.com/p/rietveld/issues/detail?id=155
37
38 The server side of the protocol is trivial:
39 1. add a &lt;link rel=&quot;hub&quot; href=&quot;hub-server&quot;&gt; tag to all
40 feeds that will be pubsubhubbubbed.
41 2. every time one of those feeds changes, tell the hub
42 with a simple POST request.
43
44 I have tested this by adding debug prints to a local hub
45 server and checking that the server got the right publish
46 requests.
47
48 I can&#39;t quite get the server to work, but I think the bug
49 is not in my code. I think that the server expects to be
50 able to grab the feed and see the feed&#39;s actual URL in
51 the link rel=&quot;self&quot;, but the default value for that drops
52 the :port from the URL, and I cannot for the life of me
53 figure out how to get the Atom generator deep inside
54 django not to do that, or even where it is doing that,
55 or even what code is running to generate the Atom feed.
56 (I thought I knew but I added some assert False statements
57 and it kept running!)
58
59 Ignoring that particular problem, I would appreciate
60 feedback on the right way to get the two values at
61 the top of feeds.py marked NOTE(rsc).
62
63
64 </summary></entry><entry><title>rietveld: correct tab handling
65 </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">
66 This fixes the buggy tab rendering that can be seen at
67 http://codereview.appspot.com/116075/diff/1/2
68
69 The fundamental problem was that the tab code was
70 not being told what column the text began in, so it
71 didn&#39;t know where to put the tab stops. Another problem
72 was that some of the code assumed that string byte
73 offsets were the same as column offsets, which is only
74 true if there are no tabs.
75
76 In the process of fixing this, I cleaned up the arguments
77 to Fold and ExpandTabs and renamed them Break and
78 _ExpandTabs so that I could be sure that I found all the
79 call sites. I also wanted to verify that ExpandTabs was
80 not being used from outside intra_region_diff.py.
81
82
83 </summary></entry></feed> `
84
85 type Feed struct {
86 XMLName Name `xml:"http://www.w3.org/2005/Atom feed"`
87 Title string `xml:"title"`
88 Id string `xml:"id"`
89 Link []Link `xml:"link"`
90 Updated time.Time `xml:"updated,attr"`
91 Author Person `xml:"author"`
92 Entry []Entry `xml:"entry"`
93 }
94
95 type Entry struct {
96 Title string `xml:"title"`
97 Id string `xml:"id"`
98 Link []Link `xml:"link"`
99 Updated time.Time `xml:"updated"`
100 Author Person `xml:"author"`
101 Summary Text `xml:"summary"`
102 }
103
104 type Link struct {
105 Rel string `xml:"rel,attr,omitempty"`
106 Href string `xml:"href,attr"`
107 }
108
109 type Person struct {
110 Name string `xml:"name"`
111 URI string `xml:"uri"`
112 Email string `xml:"email"`
113 InnerXML string `xml:",innerxml"`
114 }
115
116 type Text struct {
117 Type string `xml:"type,attr,omitempty"`
118 Body string `xml:",chardata"`
119 }
120
121 var atomFeed = Feed{
122 XMLName: Name{"http://www.w3.org/2005/Atom", "feed"},
123 Title: "Code Review - My issues",
124 Link: []Link{
125 {Rel: "alternate", Href: "http://codereview.appspot.com/"},
126 {Rel: "self", Href: "http://codereview.appspot.com/rss/mine/rsc"},
127 },
128 Id: "http://codereview.appspot.com/",
129 Updated: ParseTime("2009-10-04T01:35:58+00:00"),
130 Author: Person{
131 Name: "rietveld<>",
132 InnerXML: "<name>rietveld<></name>",
133 },
134 Entry: []Entry{
135 {
136 Title: "rietveld: an attempt at pubsubhubbub\n",
137 Link: []Link{
138 {Rel: "alternate", Href: "http://codereview.appspot.com/126085"},
139 },
140 Updated: ParseTime("2009-10-04T01:35:58+00:00"),
141 Author: Person{
142 Name: "email-address-removed",
143 InnerXML: "<name>email-address-removed</name>",
144 },
145 Id: "urn:md5:134d9179c41f806be79b3a5f7877d19a",
146 Summary: Text{
147 Type: "html",
148 Body: `
149 An attempt at adding pubsubhubbub support to Rietveld.
150 http://code.google.com/p/pubsubhubbub
151 http://code.google.com/p/rietveld/issues/detail?id=155
152
153 The server side of the protocol is trivial:
154 1. add a <link rel="hub" href="hub-server"> tag to all
155 feeds that will be pubsubhubbubbed.
156 2. every time one of those feeds changes, tell the hub
157 with a simple POST request.
158
159 I have tested this by adding debug prints to a local hub
160 server and checking that the server got the right publish
161 requests.
162
163 I can't quite get the server to work, but I think the bug
164 is not in my code. I think that the server expects to be
165 able to grab the feed and see the feed's actual URL in
166 the link rel="self", but the default value for that drops
167 the :port from the URL, and I cannot for the life of me
168 figure out how to get the Atom generator deep inside
169 django not to do that, or even where it is doing that,
170 or even what code is running to generate the Atom feed.
171 (I thought I knew but I added some assert False statements
172 and it kept running!)
173
174 Ignoring that particular problem, I would appreciate
175 feedback on the right way to get the two values at
176 the top of feeds.py marked NOTE(rsc).
177
178
179 `,
180 },
181 },
182 {
183 Title: "rietveld: correct tab handling\n",
184 Link: []Link{
185 {Rel: "alternate", Href: "http://codereview.appspot.com/124106"},
186 },
187 Updated: ParseTime("2009-10-03T23:02:17+00:00"),
188 Author: Person{
189 Name: "email-address-removed",
190 InnerXML: "<name>email-address-removed</name>",
191 },
192 Id: "urn:md5:0a2a4f19bb815101f0ba2904aed7c35a",
193 Summary: Text{
194 Type: "html",
195 Body: `
196 This fixes the buggy tab rendering that can be seen at
197 http://codereview.appspot.com/116075/diff/1/2
198
199 The fundamental problem was that the tab code was
200 not being told what column the text began in, so it
201 didn't know where to put the tab stops. Another problem
202 was that some of the code assumed that string byte
203 offsets were the same as column offsets, which is only
204 true if there are no tabs.
205
206 In the process of fixing this, I cleaned up the arguments
207 to Fold and ExpandTabs and renamed them Break and
208 _ExpandTabs so that I could be sure that I found all the
209 call sites. I also wanted to verify that ExpandTabs was
210 not being used from outside intra_region_diff.py.
211
212
213 `,
214 },
215 },
216 },
217 }
218
219 const pathTestString = `
220 <Result>
221 <Before>1</Before>
222 <Items>
223 <Item1>
224 <Value>A</Value>
225 </Item1>
226 <Item2>
227 <Value>B</Value>
228 </Item2>
229 <Item1>
230 <Value>C</Value>
231 <Value>D</Value>
232 </Item1>
233 <_>
234 <Value>E</Value>
235 </_>
236 </Items>
237 <After>2</After>
238 </Result>
239 `
240
241 type PathTestItem struct {
242 Value string
243 }
244
245 type PathTestA struct {
246 Items []PathTestItem `xml:">Item1"`
247 Before, After string
248 }
249
250 type PathTestB struct {
251 Other []PathTestItem `xml:"Items>Item1"`
252 Before, After string
253 }
254
255 type PathTestC struct {
256 Values1 []string `xml:"Items>Item1>Value"`
257 Values2 []string `xml:"Items>Item2>Value"`
258 Before, After string
259 }
260
261 type PathTestSet struct {
262 Item1 []PathTestItem
263 }
264
265 type PathTestD struct {
266 Other PathTestSet `xml:"Items"`
267 Before, After string
268 }
269
270 type PathTestE struct {
271 Underline string `xml:"Items>_>Value"`
272 Before, After string
273 }
274
275 var pathTests = []interface{}{
276 &PathTestA{Items: []PathTestItem{{"A"}, {"D"}}, Before: "1", After: "2"},
277 &PathTestB{Other: []PathTestItem{{"A"}, {"D"}}, Before: "1", After: "2"},
278 &PathTestC{Values1: []string{"A", "C", "D"}, Values2: []string{"B"}, Before: "1", After: "2"},
279 &PathTestD{Other: PathTestSet{Item1: []PathTestItem{{"A"}, {"D"}}}, Before: "1", After: "2"},
280 &PathTestE{Underline: "E", Before: "1", After: "2"},
281 }
282
283 func TestUnmarshalPaths(t *testing.T) {
284 for _, pt := range pathTests {
285 v := reflect.New(reflect.TypeOf(pt).Elem()).Interface()
286 if err := Unmarshal([]byte(pathTestString), v); err != nil {
287 t.Fatalf("Unmarshal: %s", err)
288 }
289 if !reflect.DeepEqual(v, pt) {
290 t.Fatalf("have %#v\nwant %#v", v, pt)
291 }
292 }
293 }
294
295 type BadPathTestA struct {
296 First string `xml:"items>item1"`
297 Other string `xml:"items>item2"`
298 Second string `xml:"items"`
299 }
300
301 type BadPathTestB struct {
302 Other string `xml:"items>item2>value"`
303 First string `xml:"items>item1"`
304 Second string `xml:"items>item1>value"`
305 }
306
307 type BadPathTestC struct {
308 First string
309 Second string `xml:"First"`
310 }
311
312 type BadPathTestD struct {
313 BadPathEmbeddedA
314 BadPathEmbeddedB
315 }
316
317 type BadPathEmbeddedA struct {
318 First string
319 }
320
321 type BadPathEmbeddedB struct {
322 Second string `xml:"First"`
323 }
324
325 var badPathTests = []struct {
326 v, e interface{}
327 }{
328 {&BadPathTestA{}, &TagPathError{reflect.TypeOf(BadPathTestA{}), "First", "items>item1", "Second", "items"}},
329 {&BadPathTestB{}, &TagPathError{reflect.TypeOf(BadPathTestB{}), "First", "items>item1", "Second", "items>item1>value"}},
330 {&BadPathTestC{}, &TagPathError{reflect.TypeOf(BadPathTestC{}), "First", "", "Second", "First"}},
331 {&BadPathTestD{}, &TagPathError{reflect.TypeOf(BadPathTestD{}), "First", "", "Second", "First"}},
332 }
333
334 func TestUnmarshalBadPaths(t *testing.T) {
335 for _, tt := range badPathTests {
336 err := Unmarshal([]byte(pathTestString), tt.v)
337 if !reflect.DeepEqual(err, tt.e) {
338 t.Fatalf("Unmarshal with %#v didn't fail properly:\nhave %#v,\nwant %#v", tt.v, err, tt.e)
339 }
340 }
341 }
342
343 const OK = "OK"
344 const withoutNameTypeData = `
345 <?xml version="1.0" charset="utf-8"?>
346 <Test3 Attr="OK" />`
347
348 type TestThree struct {
349 XMLName Name `xml:"Test3"`
350 Attr string `xml:",attr"`
351 }
352
353 func TestUnmarshalWithoutNameType(t *testing.T) {
354 var x TestThree
355 if err := Unmarshal([]byte(withoutNameTypeData), &x); err != nil {
356 t.Fatalf("Unmarshal: %s", err)
357 }
358 if x.Attr != OK {
359 t.Fatalf("have %v\nwant %v", x.Attr, OK)
360 }
361 }
362
363 func TestUnmarshalAttr(t *testing.T) {
364 type ParamVal struct {
365 Int int `xml:"int,attr"`
366 }
367
368 type ParamPtr struct {
369 Int *int `xml:"int,attr"`
370 }
371
372 type ParamStringPtr struct {
373 Int *string `xml:"int,attr"`
374 }
375
376 x := []byte(`<Param int="1" />`)
377
378 p1 := &ParamPtr{}
379 if err := Unmarshal(x, p1); err != nil {
380 t.Fatalf("Unmarshal: %s", err)
381 }
382 if p1.Int == nil {
383 t.Fatalf("Unmarshal failed in to *int field")
384 } else if *p1.Int != 1 {
385 t.Fatalf("Unmarshal with %s failed:\nhave %#v,\n want %#v", x, p1.Int, 1)
386 }
387
388 p2 := &ParamVal{}
389 if err := Unmarshal(x, p2); err != nil {
390 t.Fatalf("Unmarshal: %s", err)
391 }
392 if p2.Int != 1 {
393 t.Fatalf("Unmarshal with %s failed:\nhave %#v,\n want %#v", x, p2.Int, 1)
394 }
395
396 p3 := &ParamStringPtr{}
397 if err := Unmarshal(x, p3); err != nil {
398 t.Fatalf("Unmarshal: %s", err)
399 }
400 if p3.Int == nil {
401 t.Fatalf("Unmarshal failed in to *string field")
402 } else if *p3.Int != "1" {
403 t.Fatalf("Unmarshal with %s failed:\nhave %#v,\n want %#v", x, p3.Int, 1)
404 }
405 }
406
407 type Tables struct {
408 HTable string `xml:"http://www.w3.org/TR/html4/ table"`
409 FTable string `xml:"http://www.w3schools.com/furniture table"`
410 }
411
412 var tables = []struct {
413 xml string
414 tab Tables
415 ns string
416 }{
417 {
418 xml: `<Tables>` +
419 `<table xmlns="http://www.w3.org/TR/html4/">hello</table>` +
420 `<table xmlns="http://www.w3schools.com/furniture">world</table>` +
421 `</Tables>`,
422 tab: Tables{"hello", "world"},
423 },
424 {
425 xml: `<Tables>` +
426 `<table xmlns="http://www.w3schools.com/furniture">world</table>` +
427 `<table xmlns="http://www.w3.org/TR/html4/">hello</table>` +
428 `</Tables>`,
429 tab: Tables{"hello", "world"},
430 },
431 {
432 xml: `<Tables xmlns:f="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/">` +
433 `<f:table>world</f:table>` +
434 `<h:table>hello</h:table>` +
435 `</Tables>`,
436 tab: Tables{"hello", "world"},
437 },
438 {
439 xml: `<Tables>` +
440 `<table>bogus</table>` +
441 `</Tables>`,
442 tab: Tables{},
443 },
444 {
445 xml: `<Tables>` +
446 `<table>only</table>` +
447 `</Tables>`,
448 tab: Tables{HTable: "only"},
449 ns: "http://www.w3.org/TR/html4/",
450 },
451 {
452 xml: `<Tables>` +
453 `<table>only</table>` +
454 `</Tables>`,
455 tab: Tables{FTable: "only"},
456 ns: "http://www.w3schools.com/furniture",
457 },
458 {
459 xml: `<Tables>` +
460 `<table>only</table>` +
461 `</Tables>`,
462 tab: Tables{},
463 ns: "something else entirely",
464 },
465 }
466
467 func TestUnmarshalNS(t *testing.T) {
468 for i, tt := range tables {
469 var dst Tables
470 var err error
471 if tt.ns != "" {
472 d := NewDecoder(strings.NewReader(tt.xml))
473 d.DefaultSpace = tt.ns
474 err = d.Decode(&dst)
475 } else {
476 err = Unmarshal([]byte(tt.xml), &dst)
477 }
478 if err != nil {
479 t.Errorf("#%d: Unmarshal: %v", i, err)
480 continue
481 }
482 want := tt.tab
483 if dst != want {
484 t.Errorf("#%d: dst=%+v, want %+v", i, dst, want)
485 }
486 }
487 }
488
489 func TestRoundTrip(t *testing.T) {
490
491 const s = `<ex:element xmlns:ex="http://example.com/schema"></ex:element>`
492 in := bytes.NewBufferString(s)
493 for i := 0; i < 10; i++ {
494 out := &bytes.Buffer{}
495 d := NewDecoder(in)
496 e := NewEncoder(out)
497
498 for {
499 t, err := d.Token()
500 if err == io.EOF {
501 break
502 }
503 if err != nil {
504 fmt.Println("failed:", err)
505 return
506 }
507 e.EncodeToken(t)
508 }
509 e.Flush()
510 in = out
511 }
512 if got := in.String(); got != s {
513 t.Errorf("have: %q\nwant: %q\n", got, s)
514 }
515 }
516
517 func TestMarshalNS(t *testing.T) {
518 dst := Tables{"hello", "world"}
519 data, err := Marshal(&dst)
520 if err != nil {
521 t.Fatalf("Marshal: %v", err)
522 }
523 want := `<Tables><table xmlns="http://www.w3.org/TR/html4/">hello</table><table xmlns="http://www.w3schools.com/furniture">world</table></Tables>`
524 str := string(data)
525 if str != want {
526 t.Errorf("have: %q\nwant: %q\n", str, want)
527 }
528 }
529
530 type TableAttrs struct {
531 TAttr TAttr
532 }
533
534 type TAttr struct {
535 HTable string `xml:"http://www.w3.org/TR/html4/ table,attr"`
536 FTable string `xml:"http://www.w3schools.com/furniture table,attr"`
537 Lang string `xml:"http://www.w3.org/XML/1998/namespace lang,attr,omitempty"`
538 Other1 string `xml:"http://golang.org/xml/ other,attr,omitempty"`
539 Other2 string `xml:"http://golang.org/xmlfoo/ other,attr,omitempty"`
540 Other3 string `xml:"http://golang.org/json/ other,attr,omitempty"`
541 Other4 string `xml:"http://golang.org/2/json/ other,attr,omitempty"`
542 }
543
544 var tableAttrs = []struct {
545 xml string
546 tab TableAttrs
547 ns string
548 }{
549 {
550 xml: `<TableAttrs xmlns:f="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/"><TAttr ` +
551 `h:table="hello" f:table="world" ` +
552 `/></TableAttrs>`,
553 tab: TableAttrs{TAttr{HTable: "hello", FTable: "world"}},
554 },
555 {
556 xml: `<TableAttrs><TAttr xmlns:f="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/" ` +
557 `h:table="hello" f:table="world" ` +
558 `/></TableAttrs>`,
559 tab: TableAttrs{TAttr{HTable: "hello", FTable: "world"}},
560 },
561 {
562 xml: `<TableAttrs><TAttr ` +
563 `h:table="hello" f:table="world" xmlns:f="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/" ` +
564 `/></TableAttrs>`,
565 tab: TableAttrs{TAttr{HTable: "hello", FTable: "world"}},
566 },
567 {
568
569 xml: `<TableAttrs xmlns="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/"><TAttr ` +
570 `h:table="hello" table="world" ` +
571 `/></TableAttrs>`,
572 tab: TableAttrs{TAttr{HTable: "hello", FTable: ""}},
573 },
574 {
575
576 xml: `<TableAttrs xmlns:f="http://www.w3schools.com/furniture"><TAttr xmlns="http://www.w3.org/TR/html4/" ` +
577 `table="hello" f:table="world" ` +
578 `/></TableAttrs>`,
579 tab: TableAttrs{TAttr{HTable: "", FTable: "world"}},
580 },
581 {
582 xml: `<TableAttrs><TAttr ` +
583 `table="bogus" ` +
584 `/></TableAttrs>`,
585 tab: TableAttrs{},
586 },
587 {
588
589 xml: `<TableAttrs xmlns:h="http://www.w3.org/TR/html4/"><TAttr ` +
590 `h:table="hello" table="world" ` +
591 `/></TableAttrs>`,
592 tab: TableAttrs{TAttr{HTable: "hello", FTable: ""}},
593 ns: "http://www.w3schools.com/furniture",
594 },
595 {
596
597 xml: `<TableAttrs xmlns:f="http://www.w3schools.com/furniture"><TAttr ` +
598 `table="hello" f:table="world" ` +
599 `/></TableAttrs>`,
600 tab: TableAttrs{TAttr{HTable: "", FTable: "world"}},
601 ns: "http://www.w3.org/TR/html4/",
602 },
603 {
604 xml: `<TableAttrs><TAttr ` +
605 `table="bogus" ` +
606 `/></TableAttrs>`,
607 tab: TableAttrs{},
608 ns: "something else entirely",
609 },
610 }
611
612 func TestUnmarshalNSAttr(t *testing.T) {
613 for i, tt := range tableAttrs {
614 var dst TableAttrs
615 var err error
616 if tt.ns != "" {
617 d := NewDecoder(strings.NewReader(tt.xml))
618 d.DefaultSpace = tt.ns
619 err = d.Decode(&dst)
620 } else {
621 err = Unmarshal([]byte(tt.xml), &dst)
622 }
623 if err != nil {
624 t.Errorf("#%d: Unmarshal: %v", i, err)
625 continue
626 }
627 want := tt.tab
628 if dst != want {
629 t.Errorf("#%d: dst=%+v, want %+v", i, dst, want)
630 }
631 }
632 }
633
634 func TestMarshalNSAttr(t *testing.T) {
635 src := TableAttrs{TAttr{"hello", "world", "en_US", "other1", "other2", "other3", "other4"}}
636 data, err := Marshal(&src)
637 if err != nil {
638 t.Fatalf("Marshal: %v", err)
639 }
640 want := `<TableAttrs><TAttr xmlns:json_1="http://golang.org/2/json/" xmlns:json="http://golang.org/json/" xmlns:_xmlfoo="http://golang.org/xmlfoo/" xmlns:_xml="http://golang.org/xml/" xmlns:furniture="http://www.w3schools.com/furniture" xmlns:html4="http://www.w3.org/TR/html4/" html4:table="hello" furniture:table="world" xml:lang="en_US" _xml:other="other1" _xmlfoo:other="other2" json:other="other3" json_1:other="other4"></TAttr></TableAttrs>`
641 str := string(data)
642 if str != want {
643 t.Errorf("Marshal:\nhave: %#q\nwant: %#q\n", str, want)
644 }
645
646 var dst TableAttrs
647 if err := Unmarshal(data, &dst); err != nil {
648 t.Errorf("Unmarshal: %v", err)
649 }
650
651 if dst != src {
652 t.Errorf("Unmarshal = %q, want %q", dst, src)
653 }
654 }
655
656 type MyCharData struct {
657 body string
658 }
659
660 func (m *MyCharData) UnmarshalXML(d *Decoder, start StartElement) error {
661 for {
662 t, err := d.Token()
663 if err == io.EOF {
664 break
665 }
666 if err != nil {
667 return err
668 }
669 if char, ok := t.(CharData); ok {
670 m.body += string(char)
671 }
672 }
673 return nil
674 }
675
676 var _ Unmarshaler = (*MyCharData)(nil)
677
678 func (m *MyCharData) UnmarshalXMLAttr(attr Attr) error {
679 panic("must not call")
680 }
681
682 type MyAttr struct {
683 attr string
684 }
685
686 func (m *MyAttr) UnmarshalXMLAttr(attr Attr) error {
687 m.attr = attr.Value
688 return nil
689 }
690
691 var _ UnmarshalerAttr = (*MyAttr)(nil)
692
693 type MyStruct struct {
694 Data *MyCharData
695 Attr *MyAttr `xml:",attr"`
696
697 Data2 MyCharData
698 Attr2 MyAttr `xml:",attr"`
699 }
700
701 func TestUnmarshaler(t *testing.T) {
702 xml := `<?xml version="1.0" encoding="utf-8"?>
703 <MyStruct Attr="attr1" Attr2="attr2">
704 <Data>hello <!-- comment -->world</Data>
705 <Data2>howdy <!-- comment -->world</Data2>
706 </MyStruct>
707 `
708
709 var m MyStruct
710 if err := Unmarshal([]byte(xml), &m); err != nil {
711 t.Fatal(err)
712 }
713
714 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" {
715 t.Errorf("m=%#+v\n", m)
716 }
717 }
718
719 type Pea struct {
720 Cotelydon string
721 }
722
723 type Pod struct {
724 Pea interface{} `xml:"Pea"`
725 }
726
727
728 func TestUnmarshalIntoInterface(t *testing.T) {
729 pod := new(Pod)
730 pod.Pea = new(Pea)
731 xml := `<Pod><Pea><Cotelydon>Green stuff</Cotelydon></Pea></Pod>`
732 err := Unmarshal([]byte(xml), pod)
733 if err != nil {
734 t.Fatalf("failed to unmarshal %q: %v", xml, err)
735 }
736 pea, ok := pod.Pea.(*Pea)
737 if !ok {
738 t.Fatalf("unmarshalled into wrong type: have %T want *Pea", pod.Pea)
739 }
740 have, want := pea.Cotelydon, "Green stuff"
741 if have != want {
742 t.Errorf("failed to unmarshal into interface, have %q want %q", have, want)
743 }
744 }
745
View as plain text