1// Copyright 2015-2019 Brett Vickers. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package etree 6 7import ( 8 "encoding/xml" 9 "io" 10 "strings" 11 "testing" 12) 13 14func checkStrEq(t *testing.T, got, want string) { 15 t.Helper() 16 if got != want { 17 t.Errorf("etree: unexpected result.\nGot:\n%s\nWanted:\n%s\n", got, want) 18 } 19} 20 21func checkStrBinaryEq(t *testing.T, got, want string) { 22 t.Helper() 23 if got != want { 24 t.Errorf("etree: unexpected result.\nGot:\n%v\nWanted:\n%v\n", []byte(got), []byte(want)) 25 } 26} 27 28func checkIntEq(t *testing.T, got, want int) { 29 t.Helper() 30 if got != want { 31 t.Errorf("etree: unexpected integer. Got: %d. Wanted: %d\n", got, want) 32 } 33} 34 35func checkElementEq(t *testing.T, got, want *Element) { 36 t.Helper() 37 if got != want { 38 t.Errorf("etree: unexpected element. Got: %v. Wanted: %v.\n", got, want) 39 } 40} 41 42func checkDocEq(t *testing.T, doc *Document, expected string) { 43 t.Helper() 44 doc.Indent(NoIndent) 45 s, err := doc.WriteToString() 46 if err != nil { 47 t.Error("etree: failed to serialize document") 48 } 49 if s != expected { 50 t.Errorf("etree: unexpected document.\nGot:\n%s\nWanted:\n%s\n", s, expected) 51 } 52} 53 54func checkIndexes(t *testing.T, e *Element) { 55 t.Helper() 56 for i := 0; i < len(e.Child); i++ { 57 c := e.Child[i] 58 if c.Index() != i { 59 t.Errorf("Child index mismatch. Got %d, expected %d.", c.Index(), i) 60 } 61 if ce, ok := c.(*Element); ok { 62 checkIndexes(t, ce) 63 } 64 } 65} 66 67func TestDocument(t *testing.T) { 68 // Create a document 69 doc := NewDocument() 70 doc.CreateProcInst("xml", `version="1.0" encoding="UTF-8"`) 71 doc.CreateProcInst("xml-stylesheet", `type="text/xsl" href="style.xsl"`) 72 store := doc.CreateElement("store") 73 store.CreateAttr("xmlns:t", "urn:books-com:titles") 74 store.CreateDirective("Directive") 75 store.CreateComment("This is a comment") 76 book := store.CreateElement("book") 77 book.CreateAttr("lang", "fr") 78 book.CreateAttr("lang", "en") 79 title := book.CreateElement("t:title") 80 title.SetText("Nicholas Nickleby") 81 title.SetText("Great Expectations") 82 author := book.CreateElement("author") 83 author.CreateCharData("Charles Dickens") 84 review := book.CreateElement("review") 85 review.CreateCData("<<< Will be replaced") 86 review.SetCData(">>> Excellent book") 87 doc.IndentTabs() 88 89 checkIndexes(t, &doc.Element) 90 91 // Serialize the document to a string 92 s, err := doc.WriteToString() 93 if err != nil { 94 t.Error("etree: failed to serialize document") 95 } 96 97 // Make sure the serialized XML matches expectation. 98 expected := `<?xml version="1.0" encoding="UTF-8"?> 99<?xml-stylesheet type="text/xsl" href="style.xsl"?> 100<store xmlns:t="urn:books-com:titles"> 101 <!Directive> 102 <!--This is a comment--> 103 <book lang="en"> 104 <t:title>Great Expectations</t:title> 105 <author>Charles Dickens</author> 106 <review><![CDATA[>>> Excellent book]]></review> 107 </book> 108</store> 109` 110 checkStrEq(t, s, expected) 111 112 // Test the structure of the XML 113 if doc.Root() != store { 114 t.Error("etree: root mismatch") 115 } 116 if len(store.ChildElements()) != 1 || len(store.Child) != 7 { 117 t.Error("etree: incorrect tree structure") 118 } 119 if len(book.ChildElements()) != 3 || len(book.Attr) != 1 || len(book.Child) != 7 { 120 t.Error("etree: incorrect tree structure") 121 } 122 if len(title.ChildElements()) != 0 || len(title.Child) != 1 || len(title.Attr) != 0 { 123 t.Error("etree: incorrect tree structure") 124 } 125 if len(author.ChildElements()) != 0 || len(author.Child) != 1 || len(author.Attr) != 0 { 126 t.Error("etree: incorrect tree structure") 127 } 128 if len(review.ChildElements()) != 0 || len(review.Child) != 1 || len(review.Attr) != 0 { 129 t.Error("etree: incorrect tree structure") 130 } 131 if book.parent != store || store.parent != &doc.Element || doc.parent != nil { 132 t.Error("etree: incorrect tree structure") 133 } 134 if title.parent != book || author.parent != book { 135 t.Error("etree: incorrect tree structure") 136 } 137 138 // Perform some basic queries on the document 139 elements := doc.SelectElements("store") 140 if len(elements) != 1 || elements[0] != store { 141 t.Error("etree: incorrect SelectElements result") 142 } 143 element := doc.SelectElement("store") 144 if element != store { 145 t.Error("etree: incorrect SelectElement result") 146 } 147 elements = store.SelectElements("book") 148 if len(elements) != 1 || elements[0] != book { 149 t.Error("etree: incorrect SelectElements result") 150 } 151 element = store.SelectElement("book") 152 if element != book { 153 t.Error("etree: incorrect SelectElement result") 154 } 155 attr := book.SelectAttr("lang") 156 if attr == nil || attr.Key != "lang" || attr.Value != "en" { 157 t.Error("etree: incorrect SelectAttr result") 158 } 159 if book.SelectAttrValue("lang", "unknown") != "en" { 160 t.Error("etree: incorrect SelectAttrValue result") 161 } 162 if book.SelectAttrValue("t:missing", "unknown") != "unknown" { 163 t.Error("etree: incorrect SelectAttrValue result") 164 } 165 attr = book.RemoveAttr("lang") 166 if attr.Value != "en" { 167 t.Error("etree: incorrect RemoveAttr result") 168 } 169 book.CreateAttr("lang", "de") 170 attr = book.RemoveAttr("lang") 171 if attr.Value != "de" { 172 t.Error("etree: incorrect RemoveAttr result") 173 } 174 element = book.SelectElement("t:title") 175 if element != title || element.Text() != "Great Expectations" || len(element.Attr) != 0 { 176 t.Error("etree: incorrect SelectElement result") 177 } 178 element = book.SelectElement("title") 179 if element != title { 180 t.Error("etree: incorrect SelectElement result") 181 } 182 element = book.SelectElement("p:title") 183 if element != nil { 184 t.Error("etree: incorrect SelectElement result") 185 } 186 element = book.RemoveChildAt(title.Index()).(*Element) 187 if element != title { 188 t.Error("etree: incorrect RemoveElement result") 189 } 190 element = book.SelectElement("title") 191 if element != nil { 192 t.Error("etree: incorrect SelectElement result") 193 } 194 element = book.SelectElement("review") 195 if element != review || element.Text() != ">>> Excellent book" || len(element.Attr) != 0 { 196 t.Error("etree: incorrect SelectElement result") 197 } 198} 199 200func TestDocumentRead_NonUTF8Encodings(t *testing.T) { 201 s := `<?xml version="1.0" encoding="ISO-8859-1"?> 202 <store> 203 <book lang="en"> 204 <title>Great Expectations</title> 205 <author>Charles Dickens</author> 206 </book> 207</store>` 208 209 doc := NewDocument() 210 doc.ReadSettings.CharsetReader = func(label string, input io.Reader) (io.Reader, error) { 211 return input, nil 212 } 213 err := doc.ReadFromString(s) 214 if err != nil { 215 t.Fatal("etree: incorrect ReadFromString result") 216 } 217} 218 219func TestDocumentRead_Permissive(t *testing.T) { 220 s := "<select disabled></select>" 221 222 doc := NewDocument() 223 err := doc.ReadFromString(s) 224 if err == nil { 225 t.Fatal("etree: incorrect ReadFromString result") 226 } 227 228 doc.ReadSettings.Permissive = true 229 err = doc.ReadFromString(s) 230 if err != nil { 231 t.Fatal("etree: incorrect ReadFromString result") 232 } 233} 234 235func TestDocumentRead_HTMLEntities(t *testing.T) { 236 s := `<store> 237 <book lang="en"> 238 <title>→ Great Expectations</title> 239 <author>Charles Dickens</author> 240 </book> 241</store>` 242 243 doc := NewDocument() 244 err := doc.ReadFromString(s) 245 if err == nil { 246 t.Fatal("etree: incorrect ReadFromString result") 247 } 248 249 doc.ReadSettings.Entity = xml.HTMLEntity 250 err = doc.ReadFromString(s) 251 if err != nil { 252 t.Fatal("etree: incorrect ReadFromString result") 253 } 254} 255 256func TestEscapeCodes(t *testing.T) { 257 cases := []struct { 258 input string 259 normal string 260 attrCanonical string 261 textCanonical string 262 }{ 263 { 264 "&<>'\"\t\n\r", 265 "<e a=\"&<>'"\t\n\r\">&<>'"\t\n\r</e>", 266 "<e a=\"&<>'"	

