...
Run Format

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		"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://code.google.com/p/go/issues/detail?id=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("unmarshalled 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	

View as plain text