Home | History | Annotate | Download | only in xml
      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 	"io"
      9 	"reflect"
     10 	"strings"
     11 	"testing"
     12 	"time"
     13 )
     14 
     15 // Stripped down Atom feed data structures.
     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 // hget http://codereview.appspot.com/rss/mine/rsc
     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&lt;&gt;</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 &amp;lt;link rel=&amp;quot;hub&amp;quot; href=&amp;quot;hub-server&amp;quot;&amp;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&amp;#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&amp;#39;s actual URL in
     49 the link rel=&amp;quot;self&amp;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&amp;#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&lt;&gt;</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 &lt;link rel=&quot;hub&quot; href=&quot;hub-server&quot;&gt; 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&#39;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&#39;s actual URL in
    164 the link rel=&quot;self&quot;, 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&#39;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 = []interface{}{
    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 interface{}
    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 		// Default space does not apply to attribute names.
    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 		// Default space does not apply to attribute names.
    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 		// Default space does not apply to attribute names.
    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 		// Default space does not apply to attribute names.
    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 { // found end of element
    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 interface{} `xml:"Pea"`
    695 }
    696 
    697 // https://golang.org/issue/6836
    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 // Issue 11112. Unmarshal must reject invalid comments.
    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 // Issue 15600. ",innerxml" on a field that can't hold it.
    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 // golang.org/issues/13417
    823 func TestUnmarshalEmptyValues(t *testing.T) {
    824 	// Test first with a zero-valued dst.
    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 	// Test with a pre-populated dst.
    855 	// Multiple addressable copies, as pointer-to fields will replace value during unmarshal.
    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}}, // I should == zInt0? (zero value)
    904 		Children: []Child{{G: struct{ I int }{I: vInt0}}, {}},
    905 		ChildPtr: &Child{G: struct{ I int }{I: vInt0}}, // I should == zInt0? (zero value)
    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 // golang.org/issues/22146
    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 // golang.org/issues/22146
   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