\">&<>'"\t\n\r</e>", 267 "<e a=\"&<>'"\t\n\r\">&<>'\"\t\n
</e>", 268 }, 269 { 270 "\x00\x1f\x08\x09\x0a\x0d", 271 "<e a=\"���\t\n\r\">���\t\n\r</e>", 272 "<e a=\"���	

\">���\t\n\r</e>", 273 "<e a=\"���\t\n\r\">���\t\n
</e>", 274 }, 275 } 276 for _, c := range cases { 277 doc := NewDocument() 278 279 e := doc.CreateElement("e") 280 e.SetText(c.input) 281 e.CreateAttr("a", c.input) 282 283 doc.WriteSettings.CanonicalText = false 284 doc.WriteSettings.CanonicalAttrVal = false 285 s, err := doc.WriteToString() 286 if err != nil { 287 t.Error("etree: Escape test produced inocrrect result.") 288 } 289 checkStrEq(t, s, c.normal) 290 291 doc.WriteSettings.CanonicalText = false 292 doc.WriteSettings.CanonicalAttrVal = true 293 s, err = doc.WriteToString() 294 if err != nil { 295 t.Error("etree: Escape test produced inocrrect result.") 296 } 297 checkStrEq(t, s, c.attrCanonical) 298 299 doc.WriteSettings.CanonicalText = true 300 doc.WriteSettings.CanonicalAttrVal = false 301 s, err = doc.WriteToString() 302 if err != nil { 303 t.Error("etree: Escape test produced inocrrect result.") 304 } 305 checkStrEq(t, s, c.textCanonical) 306 } 307} 308 309func TestCanonical(t *testing.T) { 310 BOM := "\xef\xbb\xbf" 311 312 doc := NewDocument() 313 doc.WriteSettings.CanonicalEndTags = true 314 doc.WriteSettings.CanonicalText = true 315 doc.WriteSettings.CanonicalAttrVal = true 316 doc.CreateCharData(BOM) 317 doc.CreateProcInst("xml-stylesheet", `type="text/xsl" href="style.xsl"`) 318 319 people := doc.CreateElement("People") 320 people.CreateComment("These are all known people") 321 322 jon := people.CreateElement("Person") 323 jon.CreateAttr("name", "Jon O'Reilly") 324 jon.SetText("\r<'\">&\u0004\u0005\u001f�") 325 326 sally := people.CreateElement("Person") 327 sally.CreateAttr("name", "Sally") 328 sally.CreateAttr("escape", "\r\n\t<'\">&") 329 330 doc.Indent(2) 331 s, err := doc.WriteToString() 332 if err != nil { 333 t.Error("etree: WriteSettings WriteTo produced incorrect result.") 334 } 335 336 expected := BOM + `<?xml-stylesheet type="text/xsl" href="style.xsl"?> 337<People> 338 <!--These are all known people--> 339 <Person name="Jon O'Reilly">
<'">&����</Person> 340 <Person name="Sally" escape="
	<'">&"></Person> 341</People> 342` 343 checkStrEq(t, s, expected) 344} 345 346func TestCopy(t *testing.T) { 347 s := `<store> 348 <book lang="en"> 349 <title>Great Expectations</title> 350 <author>Charles Dickens</author> 351 </book> 352</store>` 353 354 doc := NewDocument() 355 err := doc.ReadFromString(s) 356 if err != nil { 357 t.Fatal("etree: incorrect ReadFromString result") 358 } 359 360 s1, err := doc.WriteToString() 361 if err != nil { 362 t.Error("etree: incorrect WriteToString result") 363 } 364 365 doc2 := doc.Copy() 366 checkIndexes(t, &doc2.Element) 367 s2, err := doc2.WriteToString() 368 if err != nil { 369 t.Error("etree: incorrect Copy result") 370 } 371 372 if s1 != s2 { 373 t.Error("etree: mismatched Copy result") 374 t.Error("wanted:\n" + s1) 375 t.Error("got:\n" + s2) 376 } 377 378 e1 := doc.FindElement("./store/book/title") 379 e2 := doc2.FindElement("./store/book/title") 380 if e1 == nil || e2 == nil { 381 t.Error("etree: incorrect FindElement result") 382 } 383 if e1 == e2 { 384 t.Error("etree: incorrect FindElement result") 385 } 386 387 e1.parent.RemoveChildAt(e1.Index()) 388 s1, _ = doc.WriteToString() 389 s2, _ = doc2.WriteToString() 390 if s1 == s2 { 391 t.Error("etree: incorrect result after RemoveElement") 392 } 393} 394 395func TestGetPath(t *testing.T) { 396 testdoc := `<a> 397 <b1> 398 <c1> 399 <d1/> 400 <d1a/> 401 </c1> 402 </b1> 403 <b2> 404 <c2> 405 <d2/> 406 </c2> 407 </b2> 408</a>` 409 410 doc := NewDocument() 411 err := doc.ReadFromString(testdoc) 412 if err != nil { 413 t.Fatalf("etree ReadFromString: %v\n", err) 414 } 415 416 cases := []struct { 417 from string 418 to string 419 relpath string 420 topath string 421 }{ 422 {"a", ".", "..", "/"}, 423 {".", "a", "./a", "/a"}, 424 {"a/b1/c1/d1", ".", "../../../..", "/"}, 425 {".", "a/b1/c1/d1", "./a/b1/c1/d1", "/a/b1/c1/d1"}, 426 {"a", "a", ".", "/a"}, 427 {"a/b1", "a/b1/c1", "./c1", "/a/b1/c1"}, 428 {"a/b1/c1", "a/b1", "..", "/a/b1"}, 429 {"a/b1/c1", "a/b1/c1", ".", "/a/b1/c1"}, 430 {"a", "a/b1", "./b1", "/a/b1"}, 431 {"a/b1", "a", "..", "/a"}, 432 {"a", "a/b1/c1", "./b1/c1", "/a/b1/c1"}, 433 {"a/b1/c1", "a", "../..", "/a"}, 434 {"a/b1/c1/d1", "a", "../../..", "/a"}, 435 {"a", "a/b1/c1/d1", "./b1/c1/d1", "/a/b1/c1/d1"}, 436 {"a/b1", "a/b2", "../b2", "/a/b2"}, 437 {"a/b2", "a/b1", "../b1", "/a/b1"}, 438 {"a/b1/c1/d1", "a/b2/c2/d2", "../../../b2/c2/d2", "/a/b2/c2/d2"}, 439 {"a/b2/c2/d2", "a/b1/c1/d1", "../../../b1/c1/d1", "/a/b1/c1/d1"}, 440 {"a/b1/c1/d1", "a/b1/c1/d1a", "../d1a", "/a/b1/c1/d1a"}, 441 } 442 443 for _, c := range cases { 444 fe := doc.FindElement(c.from) 445 te := doc.FindElement(c.to) 446 447 rp := te.GetRelativePath(fe) 448 if rp != c.relpath { 449 t.Errorf("GetRelativePath from '%s' to '%s'. Expected '%s', got '%s'.\n", c.from, c.to, c.relpath, rp) 450 } 451 452 p := te.GetPath() 453 if p != c.topath { 454 t.Errorf("GetPath for '%s'. Expected '%s', got '%s'.\n", c.to, c.topath, p) 455 } 456 } 457} 458 459func TestInsertChild(t *testing.T) { 460 testdoc := `<book lang="en"> 461 <t:title>Great Expectations</t:title> 462 <author>Charles Dickens</author> 463</book> 464` 465 466 doc := NewDocument() 467 err := doc.ReadFromString(testdoc) 468 if err != nil { 469 t.Fatal("etree ReadFromString: " + err.Error()) 470 } 471 472 year := NewElement("year") 473 year.SetText("1861") 474 475 book := doc.FindElement("//book") 476 book.InsertChildAt(book.SelectElement("t:title").Index(), year) 477 478 expected1 := `<book lang="en"> 479 <year>1861</year> 480 <t:title>Great Expectations</t:title> 481 <author>Charles Dickens</author> 482</book> 483` 484 doc.Indent(2) 485 s1, _ := doc.WriteToString() 486 checkStrEq(t, s1, expected1) 487 488 book.RemoveChildAt(year.Index()) 489 book.InsertChildAt(book.SelectElement("author").Index(), year) 490 491 expected2 := `<book lang="en"> 492 <t:title>Great Expectations</t:title> 493 <year>1861</year> 494 <author>Charles Dickens</author> 495</book> 496` 497 doc.Indent(2) 498 s2, _ := doc.WriteToString() 499 checkStrEq(t, s2, expected2) 500 501 book.RemoveChildAt(year.Index()) 502 book.InsertChildAt(len(book.Child), year) 503 504 expected3 := `<book lang="en"> 505 <t:title>Great Expectations</t:title> 506 <author>Charles Dickens</author> 507 <year>1861</year> 508</book> 509` 510 doc.Indent(2) 511 s3, _ := doc.WriteToString() 512 checkStrEq(t, s3, expected3) 513 514 book.RemoveChildAt(year.Index()) 515 book.InsertChildAt(999, year) 516 517 expected4 := `<book lang="en"> 518 <t:title>Great Expectations</t:title> 519 <author>Charles Dickens</author> 520 <year>1861</year> 521</book> 522` 523 doc.Indent(2) 524 s4, _ := doc.WriteToString() 525 checkStrEq(t, s4, expected4) 526} 527 528func TestCdata(t *testing.T) { 529 var tests = []struct { 530 in, out string 531 }{ 532 {`<tag>1234567</tag>`, "1234567"}, 533 {`<tag><![CDATA[1234567]]></tag>`, "1234567"}, 534 {`<tag>1<![CDATA[2]]>3<![CDATA[4]]>5<![CDATA[6]]>7</tag>`, "1234567"}, 535 {`<tag>1<![CDATA[2]]>3<inner>4</inner>5<![CDATA[6]]>7</tag>`, "123"}, 536 {`<tag>1<inner>4</inner>5<![CDATA[6]]>7</tag>`, "1"}, 537 {`<tag><![CDATA[1]]><inner>4</inner>5<![CDATA[6]]>7</tag>`, "1"}, 538 } 539 540 for _, test := range tests { 541 doc := NewDocument() 542 err := doc.ReadFromString(test.in) 543 if err != nil { 544 t.Fatal("etree ReadFromString: " + err.Error()) 545 } 546 547 tag := doc.FindElement("tag") 548 if tag.Text() != test.out { 549 t.Fatalf("etree invalid cdata. Expected: %v. Got: %v\n", test.out, tag.Text()) 550 } 551 } 552} 553 554func TestAddChild(t *testing.T) { 555 testdoc := `<book lang="en"> 556 <t:title>Great Expectations</t:title> 557 <author>Charles Dickens</author> 558</book> 559` 560 doc1 := NewDocument() 561 err := doc1.ReadFromString(testdoc) 562 if err != nil { 563 t.Fatal("etree ReadFromString: " + err.Error()) 564 } 565 566 doc2 := NewDocument() 567 root := doc2.CreateElement("root") 568 569 for _, e := range doc1.FindElements("//book/*") { 570 root.AddChild(e) 571 } 572 573 expected1 := `<book lang="en"/> 574` 575 doc1.Indent(2) 576 s1, _ := doc1.WriteToString() 577 checkStrEq(t, s1, expected1) 578 579 expected2 := `<root> 580 <t:title>Great Expectations</t:title> 581 <author>Charles Dickens</author> 582</root> 583` 584 doc2.Indent(2) 585 s2, _ := doc2.WriteToString() 586 checkStrEq(t, s2, expected2) 587} 588 589func TestSetRoot(t *testing.T) { 590 testdoc := `<?test a="wow"?> 591<book> 592 <title>Great Expectations</title> 593 <author>Charles Dickens</author> 594</book> 595` 596 doc := NewDocument() 597 err := doc.ReadFromString(testdoc) 598 if err != nil { 599 t.Fatal("etree ReadFromString: " + err.Error()) 600 } 601 602 origroot := doc.Root() 603 if origroot.Parent() != &doc.Element { 604 t.Error("Root incorrect") 605 } 606 607 newroot := NewElement("root") 608 doc.SetRoot(newroot) 609 610 if doc.Root() != newroot { 611 t.Error("doc.Root() != newroot") 612 } 613 if origroot.Parent() != nil { 614 t.Error("origroot.Parent() != nil") 615 } 616 617 expected1 := `<?test a="wow"?> 618<root/> 619` 620 doc.Indent(2) 621 s1, _ := doc.WriteToString() 622 checkStrEq(t, s1, expected1) 623 624 doc.SetRoot(origroot) 625 doc.Indent(2) 626 expected2 := testdoc 627 s2, _ := doc.WriteToString() 628 checkStrEq(t, s2, expected2) 629 630 doc2 := NewDocument() 631 doc2.CreateProcInst("test", `a="wow"`) 632 doc2.SetRoot(NewElement("root")) 633 doc2.Indent(2) 634 expected3 := expected1 635 s3, _ := doc2.WriteToString() 636 checkStrEq(t, s3, expected3) 637 638 doc2.SetRoot(doc.Root()) 639 doc2.Indent(2) 640 expected4 := testdoc 641 s4, _ := doc2.WriteToString() 642 checkStrEq(t, s4, expected4) 643 644 expected5 := `<?test a="wow"?> 645` 646 doc.Indent(2) 647 s5, _ := doc.WriteToString() 648 checkStrEq(t, s5, expected5) 649} 650 651func TestSortAttrs(t *testing.T) { 652 testdoc := `<el foo='5' Foo='2' aaa='4' สวัสดี='7' AAA='1' a01='3' z='6' a:ZZZ='9' a:AAA='8'/>` 653 doc := NewDocument() 654 err := doc.ReadFromString(testdoc) 655 if err != nil { 656 t.Fatal("etree ReadFromString: " + err.Error()) 657 } 658 659 doc.Root().SortAttrs() 660 doc.Indent(2) 661 out, _ := doc.WriteToString() 662 checkStrEq(t, out, `<el AAA="1" Foo="2" a01="3" aaa="4" foo="5" z="6" สวัสดี="7" a:AAA="8" a:ZZZ="9"/>`+"\n") 663} 664 665func TestCharsetReaderEncoding(t *testing.T) { 666 cases := []string{ 667 `<?xml version="1.0" encoding="ISO-8859-1"?><foo></foo>`, 668 `<?xml version="1.0" encoding="UTF-8"?><foo></foo>`, 669 `<?xml version="1.0" encoding="US-ASCII"?><foo></foo>`, 670 } 671 672 for _, c := range cases { 673 doc := NewDocument() 674 if err := doc.ReadFromBytes([]byte(c)); err != nil { 675 t.Error(err) 676 } 677 } 678} 679 680func TestCharData(t *testing.T) { 681 doc := NewDocument() 682 root := doc.CreateElement("root") 683 root.CreateCharData("This ") 684 root.CreateCData("is ") 685 e1 := NewText("a ") 686 e2 := NewCData("text ") 687 root.AddChild(e1) 688 root.AddChild(e2) 689 root.CreateCharData("Element!!") 690 691 s, err := doc.WriteToString() 692 if err != nil { 693 t.Error("etree: failed to serialize document") 694 } 695 696 checkStrEq(t, s, `<root>This <![CDATA[is ]]>a <![CDATA[text ]]>Element!!</root>`) 697 698 // Check we can parse the output 699 err = doc.ReadFromString(s) 700 if err != nil { 701 t.Fatal("etree: incorrect ReadFromString result") 702 } 703 if doc.Root().Text() != "This is a text Element!!" { 704 t.Error("etree: invalid text") 705 } 706} 707 708func TestIndentSettings(t *testing.T) { 709 doc := NewDocument() 710 root := doc.CreateElement("root") 711 ch1 := root.CreateElement("child1") 712 ch1.CreateElement("child2") 713 714 // First test with NoIndent. 715 doc.Indent(NoIndent) 716 s, err := doc.WriteToString() 717 if err != nil { 718 t.Error("etree: failed to serialize document") 719 } 720 expected := "<root><child1><child2/></child1></root>" 721 checkStrEq(t, s, expected) 722 723 // Run all indent test cases. 724 tests := []struct { 725 useTabs, useCRLF bool 726 ws, nl string 727 }{ 728 {false, false, " ", "\n"}, 729 {false, true, " ", "\r\n"}, 730 {true, false, "\t", "\n"}, 731 {true, true, "\t", "\r\n"}, 732 } 733 734 for _, test := range tests { 735 doc.WriteSettings.UseCRLF = test.useCRLF 736 if test.useTabs { 737 doc.IndentTabs() 738 s, err := doc.WriteToString() 739 if err != nil { 740 t.Error("etree: failed to serialize document") 741 } 742 tab := test.ws 743 expected := "<root>" + test.nl + tab + "<child1>" + test.nl + 744 tab + tab + "<child2/>" + test.nl + tab + 745 "</child1>" + test.nl + "</root>" + test.nl 746 checkStrEq(t, s, expected) 747 } else { 748 for i := 0; i < 256; i++ { 749 doc.Indent(i) 750 s, err := doc.WriteToString() 751 if err != nil { 752 t.Error("etree: failed to serialize document") 753 } 754 tab := strings.Repeat(test.ws, i) 755 expected := "<root>" + test.nl + tab + "<child1>" + test.nl + 756 tab + tab + "<child2/>" + test.nl + tab + 757 "</child1>" + test.nl + "</root>" + test.nl 758 checkStrEq(t, s, expected) 759 } 760 } 761 } 762} 763 764func TestTokenIndexing(t *testing.T) { 765 s := `<?xml version="1.0" encoding="UTF-8"?> 766<?xml-stylesheet type="text/xsl" href="style.xsl"?> 767<store xmlns:t="urn:books-com:titles"> 768 <!Directive> 769 <!--This is a comment--> 770 <book lang="en"> 771 <t:title>Great Expectations</t:title> 772 <author>Charles Dickens</author> 773 <review/> 774 </book> 775</store>` 776 777 doc := NewDocument() 778 err := doc.ReadFromString(s) 779 if err != nil { 780 t.Error("etree: failed to parse document") 781 } 782 783 review := doc.FindElement("/store/book/review") 784 review.SetText("Excellent") 785 786 checkIndexes(t, &doc.Element) 787 788 doc.Indent(4) 789 checkIndexes(t, &doc.Element) 790 791 doc.Indent(NoIndent) 792 checkIndexes(t, &doc.Element) 793 794 e := NewElement("foo") 795 store := doc.SelectElement("store") 796 store.InsertChildAt(0, e) 797 checkIndexes(t, &doc.Element) 798 799 store.RemoveChildAt(0) 800 checkIndexes(t, &doc.Element) 801} 802 803func TestSetText(t *testing.T) { 804 doc := NewDocument() 805 root := doc.CreateElement("root") 806 807 checkDocEq(t, doc, `<root/>`) 808 checkStrEq(t, root.Text(), "") 809 checkIntEq(t, len(root.Child), 0) 810 811 root.SetText("foo") 812 checkDocEq(t, doc, `<root>foo</root>`) 813 checkStrEq(t, root.Text(), "foo") 814 checkIntEq(t, len(root.Child), 1) 815 816 root.SetText("bar") 817 checkDocEq(t, doc, `<root>bar</root>`) 818 checkStrEq(t, root.Text(), "bar") 819 checkIntEq(t, len(root.Child), 1) 820 821 root.CreateCData("cdata") 822 checkDocEq(t, doc, `<root>bar<![CDATA[cdata]]></root>`) 823 checkStrEq(t, root.Text(), "barcdata") 824 checkIntEq(t, len(root.Child), 2) 825 826 root.SetText("qux") 827 checkDocEq(t, doc, `<root>qux</root>`) 828 checkStrEq(t, root.Text(), "qux") 829 checkIntEq(t, len(root.Child), 1) 830 831 root.CreateCData("cdata") 832 checkDocEq(t, doc, `<root>qux<![CDATA[cdata]]></root>`) 833 checkStrEq(t, root.Text(), "quxcdata") 834 checkIntEq(t, len(root.Child), 2) 835 836 root.SetCData("baz") 837 checkDocEq(t, doc, `<root><![CDATA[baz]]></root>`) 838 checkStrEq(t, root.Text(), "baz") 839 checkIntEq(t, len(root.Child), 1) 840 841 root.CreateText("corge") 842 root.CreateCData("grault") 843 root.CreateText("waldo") 844 root.CreateCData("fred") 845 root.CreateElement("child") 846 checkDocEq(t, doc, `<root><![CDATA[baz]]>corge<![CDATA[grault]]>waldo<![CDATA[fred]]><child/></root>`) 847 checkStrEq(t, root.Text(), "bazcorgegraultwaldofred") 848 checkIntEq(t, len(root.Child), 6) 849 850 root.SetText("plugh") 851 checkDocEq(t, doc, `<root>plugh<child/></root>`) 852 checkStrEq(t, root.Text(), "plugh") 853 checkIntEq(t, len(root.Child), 2) 854 855 root.SetText("") 856 checkDocEq(t, doc, `<root><child/></root>`) 857 checkStrEq(t, root.Text(), "") 858 checkIntEq(t, len(root.Child), 1) 859 860 root.SetText("") 861 checkDocEq(t, doc, `<root><child/></root>`) 862 checkStrEq(t, root.Text(), "") 863 checkIntEq(t, len(root.Child), 1) 864 865 root.RemoveChildAt(0) 866 root.CreateText("corge") 867 root.CreateCData("grault") 868 root.CreateText("waldo") 869 root.CreateCData("fred") 870 root.CreateElement("child") 871 checkDocEq(t, doc, `<root>corge<![CDATA[grault]]>waldo<![CDATA[fred]]><child/></root>`) 872 checkStrEq(t, root.Text(), "corgegraultwaldofred") 873 checkIntEq(t, len(root.Child), 5) 874 875 root.SetText("") 876 checkDocEq(t, doc, `<root><child/></root>`) 877 checkStrEq(t, root.Text(), "") 878 checkIntEq(t, len(root.Child), 1) 879} 880 881func TestSetTail(t *testing.T) { 882 doc := NewDocument() 883 root := doc.CreateElement("root") 884 child := root.CreateElement("child") 885 root.CreateText("\n\t") 886 child.SetText("foo") 887 checkDocEq(t, doc, "<root><child>foo</child>\n\t</root>") 888 checkStrEq(t, child.Tail(), "\n\t") 889 checkIntEq(t, len(root.Child), 2) 890 checkIntEq(t, len(child.Child), 1) 891 892 root.CreateCData(" ") 893 checkDocEq(t, doc, "<root><child>foo</child>\n\t<![CDATA[ ]]></root>") 894 checkStrEq(t, child.Tail(), "\n\t ") 895 checkIntEq(t, len(root.Child), 3) 896 checkIntEq(t, len(child.Child), 1) 897 898 child.SetTail("") 899 checkDocEq(t, doc, "<root><child>foo</child></root>") 900 checkStrEq(t, child.Tail(), "") 901 checkIntEq(t, len(root.Child), 1) 902 checkIntEq(t, len(child.Child), 1) 903 904 child.SetTail("\t\t\t") 905 checkDocEq(t, doc, "<root><child>foo</child>\t\t\t</root>") 906 checkStrEq(t, child.Tail(), "\t\t\t") 907 checkIntEq(t, len(root.Child), 2) 908 checkIntEq(t, len(child.Child), 1) 909 910 child.SetTail("\t\n\n\t") 911 checkDocEq(t, doc, "<root><child>foo</child>\t\n\n\t</root>") 912 checkStrEq(t, child.Tail(), "\t\n\n\t") 913 checkIntEq(t, len(root.Child), 2) 914 checkIntEq(t, len(child.Child), 1) 915 916 child.SetTail("") 917 checkDocEq(t, doc, "<root><child>foo</child></root>") 918 checkStrEq(t, child.Tail(), "") 919 checkIntEq(t, len(root.Child), 1) 920 checkIntEq(t, len(child.Child), 1) 921} 922 923func TestAttrParent(t *testing.T) { 924 doc := NewDocument() 925 root := doc.CreateElement("root") 926 attr1 := root.CreateAttr("bar", "1") 927 attr2 := root.CreateAttr("qux", "2") 928 929 checkIntEq(t, len(root.Attr), 2) 930 checkElementEq(t, attr1.Element(), root) 931 checkElementEq(t, attr2.Element(), root) 932 933 attr1 = root.RemoveAttr("bar") 934 attr2 = root.RemoveAttr("qux") 935 checkElementEq(t, attr1.Element(), nil) 936 checkElementEq(t, attr2.Element(), nil) 937 938 s := `<root a="1" b="2" c="3" d="4"/>` 939 err := doc.ReadFromString(s) 940 if err != nil { 941 t.Error("etree: failed to parse document") 942 } 943 944 root = doc.SelectElement("root") 945 for i := range root.Attr { 946 checkElementEq(t, root.Attr[i].Element(), root) 947 } 948} 949 950func TestDefaultNamespaceURI(t *testing.T) { 951 s := ` 952<root xmlns="http://root.example.com" a="foo"> 953 <child1 xmlns="http://child.example.com" a="foo"> 954 <grandchild1 xmlns="http://grandchild.example.com" a="foo"> 955 </grandchild1> 956 <grandchild2 a="foo"> 957 <greatgrandchild1 a="foo"/> 958 </grandchild2> 959 </child1> 960 <child2 a="foo"/> 961</root>` 962 963 doc := NewDocument() 964 err := doc.ReadFromString(s) 965 if err != nil { 966 t.Error("etree: failed to parse document") 967 } 968 969 root := doc.SelectElement("root") 970 child1 := root.SelectElement("child1") 971 child2 := root.SelectElement("child2") 972 grandchild1 := child1.SelectElement("grandchild1") 973 grandchild2 := child1.SelectElement("grandchild2") 974 greatgrandchild1 := grandchild2.SelectElement("greatgrandchild1") 975 976 checkStrEq(t, doc.NamespaceURI(), "") 977 checkStrEq(t, root.NamespaceURI(), "http://root.example.com") 978 checkStrEq(t, child1.NamespaceURI(), "http://child.example.com") 979 checkStrEq(t, child2.NamespaceURI(), "http://root.example.com") 980 checkStrEq(t, grandchild1.NamespaceURI(), "http://grandchild.example.com") 981 checkStrEq(t, grandchild2.NamespaceURI(), "http://child.example.com") 982 checkStrEq(t, greatgrandchild1.NamespaceURI(), "http://child.example.com") 983 984 checkStrEq(t, root.Attr[0].NamespaceURI(), "http://root.example.com") 985 checkStrEq(t, child1.Attr[0].NamespaceURI(), "http://child.example.com") 986 checkStrEq(t, child2.Attr[0].NamespaceURI(), "http://root.example.com") 987 checkStrEq(t, grandchild1.Attr[0].NamespaceURI(), "http://grandchild.example.com") 988 checkStrEq(t, grandchild2.Attr[0].NamespaceURI(), "http://child.example.com") 989 checkStrEq(t, greatgrandchild1.Attr[0].NamespaceURI(), "http://child.example.com") 990 991 f := doc.FindElements("//*[namespace-uri()='http://root.example.com']") 992 if len(f) != 2 || f[0] != root || f[1] != child2 { 993 t.Error("etree: failed namespace-uri test") 994 } 995 996 f = doc.FindElements("//*[namespace-uri()='http://child.example.com']") 997 if len(f) != 3 || f[0] != child1 || f[1] != grandchild2 || f[2] != greatgrandchild1 { 998 t.Error("etree: failed namespace-uri test") 999 } 1000 1001 f = doc.FindElements("//*[namespace-uri()='http://grandchild.example.com']") 1002 if len(f) != 1 || f[0] != grandchild1 { 1003 t.Error("etree: failed namespace-uri test") 1004 } 1005 1006 f = doc.FindElements("//*[namespace-uri()='']") 1007 if len(f) != 0 { 1008 t.Error("etree: failed namespace-uri test") 1009 } 1010 1011 f = doc.FindElements("//*[namespace-uri()='foo']") 1012 if len(f) != 0 { 1013 t.Error("etree: failed namespace-uri test") 1014 } 1015} 1016 1017func TestLocalNamespaceURI(t *testing.T) { 1018 s := ` 1019<a:root xmlns:a="http://root.example.com"> 1020 <b:child1 xmlns:b="http://child.example.com"> 1021 <c:grandchild1 xmlns:c="http://grandchild.example.com"/> 1022 <b:grandchild2> 1023 <a:greatgrandchild1/> 1024 </b:grandchild2> 1025 <a:grandchild3/> 1026 <grandchild4/> 1027 </b:child1> 1028 <a:child2> 1029 </a:child2> 1030 <child3> 1031 </child3> 1032</a:root>` 1033 1034 doc := NewDocument() 1035 err := doc.ReadFromString(s) 1036 if err != nil { 1037 t.Error("etree: failed to parse document") 1038 } 1039 1040 root := doc.SelectElement("root") 1041 child1 := root.SelectElement("child1") 1042 child2 := root.SelectElement("child2") 1043 child3 := root.SelectElement("child3") 1044 grandchild1 := child1.SelectElement("grandchild1") 1045 grandchild2 := child1.SelectElement("grandchild2") 1046 grandchild3 := child1.SelectElement("grandchild3") 1047 grandchild4 := child1.SelectElement("grandchild4") 1048 greatgrandchild1 := grandchild2.SelectElement("greatgrandchild1") 1049 1050 checkStrEq(t, doc.NamespaceURI(), "") 1051 checkStrEq(t, root.NamespaceURI(), "http://root.example.com") 1052 checkStrEq(t, child1.NamespaceURI(), "http://child.example.com") 1053 checkStrEq(t, child2.NamespaceURI(), "http://root.example.com") 1054 checkStrEq(t, child3.NamespaceURI(), "") 1055 checkStrEq(t, grandchild1.NamespaceURI(), "http://grandchild.example.com") 1056 checkStrEq(t, grandchild2.NamespaceURI(), "http://child.example.com") 1057 checkStrEq(t, grandchild3.NamespaceURI(), "http://root.example.com") 1058 checkStrEq(t, grandchild4.NamespaceURI(), "") 1059 checkStrEq(t, greatgrandchild1.NamespaceURI(), "http://root.example.com") 1060 1061 f := doc.FindElements("//*[namespace-uri()='http://root.example.com']") 1062 if len(f) != 4 || f[0] != root || f[1] != child2 || f[2] != grandchild3 || f[3] != greatgrandchild1 { 1063 t.Error("etree: failed namespace-uri test") 1064 } 1065 1066 f = doc.FindElements("//*[namespace-uri()='http://child.example.com']") 1067 if len(f) != 2 || f[0] != child1 || f[1] != grandchild2 { 1068 t.Error("etree: failed namespace-uri test") 1069 } 1070 1071 f = doc.FindElements("//*[namespace-uri()='http://grandchild.example.com']") 1072 if len(f) != 1 || f[0] != grandchild1 { 1073 t.Error("etree: failed namespace-uri test") 1074 } 1075 1076 f = doc.FindElements("//*[namespace-uri()='']") 1077 if len(f) != 2 || f[0] != child3 || f[1] != grandchild4 { 1078 t.Error("etree: failed namespace-uri test") 1079 } 1080 1081 f = doc.FindElements("//*[namespace-uri()='foo']") 1082 if len(f) != 0 { 1083 t.Error("etree: failed namespace-uri test") 1084 } 1085} 1086