Run Format

Source file src/pkg/encoding/xml/marshal_test.go

     1	// Copyright 2011 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		"fmt"
    11		"io"
    12		"reflect"
    13		"strconv"
    14		"strings"
    15		"testing"
    16		"time"
    17	)
    18	
    19	type DriveType int
    20	
    21	const (
    22		HyperDrive DriveType = iota
    23		ImprobabilityDrive
    24	)
    25	
    26	type Passenger struct {
    27		Name   []string `xml:"name"`
    28		Weight float32  `xml:"weight"`
    29	}
    30	
    31	type Ship struct {
    32		XMLName struct{} `xml:"spaceship"`
    33	
    34		Name      string       `xml:"name,attr"`
    35		Pilot     string       `xml:"pilot,attr"`
    36		Drive     DriveType    `xml:"drive"`
    37		Age       uint         `xml:"age"`
    38		Passenger []*Passenger `xml:"passenger"`
    39		secret    string
    40	}
    41	
    42	type NamedType string
    43	
    44	type Port struct {
    45		XMLName struct{} `xml:"port"`
    46		Type    string   `xml:"type,attr,omitempty"`
    47		Comment string   `xml:",comment"`
    48		Number  string   `xml:",chardata"`
    49	}
    50	
    51	type Domain struct {
    52		XMLName struct{} `xml:"domain"`
    53		Country string   `xml:",attr,omitempty"`
    54		Name    []byte   `xml:",chardata"`
    55		Comment []byte   `xml:",comment"`
    56	}
    57	
    58	type Book struct {
    59		XMLName struct{} `xml:"book"`
    60		Title   string   `xml:",chardata"`
    61	}
    62	
    63	type Event struct {
    64		XMLName struct{} `xml:"event"`
    65		Year    int      `xml:",chardata"`
    66	}
    67	
    68	type Movie struct {
    69		XMLName struct{} `xml:"movie"`
    70		Length  uint     `xml:",chardata"`
    71	}
    72	
    73	type Pi struct {
    74		XMLName       struct{} `xml:"pi"`
    75		Approximation float32  `xml:",chardata"`
    76	}
    77	
    78	type Universe struct {
    79		XMLName struct{} `xml:"universe"`
    80		Visible float64  `xml:",chardata"`
    81	}
    82	
    83	type Particle struct {
    84		XMLName struct{} `xml:"particle"`
    85		HasMass bool     `xml:",chardata"`
    86	}
    87	
    88	type Departure struct {
    89		XMLName struct{}  `xml:"departure"`
    90		When    time.Time `xml:",chardata"`
    91	}
    92	
    93	type SecretAgent struct {
    94		XMLName   struct{} `xml:"agent"`
    95		Handle    string   `xml:"handle,attr"`
    96		Identity  string
    97		Obfuscate string `xml:",innerxml"`
    98	}
    99	
   100	type NestedItems struct {
   101		XMLName struct{} `xml:"result"`
   102		Items   []string `xml:">item"`
   103		Item1   []string `xml:"Items>item1"`
   104	}
   105	
   106	type NestedOrder struct {
   107		XMLName struct{} `xml:"result"`
   108		Field1  string   `xml:"parent>c"`
   109		Field2  string   `xml:"parent>b"`
   110		Field3  string   `xml:"parent>a"`
   111	}
   112	
   113	type MixedNested struct {
   114		XMLName struct{} `xml:"result"`
   115		A       string   `xml:"parent1>a"`
   116		B       string   `xml:"b"`
   117		C       string   `xml:"parent1>parent2>c"`
   118		D       string   `xml:"parent1>d"`
   119	}
   120	
   121	type NilTest struct {
   122		A interface{} `xml:"parent1>parent2>a"`
   123		B interface{} `xml:"parent1>b"`
   124		C interface{} `xml:"parent1>parent2>c"`
   125	}
   126	
   127	type Service struct {
   128		XMLName struct{} `xml:"service"`
   129		Domain  *Domain  `xml:"host>domain"`
   130		Port    *Port    `xml:"host>port"`
   131		Extra1  interface{}
   132		Extra2  interface{} `xml:"host>extra2"`
   133	}
   134	
   135	var nilStruct *Ship
   136	
   137	type EmbedA struct {
   138		EmbedC
   139		EmbedB EmbedB
   140		FieldA string
   141	}
   142	
   143	type EmbedB struct {
   144		FieldB string
   145		*EmbedC
   146	}
   147	
   148	type EmbedC struct {
   149		FieldA1 string `xml:"FieldA>A1"`
   150		FieldA2 string `xml:"FieldA>A2"`
   151		FieldB  string
   152		FieldC  string
   153	}
   154	
   155	type NameCasing struct {
   156		XMLName struct{} `xml:"casing"`
   157		Xy      string
   158		XY      string
   159		XyA     string `xml:"Xy,attr"`
   160		XYA     string `xml:"XY,attr"`
   161	}
   162	
   163	type NamePrecedence struct {
   164		XMLName     Name              `xml:"Parent"`
   165		FromTag     XMLNameWithoutTag `xml:"InTag"`
   166		FromNameVal XMLNameWithoutTag
   167		FromNameTag XMLNameWithTag
   168		InFieldName string
   169	}
   170	
   171	type XMLNameWithTag struct {
   172		XMLName Name   `xml:"InXMLNameTag"`
   173		Value   string `xml:",chardata"`
   174	}
   175	
   176	type XMLNameWithoutTag struct {
   177		XMLName Name
   178		Value   string `xml:",chardata"`
   179	}
   180	
   181	type NameInField struct {
   182		Foo Name `xml:"ns foo"`
   183	}
   184	
   185	type AttrTest struct {
   186		Int   int     `xml:",attr"`
   187		Named int     `xml:"int,attr"`
   188		Float float64 `xml:",attr"`
   189		Uint8 uint8   `xml:",attr"`
   190		Bool  bool    `xml:",attr"`
   191		Str   string  `xml:",attr"`
   192		Bytes []byte  `xml:",attr"`
   193	}
   194	
   195	type OmitAttrTest struct {
   196		Int   int     `xml:",attr,omitempty"`
   197		Named int     `xml:"int,attr,omitempty"`
   198		Float float64 `xml:",attr,omitempty"`
   199		Uint8 uint8   `xml:",attr,omitempty"`
   200		Bool  bool    `xml:",attr,omitempty"`
   201		Str   string  `xml:",attr,omitempty"`
   202		Bytes []byte  `xml:",attr,omitempty"`
   203	}
   204	
   205	type OmitFieldTest struct {
   206		Int   int           `xml:",omitempty"`
   207		Named int           `xml:"int,omitempty"`
   208		Float float64       `xml:",omitempty"`
   209		Uint8 uint8         `xml:",omitempty"`
   210		Bool  bool          `xml:",omitempty"`
   211		Str   string        `xml:",omitempty"`
   212		Bytes []byte        `xml:",omitempty"`
   213		Ptr   *PresenceTest `xml:",omitempty"`
   214	}
   215	
   216	type AnyTest struct {
   217		XMLName  struct{}  `xml:"a"`
   218		Nested   string    `xml:"nested>value"`
   219		AnyField AnyHolder `xml:",any"`
   220	}
   221	
   222	type AnyOmitTest struct {
   223		XMLName  struct{}   `xml:"a"`
   224		Nested   string     `xml:"nested>value"`
   225		AnyField *AnyHolder `xml:",any,omitempty"`
   226	}
   227	
   228	type AnySliceTest struct {
   229		XMLName  struct{}    `xml:"a"`
   230		Nested   string      `xml:"nested>value"`
   231		AnyField []AnyHolder `xml:",any"`
   232	}
   233	
   234	type AnyHolder struct {
   235		XMLName Name
   236		XML     string `xml:",innerxml"`
   237	}
   238	
   239	type RecurseA struct {
   240		A string
   241		B *RecurseB
   242	}
   243	
   244	type RecurseB struct {
   245		A *RecurseA
   246		B string
   247	}
   248	
   249	type PresenceTest struct {
   250		Exists *struct{}
   251	}
   252	
   253	type IgnoreTest struct {
   254		PublicSecret string `xml:"-"`
   255	}
   256	
   257	type MyBytes []byte
   258	
   259	type Data struct {
   260		Bytes  []byte
   261		Attr   []byte `xml:",attr"`
   262		Custom MyBytes
   263	}
   264	
   265	type Plain struct {
   266		V interface{}
   267	}
   268	
   269	type MyInt int
   270	
   271	type EmbedInt struct {
   272		MyInt
   273	}
   274	
   275	type Strings struct {
   276		X []string `xml:"A>B,omitempty"`
   277	}
   278	
   279	type PointerFieldsTest struct {
   280		XMLName  Name    `xml:"dummy"`
   281		Name     *string `xml:"name,attr"`
   282		Age      *uint   `xml:"age,attr"`
   283		Empty    *string `xml:"empty,attr"`
   284		Contents *string `xml:",chardata"`
   285	}
   286	
   287	type ChardataEmptyTest struct {
   288		XMLName  Name    `xml:"test"`
   289		Contents *string `xml:",chardata"`
   290	}
   291	
   292	type MyMarshalerTest struct {
   293	}
   294	
   295	var _ Marshaler = (*MyMarshalerTest)(nil)
   296	
   297	func (m *MyMarshalerTest) MarshalXML(e *Encoder, start StartElement) error {
   298		e.EncodeToken(start)
   299		e.EncodeToken(CharData([]byte("hello world")))
   300		e.EncodeToken(EndElement{start.Name})
   301		return nil
   302	}
   303	
   304	type MyMarshalerAttrTest struct {
   305	}
   306	
   307	var _ MarshalerAttr = (*MyMarshalerAttrTest)(nil)
   308	
   309	func (m *MyMarshalerAttrTest) MarshalXMLAttr(name Name) (Attr, error) {
   310		return Attr{name, "hello world"}, nil
   311	}
   312	
   313	type MarshalerStruct struct {
   314		Foo MyMarshalerAttrTest `xml:",attr"`
   315	}
   316	
   317	func ifaceptr(x interface{}) interface{} {
   318		return &x
   319	}
   320	
   321	var (
   322		nameAttr     = "Sarah"
   323		ageAttr      = uint(12)
   324		contentsAttr = "lorem ipsum"
   325	)
   326	
   327	// Unless explicitly stated as such (or *Plain), all of the
   328	// tests below are two-way tests. When introducing new tests,
   329	// please try to make them two-way as well to ensure that
   330	// marshalling and unmarshalling are as symmetrical as feasible.
   331	var marshalTests = []struct {
   332		Value         interface{}
   333		ExpectXML     string
   334		MarshalOnly   bool
   335		UnmarshalOnly bool
   336	}{
   337		// Test nil marshals to nothing
   338		{Value: nil, ExpectXML: ``, MarshalOnly: true},
   339		{Value: nilStruct, ExpectXML: ``, MarshalOnly: true},
   340	
   341		// Test value types
   342		{Value: &Plain{true}, ExpectXML: `<Plain><V>true</V></Plain>`},
   343		{Value: &Plain{false}, ExpectXML: `<Plain><V>false</V></Plain>`},
   344		{Value: &Plain{int(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
   345		{Value: &Plain{int8(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
   346		{Value: &Plain{int16(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
   347		{Value: &Plain{int32(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
   348		{Value: &Plain{uint(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
   349		{Value: &Plain{uint8(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
   350		{Value: &Plain{uint16(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
   351		{Value: &Plain{uint32(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
   352		{Value: &Plain{float32(1.25)}, ExpectXML: `<Plain><V>1.25</V></Plain>`},
   353		{Value: &Plain{float64(1.25)}, ExpectXML: `<Plain><V>1.25</V></Plain>`},
   354		{Value: &Plain{uintptr(0xFFDD)}, ExpectXML: `<Plain><V>65501</V></Plain>`},
   355		{Value: &Plain{"gopher"}, ExpectXML: `<Plain><V>gopher</V></Plain>`},
   356		{Value: &Plain{[]byte("gopher")}, ExpectXML: `<Plain><V>gopher</V></Plain>`},
   357		{Value: &Plain{"</>"}, ExpectXML: `<Plain><V>&lt;/&gt;</V></Plain>`},
   358		{Value: &Plain{[]byte("</>")}, ExpectXML: `<Plain><V>&lt;/&gt;</V></Plain>`},
   359		{Value: &Plain{[3]byte{'<', '/', '>'}}, ExpectXML: `<Plain><V>&lt;/&gt;</V></Plain>`},
   360		{Value: &Plain{NamedType("potato")}, ExpectXML: `<Plain><V>potato</V></Plain>`},
   361		{Value: &Plain{[]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3</V></Plain>`},
   362		{Value: &Plain{[3]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3</V></Plain>`},
   363		{Value: ifaceptr(true), MarshalOnly: true, ExpectXML: `<bool>true</bool>`},
   364	
   365		// Test time.
   366		{
   367			Value:     &Plain{time.Unix(1e9, 123456789).UTC()},
   368			ExpectXML: `<Plain><V>2001-09-09T01:46:40.123456789Z</V></Plain>`,
   369		},
   370	
   371		// A pointer to struct{} may be used to test for an element's presence.
   372		{
   373			Value:     &PresenceTest{new(struct{})},
   374			ExpectXML: `<PresenceTest><Exists></Exists></PresenceTest>`,
   375		},
   376		{
   377			Value:     &PresenceTest{},
   378			ExpectXML: `<PresenceTest></PresenceTest>`,
   379		},
   380	
   381		// A pointer to struct{} may be used to test for an element's presence.
   382		{
   383			Value:     &PresenceTest{new(struct{})},
   384			ExpectXML: `<PresenceTest><Exists></Exists></PresenceTest>`,
   385		},
   386		{
   387			Value:     &PresenceTest{},
   388			ExpectXML: `<PresenceTest></PresenceTest>`,
   389		},
   390	
   391		// A []byte field is only nil if the element was not found.
   392		{
   393			Value:         &Data{},
   394			ExpectXML:     `<Data></Data>`,
   395			UnmarshalOnly: true,
   396		},
   397		{
   398			Value:         &Data{Bytes: []byte{}, Custom: MyBytes{}, Attr: []byte{}},
   399			ExpectXML:     `<Data Attr=""><Bytes></Bytes><Custom></Custom></Data>`,
   400			UnmarshalOnly: true,
   401		},
   402	
   403		// Check that []byte works, including named []byte types.
   404		{
   405			Value:     &Data{Bytes: []byte("ab"), Custom: MyBytes("cd"), Attr: []byte{'v'}},
   406			ExpectXML: `<Data Attr="v"><Bytes>ab</Bytes><Custom>cd</Custom></Data>`,
   407		},
   408	
   409		// Test innerxml
   410		{
   411			Value: &SecretAgent{
   412				Handle:    "007",
   413				Identity:  "James Bond",
   414				Obfuscate: "<redacted/>",
   415			},
   416			ExpectXML:   `<agent handle="007"><Identity>James Bond</Identity><redacted/></agent>`,
   417			MarshalOnly: true,
   418		},
   419		{
   420			Value: &SecretAgent{
   421				Handle:    "007",
   422				Identity:  "James Bond",
   423				Obfuscate: "<Identity>James Bond</Identity><redacted/>",
   424			},
   425			ExpectXML:     `<agent handle="007"><Identity>James Bond</Identity><redacted/></agent>`,
   426			UnmarshalOnly: true,
   427		},
   428	
   429		// Test structs
   430		{Value: &Port{Type: "ssl", Number: "443"}, ExpectXML: `<port type="ssl">443</port>`},
   431		{Value: &Port{Number: "443"}, ExpectXML: `<port>443</port>`},
   432		{Value: &Port{Type: "<unix>"}, ExpectXML: `<port type="&lt;unix&gt;"></port>`},
   433		{Value: &Port{Number: "443", Comment: "https"}, ExpectXML: `<port><!--https-->443</port>`},
   434		{Value: &Port{Number: "443", Comment: "add space-"}, ExpectXML: `<port><!--add space- -->443</port>`, MarshalOnly: true},
   435		{Value: &Domain{Name: []byte("google.com&friends")}, ExpectXML: `<domain>google.com&amp;friends</domain>`},
   436		{Value: &Domain{Name: []byte("google.com"), Comment: []byte(" &friends ")}, ExpectXML: `<domain>google.com<!-- &friends --></domain>`},
   437		{Value: &Book{Title: "Pride & Prejudice"}, ExpectXML: `<book>Pride &amp; Prejudice</book>`},
   438		{Value: &Event{Year: -3114}, ExpectXML: `<event>-3114</event>`},
   439		{Value: &Movie{Length: 13440}, ExpectXML: `<movie>13440</movie>`},
   440		{Value: &Pi{Approximation: 3.14159265}, ExpectXML: `<pi>3.1415927</pi>`},
   441		{Value: &Universe{Visible: 9.3e13}, ExpectXML: `<universe>9.3e+13</universe>`},
   442		{Value: &Particle{HasMass: true}, ExpectXML: `<particle>true</particle>`},
   443		{Value: &Departure{When: ParseTime("2013-01-09T00:15:00-09:00")}, ExpectXML: `<departure>2013-01-09T00:15:00-09:00</departure>`},
   444		{Value: atomValue, ExpectXML: atomXml},
   445		{
   446			Value: &Ship{
   447				Name:  "Heart of Gold",
   448				Pilot: "Computer",
   449				Age:   1,
   450				Drive: ImprobabilityDrive,
   451				Passenger: []*Passenger{
   452					{
   453						Name:   []string{"Zaphod", "Beeblebrox"},
   454						Weight: 7.25,
   455					},
   456					{
   457						Name:   []string{"Trisha", "McMillen"},
   458						Weight: 5.5,
   459					},
   460					{
   461						Name:   []string{"Ford", "Prefect"},
   462						Weight: 7,
   463					},
   464					{
   465						Name:   []string{"Arthur", "Dent"},
   466						Weight: 6.75,
   467					},
   468				},
   469			},
   470			ExpectXML: `<spaceship name="Heart of Gold" pilot="Computer">` +
   471				`<drive>` + strconv.Itoa(int(ImprobabilityDrive)) + `</drive>` +
   472				`<age>1</age>` +
   473				`<passenger>` +
   474				`<name>Zaphod</name>` +
   475				`<name>Beeblebrox</name>` +
   476				`<weight>7.25</weight>` +
   477				`</passenger>` +
   478				`<passenger>` +
   479				`<name>Trisha</name>` +
   480				`<name>McMillen</name>` +
   481				`<weight>5.5</weight>` +
   482				`</passenger>` +
   483				`<passenger>` +
   484				`<name>Ford</name>` +
   485				`<name>Prefect</name>` +
   486				`<weight>7</weight>` +
   487				`</passenger>` +
   488				`<passenger>` +
   489				`<name>Arthur</name>` +
   490				`<name>Dent</name>` +
   491				`<weight>6.75</weight>` +
   492				`</passenger>` +
   493				`</spaceship>`,
   494		},
   495	
   496		// Test a>b
   497		{
   498			Value: &NestedItems{Items: nil, Item1: nil},
   499			ExpectXML: `<result>` +
   500				`<Items>` +
   501				`</Items>` +
   502				`</result>`,
   503		},
   504		{
   505			Value: &NestedItems{Items: []string{}, Item1: []string{}},
   506			ExpectXML: `<result>` +
   507				`<Items>` +
   508				`</Items>` +
   509				`</result>`,
   510			MarshalOnly: true,
   511		},
   512		{
   513			Value: &NestedItems{Items: nil, Item1: []string{"A"}},
   514			ExpectXML: `<result>` +
   515				`<Items>` +
   516				`<item1>A</item1>` +
   517				`</Items>` +
   518				`</result>`,
   519		},
   520		{
   521			Value: &NestedItems{Items: []string{"A", "B"}, Item1: nil},
   522			ExpectXML: `<result>` +
   523				`<Items>` +
   524				`<item>A</item>` +
   525				`<item>B</item>` +
   526				`</Items>` +
   527				`</result>`,
   528		},
   529		{
   530			Value: &NestedItems{Items: []string{"A", "B"}, Item1: []string{"C"}},
   531			ExpectXML: `<result>` +
   532				`<Items>` +
   533				`<item>A</item>` +
   534				`<item>B</item>` +
   535				`<item1>C</item1>` +
   536				`</Items>` +
   537				`</result>`,
   538		},
   539		{
   540			Value: &NestedOrder{Field1: "C", Field2: "B", Field3: "A"},
   541			ExpectXML: `<result>` +
   542				`<parent>` +
   543				`<c>C</c>` +
   544				`<b>B</b>` +
   545				`<a>A</a>` +
   546				`</parent>` +
   547				`</result>`,
   548		},
   549		{
   550			Value: &NilTest{A: "A", B: nil, C: "C"},
   551			ExpectXML: `<NilTest>` +
   552				`<parent1>` +
   553				`<parent2><a>A</a></parent2>` +
   554				`<parent2><c>C</c></parent2>` +
   555				`</parent1>` +
   556				`</NilTest>`,
   557			MarshalOnly: true, // Uses interface{}
   558		},
   559		{
   560			Value: &MixedNested{A: "A", B: "B", C: "C", D: "D"},
   561			ExpectXML: `<result>` +
   562				`<parent1><a>A</a></parent1>` +
   563				`<b>B</b>` +
   564				`<parent1>` +
   565				`<parent2><c>C</c></parent2>` +
   566				`<d>D</d>` +
   567				`</parent1>` +
   568				`</result>`,
   569		},
   570		{
   571			Value:     &Service{Port: &Port{Number: "80"}},
   572			ExpectXML: `<service><host><port>80</port></host></service>`,
   573		},
   574		{
   575			Value:     &Service{},
   576			ExpectXML: `<service></service>`,
   577		},
   578		{
   579			Value: &Service{Port: &Port{Number: "80"}, Extra1: "A", Extra2: "B"},
   580			ExpectXML: `<service>` +
   581				`<host><port>80</port></host>` +
   582				`<Extra1>A</Extra1>` +
   583				`<host><extra2>B</extra2></host>` +
   584				`</service>`,
   585			MarshalOnly: true,
   586		},
   587		{
   588			Value: &Service{Port: &Port{Number: "80"}, Extra2: "example"},
   589			ExpectXML: `<service>` +
   590				`<host><port>80</port></host>` +
   591				`<host><extra2>example</extra2></host>` +
   592				`</service>`,
   593			MarshalOnly: true,
   594		},
   595	
   596		// Test struct embedding
   597		{
   598			Value: &EmbedA{
   599				EmbedC: EmbedC{
   600					FieldA1: "", // Shadowed by A.A
   601					FieldA2: "", // Shadowed by A.A
   602					FieldB:  "A.C.B",
   603					FieldC:  "A.C.C",
   604				},
   605				EmbedB: EmbedB{
   606					FieldB: "A.B.B",
   607					EmbedC: &EmbedC{
   608						FieldA1: "A.B.C.A1",
   609						FieldA2: "A.B.C.A2",
   610						FieldB:  "", // Shadowed by A.B.B
   611						FieldC:  "A.B.C.C",
   612					},
   613				},
   614				FieldA: "A.A",
   615			},
   616			ExpectXML: `<EmbedA>` +
   617				`<FieldB>A.C.B</FieldB>` +
   618				`<FieldC>A.C.C</FieldC>` +
   619				`<EmbedB>` +
   620				`<FieldB>A.B.B</FieldB>` +
   621				`<FieldA>` +
   622				`<A1>A.B.C.A1</A1>` +
   623				`<A2>A.B.C.A2</A2>` +
   624				`</FieldA>` +
   625				`<FieldC>A.B.C.C</FieldC>` +
   626				`</EmbedB>` +
   627				`<FieldA>A.A</FieldA>` +
   628				`</EmbedA>`,
   629		},
   630	
   631		// Test that name casing matters
   632		{
   633			Value:     &NameCasing{Xy: "mixed", XY: "upper", XyA: "mixedA", XYA: "upperA"},
   634			ExpectXML: `<casing Xy="mixedA" XY="upperA"><Xy>mixed</Xy><XY>upper</XY></casing>`,
   635		},
   636	
   637		// Test the order in which the XML element name is chosen
   638		{
   639			Value: &NamePrecedence{
   640				FromTag:     XMLNameWithoutTag{Value: "A"},
   641				FromNameVal: XMLNameWithoutTag{XMLName: Name{Local: "InXMLName"}, Value: "B"},
   642				FromNameTag: XMLNameWithTag{Value: "C"},
   643				InFieldName: "D",
   644			},
   645			ExpectXML: `<Parent>` +
   646				`<InTag>A</InTag>` +
   647				`<InXMLName>B</InXMLName>` +
   648				`<InXMLNameTag>C</InXMLNameTag>` +
   649				`<InFieldName>D</InFieldName>` +
   650				`</Parent>`,
   651			MarshalOnly: true,
   652		},
   653		{
   654			Value: &NamePrecedence{
   655				XMLName:     Name{Local: "Parent"},
   656				FromTag:     XMLNameWithoutTag{XMLName: Name{Local: "InTag"}, Value: "A"},
   657				FromNameVal: XMLNameWithoutTag{XMLName: Name{Local: "FromNameVal"}, Value: "B"},
   658				FromNameTag: XMLNameWithTag{XMLName: Name{Local: "InXMLNameTag"}, Value: "C"},
   659				InFieldName: "D",
   660			},
   661			ExpectXML: `<Parent>` +
   662				`<InTag>A</InTag>` +
   663				`<FromNameVal>B</FromNameVal>` +
   664				`<InXMLNameTag>C</InXMLNameTag>` +
   665				`<InFieldName>D</InFieldName>` +
   666				`</Parent>`,
   667			UnmarshalOnly: true,
   668		},
   669	
   670		// xml.Name works in a plain field as well.
   671		{
   672			Value:     &NameInField{Name{Space: "ns", Local: "foo"}},
   673			ExpectXML: `<NameInField><foo xmlns="ns"></foo></NameInField>`,
   674		},
   675		{
   676			Value:         &NameInField{Name{Space: "ns", Local: "foo"}},
   677			ExpectXML:     `<NameInField><foo xmlns="ns"><ignore></ignore></foo></NameInField>`,
   678			UnmarshalOnly: true,
   679		},
   680	
   681		// Marshaling zero xml.Name uses the tag or field name.
   682		{
   683			Value:       &NameInField{},
   684			ExpectXML:   `<NameInField><foo xmlns="ns"></foo></NameInField>`,
   685			MarshalOnly: true,
   686		},
   687	
   688		// Test attributes
   689		{
   690			Value: &AttrTest{
   691				Int:   8,
   692				Named: 9,
   693				Float: 23.5,
   694				Uint8: 255,
   695				Bool:  true,
   696				Str:   "str",
   697				Bytes: []byte("byt"),
   698			},
   699			ExpectXML: `<AttrTest Int="8" int="9" Float="23.5" Uint8="255"` +
   700				` Bool="true" Str="str" Bytes="byt"></AttrTest>`,
   701		},
   702		{
   703			Value: &AttrTest{Bytes: []byte{}},
   704			ExpectXML: `<AttrTest Int="0" int="0" Float="0" Uint8="0"` +
   705				` Bool="false" Str="" Bytes=""></AttrTest>`,
   706		},
   707		{
   708			Value: &OmitAttrTest{
   709				Int:   8,
   710				Named: 9,
   711				Float: 23.5,
   712				Uint8: 255,
   713				Bool:  true,
   714				Str:   "str",
   715				Bytes: []byte("byt"),
   716			},
   717			ExpectXML: `<OmitAttrTest Int="8" int="9" Float="23.5" Uint8="255"` +
   718				` Bool="true" Str="str" Bytes="byt"></OmitAttrTest>`,
   719		},
   720		{
   721			Value:     &OmitAttrTest{},
   722			ExpectXML: `<OmitAttrTest></OmitAttrTest>`,
   723		},
   724	
   725		// pointer fields
   726		{
   727			Value:       &PointerFieldsTest{Name: &nameAttr, Age: &ageAttr, Contents: &contentsAttr},
   728			ExpectXML:   `<dummy name="Sarah" age="12">lorem ipsum</dummy>`,
   729			MarshalOnly: true,
   730		},
   731	
   732		// empty chardata pointer field
   733		{
   734			Value:       &ChardataEmptyTest{},
   735			ExpectXML:   `<test></test>`,
   736			MarshalOnly: true,
   737		},
   738	
   739		// omitempty on fields
   740		{
   741			Value: &OmitFieldTest{
   742				Int:   8,
   743				Named: 9,
   744				Float: 23.5,
   745				Uint8: 255,
   746				Bool:  true,
   747				Str:   "str",
   748				Bytes: []byte("byt"),
   749				Ptr:   &PresenceTest{},
   750			},
   751			ExpectXML: `<OmitFieldTest>` +
   752				`<Int>8</Int>` +
   753				`<int>9</int>` +
   754				`<Float>23.5</Float>` +
   755				`<Uint8>255</Uint8>` +
   756				`<Bool>true</Bool>` +
   757				`<Str>str</Str>` +
   758				`<Bytes>byt</Bytes>` +
   759				`<Ptr></Ptr>` +
   760				`</OmitFieldTest>`,
   761		},
   762		{
   763			Value:     &OmitFieldTest{},
   764			ExpectXML: `<OmitFieldTest></OmitFieldTest>`,
   765		},
   766	
   767		// Test ",any"
   768		{
   769			ExpectXML: `<a><nested><value>known</value></nested><other><sub>unknown</sub></other></a>`,
   770			Value: &AnyTest{
   771				Nested: "known",
   772				AnyField: AnyHolder{
   773					XMLName: Name{Local: "other"},
   774					XML:     "<sub>unknown</sub>",
   775				},
   776			},
   777		},
   778		{
   779			Value: &AnyTest{Nested: "known",
   780				AnyField: AnyHolder{
   781					XML:     "<unknown/>",
   782					XMLName: Name{Local: "AnyField"},
   783				},
   784			},
   785			ExpectXML: `<a><nested><value>known</value></nested><AnyField><unknown/></AnyField></a>`,
   786		},
   787		{
   788			ExpectXML: `<a><nested><value>b</value></nested></a>`,
   789			Value: &AnyOmitTest{
   790				Nested: "b",
   791			},
   792		},
   793		{
   794			ExpectXML: `<a><nested><value>b</value></nested><c><d>e</d></c><g xmlns="f"><h>i</h></g></a>`,
   795			Value: &AnySliceTest{
   796				Nested: "b",
   797				AnyField: []AnyHolder{
   798					{
   799						XMLName: Name{Local: "c"},
   800						XML:     "<d>e</d>",
   801					},
   802					{
   803						XMLName: Name{Space: "f", Local: "g"},
   804						XML:     "<h>i</h>",
   805					},
   806				},
   807			},
   808		},
   809		{
   810			ExpectXML: `<a><nested><value>b</value></nested></a>`,
   811			Value: &AnySliceTest{
   812				Nested: "b",
   813			},
   814		},
   815	
   816		// Test recursive types.
   817		{
   818			Value: &RecurseA{
   819				A: "a1",
   820				B: &RecurseB{
   821					A: &RecurseA{"a2", nil},
   822					B: "b1",
   823				},
   824			},
   825			ExpectXML: `<RecurseA><A>a1</A><B><A><A>a2</A></A><B>b1</B></B></RecurseA>`,
   826		},
   827	
   828		// Test ignoring fields via "-" tag
   829		{
   830			ExpectXML: `<IgnoreTest></IgnoreTest>`,
   831			Value:     &IgnoreTest{},
   832		},
   833		{
   834			ExpectXML:   `<IgnoreTest></IgnoreTest>`,
   835			Value:       &IgnoreTest{PublicSecret: "can't tell"},
   836			MarshalOnly: true,
   837		},
   838		{
   839			ExpectXML:     `<IgnoreTest><PublicSecret>ignore me</PublicSecret></IgnoreTest>`,
   840			Value:         &IgnoreTest{},
   841			UnmarshalOnly: true,
   842		},
   843	
   844		// Test escaping.
   845		{
   846			ExpectXML: `<a><nested><value>dquote: &#34;; squote: &#39;; ampersand: &amp;; less: &lt;; greater: &gt;;</value></nested><empty></empty></a>`,
   847			Value: &AnyTest{
   848				Nested:   `dquote: "; squote: '; ampersand: &; less: <; greater: >;`,
   849				AnyField: AnyHolder{XMLName: Name{Local: "empty"}},
   850			},
   851		},
   852		{
   853			ExpectXML: `<a><nested><value>newline: &#xA;; cr: &#xD;; tab: &#x9;;</value></nested><AnyField></AnyField></a>`,
   854			Value: &AnyTest{
   855				Nested:   "newline: \n; cr: \r; tab: \t;",
   856				AnyField: AnyHolder{XMLName: Name{Local: "AnyField"}},
   857			},
   858		},
   859		{
   860			ExpectXML: "<a><nested><value>1\r2\r\n3\n\r4\n5</value></nested></a>",
   861			Value: &AnyTest{
   862				Nested: "1\n2\n3\n\n4\n5",
   863			},
   864			UnmarshalOnly: true,
   865		},
   866		{
   867			ExpectXML: `<EmbedInt><MyInt>42</MyInt></EmbedInt>`,
   868			Value: &EmbedInt{
   869				MyInt: 42,
   870			},
   871		},
   872		// Test omitempty with parent chain; see golang.org/issue/4168.
   873		{
   874			ExpectXML: `<Strings><A></A></Strings>`,
   875			Value:     &Strings{},
   876		},
   877		// Custom marshalers.
   878		{
   879			ExpectXML: `<MyMarshalerTest>hello world</MyMarshalerTest>`,
   880			Value:     &MyMarshalerTest{},
   881		},
   882		{
   883			ExpectXML: `<MarshalerStruct Foo="hello world"></MarshalerStruct>`,
   884			Value:     &MarshalerStruct{},
   885		},
   886	}
   887	
   888	func TestMarshal(t *testing.T) {
   889		for idx, test := range marshalTests {
   890			if test.UnmarshalOnly {
   891				continue
   892			}
   893			data, err := Marshal(test.Value)
   894			if err != nil {
   895				t.Errorf("#%d: Error: %s", idx, err)
   896				continue
   897			}
   898			if got, want := string(data), test.ExpectXML; got != want {
   899				if strings.Contains(want, "\n") {
   900					t.Errorf("#%d: marshal(%#v):\nHAVE:\n%s\nWANT:\n%s", idx, test.Value, got, want)
   901				} else {
   902					t.Errorf("#%d: marshal(%#v):\nhave %#q\nwant %#q", idx, test.Value, got, want)
   903				}
   904			}
   905		}
   906	}
   907	
   908	type AttrParent struct {
   909		X string `xml:"X>Y,attr"`
   910	}
   911	
   912	type BadAttr struct {
   913		Name []string `xml:"name,attr"`
   914	}
   915	
   916	var marshalErrorTests = []struct {
   917		Value interface{}
   918		Err   string
   919		Kind  reflect.Kind
   920	}{
   921		{
   922			Value: make(chan bool),
   923			Err:   "xml: unsupported type: chan bool",
   924			Kind:  reflect.Chan,
   925		},
   926		{
   927			Value: map[string]string{
   928				"question": "What do you get when you multiply six by nine?",
   929				"answer":   "42",
   930			},
   931			Err:  "xml: unsupported type: map[string]string",
   932			Kind: reflect.Map,
   933		},
   934		{
   935			Value: map[*Ship]bool{nil: false},
   936			Err:   "xml: unsupported type: map[*xml.Ship]bool",
   937			Kind:  reflect.Map,
   938		},
   939		{
   940			Value: &Domain{Comment: []byte("f--bar")},
   941			Err:   `xml: comments must not contain "--"`,
   942		},
   943		// Reject parent chain with attr, never worked; see golang.org/issue/5033.
   944		{
   945			Value: &AttrParent{},
   946			Err:   `xml: X>Y chain not valid with attr flag`,
   947		},
   948		{
   949			Value: BadAttr{[]string{"X", "Y"}},
   950			Err:   `xml: unsupported type: []string`,
   951		},
   952	}
   953	
   954	var marshalIndentTests = []struct {
   955		Value     interface{}
   956		Prefix    string
   957		Indent    string
   958		ExpectXML string
   959	}{
   960		{
   961			Value: &SecretAgent{
   962				Handle:    "007",
   963				Identity:  "James Bond",
   964				Obfuscate: "<redacted/>",
   965			},
   966			Prefix:    "",
   967			Indent:    "\t",
   968			ExpectXML: fmt.Sprintf("<agent handle=\"007\">\n\t<Identity>James Bond</Identity><redacted/>\n</agent>"),
   969		},
   970	}
   971	
   972	func TestMarshalErrors(t *testing.T) {
   973		for idx, test := range marshalErrorTests {
   974			data, err := Marshal(test.Value)
   975			if err == nil {
   976				t.Errorf("#%d: marshal(%#v) = [success] %q, want error %v", idx, test.Value, data, test.Err)
   977				continue
   978			}
   979			if err.Error() != test.Err {
   980				t.Errorf("#%d: marshal(%#v) = [error] %v, want %v", idx, test.Value, err, test.Err)
   981			}
   982			if test.Kind != reflect.Invalid {
   983				if kind := err.(*UnsupportedTypeError).Type.Kind(); kind != test.Kind {
   984					t.Errorf("#%d: marshal(%#v) = [error kind] %s, want %s", idx, test.Value, kind, test.Kind)
   985				}
   986			}
   987		}
   988	}
   989	
   990	// Do invertibility testing on the various structures that we test
   991	func TestUnmarshal(t *testing.T) {
   992		for i, test := range marshalTests {
   993			if test.MarshalOnly {
   994				continue
   995			}
   996			if _, ok := test.Value.(*Plain); ok {
   997				continue
   998			}
   999	
  1000			vt := reflect.TypeOf(test.Value)
  1001			dest := reflect.New(vt.Elem()).Interface()
  1002			err := Unmarshal([]byte(test.ExpectXML), dest)
  1003	
  1004			switch fix := dest.(type) {
  1005			case *Feed:
  1006				fix.Author.InnerXML = ""
  1007				for i := range fix.Entry {
  1008					fix.Entry[i].Author.InnerXML = ""
  1009				}
  1010			}
  1011	
  1012			if err != nil {
  1013				t.Errorf("#%d: unexpected error: %#v", i, err)
  1014			} else if got, want := dest, test.Value; !reflect.DeepEqual(got, want) {
  1015				t.Errorf("#%d: unmarshal(%q):\nhave %#v\nwant %#v", i, test.ExpectXML, got, want)
  1016			}
  1017		}
  1018	}
  1019	
  1020	func TestMarshalIndent(t *testing.T) {
  1021		for i, test := range marshalIndentTests {
  1022			data, err := MarshalIndent(test.Value, test.Prefix, test.Indent)
  1023			if err != nil {
  1024				t.Errorf("#%d: Error: %s", i, err)
  1025				continue
  1026			}
  1027			if got, want := string(data), test.ExpectXML; got != want {
  1028				t.Errorf("#%d: MarshalIndent:\nGot:%s\nWant:\n%s", i, got, want)
  1029			}
  1030		}
  1031	}
  1032	
  1033	type limitedBytesWriter struct {
  1034		w      io.Writer
  1035		remain int // until writes fail
  1036	}
  1037	
  1038	func (lw *limitedBytesWriter) Write(p []byte) (n int, err error) {
  1039		if lw.remain <= 0 {
  1040			println("error")
  1041			return 0, errors.New("write limit hit")
  1042		}
  1043		if len(p) > lw.remain {
  1044			p = p[:lw.remain]
  1045			n, _ = lw.w.Write(p)
  1046			lw.remain = 0
  1047			return n, errors.New("write limit hit")
  1048		}
  1049		n, err = lw.w.Write(p)
  1050		lw.remain -= n
  1051		return n, err
  1052	}
  1053	
  1054	func TestMarshalWriteErrors(t *testing.T) {
  1055		var buf bytes.Buffer
  1056		const writeCap = 1024
  1057		w := &limitedBytesWriter{&buf, writeCap}
  1058		enc := NewEncoder(w)
  1059		var err error
  1060		var i int
  1061		const n = 4000
  1062		for i = 1; i <= n; i++ {
  1063			err = enc.Encode(&Passenger{
  1064				Name:   []string{"Alice", "Bob"},
  1065				Weight: 5,
  1066			})
  1067			if err != nil {
  1068				break
  1069			}
  1070		}
  1071		if err == nil {
  1072			t.Error("expected an error")
  1073		}
  1074		if i == n {
  1075			t.Errorf("expected to fail before the end")
  1076		}
  1077		if buf.Len() != writeCap {
  1078			t.Errorf("buf.Len() = %d; want %d", buf.Len(), writeCap)
  1079		}
  1080	}
  1081	
  1082	func TestMarshalWriteIOErrors(t *testing.T) {
  1083		enc := NewEncoder(errWriter{})
  1084	
  1085		expectErr := "unwritable"
  1086		err := enc.Encode(&Passenger{})
  1087		if err == nil || err.Error() != expectErr {
  1088			t.Errorf("EscapeTest = [error] %v, want %v", err, expectErr)
  1089		}
  1090	}
  1091	
  1092	func TestMarshalFlush(t *testing.T) {
  1093		var buf bytes.Buffer
  1094		enc := NewEncoder(&buf)
  1095		if err := enc.EncodeToken(CharData("hello world")); err != nil {
  1096			t.Fatalf("enc.EncodeToken: %v", err)
  1097		}
  1098		if buf.Len() > 0 {
  1099			t.Fatalf("enc.EncodeToken caused actual write: %q", buf.Bytes())
  1100		}
  1101		if err := enc.Flush(); err != nil {
  1102			t.Fatalf("enc.Flush: %v", err)
  1103		}
  1104		if buf.String() != "hello world" {
  1105			t.Fatalf("after enc.Flush, buf.String() = %q, want %q", buf.String(), "hello world")
  1106		}
  1107	}
  1108	
  1109	func BenchmarkMarshal(b *testing.B) {
  1110		for i := 0; i < b.N; i++ {
  1111			Marshal(atomValue)
  1112		}
  1113	}
  1114	
  1115	func BenchmarkUnmarshal(b *testing.B) {
  1116		xml := []byte(atomXml)
  1117		for i := 0; i < b.N; i++ {
  1118			Unmarshal(xml, &Feed{})
  1119		}
  1120	}
  1121	
  1122	// golang.org/issue/6556
  1123	func TestStructPointerMarshal(t *testing.T) {
  1124		type A struct {
  1125			XMLName string `xml:"a"`
  1126			B       []interface{}
  1127		}
  1128		type C struct {
  1129			XMLName Name
  1130			Value   string `xml:"value"`
  1131		}
  1132	
  1133		a := new(A)
  1134		a.B = append(a.B, &C{
  1135			XMLName: Name{Local: "c"},
  1136			Value:   "x",
  1137		})
  1138	
  1139		b, err := Marshal(a)
  1140		if err != nil {
  1141			t.Fatal(err)
  1142		}
  1143		if x := string(b); x != "<a><c><value>x</value></c></a>" {
  1144			t.Fatal(x)
  1145		}
  1146		var v A
  1147		err = Unmarshal(b, &v)
  1148		if err != nil {
  1149			t.Fatal(err)
  1150		}
  1151	}

View as plain text