github.com/FenixAra/go@v0.0.0-20170127160404-96ea0918e670/src/encoding/xml/marshal_test.go (about) 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 "sync" 16 "testing" 17 "time" 18 ) 19 20 type DriveType int 21 22 const ( 23 HyperDrive DriveType = iota 24 ImprobabilityDrive 25 ) 26 27 type Passenger struct { 28 Name []string `xml:"name"` 29 Weight float32 `xml:"weight"` 30 } 31 32 type Ship struct { 33 XMLName struct{} `xml:"spaceship"` 34 35 Name string `xml:"name,attr"` 36 Pilot string `xml:"pilot,attr"` 37 Drive DriveType `xml:"drive"` 38 Age uint `xml:"age"` 39 Passenger []*Passenger `xml:"passenger"` 40 secret string 41 } 42 43 type NamedType string 44 45 type Port struct { 46 XMLName struct{} `xml:"port"` 47 Type string `xml:"type,attr,omitempty"` 48 Comment string `xml:",comment"` 49 Number string `xml:",chardata"` 50 } 51 52 type Domain struct { 53 XMLName struct{} `xml:"domain"` 54 Country string `xml:",attr,omitempty"` 55 Name []byte `xml:",chardata"` 56 Comment []byte `xml:",comment"` 57 } 58 59 type Book struct { 60 XMLName struct{} `xml:"book"` 61 Title string `xml:",chardata"` 62 } 63 64 type Event struct { 65 XMLName struct{} `xml:"event"` 66 Year int `xml:",chardata"` 67 } 68 69 type Movie struct { 70 XMLName struct{} `xml:"movie"` 71 Length uint `xml:",chardata"` 72 } 73 74 type Pi struct { 75 XMLName struct{} `xml:"pi"` 76 Approximation float32 `xml:",chardata"` 77 } 78 79 type Universe struct { 80 XMLName struct{} `xml:"universe"` 81 Visible float64 `xml:",chardata"` 82 } 83 84 type Particle struct { 85 XMLName struct{} `xml:"particle"` 86 HasMass bool `xml:",chardata"` 87 } 88 89 type Departure struct { 90 XMLName struct{} `xml:"departure"` 91 When time.Time `xml:",chardata"` 92 } 93 94 type SecretAgent struct { 95 XMLName struct{} `xml:"agent"` 96 Handle string `xml:"handle,attr"` 97 Identity string 98 Obfuscate string `xml:",innerxml"` 99 } 100 101 type NestedItems struct { 102 XMLName struct{} `xml:"result"` 103 Items []string `xml:">item"` 104 Item1 []string `xml:"Items>item1"` 105 } 106 107 type NestedOrder struct { 108 XMLName struct{} `xml:"result"` 109 Field1 string `xml:"parent>c"` 110 Field2 string `xml:"parent>b"` 111 Field3 string `xml:"parent>a"` 112 } 113 114 type MixedNested struct { 115 XMLName struct{} `xml:"result"` 116 A string `xml:"parent1>a"` 117 B string `xml:"b"` 118 C string `xml:"parent1>parent2>c"` 119 D string `xml:"parent1>d"` 120 } 121 122 type NilTest struct { 123 A interface{} `xml:"parent1>parent2>a"` 124 B interface{} `xml:"parent1>b"` 125 C interface{} `xml:"parent1>parent2>c"` 126 } 127 128 type Service struct { 129 XMLName struct{} `xml:"service"` 130 Domain *Domain `xml:"host>domain"` 131 Port *Port `xml:"host>port"` 132 Extra1 interface{} 133 Extra2 interface{} `xml:"host>extra2"` 134 } 135 136 var nilStruct *Ship 137 138 type EmbedA struct { 139 EmbedC 140 EmbedB EmbedB 141 FieldA string 142 embedD 143 } 144 145 type EmbedB struct { 146 FieldB string 147 *EmbedC 148 } 149 150 type EmbedC struct { 151 FieldA1 string `xml:"FieldA>A1"` 152 FieldA2 string `xml:"FieldA>A2"` 153 FieldB string 154 FieldC string 155 } 156 157 type embedD struct { 158 fieldD string 159 FieldE string // Promoted and visible when embedD is embedded. 160 } 161 162 type NameCasing struct { 163 XMLName struct{} `xml:"casing"` 164 Xy string 165 XY string 166 XyA string `xml:"Xy,attr"` 167 XYA string `xml:"XY,attr"` 168 } 169 170 type NamePrecedence struct { 171 XMLName Name `xml:"Parent"` 172 FromTag XMLNameWithoutTag `xml:"InTag"` 173 FromNameVal XMLNameWithoutTag 174 FromNameTag XMLNameWithTag 175 InFieldName string 176 } 177 178 type XMLNameWithTag struct { 179 XMLName Name `xml:"InXMLNameTag"` 180 Value string `xml:",chardata"` 181 } 182 183 type XMLNameWithoutTag struct { 184 XMLName Name 185 Value string `xml:",chardata"` 186 } 187 188 type NameInField struct { 189 Foo Name `xml:"ns foo"` 190 } 191 192 type AttrTest struct { 193 Int int `xml:",attr"` 194 Named int `xml:"int,attr"` 195 Float float64 `xml:",attr"` 196 Uint8 uint8 `xml:",attr"` 197 Bool bool `xml:",attr"` 198 Str string `xml:",attr"` 199 Bytes []byte `xml:",attr"` 200 } 201 202 type AttrsTest struct { 203 Attrs []Attr `xml:",any,attr"` 204 Int int `xml:",attr"` 205 Named int `xml:"int,attr"` 206 Float float64 `xml:",attr"` 207 Uint8 uint8 `xml:",attr"` 208 Bool bool `xml:",attr"` 209 Str string `xml:",attr"` 210 Bytes []byte `xml:",attr"` 211 } 212 213 type OmitAttrTest struct { 214 Int int `xml:",attr,omitempty"` 215 Named int `xml:"int,attr,omitempty"` 216 Float float64 `xml:",attr,omitempty"` 217 Uint8 uint8 `xml:",attr,omitempty"` 218 Bool bool `xml:",attr,omitempty"` 219 Str string `xml:",attr,omitempty"` 220 Bytes []byte `xml:",attr,omitempty"` 221 PStr *string `xml:",attr,omitempty"` 222 } 223 224 type OmitFieldTest struct { 225 Int int `xml:",omitempty"` 226 Named int `xml:"int,omitempty"` 227 Float float64 `xml:",omitempty"` 228 Uint8 uint8 `xml:",omitempty"` 229 Bool bool `xml:",omitempty"` 230 Str string `xml:",omitempty"` 231 Bytes []byte `xml:",omitempty"` 232 PStr *string `xml:",omitempty"` 233 Ptr *PresenceTest `xml:",omitempty"` 234 } 235 236 type AnyTest struct { 237 XMLName struct{} `xml:"a"` 238 Nested string `xml:"nested>value"` 239 AnyField AnyHolder `xml:",any"` 240 } 241 242 type AnyOmitTest struct { 243 XMLName struct{} `xml:"a"` 244 Nested string `xml:"nested>value"` 245 AnyField *AnyHolder `xml:",any,omitempty"` 246 } 247 248 type AnySliceTest struct { 249 XMLName struct{} `xml:"a"` 250 Nested string `xml:"nested>value"` 251 AnyField []AnyHolder `xml:",any"` 252 } 253 254 type AnyHolder struct { 255 XMLName Name 256 XML string `xml:",innerxml"` 257 } 258 259 type RecurseA struct { 260 A string 261 B *RecurseB 262 } 263 264 type RecurseB struct { 265 A *RecurseA 266 B string 267 } 268 269 type PresenceTest struct { 270 Exists *struct{} 271 } 272 273 type IgnoreTest struct { 274 PublicSecret string `xml:"-"` 275 } 276 277 type MyBytes []byte 278 279 type Data struct { 280 Bytes []byte 281 Attr []byte `xml:",attr"` 282 Custom MyBytes 283 } 284 285 type Plain struct { 286 V interface{} 287 } 288 289 type MyInt int 290 291 type EmbedInt struct { 292 MyInt 293 } 294 295 type Strings struct { 296 X []string `xml:"A>B,omitempty"` 297 } 298 299 type PointerFieldsTest struct { 300 XMLName Name `xml:"dummy"` 301 Name *string `xml:"name,attr"` 302 Age *uint `xml:"age,attr"` 303 Empty *string `xml:"empty,attr"` 304 Contents *string `xml:",chardata"` 305 } 306 307 type ChardataEmptyTest struct { 308 XMLName Name `xml:"test"` 309 Contents *string `xml:",chardata"` 310 } 311 312 type MyMarshalerTest struct { 313 } 314 315 var _ Marshaler = (*MyMarshalerTest)(nil) 316 317 func (m *MyMarshalerTest) MarshalXML(e *Encoder, start StartElement) error { 318 e.EncodeToken(start) 319 e.EncodeToken(CharData([]byte("hello world"))) 320 e.EncodeToken(EndElement{start.Name}) 321 return nil 322 } 323 324 type MyMarshalerAttrTest struct { 325 } 326 327 var _ MarshalerAttr = (*MyMarshalerAttrTest)(nil) 328 329 func (m *MyMarshalerAttrTest) MarshalXMLAttr(name Name) (Attr, error) { 330 return Attr{name, "hello world"}, nil 331 } 332 333 func (m *MyMarshalerAttrTest) UnmarshalXMLAttr(attr Attr) error { 334 return nil 335 } 336 337 type MarshalerStruct struct { 338 Foo MyMarshalerAttrTest `xml:",attr"` 339 } 340 341 type InnerStruct struct { 342 XMLName Name `xml:"testns outer"` 343 } 344 345 type OuterStruct struct { 346 InnerStruct 347 IntAttr int `xml:"int,attr"` 348 } 349 350 type OuterNamedStruct struct { 351 InnerStruct 352 XMLName Name `xml:"outerns test"` 353 IntAttr int `xml:"int,attr"` 354 } 355 356 type OuterNamedOrderedStruct struct { 357 XMLName Name `xml:"outerns test"` 358 InnerStruct 359 IntAttr int `xml:"int,attr"` 360 } 361 362 type OuterOuterStruct struct { 363 OuterStruct 364 } 365 366 type NestedAndChardata struct { 367 AB []string `xml:"A>B"` 368 Chardata string `xml:",chardata"` 369 } 370 371 type NestedAndComment struct { 372 AB []string `xml:"A>B"` 373 Comment string `xml:",comment"` 374 } 375 376 type CDataTest struct { 377 Chardata string `xml:",cdata"` 378 } 379 380 type NestedAndCData struct { 381 AB []string `xml:"A>B"` 382 CDATA string `xml:",cdata"` 383 } 384 385 func ifaceptr(x interface{}) interface{} { 386 return &x 387 } 388 389 var ( 390 nameAttr = "Sarah" 391 ageAttr = uint(12) 392 contentsAttr = "lorem ipsum" 393 empty = "" 394 ) 395 396 // Unless explicitly stated as such (or *Plain), all of the 397 // tests below are two-way tests. When introducing new tests, 398 // please try to make them two-way as well to ensure that 399 // marshaling and unmarshaling are as symmetrical as feasible. 400 var marshalTests = []struct { 401 Value interface{} 402 ExpectXML string 403 MarshalOnly bool 404 UnmarshalOnly bool 405 }{ 406 // Test nil marshals to nothing 407 {Value: nil, ExpectXML: ``, MarshalOnly: true}, 408 {Value: nilStruct, ExpectXML: ``, MarshalOnly: true}, 409 410 // Test value types 411 {Value: &Plain{true}, ExpectXML: `<Plain><V>true</V></Plain>`}, 412 {Value: &Plain{false}, ExpectXML: `<Plain><V>false</V></Plain>`}, 413 {Value: &Plain{int(42)}, ExpectXML: `<Plain><V>42</V></Plain>`}, 414 {Value: &Plain{int8(42)}, ExpectXML: `<Plain><V>42</V></Plain>`}, 415 {Value: &Plain{int16(42)}, ExpectXML: `<Plain><V>42</V></Plain>`}, 416 {Value: &Plain{int32(42)}, ExpectXML: `<Plain><V>42</V></Plain>`}, 417 {Value: &Plain{uint(42)}, ExpectXML: `<Plain><V>42</V></Plain>`}, 418 {Value: &Plain{uint8(42)}, ExpectXML: `<Plain><V>42</V></Plain>`}, 419 {Value: &Plain{uint16(42)}, ExpectXML: `<Plain><V>42</V></Plain>`}, 420 {Value: &Plain{uint32(42)}, ExpectXML: `<Plain><V>42</V></Plain>`}, 421 {Value: &Plain{float32(1.25)}, ExpectXML: `<Plain><V>1.25</V></Plain>`}, 422 {Value: &Plain{float64(1.25)}, ExpectXML: `<Plain><V>1.25</V></Plain>`}, 423 {Value: &Plain{uintptr(0xFFDD)}, ExpectXML: `<Plain><V>65501</V></Plain>`}, 424 {Value: &Plain{"gopher"}, ExpectXML: `<Plain><V>gopher</V></Plain>`}, 425 {Value: &Plain{[]byte("gopher")}, ExpectXML: `<Plain><V>gopher</V></Plain>`}, 426 {Value: &Plain{"</>"}, ExpectXML: `<Plain><V></></V></Plain>`}, 427 {Value: &Plain{[]byte("</>")}, ExpectXML: `<Plain><V></></V></Plain>`}, 428 {Value: &Plain{[3]byte{'<', '/', '>'}}, ExpectXML: `<Plain><V></></V></Plain>`}, 429 {Value: &Plain{NamedType("potato")}, ExpectXML: `<Plain><V>potato</V></Plain>`}, 430 {Value: &Plain{[]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3</V></Plain>`}, 431 {Value: &Plain{[3]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3</V></Plain>`}, 432 {Value: ifaceptr(true), MarshalOnly: true, ExpectXML: `<bool>true</bool>`}, 433 434 // Test time. 435 { 436 Value: &Plain{time.Unix(1e9, 123456789).UTC()}, 437 ExpectXML: `<Plain><V>2001-09-09T01:46:40.123456789Z</V></Plain>`, 438 }, 439 440 // A pointer to struct{} may be used to test for an element's presence. 441 { 442 Value: &PresenceTest{new(struct{})}, 443 ExpectXML: `<PresenceTest><Exists></Exists></PresenceTest>`, 444 }, 445 { 446 Value: &PresenceTest{}, 447 ExpectXML: `<PresenceTest></PresenceTest>`, 448 }, 449 450 // A pointer to struct{} may be used to test for an element's presence. 451 { 452 Value: &PresenceTest{new(struct{})}, 453 ExpectXML: `<PresenceTest><Exists></Exists></PresenceTest>`, 454 }, 455 { 456 Value: &PresenceTest{}, 457 ExpectXML: `<PresenceTest></PresenceTest>`, 458 }, 459 460 // A []byte field is only nil if the element was not found. 461 { 462 Value: &Data{}, 463 ExpectXML: `<Data></Data>`, 464 UnmarshalOnly: true, 465 }, 466 { 467 Value: &Data{Bytes: []byte{}, Custom: MyBytes{}, Attr: []byte{}}, 468 ExpectXML: `<Data Attr=""><Bytes></Bytes><Custom></Custom></Data>`, 469 UnmarshalOnly: true, 470 }, 471 472 // Check that []byte works, including named []byte types. 473 { 474 Value: &Data{Bytes: []byte("ab"), Custom: MyBytes("cd"), Attr: []byte{'v'}}, 475 ExpectXML: `<Data Attr="v"><Bytes>ab</Bytes><Custom>cd</Custom></Data>`, 476 }, 477 478 // Test innerxml 479 { 480 Value: &SecretAgent{ 481 Handle: "007", 482 Identity: "James Bond", 483 Obfuscate: "<redacted/>", 484 }, 485 ExpectXML: `<agent handle="007"><Identity>James Bond</Identity><redacted/></agent>`, 486 MarshalOnly: true, 487 }, 488 { 489 Value: &SecretAgent{ 490 Handle: "007", 491 Identity: "James Bond", 492 Obfuscate: "<Identity>James Bond</Identity><redacted/>", 493 }, 494 ExpectXML: `<agent handle="007"><Identity>James Bond</Identity><redacted/></agent>`, 495 UnmarshalOnly: true, 496 }, 497 498 // Test structs 499 {Value: &Port{Type: "ssl", Number: "443"}, ExpectXML: `<port type="ssl">443</port>`}, 500 {Value: &Port{Number: "443"}, ExpectXML: `<port>443</port>`}, 501 {Value: &Port{Type: "<unix>"}, ExpectXML: `<port type="<unix>"></port>`}, 502 {Value: &Port{Number: "443", Comment: "https"}, ExpectXML: `<port><!--https-->443</port>`}, 503 {Value: &Port{Number: "443", Comment: "add space-"}, ExpectXML: `<port><!--add space- -->443</port>`, MarshalOnly: true}, 504 {Value: &Domain{Name: []byte("google.com&friends")}, ExpectXML: `<domain>google.com&friends</domain>`}, 505 {Value: &Domain{Name: []byte("google.com"), Comment: []byte(" &friends ")}, ExpectXML: `<domain>google.com<!-- &friends --></domain>`}, 506 {Value: &Book{Title: "Pride & Prejudice"}, ExpectXML: `<book>Pride & Prejudice</book>`}, 507 {Value: &Event{Year: -3114}, ExpectXML: `<event>-3114</event>`}, 508 {Value: &Movie{Length: 13440}, ExpectXML: `<movie>13440</movie>`}, 509 {Value: &Pi{Approximation: 3.14159265}, ExpectXML: `<pi>3.1415927</pi>`}, 510 {Value: &Universe{Visible: 9.3e13}, ExpectXML: `<universe>9.3e+13</universe>`}, 511 {Value: &Particle{HasMass: true}, ExpectXML: `<particle>true</particle>`}, 512 {Value: &Departure{When: ParseTime("2013-01-09T00:15:00-09:00")}, ExpectXML: `<departure>2013-01-09T00:15:00-09:00</departure>`}, 513 {Value: atomValue, ExpectXML: atomXml}, 514 { 515 Value: &Ship{ 516 Name: "Heart of Gold", 517 Pilot: "Computer", 518 Age: 1, 519 Drive: ImprobabilityDrive, 520 Passenger: []*Passenger{ 521 { 522 Name: []string{"Zaphod", "Beeblebrox"}, 523 Weight: 7.25, 524 }, 525 { 526 Name: []string{"Trisha", "McMillen"}, 527 Weight: 5.5, 528 }, 529 { 530 Name: []string{"Ford", "Prefect"}, 531 Weight: 7, 532 }, 533 { 534 Name: []string{"Arthur", "Dent"}, 535 Weight: 6.75, 536 }, 537 }, 538 }, 539 ExpectXML: `<spaceship name="Heart of Gold" pilot="Computer">` + 540 `<drive>` + strconv.Itoa(int(ImprobabilityDrive)) + `</drive>` + 541 `<age>1</age>` + 542 `<passenger>` + 543 `<name>Zaphod</name>` + 544 `<name>Beeblebrox</name>` + 545 `<weight>7.25</weight>` + 546 `</passenger>` + 547 `<passenger>` + 548 `<name>Trisha</name>` + 549 `<name>McMillen</name>` + 550 `<weight>5.5</weight>` + 551 `</passenger>` + 552 `<passenger>` + 553 `<name>Ford</name>` + 554 `<name>Prefect</name>` + 555 `<weight>7</weight>` + 556 `</passenger>` + 557 `<passenger>` + 558 `<name>Arthur</name>` + 559 `<name>Dent</name>` + 560 `<weight>6.75</weight>` + 561 `</passenger>` + 562 `</spaceship>`, 563 }, 564 565 // Test a>b 566 { 567 Value: &NestedItems{Items: nil, Item1: nil}, 568 ExpectXML: `<result>` + 569 `<Items>` + 570 `</Items>` + 571 `</result>`, 572 }, 573 { 574 Value: &NestedItems{Items: []string{}, Item1: []string{}}, 575 ExpectXML: `<result>` + 576 `<Items>` + 577 `</Items>` + 578 `</result>`, 579 MarshalOnly: true, 580 }, 581 { 582 Value: &NestedItems{Items: nil, Item1: []string{"A"}}, 583 ExpectXML: `<result>` + 584 `<Items>` + 585 `<item1>A</item1>` + 586 `</Items>` + 587 `</result>`, 588 }, 589 { 590 Value: &NestedItems{Items: []string{"A", "B"}, Item1: nil}, 591 ExpectXML: `<result>` + 592 `<Items>` + 593 `<item>A</item>` + 594 `<item>B</item>` + 595 `</Items>` + 596 `</result>`, 597 }, 598 { 599 Value: &NestedItems{Items: []string{"A", "B"}, Item1: []string{"C"}}, 600 ExpectXML: `<result>` + 601 `<Items>` + 602 `<item>A</item>` + 603 `<item>B</item>` + 604 `<item1>C</item1>` + 605 `</Items>` + 606 `</result>`, 607 }, 608 { 609 Value: &NestedOrder{Field1: "C", Field2: "B", Field3: "A"}, 610 ExpectXML: `<result>` + 611 `<parent>` + 612 `<c>C</c>` + 613 `<b>B</b>` + 614 `<a>A</a>` + 615 `</parent>` + 616 `</result>`, 617 }, 618 { 619 Value: &NilTest{A: "A", B: nil, C: "C"}, 620 ExpectXML: `<NilTest>` + 621 `<parent1>` + 622 `<parent2><a>A</a></parent2>` + 623 `<parent2><c>C</c></parent2>` + 624 `</parent1>` + 625 `</NilTest>`, 626 MarshalOnly: true, // Uses interface{} 627 }, 628 { 629 Value: &MixedNested{A: "A", B: "B", C: "C", D: "D"}, 630 ExpectXML: `<result>` + 631 `<parent1><a>A</a></parent1>` + 632 `<b>B</b>` + 633 `<parent1>` + 634 `<parent2><c>C</c></parent2>` + 635 `<d>D</d>` + 636 `</parent1>` + 637 `</result>`, 638 }, 639 { 640 Value: &Service{Port: &Port{Number: "80"}}, 641 ExpectXML: `<service><host><port>80</port></host></service>`, 642 }, 643 { 644 Value: &Service{}, 645 ExpectXML: `<service></service>`, 646 }, 647 { 648 Value: &Service{Port: &Port{Number: "80"}, Extra1: "A", Extra2: "B"}, 649 ExpectXML: `<service>` + 650 `<host><port>80</port></host>` + 651 `<Extra1>A</Extra1>` + 652 `<host><extra2>B</extra2></host>` + 653 `</service>`, 654 MarshalOnly: true, 655 }, 656 { 657 Value: &Service{Port: &Port{Number: "80"}, Extra2: "example"}, 658 ExpectXML: `<service>` + 659 `<host><port>80</port></host>` + 660 `<host><extra2>example</extra2></host>` + 661 `</service>`, 662 MarshalOnly: true, 663 }, 664 { 665 Value: &struct { 666 XMLName struct{} `xml:"space top"` 667 A string `xml:"x>a"` 668 B string `xml:"x>b"` 669 C string `xml:"space x>c"` 670 C1 string `xml:"space1 x>c"` 671 D1 string `xml:"space1 x>d"` 672 }{ 673 A: "a", 674 B: "b", 675 C: "c", 676 C1: "c1", 677 D1: "d1", 678 }, 679 ExpectXML: `<top xmlns="space">` + 680 `<x><a>a</a><b>b</b><c xmlns="space">c</c>` + 681 `<c xmlns="space1">c1</c>` + 682 `<d xmlns="space1">d1</d>` + 683 `</x>` + 684 `</top>`, 685 }, 686 { 687 Value: &struct { 688 XMLName Name 689 A string `xml:"x>a"` 690 B string `xml:"x>b"` 691 C string `xml:"space x>c"` 692 C1 string `xml:"space1 x>c"` 693 D1 string `xml:"space1 x>d"` 694 }{ 695 XMLName: Name{ 696 Space: "space0", 697 Local: "top", 698 }, 699 A: "a", 700 B: "b", 701 C: "c", 702 C1: "c1", 703 D1: "d1", 704 }, 705 ExpectXML: `<top xmlns="space0">` + 706 `<x><a>a</a><b>b</b>` + 707 `<c xmlns="space">c</c>` + 708 `<c xmlns="space1">c1</c>` + 709 `<d xmlns="space1">d1</d>` + 710 `</x>` + 711 `</top>`, 712 }, 713 { 714 Value: &struct { 715 XMLName struct{} `xml:"top"` 716 B string `xml:"space x>b"` 717 B1 string `xml:"space1 x>b"` 718 }{ 719 B: "b", 720 B1: "b1", 721 }, 722 ExpectXML: `<top>` + 723 `<x><b xmlns="space">b</b>` + 724 `<b xmlns="space1">b1</b></x>` + 725 `</top>`, 726 }, 727 728 // Test struct embedding 729 { 730 Value: &EmbedA{ 731 EmbedC: EmbedC{ 732 FieldA1: "", // Shadowed by A.A 733 FieldA2: "", // Shadowed by A.A 734 FieldB: "A.C.B", 735 FieldC: "A.C.C", 736 }, 737 EmbedB: EmbedB{ 738 FieldB: "A.B.B", 739 EmbedC: &EmbedC{ 740 FieldA1: "A.B.C.A1", 741 FieldA2: "A.B.C.A2", 742 FieldB: "", // Shadowed by A.B.B 743 FieldC: "A.B.C.C", 744 }, 745 }, 746 FieldA: "A.A", 747 embedD: embedD{ 748 FieldE: "A.D.E", 749 }, 750 }, 751 ExpectXML: `<EmbedA>` + 752 `<FieldB>A.C.B</FieldB>` + 753 `<FieldC>A.C.C</FieldC>` + 754 `<EmbedB>` + 755 `<FieldB>A.B.B</FieldB>` + 756 `<FieldA>` + 757 `<A1>A.B.C.A1</A1>` + 758 `<A2>A.B.C.A2</A2>` + 759 `</FieldA>` + 760 `<FieldC>A.B.C.C</FieldC>` + 761 `</EmbedB>` + 762 `<FieldA>A.A</FieldA>` + 763 `<FieldE>A.D.E</FieldE>` + 764 `</EmbedA>`, 765 }, 766 767 // Test that name casing matters 768 { 769 Value: &NameCasing{Xy: "mixed", XY: "upper", XyA: "mixedA", XYA: "upperA"}, 770 ExpectXML: `<casing Xy="mixedA" XY="upperA"><Xy>mixed</Xy><XY>upper</XY></casing>`, 771 }, 772 773 // Test the order in which the XML element name is chosen 774 { 775 Value: &NamePrecedence{ 776 FromTag: XMLNameWithoutTag{Value: "A"}, 777 FromNameVal: XMLNameWithoutTag{XMLName: Name{Local: "InXMLName"}, Value: "B"}, 778 FromNameTag: XMLNameWithTag{Value: "C"}, 779 InFieldName: "D", 780 }, 781 ExpectXML: `<Parent>` + 782 `<InTag>A</InTag>` + 783 `<InXMLName>B</InXMLName>` + 784 `<InXMLNameTag>C</InXMLNameTag>` + 785 `<InFieldName>D</InFieldName>` + 786 `</Parent>`, 787 MarshalOnly: true, 788 }, 789 { 790 Value: &NamePrecedence{ 791 XMLName: Name{Local: "Parent"}, 792 FromTag: XMLNameWithoutTag{XMLName: Name{Local: "InTag"}, Value: "A"}, 793 FromNameVal: XMLNameWithoutTag{XMLName: Name{Local: "FromNameVal"}, Value: "B"}, 794 FromNameTag: XMLNameWithTag{XMLName: Name{Local: "InXMLNameTag"}, Value: "C"}, 795 InFieldName: "D", 796 }, 797 ExpectXML: `<Parent>` + 798 `<InTag>A</InTag>` + 799 `<FromNameVal>B</FromNameVal>` + 800 `<InXMLNameTag>C</InXMLNameTag>` + 801 `<InFieldName>D</InFieldName>` + 802 `</Parent>`, 803 UnmarshalOnly: true, 804 }, 805 806 // xml.Name works in a plain field as well. 807 { 808 Value: &NameInField{Name{Space: "ns", Local: "foo"}}, 809 ExpectXML: `<NameInField><foo xmlns="ns"></foo></NameInField>`, 810 }, 811 { 812 Value: &NameInField{Name{Space: "ns", Local: "foo"}}, 813 ExpectXML: `<NameInField><foo xmlns="ns"><ignore></ignore></foo></NameInField>`, 814 UnmarshalOnly: true, 815 }, 816 817 // Marshaling zero xml.Name uses the tag or field name. 818 { 819 Value: &NameInField{}, 820 ExpectXML: `<NameInField><foo xmlns="ns"></foo></NameInField>`, 821 MarshalOnly: true, 822 }, 823 824 // Test attributes 825 { 826 Value: &AttrTest{ 827 Int: 8, 828 Named: 9, 829 Float: 23.5, 830 Uint8: 255, 831 Bool: true, 832 Str: "str", 833 Bytes: []byte("byt"), 834 }, 835 ExpectXML: `<AttrTest Int="8" int="9" Float="23.5" Uint8="255"` + 836 ` Bool="true" Str="str" Bytes="byt"></AttrTest>`, 837 }, 838 { 839 Value: &AttrTest{Bytes: []byte{}}, 840 ExpectXML: `<AttrTest Int="0" int="0" Float="0" Uint8="0"` + 841 ` Bool="false" Str="" Bytes=""></AttrTest>`, 842 }, 843 { 844 Value: &AttrsTest{ 845 Attrs: []Attr{ 846 {Name: Name{Local: "Answer"}, Value: "42"}, 847 {Name: Name{Local: "Int"}, Value: "8"}, 848 {Name: Name{Local: "int"}, Value: "9"}, 849 {Name: Name{Local: "Float"}, Value: "23.5"}, 850 {Name: Name{Local: "Uint8"}, Value: "255"}, 851 {Name: Name{Local: "Bool"}, Value: "true"}, 852 {Name: Name{Local: "Str"}, Value: "str"}, 853 {Name: Name{Local: "Bytes"}, Value: "byt"}, 854 }, 855 }, 856 ExpectXML: `<AttrsTest Answer="42" Int="8" int="9" Float="23.5" Uint8="255" Bool="true" Str="str" Bytes="byt" Int="0" int="0" Float="0" Uint8="0" Bool="false" Str="" Bytes=""></AttrsTest>`, 857 MarshalOnly: true, 858 }, 859 { 860 Value: &AttrsTest{ 861 Attrs: []Attr{ 862 {Name: Name{Local: "Answer"}, Value: "42"}, 863 }, 864 Int: 8, 865 Named: 9, 866 Float: 23.5, 867 Uint8: 255, 868 Bool: true, 869 Str: "str", 870 Bytes: []byte("byt"), 871 }, 872 ExpectXML: `<AttrsTest Answer="42" Int="8" int="9" Float="23.5" Uint8="255" Bool="true" Str="str" Bytes="byt"></AttrsTest>`, 873 }, 874 { 875 Value: &AttrsTest{ 876 Attrs: []Attr{ 877 {Name: Name{Local: "Int"}, Value: "0"}, 878 {Name: Name{Local: "int"}, Value: "0"}, 879 {Name: Name{Local: "Float"}, Value: "0"}, 880 {Name: Name{Local: "Uint8"}, Value: "0"}, 881 {Name: Name{Local: "Bool"}, Value: "false"}, 882 {Name: Name{Local: "Str"}}, 883 {Name: Name{Local: "Bytes"}}, 884 }, 885 Bytes: []byte{}, 886 }, 887 ExpectXML: `<AttrsTest Int="0" int="0" Float="0" Uint8="0" Bool="false" Str="" Bytes="" Int="0" int="0" Float="0" Uint8="0" Bool="false" Str="" Bytes=""></AttrsTest>`, 888 MarshalOnly: true, 889 }, 890 { 891 Value: &OmitAttrTest{ 892 Int: 8, 893 Named: 9, 894 Float: 23.5, 895 Uint8: 255, 896 Bool: true, 897 Str: "str", 898 Bytes: []byte("byt"), 899 PStr: &empty, 900 }, 901 ExpectXML: `<OmitAttrTest Int="8" int="9" Float="23.5" Uint8="255"` + 902 ` Bool="true" Str="str" Bytes="byt" PStr=""></OmitAttrTest>`, 903 }, 904 { 905 Value: &OmitAttrTest{}, 906 ExpectXML: `<OmitAttrTest></OmitAttrTest>`, 907 }, 908 909 // pointer fields 910 { 911 Value: &PointerFieldsTest{Name: &nameAttr, Age: &ageAttr, Contents: &contentsAttr}, 912 ExpectXML: `<dummy name="Sarah" age="12">lorem ipsum</dummy>`, 913 MarshalOnly: true, 914 }, 915 916 // empty chardata pointer field 917 { 918 Value: &ChardataEmptyTest{}, 919 ExpectXML: `<test></test>`, 920 MarshalOnly: true, 921 }, 922 923 // omitempty on fields 924 { 925 Value: &OmitFieldTest{ 926 Int: 8, 927 Named: 9, 928 Float: 23.5, 929 Uint8: 255, 930 Bool: true, 931 Str: "str", 932 Bytes: []byte("byt"), 933 PStr: &empty, 934 Ptr: &PresenceTest{}, 935 }, 936 ExpectXML: `<OmitFieldTest>` + 937 `<Int>8</Int>` + 938 `<int>9</int>` + 939 `<Float>23.5</Float>` + 940 `<Uint8>255</Uint8>` + 941 `<Bool>true</Bool>` + 942 `<Str>str</Str>` + 943 `<Bytes>byt</Bytes>` + 944 `<PStr></PStr>` + 945 `<Ptr></Ptr>` + 946 `</OmitFieldTest>`, 947 }, 948 { 949 Value: &OmitFieldTest{}, 950 ExpectXML: `<OmitFieldTest></OmitFieldTest>`, 951 }, 952 953 // Test ",any" 954 { 955 ExpectXML: `<a><nested><value>known</value></nested><other><sub>unknown</sub></other></a>`, 956 Value: &AnyTest{ 957 Nested: "known", 958 AnyField: AnyHolder{ 959 XMLName: Name{Local: "other"}, 960 XML: "<sub>unknown</sub>", 961 }, 962 }, 963 }, 964 { 965 Value: &AnyTest{Nested: "known", 966 AnyField: AnyHolder{ 967 XML: "<unknown/>", 968 XMLName: Name{Local: "AnyField"}, 969 }, 970 }, 971 ExpectXML: `<a><nested><value>known</value></nested><AnyField><unknown/></AnyField></a>`, 972 }, 973 { 974 ExpectXML: `<a><nested><value>b</value></nested></a>`, 975 Value: &AnyOmitTest{ 976 Nested: "b", 977 }, 978 }, 979 { 980 ExpectXML: `<a><nested><value>b</value></nested><c><d>e</d></c><g xmlns="f"><h>i</h></g></a>`, 981 Value: &AnySliceTest{ 982 Nested: "b", 983 AnyField: []AnyHolder{ 984 { 985 XMLName: Name{Local: "c"}, 986 XML: "<d>e</d>", 987 }, 988 { 989 XMLName: Name{Space: "f", Local: "g"}, 990 XML: "<h>i</h>", 991 }, 992 }, 993 }, 994 }, 995 { 996 ExpectXML: `<a><nested><value>b</value></nested></a>`, 997 Value: &AnySliceTest{ 998 Nested: "b", 999 }, 1000 }, 1001 1002 // Test recursive types. 1003 { 1004 Value: &RecurseA{ 1005 A: "a1", 1006 B: &RecurseB{ 1007 A: &RecurseA{"a2", nil}, 1008 B: "b1", 1009 }, 1010 }, 1011 ExpectXML: `<RecurseA><A>a1</A><B><A><A>a2</A></A><B>b1</B></B></RecurseA>`, 1012 }, 1013 1014 // Test ignoring fields via "-" tag 1015 { 1016 ExpectXML: `<IgnoreTest></IgnoreTest>`, 1017 Value: &IgnoreTest{}, 1018 }, 1019 { 1020 ExpectXML: `<IgnoreTest></IgnoreTest>`, 1021 Value: &IgnoreTest{PublicSecret: "can't tell"}, 1022 MarshalOnly: true, 1023 }, 1024 { 1025 ExpectXML: `<IgnoreTest><PublicSecret>ignore me</PublicSecret></IgnoreTest>`, 1026 Value: &IgnoreTest{}, 1027 UnmarshalOnly: true, 1028 }, 1029 1030 // Test escaping. 1031 { 1032 ExpectXML: `<a><nested><value>dquote: "; squote: '; ampersand: &; less: <; greater: >;</value></nested><empty></empty></a>`, 1033 Value: &AnyTest{ 1034 Nested: `dquote: "; squote: '; ampersand: &; less: <; greater: >;`, 1035 AnyField: AnyHolder{XMLName: Name{Local: "empty"}}, 1036 }, 1037 }, 1038 { 1039 ExpectXML: `<a><nested><value>newline: 
; cr: 
; tab: 	;</value></nested><AnyField></AnyField></a>`, 1040 Value: &AnyTest{ 1041 Nested: "newline: \n; cr: \r; tab: \t;", 1042 AnyField: AnyHolder{XMLName: Name{Local: "AnyField"}}, 1043 }, 1044 }, 1045 { 1046 ExpectXML: "<a><nested><value>1\r2\r\n3\n\r4\n5</value></nested></a>", 1047 Value: &AnyTest{ 1048 Nested: "1\n2\n3\n\n4\n5", 1049 }, 1050 UnmarshalOnly: true, 1051 }, 1052 { 1053 ExpectXML: `<EmbedInt><MyInt>42</MyInt></EmbedInt>`, 1054 Value: &EmbedInt{ 1055 MyInt: 42, 1056 }, 1057 }, 1058 // Test outputting CDATA-wrapped text. 1059 { 1060 ExpectXML: `<CDataTest></CDataTest>`, 1061 Value: &CDataTest{}, 1062 }, 1063 { 1064 ExpectXML: `<CDataTest><![CDATA[http://example.com/tests/1?foo=1&bar=baz]]></CDataTest>`, 1065 Value: &CDataTest{ 1066 Chardata: "http://example.com/tests/1?foo=1&bar=baz", 1067 }, 1068 }, 1069 { 1070 ExpectXML: `<CDataTest><![CDATA[Literal <![CDATA[Nested]]]]><![CDATA[>!]]></CDataTest>`, 1071 Value: &CDataTest{ 1072 Chardata: "Literal <![CDATA[Nested]]>!", 1073 }, 1074 }, 1075 { 1076 ExpectXML: `<CDataTest><![CDATA[<![CDATA[Nested]]]]><![CDATA[> Literal!]]></CDataTest>`, 1077 Value: &CDataTest{ 1078 Chardata: "<![CDATA[Nested]]> Literal!", 1079 }, 1080 }, 1081 { 1082 ExpectXML: `<CDataTest><![CDATA[<![CDATA[Nested]]]]><![CDATA[> Literal! <![CDATA[Nested]]]]><![CDATA[> Literal!]]></CDataTest>`, 1083 Value: &CDataTest{ 1084 Chardata: "<![CDATA[Nested]]> Literal! <![CDATA[Nested]]> Literal!", 1085 }, 1086 }, 1087 { 1088 ExpectXML: `<CDataTest><![CDATA[<![CDATA[<![CDATA[Nested]]]]><![CDATA[>]]]]><![CDATA[>]]></CDataTest>`, 1089 Value: &CDataTest{ 1090 Chardata: "<![CDATA[<![CDATA[Nested]]>]]>", 1091 }, 1092 }, 1093 1094 // Test omitempty with parent chain; see golang.org/issue/4168. 1095 { 1096 ExpectXML: `<Strings><A></A></Strings>`, 1097 Value: &Strings{}, 1098 }, 1099 // Custom marshalers. 1100 { 1101 ExpectXML: `<MyMarshalerTest>hello world</MyMarshalerTest>`, 1102 Value: &MyMarshalerTest{}, 1103 }, 1104 { 1105 ExpectXML: `<MarshalerStruct Foo="hello world"></MarshalerStruct>`, 1106 Value: &MarshalerStruct{}, 1107 }, 1108 { 1109 ExpectXML: `<outer xmlns="testns" int="10"></outer>`, 1110 Value: &OuterStruct{IntAttr: 10}, 1111 }, 1112 { 1113 ExpectXML: `<test xmlns="outerns" int="10"></test>`, 1114 Value: &OuterNamedStruct{XMLName: Name{Space: "outerns", Local: "test"}, IntAttr: 10}, 1115 }, 1116 { 1117 ExpectXML: `<test xmlns="outerns" int="10"></test>`, 1118 Value: &OuterNamedOrderedStruct{XMLName: Name{Space: "outerns", Local: "test"}, IntAttr: 10}, 1119 }, 1120 { 1121 ExpectXML: `<outer xmlns="testns" int="10"></outer>`, 1122 Value: &OuterOuterStruct{OuterStruct{IntAttr: 10}}, 1123 }, 1124 { 1125 ExpectXML: `<NestedAndChardata><A><B></B><B></B></A>test</NestedAndChardata>`, 1126 Value: &NestedAndChardata{AB: make([]string, 2), Chardata: "test"}, 1127 }, 1128 { 1129 ExpectXML: `<NestedAndComment><A><B></B><B></B></A><!--test--></NestedAndComment>`, 1130 Value: &NestedAndComment{AB: make([]string, 2), Comment: "test"}, 1131 }, 1132 { 1133 ExpectXML: `<NestedAndCData><A><B></B><B></B></A><![CDATA[test]]></NestedAndCData>`, 1134 Value: &NestedAndCData{AB: make([]string, 2), CDATA: "test"}, 1135 }, 1136 } 1137 1138 func TestMarshal(t *testing.T) { 1139 for idx, test := range marshalTests { 1140 if test.UnmarshalOnly { 1141 continue 1142 } 1143 data, err := Marshal(test.Value) 1144 if err != nil { 1145 t.Errorf("#%d: marshal(%#v): %s", idx, test.Value, err) 1146 continue 1147 } 1148 if got, want := string(data), test.ExpectXML; got != want { 1149 if strings.Contains(want, "\n") { 1150 t.Errorf("#%d: marshal(%#v):\nHAVE:\n%s\nWANT:\n%s", idx, test.Value, got, want) 1151 } else { 1152 t.Errorf("#%d: marshal(%#v):\nhave %#q\nwant %#q", idx, test.Value, got, want) 1153 } 1154 } 1155 } 1156 } 1157 1158 type AttrParent struct { 1159 X string `xml:"X>Y,attr"` 1160 } 1161 1162 type BadAttr struct { 1163 Name map[string]string `xml:"name,attr"` 1164 } 1165 1166 var marshalErrorTests = []struct { 1167 Value interface{} 1168 Err string 1169 Kind reflect.Kind 1170 }{ 1171 { 1172 Value: make(chan bool), 1173 Err: "xml: unsupported type: chan bool", 1174 Kind: reflect.Chan, 1175 }, 1176 { 1177 Value: map[string]string{ 1178 "question": "What do you get when you multiply six by nine?", 1179 "answer": "42", 1180 }, 1181 Err: "xml: unsupported type: map[string]string", 1182 Kind: reflect.Map, 1183 }, 1184 { 1185 Value: map[*Ship]bool{nil: false}, 1186 Err: "xml: unsupported type: map[*xml.Ship]bool", 1187 Kind: reflect.Map, 1188 }, 1189 { 1190 Value: &Domain{Comment: []byte("f--bar")}, 1191 Err: `xml: comments must not contain "--"`, 1192 }, 1193 // Reject parent chain with attr, never worked; see golang.org/issue/5033. 1194 { 1195 Value: &AttrParent{}, 1196 Err: `xml: X>Y chain not valid with attr flag`, 1197 }, 1198 { 1199 Value: BadAttr{map[string]string{"X": "Y"}}, 1200 Err: `xml: unsupported type: map[string]string`, 1201 }, 1202 } 1203 1204 var marshalIndentTests = []struct { 1205 Value interface{} 1206 Prefix string 1207 Indent string 1208 ExpectXML string 1209 }{ 1210 { 1211 Value: &SecretAgent{ 1212 Handle: "007", 1213 Identity: "James Bond", 1214 Obfuscate: "<redacted/>", 1215 }, 1216 Prefix: "", 1217 Indent: "\t", 1218 ExpectXML: fmt.Sprintf("<agent handle=\"007\">\n\t<Identity>James Bond</Identity><redacted/>\n</agent>"), 1219 }, 1220 } 1221 1222 func TestMarshalErrors(t *testing.T) { 1223 for idx, test := range marshalErrorTests { 1224 data, err := Marshal(test.Value) 1225 if err == nil { 1226 t.Errorf("#%d: marshal(%#v) = [success] %q, want error %v", idx, test.Value, data, test.Err) 1227 continue 1228 } 1229 if err.Error() != test.Err { 1230 t.Errorf("#%d: marshal(%#v) = [error] %v, want %v", idx, test.Value, err, test.Err) 1231 } 1232 if test.Kind != reflect.Invalid { 1233 if kind := err.(*UnsupportedTypeError).Type.Kind(); kind != test.Kind { 1234 t.Errorf("#%d: marshal(%#v) = [error kind] %s, want %s", idx, test.Value, kind, test.Kind) 1235 } 1236 } 1237 } 1238 } 1239 1240 // Do invertibility testing on the various structures that we test 1241 func TestUnmarshal(t *testing.T) { 1242 for i, test := range marshalTests { 1243 if test.MarshalOnly { 1244 continue 1245 } 1246 if _, ok := test.Value.(*Plain); ok { 1247 continue 1248 } 1249 if test.ExpectXML == `<top>`+ 1250 `<x><b xmlns="space">b</b>`+ 1251 `<b xmlns="space1">b1</b></x>`+ 1252 `</top>` { 1253 // TODO(rogpeppe): re-enable this test in 1254 // https://go-review.googlesource.com/#/c/5910/ 1255 continue 1256 } 1257 1258 vt := reflect.TypeOf(test.Value) 1259 dest := reflect.New(vt.Elem()).Interface() 1260 err := Unmarshal([]byte(test.ExpectXML), dest) 1261 1262 switch fix := dest.(type) { 1263 case *Feed: 1264 fix.Author.InnerXML = "" 1265 for i := range fix.Entry { 1266 fix.Entry[i].Author.InnerXML = "" 1267 } 1268 } 1269 1270 if err != nil { 1271 t.Errorf("#%d: unexpected error: %#v", i, err) 1272 } else if got, want := dest, test.Value; !reflect.DeepEqual(got, want) { 1273 t.Errorf("#%d: unmarshal(%q):\nhave %#v\nwant %#v", i, test.ExpectXML, got, want) 1274 } 1275 } 1276 } 1277 1278 func TestMarshalIndent(t *testing.T) { 1279 for i, test := range marshalIndentTests { 1280 data, err := MarshalIndent(test.Value, test.Prefix, test.Indent) 1281 if err != nil { 1282 t.Errorf("#%d: Error: %s", i, err) 1283 continue 1284 } 1285 if got, want := string(data), test.ExpectXML; got != want { 1286 t.Errorf("#%d: MarshalIndent:\nGot:%s\nWant:\n%s", i, got, want) 1287 } 1288 } 1289 } 1290 1291 type limitedBytesWriter struct { 1292 w io.Writer 1293 remain int // until writes fail 1294 } 1295 1296 func (lw *limitedBytesWriter) Write(p []byte) (n int, err error) { 1297 if lw.remain <= 0 { 1298 println("error") 1299 return 0, errors.New("write limit hit") 1300 } 1301 if len(p) > lw.remain { 1302 p = p[:lw.remain] 1303 n, _ = lw.w.Write(p) 1304 lw.remain = 0 1305 return n, errors.New("write limit hit") 1306 } 1307 n, err = lw.w.Write(p) 1308 lw.remain -= n 1309 return n, err 1310 } 1311 1312 func TestMarshalWriteErrors(t *testing.T) { 1313 var buf bytes.Buffer 1314 const writeCap = 1024 1315 w := &limitedBytesWriter{&buf, writeCap} 1316 enc := NewEncoder(w) 1317 var err error 1318 var i int 1319 const n = 4000 1320 for i = 1; i <= n; i++ { 1321 err = enc.Encode(&Passenger{ 1322 Name: []string{"Alice", "Bob"}, 1323 Weight: 5, 1324 }) 1325 if err != nil { 1326 break 1327 } 1328 } 1329 if err == nil { 1330 t.Error("expected an error") 1331 } 1332 if i == n { 1333 t.Errorf("expected to fail before the end") 1334 } 1335 if buf.Len() != writeCap { 1336 t.Errorf("buf.Len() = %d; want %d", buf.Len(), writeCap) 1337 } 1338 } 1339 1340 func TestMarshalWriteIOErrors(t *testing.T) { 1341 enc := NewEncoder(errWriter{}) 1342 1343 expectErr := "unwritable" 1344 err := enc.Encode(&Passenger{}) 1345 if err == nil || err.Error() != expectErr { 1346 t.Errorf("EscapeTest = [error] %v, want %v", err, expectErr) 1347 } 1348 } 1349 1350 func TestMarshalFlush(t *testing.T) { 1351 var buf bytes.Buffer 1352 enc := NewEncoder(&buf) 1353 if err := enc.EncodeToken(CharData("hello world")); err != nil { 1354 t.Fatalf("enc.EncodeToken: %v", err) 1355 } 1356 if buf.Len() > 0 { 1357 t.Fatalf("enc.EncodeToken caused actual write: %q", buf.Bytes()) 1358 } 1359 if err := enc.Flush(); err != nil { 1360 t.Fatalf("enc.Flush: %v", err) 1361 } 1362 if buf.String() != "hello world" { 1363 t.Fatalf("after enc.Flush, buf.String() = %q, want %q", buf.String(), "hello world") 1364 } 1365 } 1366 1367 func BenchmarkMarshal(b *testing.B) { 1368 b.ReportAllocs() 1369 for i := 0; i < b.N; i++ { 1370 Marshal(atomValue) 1371 } 1372 } 1373 1374 func BenchmarkUnmarshal(b *testing.B) { 1375 b.ReportAllocs() 1376 xml := []byte(atomXml) 1377 for i := 0; i < b.N; i++ { 1378 Unmarshal(xml, &Feed{}) 1379 } 1380 } 1381 1382 // golang.org/issue/6556 1383 func TestStructPointerMarshal(t *testing.T) { 1384 type A struct { 1385 XMLName string `xml:"a"` 1386 B []interface{} 1387 } 1388 type C struct { 1389 XMLName Name 1390 Value string `xml:"value"` 1391 } 1392 1393 a := new(A) 1394 a.B = append(a.B, &C{ 1395 XMLName: Name{Local: "c"}, 1396 Value: "x", 1397 }) 1398 1399 b, err := Marshal(a) 1400 if err != nil { 1401 t.Fatal(err) 1402 } 1403 if x := string(b); x != "<a><c><value>x</value></c></a>" { 1404 t.Fatal(x) 1405 } 1406 var v A 1407 err = Unmarshal(b, &v) 1408 if err != nil { 1409 t.Fatal(err) 1410 } 1411 } 1412 1413 var encodeTokenTests = []struct { 1414 desc string 1415 toks []Token 1416 want string 1417 err string 1418 }{{ 1419 desc: "start element with name space", 1420 toks: []Token{ 1421 StartElement{Name{"space", "local"}, nil}, 1422 }, 1423 want: `<local xmlns="space">`, 1424 }, { 1425 desc: "start element with no name", 1426 toks: []Token{ 1427 StartElement{Name{"space", ""}, nil}, 1428 }, 1429 err: "xml: start tag with no name", 1430 }, { 1431 desc: "end element with no name", 1432 toks: []Token{ 1433 EndElement{Name{"space", ""}}, 1434 }, 1435 err: "xml: end tag with no name", 1436 }, { 1437 desc: "char data", 1438 toks: []Token{ 1439 CharData("foo"), 1440 }, 1441 want: `foo`, 1442 }, { 1443 desc: "char data with escaped chars", 1444 toks: []Token{ 1445 CharData(" \t\n"), 1446 }, 1447 want: " 	\n", 1448 }, { 1449 desc: "comment", 1450 toks: []Token{ 1451 Comment("foo"), 1452 }, 1453 want: `<!--foo-->`, 1454 }, { 1455 desc: "comment with invalid content", 1456 toks: []Token{ 1457 Comment("foo-->"), 1458 }, 1459 err: "xml: EncodeToken of Comment containing --> marker", 1460 }, { 1461 desc: "proc instruction", 1462 toks: []Token{ 1463 ProcInst{"Target", []byte("Instruction")}, 1464 }, 1465 want: `<?Target Instruction?>`, 1466 }, { 1467 desc: "proc instruction with empty target", 1468 toks: []Token{ 1469 ProcInst{"", []byte("Instruction")}, 1470 }, 1471 err: "xml: EncodeToken of ProcInst with invalid Target", 1472 }, { 1473 desc: "proc instruction with bad content", 1474 toks: []Token{ 1475 ProcInst{"", []byte("Instruction?>")}, 1476 }, 1477 err: "xml: EncodeToken of ProcInst with invalid Target", 1478 }, { 1479 desc: "directive", 1480 toks: []Token{ 1481 Directive("foo"), 1482 }, 1483 want: `<!foo>`, 1484 }, { 1485 desc: "more complex directive", 1486 toks: []Token{ 1487 Directive("DOCTYPE doc [ <!ELEMENT doc '>'> <!-- com>ment --> ]"), 1488 }, 1489 want: `<!DOCTYPE doc [ <!ELEMENT doc '>'> <!-- com>ment --> ]>`, 1490 }, { 1491 desc: "directive instruction with bad name", 1492 toks: []Token{ 1493 Directive("foo>"), 1494 }, 1495 err: "xml: EncodeToken of Directive containing wrong < or > markers", 1496 }, { 1497 desc: "end tag without start tag", 1498 toks: []Token{ 1499 EndElement{Name{"foo", "bar"}}, 1500 }, 1501 err: "xml: end tag </bar> without start tag", 1502 }, { 1503 desc: "mismatching end tag local name", 1504 toks: []Token{ 1505 StartElement{Name{"", "foo"}, nil}, 1506 EndElement{Name{"", "bar"}}, 1507 }, 1508 err: "xml: end tag </bar> does not match start tag <foo>", 1509 want: `<foo>`, 1510 }, { 1511 desc: "mismatching end tag namespace", 1512 toks: []Token{ 1513 StartElement{Name{"space", "foo"}, nil}, 1514 EndElement{Name{"another", "foo"}}, 1515 }, 1516 err: "xml: end tag </foo> in namespace another does not match start tag <foo> in namespace space", 1517 want: `<foo xmlns="space">`, 1518 }, { 1519 desc: "start element with explicit namespace", 1520 toks: []Token{ 1521 StartElement{Name{"space", "local"}, []Attr{ 1522 {Name{"xmlns", "x"}, "space"}, 1523 {Name{"space", "foo"}, "value"}, 1524 }}, 1525 }, 1526 want: `<local xmlns="space" xmlns:_xmlns="xmlns" _xmlns:x="space" xmlns:space="space" space:foo="value">`, 1527 }, { 1528 desc: "start element with explicit namespace and colliding prefix", 1529 toks: []Token{ 1530 StartElement{Name{"space", "local"}, []Attr{ 1531 {Name{"xmlns", "x"}, "space"}, 1532 {Name{"space", "foo"}, "value"}, 1533 {Name{"x", "bar"}, "other"}, 1534 }}, 1535 }, 1536 want: `<local xmlns="space" xmlns:_xmlns="xmlns" _xmlns:x="space" xmlns:space="space" space:foo="value" xmlns:x="x" x:bar="other">`, 1537 }, { 1538 desc: "start element using previously defined namespace", 1539 toks: []Token{ 1540 StartElement{Name{"", "local"}, []Attr{ 1541 {Name{"xmlns", "x"}, "space"}, 1542 }}, 1543 StartElement{Name{"space", "foo"}, []Attr{ 1544 {Name{"space", "x"}, "y"}, 1545 }}, 1546 }, 1547 want: `<local xmlns:_xmlns="xmlns" _xmlns:x="space"><foo xmlns="space" xmlns:space="space" space:x="y">`, 1548 }, { 1549 desc: "nested name space with same prefix", 1550 toks: []Token{ 1551 StartElement{Name{"", "foo"}, []Attr{ 1552 {Name{"xmlns", "x"}, "space1"}, 1553 }}, 1554 StartElement{Name{"", "foo"}, []Attr{ 1555 {Name{"xmlns", "x"}, "space2"}, 1556 }}, 1557 StartElement{Name{"", "foo"}, []Attr{ 1558 {Name{"space1", "a"}, "space1 value"}, 1559 {Name{"space2", "b"}, "space2 value"}, 1560 }}, 1561 EndElement{Name{"", "foo"}}, 1562 EndElement{Name{"", "foo"}}, 1563 StartElement{Name{"", "foo"}, []Attr{ 1564 {Name{"space1", "a"}, "space1 value"}, 1565 {Name{"space2", "b"}, "space2 value"}, 1566 }}, 1567 }, 1568 want: `<foo xmlns:_xmlns="xmlns" _xmlns:x="space1"><foo _xmlns:x="space2"><foo xmlns:space1="space1" space1:a="space1 value" xmlns:space2="space2" space2:b="space2 value"></foo></foo><foo xmlns:space1="space1" space1:a="space1 value" xmlns:space2="space2" space2:b="space2 value">`, 1569 }, { 1570 desc: "start element defining several prefixes for the same name space", 1571 toks: []Token{ 1572 StartElement{Name{"space", "foo"}, []Attr{ 1573 {Name{"xmlns", "a"}, "space"}, 1574 {Name{"xmlns", "b"}, "space"}, 1575 {Name{"space", "x"}, "value"}, 1576 }}, 1577 }, 1578 want: `<foo xmlns="space" xmlns:_xmlns="xmlns" _xmlns:a="space" _xmlns:b="space" xmlns:space="space" space:x="value">`, 1579 }, { 1580 desc: "nested element redefines name space", 1581 toks: []Token{ 1582 StartElement{Name{"", "foo"}, []Attr{ 1583 {Name{"xmlns", "x"}, "space"}, 1584 }}, 1585 StartElement{Name{"space", "foo"}, []Attr{ 1586 {Name{"xmlns", "y"}, "space"}, 1587 {Name{"space", "a"}, "value"}, 1588 }}, 1589 }, 1590 want: `<foo xmlns:_xmlns="xmlns" _xmlns:x="space"><foo xmlns="space" _xmlns:y="space" xmlns:space="space" space:a="value">`, 1591 }, { 1592 desc: "nested element creates alias for default name space", 1593 toks: []Token{ 1594 StartElement{Name{"space", "foo"}, []Attr{ 1595 {Name{"", "xmlns"}, "space"}, 1596 }}, 1597 StartElement{Name{"space", "foo"}, []Attr{ 1598 {Name{"xmlns", "y"}, "space"}, 1599 {Name{"space", "a"}, "value"}, 1600 }}, 1601 }, 1602 want: `<foo xmlns="space" xmlns="space"><foo xmlns="space" xmlns:_xmlns="xmlns" _xmlns:y="space" xmlns:space="space" space:a="value">`, 1603 }, { 1604 desc: "nested element defines default name space with existing prefix", 1605 toks: []Token{ 1606 StartElement{Name{"", "foo"}, []Attr{ 1607 {Name{"xmlns", "x"}, "space"}, 1608 }}, 1609 StartElement{Name{"space", "foo"}, []Attr{ 1610 {Name{"", "xmlns"}, "space"}, 1611 {Name{"space", "a"}, "value"}, 1612 }}, 1613 }, 1614 want: `<foo xmlns:_xmlns="xmlns" _xmlns:x="space"><foo xmlns="space" xmlns="space" xmlns:space="space" space:a="value">`, 1615 }, { 1616 desc: "nested element uses empty attribute name space when default ns defined", 1617 toks: []Token{ 1618 StartElement{Name{"space", "foo"}, []Attr{ 1619 {Name{"", "xmlns"}, "space"}, 1620 }}, 1621 StartElement{Name{"space", "foo"}, []Attr{ 1622 {Name{"", "attr"}, "value"}, 1623 }}, 1624 }, 1625 want: `<foo xmlns="space" xmlns="space"><foo xmlns="space" attr="value">`, 1626 }, { 1627 desc: "redefine xmlns", 1628 toks: []Token{ 1629 StartElement{Name{"", "foo"}, []Attr{ 1630 {Name{"foo", "xmlns"}, "space"}, 1631 }}, 1632 }, 1633 want: `<foo xmlns:foo="foo" foo:xmlns="space">`, 1634 }, { 1635 desc: "xmlns with explicit name space #1", 1636 toks: []Token{ 1637 StartElement{Name{"space", "foo"}, []Attr{ 1638 {Name{"xml", "xmlns"}, "space"}, 1639 }}, 1640 }, 1641 want: `<foo xmlns="space" xmlns:_xml="xml" _xml:xmlns="space">`, 1642 }, { 1643 desc: "xmlns with explicit name space #2", 1644 toks: []Token{ 1645 StartElement{Name{"space", "foo"}, []Attr{ 1646 {Name{xmlURL, "xmlns"}, "space"}, 1647 }}, 1648 }, 1649 want: `<foo xmlns="space" xml:xmlns="space">`, 1650 }, { 1651 desc: "empty name space declaration is ignored", 1652 toks: []Token{ 1653 StartElement{Name{"", "foo"}, []Attr{ 1654 {Name{"xmlns", "foo"}, ""}, 1655 }}, 1656 }, 1657 want: `<foo xmlns:_xmlns="xmlns" _xmlns:foo="">`, 1658 }, { 1659 desc: "attribute with no name is ignored", 1660 toks: []Token{ 1661 StartElement{Name{"", "foo"}, []Attr{ 1662 {Name{"", ""}, "value"}, 1663 }}, 1664 }, 1665 want: `<foo>`, 1666 }, { 1667 desc: "namespace URL with non-valid name", 1668 toks: []Token{ 1669 StartElement{Name{"/34", "foo"}, []Attr{ 1670 {Name{"/34", "x"}, "value"}, 1671 }}, 1672 }, 1673 want: `<foo xmlns="/34" xmlns:_="/34" _:x="value">`, 1674 }, { 1675 desc: "nested element resets default namespace to empty", 1676 toks: []Token{ 1677 StartElement{Name{"space", "foo"}, []Attr{ 1678 {Name{"", "xmlns"}, "space"}, 1679 }}, 1680 StartElement{Name{"", "foo"}, []Attr{ 1681 {Name{"", "xmlns"}, ""}, 1682 {Name{"", "x"}, "value"}, 1683 {Name{"space", "x"}, "value"}, 1684 }}, 1685 }, 1686 want: `<foo xmlns="space" xmlns="space"><foo xmlns="" x="value" xmlns:space="space" space:x="value">`, 1687 }, { 1688 desc: "nested element requires empty default name space", 1689 toks: []Token{ 1690 StartElement{Name{"space", "foo"}, []Attr{ 1691 {Name{"", "xmlns"}, "space"}, 1692 }}, 1693 StartElement{Name{"", "foo"}, nil}, 1694 }, 1695 want: `<foo xmlns="space" xmlns="space"><foo>`, 1696 }, { 1697 desc: "attribute uses name space from xmlns", 1698 toks: []Token{ 1699 StartElement{Name{"some/space", "foo"}, []Attr{ 1700 {Name{"", "attr"}, "value"}, 1701 {Name{"some/space", "other"}, "other value"}, 1702 }}, 1703 }, 1704 want: `<foo xmlns="some/space" attr="value" xmlns:space="some/space" space:other="other value">`, 1705 }, { 1706 desc: "default name space should not be used by attributes", 1707 toks: []Token{ 1708 StartElement{Name{"space", "foo"}, []Attr{ 1709 {Name{"", "xmlns"}, "space"}, 1710 {Name{"xmlns", "bar"}, "space"}, 1711 {Name{"space", "baz"}, "foo"}, 1712 }}, 1713 StartElement{Name{"space", "baz"}, nil}, 1714 EndElement{Name{"space", "baz"}}, 1715 EndElement{Name{"space", "foo"}}, 1716 }, 1717 want: `<foo xmlns="space" xmlns="space" xmlns:_xmlns="xmlns" _xmlns:bar="space" xmlns:space="space" space:baz="foo"><baz xmlns="space"></baz></foo>`, 1718 }, { 1719 desc: "default name space not used by attributes, not explicitly defined", 1720 toks: []Token{ 1721 StartElement{Name{"space", "foo"}, []Attr{ 1722 {Name{"", "xmlns"}, "space"}, 1723 {Name{"space", "baz"}, "foo"}, 1724 }}, 1725 StartElement{Name{"space", "baz"}, nil}, 1726 EndElement{Name{"space", "baz"}}, 1727 EndElement{Name{"space", "foo"}}, 1728 }, 1729 want: `<foo xmlns="space" xmlns="space" xmlns:space="space" space:baz="foo"><baz xmlns="space"></baz></foo>`, 1730 }, { 1731 desc: "impossible xmlns declaration", 1732 toks: []Token{ 1733 StartElement{Name{"", "foo"}, []Attr{ 1734 {Name{"", "xmlns"}, "space"}, 1735 }}, 1736 StartElement{Name{"space", "bar"}, []Attr{ 1737 {Name{"space", "attr"}, "value"}, 1738 }}, 1739 }, 1740 want: `<foo xmlns="space"><bar xmlns="space" xmlns:space="space" space:attr="value">`, 1741 }} 1742 1743 func TestEncodeToken(t *testing.T) { 1744 loop: 1745 for i, tt := range encodeTokenTests { 1746 var buf bytes.Buffer 1747 enc := NewEncoder(&buf) 1748 var err error 1749 for j, tok := range tt.toks { 1750 err = enc.EncodeToken(tok) 1751 if err != nil && j < len(tt.toks)-1 { 1752 t.Errorf("#%d %s token #%d: %v", i, tt.desc, j, err) 1753 continue loop 1754 } 1755 } 1756 errorf := func(f string, a ...interface{}) { 1757 t.Errorf("#%d %s token #%d:%s", i, tt.desc, len(tt.toks)-1, fmt.Sprintf(f, a...)) 1758 } 1759 switch { 1760 case tt.err != "" && err == nil: 1761 errorf(" expected error; got none") 1762 continue 1763 case tt.err == "" && err != nil: 1764 errorf(" got error: %v", err) 1765 continue 1766 case tt.err != "" && err != nil && tt.err != err.Error(): 1767 errorf(" error mismatch; got %v, want %v", err, tt.err) 1768 continue 1769 } 1770 if err := enc.Flush(); err != nil { 1771 errorf(" %v", err) 1772 continue 1773 } 1774 if got := buf.String(); got != tt.want { 1775 errorf("\ngot %v\nwant %v", got, tt.want) 1776 continue 1777 } 1778 } 1779 } 1780 1781 func TestProcInstEncodeToken(t *testing.T) { 1782 var buf bytes.Buffer 1783 enc := NewEncoder(&buf) 1784 1785 if err := enc.EncodeToken(ProcInst{"xml", []byte("Instruction")}); err != nil { 1786 t.Fatalf("enc.EncodeToken: expected to be able to encode xml target ProcInst as first token, %s", err) 1787 } 1788 1789 if err := enc.EncodeToken(ProcInst{"Target", []byte("Instruction")}); err != nil { 1790 t.Fatalf("enc.EncodeToken: expected to be able to add non-xml target ProcInst") 1791 } 1792 1793 if err := enc.EncodeToken(ProcInst{"xml", []byte("Instruction")}); err == nil { 1794 t.Fatalf("enc.EncodeToken: expected to not be allowed to encode xml target ProcInst when not first token") 1795 } 1796 } 1797 1798 func TestDecodeEncode(t *testing.T) { 1799 var in, out bytes.Buffer 1800 in.WriteString(`<?xml version="1.0" encoding="UTF-8"?> 1801 <?Target Instruction?> 1802 <root> 1803 </root> 1804 `) 1805 dec := NewDecoder(&in) 1806 enc := NewEncoder(&out) 1807 for tok, err := dec.Token(); err == nil; tok, err = dec.Token() { 1808 err = enc.EncodeToken(tok) 1809 if err != nil { 1810 t.Fatalf("enc.EncodeToken: Unable to encode token (%#v), %v", tok, err) 1811 } 1812 } 1813 } 1814 1815 // Issue 9796. Used to fail with GORACE="halt_on_error=1" -race. 1816 func TestRace9796(t *testing.T) { 1817 type A struct{} 1818 type B struct { 1819 C []A `xml:"X>Y"` 1820 } 1821 var wg sync.WaitGroup 1822 for i := 0; i < 2; i++ { 1823 wg.Add(1) 1824 go func() { 1825 Marshal(B{[]A{{}}}) 1826 wg.Done() 1827 }() 1828 } 1829 wg.Wait() 1830 } 1831 1832 func TestIsValidDirective(t *testing.T) { 1833 testOK := []string{ 1834 "<>", 1835 "< < > >", 1836 "<!DOCTYPE '<' '>' '>' <!--nothing-->>", 1837 "<!DOCTYPE doc [ <!ELEMENT doc ANY> <!ELEMENT doc ANY> ]>", 1838 "<!DOCTYPE doc [ <!ELEMENT doc \"ANY> '<' <!E\" LEMENT '>' doc ANY> ]>", 1839 "<!DOCTYPE doc <!-- just>>>> a < comment --> [ <!ITEM anything> ] >", 1840 } 1841 testKO := []string{ 1842 "<", 1843 ">", 1844 "<!--", 1845 "-->", 1846 "< > > < < >", 1847 "<!dummy <!-- > -->", 1848 "<!DOCTYPE doc '>", 1849 "<!DOCTYPE doc '>'", 1850 "<!DOCTYPE doc <!--comment>", 1851 } 1852 for _, s := range testOK { 1853 if !isValidDirective(Directive(s)) { 1854 t.Errorf("Directive %q is expected to be valid", s) 1855 } 1856 } 1857 for _, s := range testKO { 1858 if isValidDirective(Directive(s)) { 1859 t.Errorf("Directive %q is expected to be invalid", s) 1860 } 1861 } 1862 } 1863 1864 // Issue 11719. EncodeToken used to silently eat tokens with an invalid type. 1865 func TestSimpleUseOfEncodeToken(t *testing.T) { 1866 var buf bytes.Buffer 1867 enc := NewEncoder(&buf) 1868 if err := enc.EncodeToken(&StartElement{Name: Name{"", "object1"}}); err == nil { 1869 t.Errorf("enc.EncodeToken: pointer type should be rejected") 1870 } 1871 if err := enc.EncodeToken(&EndElement{Name: Name{"", "object1"}}); err == nil { 1872 t.Errorf("enc.EncodeToken: pointer type should be rejected") 1873 } 1874 if err := enc.EncodeToken(StartElement{Name: Name{"", "object2"}}); err != nil { 1875 t.Errorf("enc.EncodeToken: StartElement %s", err) 1876 } 1877 if err := enc.EncodeToken(EndElement{Name: Name{"", "object2"}}); err != nil { 1878 t.Errorf("enc.EncodeToken: EndElement %s", err) 1879 } 1880 if err := enc.EncodeToken(Universe{}); err == nil { 1881 t.Errorf("enc.EncodeToken: invalid type not caught") 1882 } 1883 if err := enc.Flush(); err != nil { 1884 t.Errorf("enc.Flush: %s", err) 1885 } 1886 if buf.Len() == 0 { 1887 t.Errorf("enc.EncodeToken: empty buffer") 1888 } 1889 want := "<object2></object2>" 1890 if buf.String() != want { 1891 t.Errorf("enc.EncodeToken: expected %q; got %q", want, buf.String()) 1892 } 1893 } 1894 1895 // Issue 16158. Decoder.unmarshalAttr ignores the return value of copyValue. 1896 func TestIssue16158(t *testing.T) { 1897 const data = `<foo b="HELLOWORLD"></foo>` 1898 err := Unmarshal([]byte(data), &struct { 1899 B byte `xml:"b,attr,omitempty"` 1900 }{}) 1901 if err == nil { 1902 t.Errorf("Unmarshal: expected error, got nil") 1903 } 1904 }