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>&rarr;&nbsp;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=\"&amp;&lt;&gt;&apos;&quot;\t\n\r\">&amp;&lt;&gt;&apos;&quot;\t\n\r</e>",
266			"<e a=\"&amp;&lt;>'&quot;&#x9;&#xA;&#xD;\">&amp;&lt;&gt;&apos;&quot;\t\n\r</e>",
267			"<e a=\"&amp;&lt;&gt;&apos;&quot;\t\n\r\">&amp;&lt;&gt;'\"\t\n&#xD;</e>",
268		},
269		{
270			"\x00\x1f\x08\x09\x0a\x0d",
271			"<e a=\"���\t\n\r\">���\t\n\r</e>",
272			"<e a=\"���&#x9;&#xA;&#xD;\">���\t\n\r</e>",
273			"<e a=\"���\t\n\r\">���\t\n&#xD;</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">&#xD;&lt;'"&gt;&amp;����</Person>
340  <Person name="Sally" escape="&#xD;&#xA;&#x9;&lt;'&quot;>&amp;"></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