Source file src/encoding/xml/read_test.go

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

View as plain text