Black Lives Matter. Support the Equal Justice Initiative.

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  	"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  

View as plain text