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