...

Source file src/encoding/xml/read_test.go

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

View as plain text