Run Format

Source file src/pkg/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		"reflect"
     9		"strings"
    10		"testing"
    11		"time"
    12	)
    13	
    14	// Stripped down Atom feed data structures.
    15	
    16	func TestUnmarshalFeed(t *testing.T) {
    17		var f Feed
    18		if err := Unmarshal([]byte(atomFeedString), &f); err != nil {
    19			t.Fatalf("Unmarshal: %s", err)
    20		}
    21		if !reflect.DeepEqual(f, atomFeed) {
    22			t.Fatalf("have %#v\nwant %#v", f, atomFeed)
    23		}
    24	}
    25	
    26	// hget http://codereview.appspot.com/rss/mine/rsc
    27	const atomFeedString = `
    28	<?xml version="1.0" encoding="utf-8"?>
    29	<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
    30	</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">
    31	  An attempt at adding pubsubhubbub support to Rietveld.
    32	http://code.google.com/p/pubsubhubbub
    33	http://code.google.com/p/rietveld/issues/detail?id=155
    34	
    35	The server side of the protocol is trivial:
    36	  1. add a &amp;lt;link rel=&amp;quot;hub&amp;quot; href=&amp;quot;hub-server&amp;quot;&amp;gt; tag to all
    37	     feeds that will be pubsubhubbubbed.
    38	  2. every time one of those feeds changes, tell the hub
    39	     with a simple POST request.
    40	
    41	I have tested this by adding debug prints to a local hub
    42	server and checking that the server got the right publish
    43	requests.
    44	
    45	I can&amp;#39;t quite get the server to work, but I think the bug
    46	is not in my code.  I think that the server expects to be
    47	able to grab the feed and see the feed&amp;#39;s actual URL in
    48	the link rel=&amp;quot;self&amp;quot;, but the default value for that drops
    49	the :port from the URL, and I cannot for the life of me
    50	figure out how to get the Atom generator deep inside
    51	django not to do that, or even where it is doing that,
    52	or even what code is running to generate the Atom feed.
    53	(I thought I knew but I added some assert False statements
    54	and it kept running!)
    55	
    56	Ignoring that particular problem, I would appreciate
    57	feedback on the right way to get the two values at
    58	the top of feeds.py marked NOTE(rsc).
    59	
    60	
    61	</summary></entry><entry><title>rietveld: correct tab handling
    62	</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">
    63	  This fixes the buggy tab rendering that can be seen at
    64	http://codereview.appspot.com/116075/diff/1/2
    65	
    66	The fundamental problem was that the tab code was
    67	not being told what column the text began in, so it
    68	didn&amp;#39;t know where to put the tab stops.  Another problem
    69	was that some of the code assumed that string byte
    70	offsets were the same as column offsets, which is only
    71	true if there are no tabs.
    72	
    73	In the process of fixing this, I cleaned up the arguments
    74	to Fold and ExpandTabs and renamed them Break and
    75	_ExpandTabs so that I could be sure that I found all the
    76	call sites.  I also wanted to verify that ExpandTabs was
    77	not being used from outside intra_region_diff.py.
    78	
    79	
    80	</summary></entry></feed> 	   `
    81	
    82	type Feed struct {
    83		XMLName Name      `xml:"http://www.w3.org/2005/Atom feed"`
    84		Title   string    `xml:"title"`
    85		Id      string    `xml:"id"`
    86		Link    []Link    `xml:"link"`
    87		Updated time.Time `xml:"updated,attr"`
    88		Author  Person    `xml:"author"`
    89		Entry   []Entry   `xml:"entry"`
    90	}
    91	
    92	type Entry struct {
    93		Title   string    `xml:"title"`
    94		Id      string    `xml:"id"`
    95		Link    []Link    `xml:"link"`
    96		Updated time.Time `xml:"updated"`
    97		Author  Person    `xml:"author"`
    98		Summary Text      `xml:"summary"`
    99	}
   100	
   101	type Link struct {
   102		Rel  string `xml:"rel,attr,omitempty"`
   103		Href string `xml:"href,attr"`
   104	}
   105	
   106	type Person struct {
   107		Name     string `xml:"name"`
   108		URI      string `xml:"uri"`
   109		Email    string `xml:"email"`
   110		InnerXML string `xml:",innerxml"`
   111	}
   112	
   113	type Text struct {
   114		Type string `xml:"type,attr,omitempty"`
   115		Body string `xml:",chardata"`
   116	}
   117	
   118	var atomFeed = Feed{
   119		XMLName: Name{"http://www.w3.org/2005/Atom", "feed"},
   120		Title:   "Code Review - My issues",
   121		Link: []Link{
   122			{Rel: "alternate", Href: "http://codereview.appspot.com/"},
   123			{Rel: "self", Href: "http://codereview.appspot.com/rss/mine/rsc"},
   124		},
   125		Id:      "http://codereview.appspot.com/",
   126		Updated: ParseTime("2009-10-04T01:35:58+00:00"),
   127		Author: Person{
   128			Name:     "rietveld<>",
   129			InnerXML: "<name>rietveld&lt;&gt;</name>",
   130		},
   131		Entry: []Entry{
   132			{
   133				Title: "rietveld: an attempt at pubsubhubbub\n",
   134				Link: []Link{
   135					{Rel: "alternate", Href: "http://codereview.appspot.com/126085"},
   136				},
   137				Updated: ParseTime("2009-10-04T01:35:58+00:00"),
   138				Author: Person{
   139					Name:     "email-address-removed",
   140					InnerXML: "<name>email-address-removed</name>",
   141				},
   142				Id: "urn:md5:134d9179c41f806be79b3a5f7877d19a",
   143				Summary: Text{
   144					Type: "html",
   145					Body: `
   146	  An attempt at adding pubsubhubbub support to Rietveld.
   147	http://code.google.com/p/pubsubhubbub
   148	http://code.google.com/p/rietveld/issues/detail?id=155
   149	
   150	The server side of the protocol is trivial:
   151	  1. add a &lt;link rel=&quot;hub&quot; href=&quot;hub-server&quot;&gt; tag to all
   152	     feeds that will be pubsubhubbubbed.
   153	  2. every time one of those feeds changes, tell the hub
   154	     with a simple POST request.
   155	
   156	I have tested this by adding debug prints to a local hub
   157	server and checking that the server got the right publish
   158	requests.
   159	
   160	I can&#39;t quite get the server to work, but I think the bug
   161	is not in my code.  I think that the server expects to be
   162	able to grab the feed and see the feed&#39;s actual URL in
   163	the link rel=&quot;self&quot;, but the default value for that drops
   164	the :port from the URL, and I cannot for the life of me
   165	figure out how to get the Atom generator deep inside
   166	django not to do that, or even where it is doing that,
   167	or even what code is running to generate the Atom feed.
   168	(I thought I knew but I added some assert False statements
   169	and it kept running!)
   170	
   171	Ignoring that particular problem, I would appreciate
   172	feedback on the right way to get the two values at
   173	the top of feeds.py marked NOTE(rsc).
   174	
   175	
   176	`,
   177				},
   178			},
   179			{
   180				Title: "rietveld: correct tab handling\n",
   181				Link: []Link{
   182					{Rel: "alternate", Href: "http://codereview.appspot.com/124106"},
   183				},
   184				Updated: ParseTime("2009-10-03T23:02:17+00:00"),
   185				Author: Person{
   186					Name:     "email-address-removed",
   187					InnerXML: "<name>email-address-removed</name>",
   188				},
   189				Id: "urn:md5:0a2a4f19bb815101f0ba2904aed7c35a",
   190				Summary: Text{
   191					Type: "html",
   192					Body: `
   193	  This fixes the buggy tab rendering that can be seen at
   194	http://codereview.appspot.com/116075/diff/1/2
   195	
   196	The fundamental problem was that the tab code was
   197	not being told what column the text began in, so it
   198	didn&#39;t know where to put the tab stops.  Another problem
   199	was that some of the code assumed that string byte
   200	offsets were the same as column offsets, which is only
   201	true if there are no tabs.
   202	
   203	In the process of fixing this, I cleaned up the arguments
   204	to Fold and ExpandTabs and renamed them Break and
   205	_ExpandTabs so that I could be sure that I found all the
   206	call sites.  I also wanted to verify that ExpandTabs was
   207	not being used from outside intra_region_diff.py.
   208	
   209	
   210	`,
   211				},
   212			},
   213		},
   214	}
   215	
   216	const pathTestString = `
   217	<Result>
   218	    <Before>1</Before>
   219	    <Items>
   220	        <Item1>
   221	            <Value>A</Value>
   222	        </Item1>
   223	        <Item2>
   224	            <Value>B</Value>
   225	        </Item2>
   226	        <Item1>
   227	            <Value>C</Value>
   228	            <Value>D</Value>
   229	        </Item1>
   230	        <_>
   231	            <Value>E</Value>
   232	        </_>
   233	    </Items>
   234	    <After>2</After>
   235	</Result>
   236	`
   237	
   238	type PathTestItem struct {
   239		Value string
   240	}
   241	
   242	type PathTestA struct {
   243		Items         []PathTestItem `xml:">Item1"`
   244		Before, After string
   245	}
   246	
   247	type PathTestB struct {
   248		Other         []PathTestItem `xml:"Items>Item1"`
   249		Before, After string
   250	}
   251	
   252	type PathTestC struct {
   253		Values1       []string `xml:"Items>Item1>Value"`
   254		Values2       []string `xml:"Items>Item2>Value"`
   255		Before, After string
   256	}
   257	
   258	type PathTestSet struct {
   259		Item1 []PathTestItem
   260	}
   261	
   262	type PathTestD struct {
   263		Other         PathTestSet `xml:"Items"`
   264		Before, After string
   265	}
   266	
   267	type PathTestE struct {
   268		Underline     string `xml:"Items>_>Value"`
   269		Before, After string
   270	}
   271	
   272	var pathTests = []interface{}{
   273		&PathTestA{Items: []PathTestItem{{"A"}, {"D"}}, Before: "1", After: "2"},
   274		&PathTestB{Other: []PathTestItem{{"A"}, {"D"}}, Before: "1", After: "2"},
   275		&PathTestC{Values1: []string{"A", "C", "D"}, Values2: []string{"B"}, Before: "1", After: "2"},
   276		&PathTestD{Other: PathTestSet{Item1: []PathTestItem{{"A"}, {"D"}}}, Before: "1", After: "2"},
   277		&PathTestE{Underline: "E", Before: "1", After: "2"},
   278	}
   279	
   280	func TestUnmarshalPaths(t *testing.T) {
   281		for _, pt := range pathTests {
   282			v := reflect.New(reflect.TypeOf(pt).Elem()).Interface()
   283			if err := Unmarshal([]byte(pathTestString), v); err != nil {
   284				t.Fatalf("Unmarshal: %s", err)
   285			}
   286			if !reflect.DeepEqual(v, pt) {
   287				t.Fatalf("have %#v\nwant %#v", v, pt)
   288			}
   289		}
   290	}
   291	
   292	type BadPathTestA struct {
   293		First  string `xml:"items>item1"`
   294		Other  string `xml:"items>item2"`
   295		Second string `xml:"items"`
   296	}
   297	
   298	type BadPathTestB struct {
   299		Other  string `xml:"items>item2>value"`
   300		First  string `xml:"items>item1"`
   301		Second string `xml:"items>item1>value"`
   302	}
   303	
   304	type BadPathTestC struct {
   305		First  string
   306		Second string `xml:"First"`
   307	}
   308	
   309	type BadPathTestD struct {
   310		BadPathEmbeddedA
   311		BadPathEmbeddedB
   312	}
   313	
   314	type BadPathEmbeddedA struct {
   315		First string
   316	}
   317	
   318	type BadPathEmbeddedB struct {
   319		Second string `xml:"First"`
   320	}
   321	
   322	var badPathTests = []struct {
   323		v, e interface{}
   324	}{
   325		{&BadPathTestA{}, &TagPathError{reflect.TypeOf(BadPathTestA{}), "First", "items>item1", "Second", "items"}},
   326		{&BadPathTestB{}, &TagPathError{reflect.TypeOf(BadPathTestB{}), "First", "items>item1", "Second", "items>item1>value"}},
   327		{&BadPathTestC{}, &TagPathError{reflect.TypeOf(BadPathTestC{}), "First", "", "Second", "First"}},
   328		{&BadPathTestD{}, &TagPathError{reflect.TypeOf(BadPathTestD{}), "First", "", "Second", "First"}},
   329	}
   330	
   331	func TestUnmarshalBadPaths(t *testing.T) {
   332		for _, tt := range badPathTests {
   333			err := Unmarshal([]byte(pathTestString), tt.v)
   334			if !reflect.DeepEqual(err, tt.e) {
   335				t.Fatalf("Unmarshal with %#v didn't fail properly:\nhave %#v,\nwant %#v", tt.v, err, tt.e)
   336			}
   337		}
   338	}
   339	
   340	const OK = "OK"
   341	const withoutNameTypeData = `
   342	<?xml version="1.0" charset="utf-8"?>
   343	<Test3 Attr="OK" />`
   344	
   345	type TestThree struct {
   346		XMLName Name   `xml:"Test3"`
   347		Attr    string `xml:",attr"`
   348	}
   349	
   350	func TestUnmarshalWithoutNameType(t *testing.T) {
   351		var x TestThree
   352		if err := Unmarshal([]byte(withoutNameTypeData), &x); err != nil {
   353			t.Fatalf("Unmarshal: %s", err)
   354		}
   355		if x.Attr != OK {
   356			t.Fatalf("have %v\nwant %v", x.Attr, OK)
   357		}
   358	}
   359	
   360	func TestUnmarshalAttr(t *testing.T) {
   361		type ParamVal struct {
   362			Int int `xml:"int,attr"`
   363		}
   364	
   365		type ParamPtr struct {
   366			Int *int `xml:"int,attr"`
   367		}
   368	
   369		type ParamStringPtr struct {
   370			Int *string `xml:"int,attr"`
   371		}
   372	
   373		x := []byte(`<Param int="1" />`)
   374	
   375		p1 := &ParamPtr{}
   376		if err := Unmarshal(x, p1); err != nil {
   377			t.Fatalf("Unmarshal: %s", err)
   378		}
   379		if p1.Int == nil {
   380			t.Fatalf("Unmarshal failed in to *int field")
   381		} else if *p1.Int != 1 {
   382			t.Fatalf("Unmarshal with %s failed:\nhave %#v,\n want %#v", x, p1.Int, 1)
   383		}
   384	
   385		p2 := &ParamVal{}
   386		if err := Unmarshal(x, p2); err != nil {
   387			t.Fatalf("Unmarshal: %s", err)
   388		}
   389		if p2.Int != 1 {
   390			t.Fatalf("Unmarshal with %s failed:\nhave %#v,\n want %#v", x, p2.Int, 1)
   391		}
   392	
   393		p3 := &ParamStringPtr{}
   394		if err := Unmarshal(x, p3); err != nil {
   395			t.Fatalf("Unmarshal: %s", err)
   396		}
   397		if p3.Int == nil {
   398			t.Fatalf("Unmarshal failed in to *string field")
   399		} else if *p3.Int != "1" {
   400			t.Fatalf("Unmarshal with %s failed:\nhave %#v,\n want %#v", x, p3.Int, 1)
   401		}
   402	}
   403	
   404	type Tables struct {
   405		HTable string `xml:"http://www.w3.org/TR/html4/ table"`
   406		FTable string `xml:"http://www.w3schools.com/furniture table"`
   407	}
   408	
   409	var tables = []struct {
   410		xml string
   411		tab Tables
   412		ns  string
   413	}{
   414		{
   415			xml: `<Tables>` +
   416				`<table xmlns="http://www.w3.org/TR/html4/">hello</table>` +
   417				`<table xmlns="http://www.w3schools.com/furniture">world</table>` +
   418				`</Tables>`,
   419			tab: Tables{"hello", "world"},
   420		},
   421		{
   422			xml: `<Tables>` +
   423				`<table xmlns="http://www.w3schools.com/furniture">world</table>` +
   424				`<table xmlns="http://www.w3.org/TR/html4/">hello</table>` +
   425				`</Tables>`,
   426			tab: Tables{"hello", "world"},
   427		},
   428		{
   429			xml: `<Tables xmlns:f="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/">` +
   430				`<f:table>world</f:table>` +
   431				`<h:table>hello</h:table>` +
   432				`</Tables>`,
   433			tab: Tables{"hello", "world"},
   434		},
   435		{
   436			xml: `<Tables>` +
   437				`<table>bogus</table>` +
   438				`</Tables>`,
   439			tab: Tables{},
   440		},
   441		{
   442			xml: `<Tables>` +
   443				`<table>only</table>` +
   444				`</Tables>`,
   445			tab: Tables{HTable: "only"},
   446			ns:  "http://www.w3.org/TR/html4/",
   447		},
   448		{
   449			xml: `<Tables>` +
   450				`<table>only</table>` +
   451				`</Tables>`,
   452			tab: Tables{FTable: "only"},
   453			ns:  "http://www.w3schools.com/furniture",
   454		},
   455		{
   456			xml: `<Tables>` +
   457				`<table>only</table>` +
   458				`</Tables>`,
   459			tab: Tables{},
   460			ns:  "something else entirely",
   461		},
   462	}
   463	
   464	func TestUnmarshalNS(t *testing.T) {
   465		for i, tt := range tables {
   466			var dst Tables
   467			var err error
   468			if tt.ns != "" {
   469				d := NewDecoder(strings.NewReader(tt.xml))
   470				d.DefaultSpace = tt.ns
   471				err = d.Decode(&dst)
   472			} else {
   473				err = Unmarshal([]byte(tt.xml), &dst)
   474			}
   475			if err != nil {
   476				t.Errorf("#%d: Unmarshal: %v", i, err)
   477				continue
   478			}
   479			want := tt.tab
   480			if dst != want {
   481				t.Errorf("#%d: dst=%+v, want %+v", i, dst, want)
   482			}
   483		}
   484	}
   485	
   486	func TestMarshalNS(t *testing.T) {
   487		dst := Tables{"hello", "world"}
   488		data, err := Marshal(&dst)
   489		if err != nil {
   490			t.Fatalf("Marshal: %v", err)
   491		}
   492		want := `<Tables><table xmlns="http://www.w3.org/TR/html4/">hello</table><table xmlns="http://www.w3schools.com/furniture">world</table></Tables>`
   493		str := string(data)
   494		if str != want {
   495			t.Errorf("have: %q\nwant: %q\n", str, want)
   496		}
   497	}
   498	
   499	type TableAttrs struct {
   500		TAttr TAttr
   501	}
   502	
   503	type TAttr struct {
   504		HTable string `xml:"http://www.w3.org/TR/html4/ table,attr"`
   505		FTable string `xml:"http://www.w3schools.com/furniture table,attr"`
   506		Lang   string `xml:"http://www.w3.org/XML/1998/namespace lang,attr,omitempty"`
   507		Other1 string `xml:"http://golang.org/xml/ other,attr,omitempty"`
   508		Other2 string `xml:"http://golang.org/xmlfoo/ other,attr,omitempty"`
   509		Other3 string `xml:"http://golang.org/json/ other,attr,omitempty"`
   510		Other4 string `xml:"http://golang.org/2/json/ other,attr,omitempty"`
   511	}
   512	
   513	var tableAttrs = []struct {
   514		xml string
   515		tab TableAttrs
   516		ns  string
   517	}{
   518		{
   519			xml: `<TableAttrs xmlns:f="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/"><TAttr ` +
   520				`h:table="hello" f:table="world" ` +
   521				`/></TableAttrs>`,
   522			tab: TableAttrs{TAttr{HTable: "hello", FTable: "world"}},
   523		},
   524		{
   525			xml: `<TableAttrs><TAttr xmlns:f="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/" ` +
   526				`h:table="hello" f:table="world" ` +
   527				`/></TableAttrs>`,
   528			tab: TableAttrs{TAttr{HTable: "hello", FTable: "world"}},
   529		},
   530		{
   531			xml: `<TableAttrs><TAttr ` +
   532				`h:table="hello" f:table="world" xmlns:f="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/" ` +
   533				`/></TableAttrs>`,
   534			tab: TableAttrs{TAttr{HTable: "hello", FTable: "world"}},
   535		},
   536		{
   537			// Default space does not apply to attribute names.
   538			xml: `<TableAttrs xmlns="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/"><TAttr ` +
   539				`h:table="hello" table="world" ` +
   540				`/></TableAttrs>`,
   541			tab: TableAttrs{TAttr{HTable: "hello", FTable: ""}},
   542		},
   543		{
   544			// Default space does not apply to attribute names.
   545			xml: `<TableAttrs xmlns:f="http://www.w3schools.com/furniture"><TAttr xmlns="http://www.w3.org/TR/html4/" ` +
   546				`table="hello" f:table="world" ` +
   547				`/></TableAttrs>`,
   548			tab: TableAttrs{TAttr{HTable: "", FTable: "world"}},
   549		},
   550		{
   551			xml: `<TableAttrs><TAttr ` +
   552				`table="bogus" ` +
   553				`/></TableAttrs>`,
   554			tab: TableAttrs{},
   555		},
   556		{
   557			// Default space does not apply to attribute names.
   558			xml: `<TableAttrs xmlns:h="http://www.w3.org/TR/html4/"><TAttr ` +
   559				`h:table="hello" table="world" ` +
   560				`/></TableAttrs>`,
   561			tab: TableAttrs{TAttr{HTable: "hello", FTable: ""}},
   562			ns:  "http://www.w3schools.com/furniture",
   563		},
   564		{
   565			// Default space does not apply to attribute names.
   566			xml: `<TableAttrs xmlns:f="http://www.w3schools.com/furniture"><TAttr ` +
   567				`table="hello" f:table="world" ` +
   568				`/></TableAttrs>`,
   569			tab: TableAttrs{TAttr{HTable: "", FTable: "world"}},
   570			ns:  "http://www.w3.org/TR/html4/",
   571		},
   572		{
   573			xml: `<TableAttrs><TAttr ` +
   574				`table="bogus" ` +
   575				`/></TableAttrs>`,
   576			tab: TableAttrs{},
   577			ns:  "something else entirely",
   578		},
   579	}
   580	
   581	func TestUnmarshalNSAttr(t *testing.T) {
   582		for i, tt := range tableAttrs {
   583			var dst TableAttrs
   584			var err error
   585			if tt.ns != "" {
   586				d := NewDecoder(strings.NewReader(tt.xml))
   587				d.DefaultSpace = tt.ns
   588				err = d.Decode(&dst)
   589			} else {
   590				err = Unmarshal([]byte(tt.xml), &dst)
   591			}
   592			if err != nil {
   593				t.Errorf("#%d: Unmarshal: %v", i, err)
   594				continue
   595			}
   596			want := tt.tab
   597			if dst != want {
   598				t.Errorf("#%d: dst=%+v, want %+v", i, dst, want)
   599			}
   600		}
   601	}
   602	
   603	func TestMarshalNSAttr(t *testing.T) {
   604		src := TableAttrs{TAttr{"hello", "world", "en_US", "other1", "other2", "other3", "other4"}}
   605		data, err := Marshal(&src)
   606		if err != nil {
   607			t.Fatalf("Marshal: %v", err)
   608		}
   609		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>`
   610		str := string(data)
   611		if str != want {
   612			t.Errorf("Marshal:\nhave: %#q\nwant: %#q\n", str, want)
   613		}
   614	
   615		var dst TableAttrs
   616		if err := Unmarshal(data, &dst); err != nil {
   617			t.Errorf("Unmarshal: %v", err)
   618		}
   619	
   620		if dst != src {
   621			t.Errorf("Unmarshal = %q, want %q", dst, src)
   622		}
   623	}

View as plain text