OSDN Git Service

new repo
[bytom/vapor.git] / vendor / golang.org / x / net / webdav / internal / xml / read_test.go
1 // Copyright 2009 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         "fmt"
10         "io"
11         "reflect"
12         "strings"
13         "testing"
14         "time"
15 )
16
17 // Stripped down Atom feed data structures.
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 // hget http://codereview.appspot.com/rss/mine/rsc
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&lt;&gt;</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 &amp;lt;link rel=&amp;quot;hub&amp;quot; href=&amp;quot;hub-server&amp;quot;&amp;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&amp;#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&amp;#39;s actual URL in
51 the link rel=&amp;quot;self&amp;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&amp;#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&lt;&gt;</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 &lt;link rel=&quot;hub&quot; href=&quot;hub-server&quot;&gt; 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&#39;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&#39;s actual URL in
166 the link rel=&quot;self&quot;, 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&#39;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         // From issue 7535
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                 // Default space does not apply to attribute names.
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                 // Default space does not apply to attribute names.
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                 // Default space does not apply to attribute names.
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                 // Default space does not apply to attribute names.
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 { // found end of element
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 // https://golang.org/issue/6836
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 }