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