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