diff --git a/xpath_axes_test.go b/xpath_axes_test.go
new file mode 100644
index 0000000..b573638
--- /dev/null
+++ b/xpath_axes_test.go
@@ -0,0 +1,68 @@
+package xpath
+
+import "testing"
+
+func Test_self(t *testing.T) {
+ test_xpath_elements(t, employee_example, `//name/self::*`, 4, 9, 14)
+}
+
+func Test_child(t *testing.T) {
+ test_xpath_elements(t, employee_example, `/empinfo/child::*`, 3, 8, 13)
+ test_xpath_elements(t, employee_example, `/empinfo/child::node()`, 3, 8, 13)
+ test_xpath_values(t, employee_example, `//name/child::text()`, "Opal Kole", "Max Miller", "Beccaa Moss")
+ test_xpath_elements(t, employee_example, `//child::employee/child::email`, 6, 11, 16)
+}
+
+func Test_descendant(t *testing.T) {
+ test_xpath_elements(t, employee_example, `//employee/descendant::*`, 4, 5, 6, 9, 10, 11, 14, 15, 16)
+ test_xpath_count(t, employee_example, `//descendant::employee`, 3)
+}
+
+func Test_descendant_or_self(t *testing.T) {
+ test_xpath_tags(t, employee_example.FirstChild, `self::*`, "empinfo")
+ test_xpath_elements(t, employee_example, `//employee/descendant-or-self::*`, 3, 4, 5, 6, 8, 9, 10, 11, 13, 14, 15, 16)
+ test_xpath_count(t, employee_example, `//descendant-or-self::employee`, 3)
+}
+
+func Test_ancestor(t *testing.T) {
+ test_xpath_tags(t, employee_example, `//employee/ancestor::*`, "empinfo") //
+ test_xpath_elements(t, employee_example, `//ancestor::name`, 4, 9, 14)
+}
+
+func Test_ancestor_or_self(t *testing.T) {
+ test_xpath_elements(t, employee_example, `//employee/ancestor-or-self::*`, 2, 3, 8, 13)
+ test_xpath_elements(t, employee_example, `//name/ancestor-or-self::employee`, 2, 3, 8, 13)
+}
+
+func Test_parent(t *testing.T) {
+ test_xpath_elements(t, employee_example, `//name/parent::*`, 3, 8, 13)
+ test_xpath_elements(t, employee_example, `//name/parent::employee`, 3, 8, 13)
+}
+
+func Test_attribute(t *testing.T) {
+ test_xpath_values(t, employee_example, `//attribute::id`, "1", "2", "3")
+ test_xpath_tags(t, employee_example, `//attribute::*`, "id", "discipline", "experience", "id", "from", "discipline", "experience", "id", "discipline")
+}
+
+func Test_following(t *testing.T) {
+ test_xpath_elements(t, employee_example, `//employee[@id=1]/following::*`, 8, 9, 10, 11, 13, 14, 15, 16)
+}
+
+func Test_following_sibling(t *testing.T) {
+ test_xpath_elements(t, employee_example, `//employee[@id=1]/following-sibling::*`, 8, 13)
+ test_xpath_elements(t, employee_example, `//employee[@id=1]/following-sibling::employee`, 8, 13)
+}
+
+func Test_preceding(t *testing.T) {
+ //testXPath3(t, html, "//li[last()]/preceding-sibling::*[2]", selectNode(html, "//li[position()=2]"))
+ //testXPath3(t, html, "//li/preceding::*[1]", selectNode(html, "//h1"))
+ test_xpath_elements(t, employee_example, `//employee[@id=3]/preceding::*`, 8, 9, 10, 11, 3, 4, 5, 6)
+}
+
+func Test_preceding_sibling(t *testing.T) {
+ test_xpath_elements(t, employee_example, `//employee[@id=3]/preceding-sibling::*`, 8, 3)
+}
+
+func Test_namespace(t *testing.T) {
+ // TODO
+}
diff --git a/xpath_expression_test.go b/xpath_expression_test.go
new file mode 100644
index 0000000..11b700b
--- /dev/null
+++ b/xpath_expression_test.go
@@ -0,0 +1,55 @@
+package xpath
+
+import (
+ "testing"
+)
+
+// `*/employee` [Not supported]
+
+func TestRelativePaths(t *testing.T) {
+ test_xpath_elements(t, book_example, `//book`, 3, 9, 15, 25)
+ test_xpath_elements(t, book_example, `//bookstore/book`, 3, 9, 15, 25)
+ test_xpath_tags(t, book_example, `//book/..`, "bookstore")
+ test_xpath_elements(t, book_example, `//book[@category="cooking"]/..`, 2)
+ test_xpath_elements(t, book_example, `//book/year[text() = 2005]/../..`, 2) // bookstore
+ test_xpath_elements(t, book_example, `//book/year/../following-sibling::*`, 9, 15, 25)
+ test_xpath_count(t, book_example, `//bookstore/book/*`, 20)
+ test_xpath_tags(t, html_example, "//title/../..", "html")
+ test_xpath_elements(t, html_example, "//ul/../p", 19)
+}
+
+func TestAbsolutePaths(t *testing.T) {
+ test_xpath_elements(t, book_example, `bookstore`, 2)
+ test_xpath_elements(t, book_example, `bookstore/book`, 3, 9, 15, 25)
+ test_xpath_elements(t, book_example, `(bookstore/book)`, 3, 9, 15, 25)
+ test_xpath_elements(t, book_example, `bookstore/book[2]`, 9)
+ test_xpath_elements(t, book_example, `bookstore/book[last()]`, 25)
+ test_xpath_elements(t, book_example, `bookstore/book[last()]/title`, 26)
+ test_xpath_values(t, book_example, `/bookstore/book[last()]/title/text()`, "Learning XML")
+ test_xpath_values(t, book_example, `/bookstore/book[@category = "children"]/year`, "2005")
+ test_xpath_elements(t, book_example, `bookstore/book/..`, 2)
+ test_xpath_elements(t, book_example, `/bookstore/*`, 3, 9, 15, 25)
+ test_xpath_elements(t, book_example, `/bookstore/*/title`, 4, 10, 16, 26)
+}
+
+func TestAttributes(t *testing.T) {
+ test_xpath_tags(t, html_example.FirstChild, "@*", "lang")
+ test_xpath_tags(t, employee_example, `//@*`, "id", "discipline", "experience", "id", "from", "discipline", "experience", "id", "discipline")
+ test_xpath_values(t, employee_example, `//@discipline`, "web", "DBA", "appdev")
+ test_xpath_count(t, employee_example, `//employee/@id`, 3)
+}
+
+func TestExpressions(t *testing.T) {
+ test_xpath_elements(t, book_example, `//book[@category = "cooking"] | //book[@category = "children"]`, 3, 9)
+ test_xpath_count(t, html_example, `//ul/*`, 3)
+ test_xpath_count(t, html_example, `//ul/*/a`, 3)
+ // Sequence
+ //
+ // table/tbody/tr/td/(para, .[not(para)], ..)
+}
+
+func TestSequence(t *testing.T) {
+ // `//table/tbody/tr/td/(para, .[not(para)],..)`
+ test_xpath_count(t, html_example, `//body/(h1, h2, p)`, 2)
+ test_xpath_count(t, html_example, `//body/(h1, h2, p, ..)`, 3)
+}
diff --git a/xpath_function_test.go b/xpath_function_test.go
new file mode 100644
index 0000000..d9153e9
--- /dev/null
+++ b/xpath_function_test.go
@@ -0,0 +1,212 @@
+package xpath
+
+import (
+ "math"
+ "testing"
+)
+
+// Some test examples from http://zvon.org/comp/r/ref-XPath_2.html
+
+func Test_func_boolean(t *testing.T) {
+ test_xpath_eval(t, empty_example, `true()`, true)
+ test_xpath_eval(t, empty_example, `false()`, false)
+ test_xpath_eval(t, empty_example, `boolean(0)`, false)
+ test_xpath_eval(t, empty_example, `boolean(1)`, true)
+ test_xpath_eval(t, empty_example, `boolean(2)`, true)
+ test_xpath_eval(t, empty_example, `boolean(true)`, false)
+ test_xpath_eval(t, empty_example, `boolean(1 > 2)`, false)
+ test_xpath_eval(t, book_example, `boolean(//*[@lang])`, true)
+ test_xpath_eval(t, book_example, `boolean(//*[@x])`, false)
+}
+
+func Test_func_name(t *testing.T) {
+ test_xpath_eval(t, html_example, `name(//html/@lang)`, "lang")
+ test_xpath_eval(t, html_example, `name(html/head/title)`, "title")
+ test_xpath_count(t, html_example, `//*[name() = "li"]`, 3)
+}
+
+func Test_func_not(t *testing.T) {
+ //test_xpath_eval(t, empty_example, `not(0)`, true)
+ //test_xpath_eval(t, empty_example, `not(1)`, false)
+ test_xpath_elements(t, employee_example, `//employee[not(@id = "1")]`, 8, 13)
+ test_xpath_elements(t, book_example, `//book[not(year = 2005)]`, 15, 25)
+ test_xpath_count(t, book_example, `//book[not(title)]`, 0)
+}
+
+func Test_func_ceiling_floor(t *testing.T) {
+ test_xpath_eval(t, empty_example, "ceiling(5.2)", float64(6))
+ test_xpath_eval(t, empty_example, "floor(5.2)", float64(5))
+}
+
+func Test_func_concat(t *testing.T) {
+ test_xpath_eval(t, empty_example, `concat("1", "2", "3")`, "123")
+ //test_xpath_eval(t, empty_example, `concat("Ciao!", ())`, "Ciao!")
+ test_xpath_eval(t, book_example, `concat(//book[1]/title, ", ", //book[1]/year)`, "Everyday Italian, 2005")
+}
+
+func Test_func_contains(t *testing.T) {
+ test_xpath_eval(t, empty_example, `contains("tattoo", "t")`, true)
+ test_xpath_eval(t, empty_example, `contains("tattoo", "T")`, false)
+ test_xpath_eval(t, empty_example, `contains("tattoo", "ttt")`, false)
+ //test_xpath_eval(t, empty_example, `contains("", ())`, true)
+ test_xpath_elements(t, book_example, `//book[contains(title, "Potter")]`, 9)
+ test_xpath_elements(t, book_example, `//book[contains(year, "2005")]`, 3, 9)
+ assertPanic(t, func() { selectNode(html_example, "//*[contains(0, 0)]") })
+}
+
+func Test_func_count(t *testing.T) {
+ test_xpath_eval(t, book_example, `count(//book)`, float64(4))
+ test_xpath_eval(t, book_example, `count(//book[3]/author)`, float64(5))
+}
+
+func Test_func_ends_with(t *testing.T) {
+ test_xpath_eval(t, empty_example, `ends-with("tattoo", "tattoo")`, true)
+ test_xpath_eval(t, empty_example, `ends-with("tattoo", "atto")`, false)
+ test_xpath_elements(t, book_example, `//book[ends-with(@category,'ing')]`, 3)
+ test_xpath_elements(t, book_example, `//book[ends-with(./price,'.99')]`, 9, 15)
+ assertPanic(t, func() { selectNode(html_example, `//*[ends-with(0, 0)]`) }) // arg must be start with string
+ assertPanic(t, func() { selectNode(html_example, `//*[ends-with(name(), 0)]`) })
+}
+
+func Test_func_last(t *testing.T) {
+ test_xpath_elements(t, book_example, `//bookstore[last()]`, 2)
+ test_xpath_elements(t, book_example, `//bookstore/book[last()]`, 25)
+ test_xpath_elements(t, html_example, `//ul/li[last()]`, 15)
+ test_xpath_elements(t, html_example, `(//ul/li)[last()]`, 15)
+}
+
+func Test_func_local_name(t *testing.T) {
+ // TODO
+}
+
+func Test_func_starts_with(t *testing.T) {
+ test_xpath_eval(t, employee_example, `starts-with("tattoo", "tat")`, true)
+ test_xpath_eval(t, employee_example, `starts-with("tattoo", "att")`, false)
+ test_xpath_elements(t, book_example, `//book[starts-with(title,'Everyday')]`, 3)
+ assertPanic(t, func() { selectNode(html_example, `//*[starts-with(0, 0)]`) })
+ assertPanic(t, func() { selectNode(html_example, `//*[starts-with(name(), 0)]`) })
+}
+
+func Test_func_string(t *testing.T) {
+ test_xpath_eval(t, empty_example, `string(1.23)`, "1.23")
+ test_xpath_eval(t, empty_example, `string(3)`, "3")
+}
+
+func Test_func_string_join(t *testing.T) {
+ //test_xpath_eval(t, empty_example, `string-join(('Now', 'is', 'the', 'time', '...'), '')`, "Now is the time ...")
+ test_xpath_eval(t, empty_example, `string-join("some text", ";")`, "some text")
+ test_xpath_eval(t, book_example, `string-join(//book/@category, ";")`, "cooking;children;web;web")
+}
+
+func Test_func_string_length(t *testing.T) {
+ test_xpath_eval(t, empty_example, `string-length("Harp not on that string, madam; that is past.")`, float64(45))
+ test_xpath_eval(t, empty_example, `string-length(normalize-space(' abc '))`, float64(3))
+ test_xpath_eval(t, html_example, `string-length(//title/text())`, float64(len("My page")))
+ test_xpath_eval(t, html_example, `string-length(//html/@lang)`, float64(len("en")))
+ test_xpath_count(t, employee_example, `//employee[string-length(@id) > 0]`, 3) // = //employee[@id]
+}
+
+func Test_func_substring(t *testing.T) {
+ test_xpath_eval(t, empty_example, `substring("motor car", 6)`, " car")
+ test_xpath_eval(t, empty_example, `substring("metadata", 4, 3)`, "ada")
+ //test_xpath_eval(t, empty_example, `substring("12345", 5, -3)`, "") ?? it should be 1 ??
+ //test_xpath_eval(t, empty_example, `substring("12345", 1.5, 2.6)`, "234")
+ //test_xpath_eval(t, empty_example, `substring("12345", 0, 3)`, "12") // panic??
+ //test_xpath_eval(t, empty_example, `substring("12345", 5, -3)`, "1")
+ test_xpath_eval(t, html_example, `substring(//title/child::node(), 1)`, "My page")
+}
+
+func Test_func_substring_after(t *testing.T) {
+ test_xpath_eval(t, empty_example, `substring-after("tattoo", "tat")`, "too")
+ test_xpath_eval(t, empty_example, `substring-after("tattoo", "tattoo")`, "")
+}
+
+func Test_func_substring_before(t *testing.T) {
+ test_xpath_eval(t, empty_example, `substring-before("tattoo", "attoo")`, "t")
+ test_xpath_eval(t, empty_example, `substring-before("tattoo", "tatto")`, "")
+}
+
+func Test_func_sum(t *testing.T) {
+ test_xpath_eval(t, empty_example, `sum(1 + 2)`, float64(3))
+ test_xpath_eval(t, empty_example, `sum(1.1 + 2)`, float64(3.1))
+ test_xpath_eval(t, book_example, `sum(//book/price)`, float64(149.93))
+ assertPanic(t, func() { selectNode(html_example, `//title[sum('Hello') = 0]`) })
+}
+
+func Test_func_translate(t *testing.T) {
+ test_xpath_eval(t, empty_example, `translate("bar","abc","ABC")`, "BAr")
+ test_xpath_eval(t, empty_example, `translate("--aaa--","abc-","ABC")`, "AAA")
+ test_xpath_eval(t, empty_example, `translate("abcdabc", "abc", "AB")`, "ABdAB")
+ test_xpath_eval(t, empty_example, `translate('The quick brown fox', 'brown', 'red')`, "The quick red fdx")
+}
+
+func Test_func_matches(t *testing.T) {
+ test_xpath_eval(t, empty_example, `matches("abracadabra", "bra")`, true)
+ test_xpath_eval(t, empty_example, `matches("abracadabra", "(?i)^A.*A$")`, true)
+ test_xpath_eval(t, empty_example, `matches("abracadabra", "^a.*a$")`, true)
+ test_xpath_eval(t, empty_example, `matches("abracadabra", "^bra")`, false)
+ assertPanic(t, func() { selectNode(html_example, `//*[matches()]`) }) // arg len check failure
+ assertPanic(t, func() { selectNode(html_example, "//*[matches(substring(), 0)]") }) // first arg processing failure
+ assertPanic(t, func() { selectNode(html_example, "//*[matches(@href, substring())]") }) // second arg processing failure
+ assertPanic(t, func() { selectNode(html_example, "//*[matches(@href, 0)]") }) // second arg not string
+ assertPanic(t, func() { selectNode(html_example, "//*[matches(@href, '[invalid')]") }) // second arg invalid regexp
+ // testing unexpected the regular expression.
+ _, err := Compile(`//*[matches(., '^[\u0621-\u064AA-Za-z\-]+')]`)
+ assertErr(t, err)
+ _, err = Compile(`//*[matches(., '//*[matches(., '\w+`)
+ assertErr(t, err)
+}
+
+func Test_func_number(t *testing.T) {
+ test_xpath_eval(t, empty_example, `number(10)`, float64(10))
+ test_xpath_eval(t, empty_example, `number(1.11)`, float64(1.11))
+ test_xpath_eval(t, empty_example, `number("10") > 10`, false)
+ test_xpath_eval(t, empty_example, `number("10") = 10`, true)
+ test_xpath_eval(t, empty_example, `number("123") < 1000`, true)
+ assertTrue(t, math.IsNaN(MustCompile(`number("123a")`).Evaluate(createNavigator(empty_example)).(float64)))
+}
+
+func Test_func_position(t *testing.T) {
+ test_xpath_elements(t, book_example, `//book[position() = 1]`, 3)
+ //test_xpath_elements(t, book_example, `//book[(position() mod 2) = 0]`, 9, 25)
+ //test_xpath_elements(t, book_example, `//book[position() = last()]`, 25)
+ //test_xpath_elements(t, book_example, `//book/*[position() = 1]`, 4, 10, 16, 26)
+ test_xpath_elements(t, book_example, `(//book/title)[position() = 1]`, 3)
+}
+
+func Test_func_replace(t *testing.T) {
+ test_xpath_eval(t, empty_example, `replace('aa-bb-cc','bb','ee')`, "aa-ee-cc")
+ test_xpath_eval(t, empty_example, `replace("abracadabra", "bra", "*")`, "a*cada*")
+ test_xpath_eval(t, empty_example, `replace("abracadabra", "a", "")`, "brcdbr")
+ // The below xpath expressions is not supported yet
+ //
+ //test_xpath_eval(t, empty_example, `replace("abracadabra", "a.*a", "*")`, "*")
+ //test_xpath_eval(t, empty_example, `replace("abracadabra", "a.*?a", "*")`, "*c*bra")
+ //test_xpath_eval(t, empty_example, `replace("abracadabra", ".*?", "$1")`, "*c*bra") // error, because the pattern matches the zero-length string
+ //test_xpath_eval(t, empty_example, `replace("AAAA", "A+", "b")`, "b")
+ //test_xpath_eval(t, empty_example, `replace("AAAA", "A+?", "b")`, "bbb")
+ //test_xpath_eval(t, empty_example, `replace("darted", "^(.*?)d(.*)$", "$1c$2")`, "carted")
+ //test_xpath_eval(t, empty_example, `replace("abracadabra", "a(.)", "a$1$1")`, "abbraccaddabbra")
+}
+
+func Test_func_reverse(t *testing.T) {
+ //test_xpath_eval(t, employee_example, `reverse(("hello"))`, "hello") // Not passed
+ test_xpath_elements(t, employee_example, `reverse(//employee)`, 13, 8, 3)
+ test_xpath_elements(t, employee_example, `//employee[reverse(.) = reverse(.)]`, 3, 8, 13)
+ assertPanic(t, func() { selectNode(html_example, "reverse(concat())") }) // invalid node-sets argument.
+ assertPanic(t, func() { selectNode(html_example, "reverse()") }) // missing node-sets argument.
+}
+
+func Test_func_round(t *testing.T) {
+ test_xpath_eval(t, employee_example, `round(2.5)`, 3) // int
+ test_xpath_eval(t, employee_example, `round(2.5)`, 3)
+ test_xpath_eval(t, employee_example, `round(2.4999)`, 2)
+}
+
+func Test_func_namespace_uri(t *testing.T) {
+ // TODO
+}
+
+func Test_func_normalize_space(t *testing.T) {
+ // TODO
+}
diff --git a/xpath_node_test.go b/xpath_node_test.go
deleted file mode 100644
index a34dfd4..0000000
--- a/xpath_node_test.go
+++ /dev/null
@@ -1,327 +0,0 @@
-package xpath
-
-import "testing"
-
-func TestXPathNode_self(t *testing.T) {
- s := `//name/self::*`
- nodes := selectNodes(employee_example, s)
- assertEqual(t, 3, len(nodes))
-
- expecteds := []struct {
- lines int
- value string
- }{
- {lines: 4, value: "Opal Kole"},
- {lines: 9, value: "Max Miller"},
- {lines: 14, value: "Beccaa Moss"},
- }
- for i := 0; i < 3; i++ {
- n := nodes[i]
- assertEqual(t, "name", n.Data)
- e := expecteds[i]
- assertEqual(t, e.lines, n.lines)
- assertEqual(t, e.value, n.Value())
- }
-}
-
-func TestXPathNode_child(t *testing.T) {
- s := `/empinfo/child::*`
- nodes := selectNodes(employee_example, s)
- assertEqual(t, 3, len(nodes))
- expected_1 := []struct {
- lines int
- value string
- }{{lines: 3}, {lines: 8}, {lines: 13}}
- for i := 0; i < 3; i++ {
- n := nodes[i]
- assertEqual(t, "employee", n.Data)
- assertEqual(t, expected_1[i].lines, n.lines)
- }
-
- s = `/empinfo/child::node()`
- nodes = selectNodes(employee_example, s)
- assertEqual(t, 3, len(nodes))
- for _, n := range nodes {
- assertEqual(t, ElementNode, n.Type)
- assertEqual(t, "employee", n.Data)
- }
-
- s = `//name/child::text()`
- nodes = selectNodes(employee_example, s)
- assertEqual(t, 3, len(nodes))
- expected_2 := []string{"Opal Kole", "Max Miller", "Beccaa Moss"}
- for i := 0; i < 3; i++ {
- n := nodes[i]
- assertEqual(t, TextNode, n.Type)
- assertEqual(t, expected_2[i], n.Value())
- }
-
- s = `//child::employee/child::email`
- nodes = selectNodes(employee_example, s)
- assertEqual(t, 3, len(nodes))
- for i := 0; i < 3; i++ {
- n := nodes[i]
- assertEqual(t, "email", n.Data)
- }
-}
-
-func TestXPathNode_descendant(t *testing.T) {
- s := `//employee/descendant::*`
- nodes := selectNodes(employee_example, s)
- assertEqual(t, 9, len(nodes))
- expecteds := []struct {
- lines int
- tag string
- }{
- {4, "name"},
- {5, "designation"},
- {6, "email"},
- {9, "name"},
- {10, "designation"},
- {11, "email"},
- {14, "name"},
- {15, "designation"},
- {16, "email"},
- }
- for i := 0; i < 9; i++ {
- n := nodes[i]
- assertEqual(t, expecteds[i].lines, n.lines)
- assertEqual(t, expecteds[i].tag, n.Data)
- }
-
- s = `//descendant::employee`
- nodes = selectNodes(employee_example, s)
- assertEqual(t, 3, len(nodes))
- for _, n := range nodes {
- assertEqual(t, "employee", n.Data)
- }
-}
-
-func TestXPathNode_descendant_or_self(t *testing.T) {
- s := `//employee/descendant-or-self::*`
- nodes := selectNodes(employee_example, s)
- assertEqual(t, 12, len(nodes))
- expected_1 := []struct {
- lines int
- tag string
- }{
- {3, "employee"},
- {4, "name"},
- {5, "designation"},
- {6, "email"},
- {8, "employee"},
- {9, "name"},
- {10, "designation"},
- {11, "email"},
- {13, "employee"},
- {14, "name"},
- {15, "designation"},
- {16, "email"},
- }
- for i := 0; i < 12; i++ {
- n := nodes[i]
- assertEqual(t, expected_1[i].lines, n.lines)
- assertEqual(t, expected_1[i].tag, n.Data)
- }
-
- s = `//descendant-or-self::employee`
- nodes = selectNodes(employee_example, s)
- // Not Passed
- assertEqual(t, 3, len(nodes))
- for _, n := range nodes {
- assertEqual(t, "employee", n.Data)
- }
-}
-
-func TestXPathNode_ancestor(t *testing.T) {
- s := `//employee/ancestor::*`
- nodes := selectNodes(employee_example, s)
- assertEqual(t, 1, len(nodes))
- n := nodes[0]
- assertEqual(t, "empinfo", n.Data)
-
- s = `//ancestor::name`
- nodes = selectNodes(employee_example, s)
- assertEqual(t, 3, len(nodes))
- for _, n := range nodes {
- assertEqual(t, "name", n.Data)
- }
-}
-
-func TestXPathNode_ancestor_or_self(t *testing.T) {
- s := `//employee/ancestor-or-self::*`
- nodes := selectNodes(employee_example, s)
- assertEqual(t, 4, len(nodes))
-
- expecteds := []struct {
- lines int
- tag string
- }{
- {2, "empinfo"},
- {3, "employee"},
- {8, "employee"},
- {13, "employee"},
- }
-
- for i := 0; i < 4; i++ {
- n := nodes[i]
- assertEqual(t, expecteds[i].lines, n.lines)
- assertEqual(t, expecteds[i].tag, n.Data)
- }
-
- s = `//name/ancestor-or-self::employee`
- nodes = selectNodes(employee_example, s)
- assertEqual(t, 3, len(nodes))
- for i := 0; i < 3; i++ {
- n := nodes[i]
- assertEqual(t, expecteds[i+1].lines, n.lines)
- assertEqual(t, expecteds[i+1].tag, n.Data)
- }
-}
-
-func TestXPathNode_parent(t *testing.T) {
- s := `//name/parent::*`
- nodes := selectNodes(employee_example, s)
- assertEqual(t, 3, len(nodes))
-
- expecteds := []struct {
- lines int
- tag string
- }{
- {3, "employee"},
- {8, "employee"},
- {13, "employee"},
- }
- for i := 0; i < 3; i++ {
- n := nodes[i]
- assertEqual(t, expecteds[i].lines, n.lines)
- assertEqual(t, expecteds[i].tag, n.Data)
- }
-
- s = `//name/parent::employee`
- nodes = selectNodes(employee_example, s)
- assertEqual(t, 3, len(nodes))
- for i := 0; i < 3; i++ {
- n := nodes[i]
- assertEqual(t, expecteds[i].lines, n.lines)
- assertEqual(t, expecteds[i].tag, n.Data)
- }
-}
-
-func TestXPathNode_attribute(t *testing.T) {
- s := `//attribute::id`
- nodes := selectNodes(employee_example, s)
- assertEqual(t, 3, len(nodes))
-
- expecteds := []string{"1", "2", "3"}
- for i := 0; i < 3; i++ {
- n := nodes[i]
- assertEqual(t, AttributeNode.String(), n.Type.String())
- assertEqual(t, expecteds[i], n.Data)
- }
-
- s = `//attribute::*`
- nodes = selectNodes(employee_example, s)
- assertEqual(t, 9, len(nodes))
- expected_attributes := []string{"id", "discipline", "experience", "id", "from", "discipline", "experience", "id", "discipline"}
- for i := 0; i < 9; i++ {
- n := nodes[i]
- assertEqual(t, expected_attributes[i], n.Data)
- }
-}
-
-func TestXPathNode_following(t *testing.T) {
- s := `//employee[@id=1]/following::*`
- nodes := selectNodes(employee_example, s)
- assertEqual(t, 8, len(nodes))
-
- expecteds := []struct {
- lines int
- tag string
- }{
- {8, "employee"},
- {9, "name"},
- {10, "designation"},
- {11, "email"},
- {13, "employee"},
- {14, "name"},
- {15, "designation"},
- {16, "email"},
- }
-
- for i := 0; i < 8; i++ {
- n := nodes[i]
- assertEqual(t, expecteds[i].lines, n.lines)
- assertEqual(t, expecteds[i].tag, n.Data)
- }
-}
-
-func TestXPathNode_following_sibling(t *testing.T) {
- s := `//employee[@id=1]/following-sibling::*`
- nodes := selectNodes(employee_example, s)
- assertEqual(t, 2, len(nodes))
-
- expecteds := []struct {
- lines int
- tag string
- }{
- {8, "employee"},
- {13, "employee"},
- }
-
- for i := 0; i < 2; i++ {
- n := nodes[i]
- assertEqual(t, expecteds[i].lines, n.lines)
- assertEqual(t, expecteds[i].tag, n.Data)
- }
-}
-
-func TestXPathNode_preceding(t *testing.T) {
- s := `//employee[@id=3]/preceding::*`
- nodes := selectNodes(employee_example, s)
- // Warning: The sorted result nodes is incorrect. [3, 4, ..] should be at first.
- assertEqual(t, 8, len(nodes))
-
- expecteds := []struct {
- lines int
- tag string
- }{
- {8, "employee"},
- {9, "name"},
- {10, "designation"},
- {11, "email"},
-
- {3, "employee"},
- {4, "name"},
- {5, "designation"},
- {6, "email"},
- }
-
- for i := 0; i < 8; i++ {
- n := nodes[i]
- assertEqual(t, expecteds[i].lines, n.lines)
- assertEqual(t, expecteds[i].tag, n.Data)
- }
-}
-
-func TestXPathNode_preceding_sibling(t *testing.T) {
- s := `//employee[@id=3]/preceding-sibling::*`
- nodes := selectNodes(employee_example, s)
- // Warning: The sorted result nodes is incorrect. [3, 8] should be at first.
- assertEqual(t, 2, len(nodes))
-
- expecteds := []struct {
- lines int
- tag string
- }{
- {8, "employee"},
- {3, "employee"},
- }
-
- for i := 0; i < 8; i++ {
- n := nodes[i]
- assertEqual(t, expecteds[i].lines, n.lines)
- assertEqual(t, expecteds[i].tag, n.Data)
- }
-
-}
diff --git a/xpath_predicate_test.go b/xpath_predicate_test.go
index 48fbc53..30126e1 100644
--- a/xpath_predicate_test.go
+++ b/xpath_predicate_test.go
@@ -1,140 +1,57 @@
package xpath
-import "testing"
-
-func TestXPathPredicate_Positions(t *testing.T) {
- s := `/empinfo/employee[2]`
- nodes := selectNodes(employee_example, s)
- assertEqual(t, 1, len(nodes))
- assertEqual(t, 8, nodes[0].lines)
-
- s = `/empinfo/employee[2]/name`
- nodes = selectNodes(employee_example, s)
- assertEqual(t, 1, len(nodes))
- assertEqual(t, 9, nodes[0].lines)
-
- s = `//employee[position()=2]`
- nodes = selectNodes(employee_example, s)
- assertEqual(t, 1, len(nodes))
- assertEqual(t, 8, nodes[0].lines)
-
- s = `//employee[position()>1]`
- nodes = selectNodes(employee_example, s)
- assertEqual(t, 2, len(nodes))
-
- expecteds := []int{8, 13}
- for i := 0; i < 2; i++ {
- n := nodes[i]
- assertEqual(t, expecteds[i], n.lines)
- }
-
- s = `//employee[position()<=3]`
- nodes = selectNodes(employee_example, s)
- expecteds = []int{3, 8, 13}
- for i := 0; i < 2; i++ {
- n := nodes[i]
- assertEqual(t, expecteds[i], n.lines)
- }
+import (
+ "testing"
+)
+
+func TestLogicals(t *testing.T) {
+ test_xpath_elements(t, book_example, `//book[1 + 1]`, 9)
+ test_xpath_elements(t, book_example, `//book[1 * 2]`, 9)
+ test_xpath_elements(t, book_example, `//book[5 div 2]`, 9) // equal to `//book[2]`
+ test_xpath_elements(t, book_example, `//book[3 div 2]`, 3)
+ test_xpath_elements(t, book_example, `//book[3 - 2]`, 3)
+ test_xpath_elements(t, book_example, `//book[price > 35]`, 15, 25)
+ test_xpath_elements(t, book_example, `//book[price >= 30]`, 3, 15, 25)
+ test_xpath_elements(t, book_example, `//book[price < 30]`, 9)
+ test_xpath_elements(t, book_example, `//book[price <= 30]`, 3, 9)
+ test_xpath_elements(t, book_example, `//book[count(author) > 1]`, 15)
+ test_xpath_elements(t, book_example, `//book[position() mod 2 = 0]`, 9, 25)
}
-func TestXPathPredicate_Nodes(t *testing.T) {
- s := `//employee[name]`
- nodes := selectNodes(employee_example, s)
- assertEqual(t, 3, len(nodes))
- expecteds := []int{3, 8, 13}
- for i := 0; i < 2; i++ {
- n := nodes[i]
- assertEqual(t, expecteds[i], n.lines)
- }
+func TestPositions(t *testing.T) {
+ test_xpath_elements(t, employee_example, `/empinfo/employee[2]`, 8)
+ test_xpath_elements(t, employee_example, `//employee[position() = 2]`, 8)
+ test_xpath_elements(t, employee_example, `/empinfo/employee[2]/name`, 9)
+ test_xpath_elements(t, employee_example, `//employee[position() > 1]`, 8, 13)
+ test_xpath_elements(t, employee_example, `//employee[position() <= 2]`, 3, 8)
+ test_xpath_elements(t, employee_example, `//employee[last()]`, 13)
+ test_xpath_elements(t, employee_example, `//employee[position() = last()]`, 13)
+ test_xpath_elements(t, book_example, `//book[@category = "web"][2]`, 25)
+ test_xpath_elements(t, book_example, `(//book[@category = "web"])[2]`, 25)
}
-func TestXPathPredicate_Operators(t *testing.T) {
- s := `/empinfo/employee[@id]`
- nodes := selectNodes(employee_example, s)
- assertEqual(t, 3, len(nodes))
-
- s = `/empinfo/employee[@id = 2]`
- nodes = selectNodes(employee_example, s)
- assertEqual(t, 1, len(nodes))
- assertEqual(t, 8, nodes[0].lines)
-
- s = `/empinfo/employee[1][@id=1]`
- nodes = selectNodes(employee_example, s)
- assertEqual(t, 1, len(nodes))
- assertEqual(t, 3, nodes[0].lines)
-
- s = `/empinfo/employee[@id][2]`
- nodes = selectNodes(employee_example, s)
- assertEqual(t, 1, len(nodes))
- assertEqual(t, 8, nodes[0].lines)
-
- s = `//designation[@discipline and @experience]`
- nodes = selectNodes(employee_example, s)
- assertEqual(t, 2, len(nodes))
- expecteds := []int{5, 10}
- for i := 0; i < 2; i++ {
- n := nodes[i]
- assertEqual(t, expecteds[i], n.lines)
- }
-
- s = `//designation[@discipline or @experience]`
- nodes = selectNodes(employee_example, s)
- assertEqual(t, 3, len(nodes))
- expecteds = []int{5, 10, 15}
- for i := 0; i < 3; i++ {
- n := nodes[i]
- assertEqual(t, expecteds[i], n.lines)
- }
-
- s = `//designation[@discipline | @experience]`
- nodes = selectNodes(employee_example, s)
- assertEqual(t, 3, len(nodes))
- expecteds = []int{5, 10, 15}
- for i := 0; i < 3; i++ {
- n := nodes[i]
- assertEqual(t, expecteds[i], n.lines)
- }
-
- s = `/empinfo/employee[@id != "2" ]`
- nodes = selectNodes(employee_example, s)
- assertEqual(t, 2, len(nodes))
- expecteds = []int{3, 13}
- for i := 0; i < 2; i++ {
- n := nodes[i]
- assertEqual(t, expecteds[i], n.lines)
- }
-
- s = `/empinfo/employee[@id > 1 ]`
- nodes = selectNodes(employee_example, s)
- assertEqual(t, 2, len(nodes))
- expecteds = []int{8, 13}
- for i := 0; i < 2; i++ {
- n := nodes[i]
- assertEqual(t, expecteds[i], n.lines)
- }
-
- s = `/empinfo/employee[@id and @id = "2"]`
- nodes = selectNodes(employee_example, s)
- assertEqual(t, 1, len(nodes))
- assertEqual(t, 8, nodes[0].lines)
-
- s = `/empinfo/employee[@id = "1" or @id = "2"]`
- nodes = selectNodes(employee_example, s)
- expecteds = []int{3, 8}
- for i := 0; i < 2; i++ {
- n := nodes[i]
- assertEqual(t, expecteds[i], n.lines)
- }
+func TestPredicates(t *testing.T) {
+ test_xpath_elements(t, employee_example, `//employee[name]`, 3, 8, 13)
+ test_xpath_elements(t, employee_example, `/empinfo/employee[@id]`, 3, 8, 13)
+ test_xpath_elements(t, book_example, `//book[@category = "web"]`, 15, 25)
+ test_xpath_elements(t, book_example, `//book[author = "J K. Rowling"]`, 9)
+ test_xpath_elements(t, book_example, `//book[./author/text() = "J K. Rowling"]`, 9)
+ test_xpath_elements(t, book_example, `//book[year = 2005]`, 3, 9)
+ test_xpath_elements(t, book_example, `//year[text() = 2005]`, 6, 12)
+ test_xpath_elements(t, employee_example, `/empinfo/employee[1][@id=1]`, 3)
+ test_xpath_elements(t, employee_example, `/empinfo/employee[@id][2]`, 8)
}
-func TestXPathPredicate_NestedPredicate(t *testing.T) {
- s := `//employee[./name[@from]]`
- nodes := selectNodes(employee_example, s)
- assertEqual(t, 1, len(nodes))
- assertEqual(t, 8, nodes[0].lines)
+func TestOperators(t *testing.T) {
+ test_xpath_elements(t, employee_example, `//designation[@discipline and @experience]`, 5, 10)
+ test_xpath_elements(t, employee_example, `//designation[@discipline or @experience]`, 5, 10, 15)
+ test_xpath_elements(t, employee_example, `//designation[@discipline | @experience]`, 5, 10, 15)
+ test_xpath_elements(t, employee_example, `/empinfo/employee[@id != "2" ]`, 3, 13)
+ test_xpath_elements(t, employee_example, `/empinfo/employee[@id and @id = "2"]`, 8)
+ test_xpath_elements(t, employee_example, `/empinfo/employee[@id = "1" or @id = "2"]`, 3, 8)
+}
- s = `//employee[.//name[@from = "CA"]]`
- nodes = selectNodes(employee_example, s)
- assertEqual(t, 1, len(nodes))
- assertEqual(t, 8, nodes[0].lines)
+func TestNestedPredicates(t *testing.T) {
+ test_xpath_elements(t, employee_example, `//employee[./name[@from]]`, 8)
+ test_xpath_elements(t, employee_example, `//employee[.//name[@from = "CA"]]`, 8)
}
diff --git a/xpath_test.go b/xpath_test.go
index a5a7b5d..9761d70 100644
--- a/xpath_test.go
+++ b/xpath_test.go
@@ -7,516 +7,156 @@ import (
"testing"
)
-func (n NodeType) String() string {
- switch n {
- case RootNode:
- return "RootNode"
- case ElementNode:
- return "ElementNode"
- case AttributeNode:
- return "AttributeNode"
- case TextNode:
- return "TextNode"
- case CommentNode:
- return "CommentNode"
- }
- return "allNode"
-}
-
var (
- html = example()
- html2 = example2()
employee_example = createEmployeeExample()
+ book_example = createBookExample()
+ html_example = createHtmlExample()
+ empty_example = createNode("", RootNode)
)
+func test_xpath_elements(t *testing.T, root *TNode, expr string, expected ...int) {
+ result := selectNodes(root, expr)
+ assertEqual(t, len(expected), len(result))
+
+ for i := 0; i < len(expected); i++ {
+ assertEqual(t, expected[i], result[i].lines)
+ }
+}
+
+func test_xpath_values(t *testing.T, root *TNode, expr string, expected ...string) {
+ result := selectNodes(root, expr)
+ assertEqual(t, len(expected), len(result))
+
+ for i := 0; i < len(expected); i++ {
+ assertEqual(t, expected[i], result[i].Value())
+ }
+}
+
+func test_xpath_tags(t *testing.T, root *TNode, expr string, expected ...string) {
+ result := selectNodes(root, expr)
+ assertEqual(t, len(expected), len(result))
+
+ for i := 0; i < len(expected); i++ {
+ assertEqual(t, expected[i], result[i].Data)
+ }
+}
+
+func test_xpath_count(t *testing.T, root *TNode, expr string, expected int) {
+ result := selectNodes(root, expr)
+ assertEqual(t, expected, len(result))
+}
+
+func test_xpath_eval(t *testing.T, root *TNode, expr string, expected ...interface{}) {
+ e, err := Compile(expr)
+ assertNoErr(t, err)
+
+ v := e.Evaluate(createNavigator(root))
+ // if is a node-set
+ if iter, ok := v.(*NodeIterator); ok {
+ got := iterateNavs(iter)
+ assertEqual(t, len(expected), len(got))
+ for i := 0; i < len(expected); i++ {
+ assertEqual(t, expected[i], got[i])
+ }
+ return
+ }
+ assertEqual(t, expected[0], v)
+}
+
func TestCompile(t *testing.T) {
var err error
_, err = Compile("//a")
- if err != nil {
- t.Fatalf("//a should be correct but got error %s", err)
- }
+ assertNil(t, err)
_, err = Compile("//a[id=']/span")
- if err == nil {
- t.Fatal("//a[id=] should be got correct but is nil")
- }
+ assertErr(t, err)
_, err = Compile("//ul/li/@class")
- if err != nil {
- t.Fatalf("//ul/li/@class should be correct but got error %s", err)
- }
+ assertNil(t, err)
_, err = Compile("/a/b/(c, .[not(c)])")
- if err != nil {
- t.Fatalf("/a/b/(c, .[not(c)]) should be correct but got error %s", err)
- }
+ assertNil(t, err)
_, err = Compile("/pre:foo")
- if err != nil {
- t.Fatalf("/pre:foo should be correct but got error %s", err)
- }
+ assertNil(t, err)
}
func TestCompileWithNS(t *testing.T) {
_, err := CompileWithNS("/foo", nil)
- if err != nil {
- t.Fatalf("/foo {nil} should be correct but got error %s", err)
- }
+ assertNil(t, err)
_, err = CompileWithNS("/foo", map[string]string{})
- if err != nil {
- t.Fatalf("/foo {} should be correct but got error %s", err)
- }
+ assertNil(t, err)
_, err = CompileWithNS("/foo", map[string]string{"a": "b"})
- if err != nil {
- t.Fatalf("/foo {a:b} should be correct but got error %s", err)
- }
+ assertNil(t, err)
_, err = CompileWithNS("/a:foo", map[string]string{"a": "b"})
- if err != nil {
- t.Fatalf("/a:foo should be correct but got error %s", err)
- }
+ assertNil(t, err)
_, err = CompileWithNS("/u:foo", map[string]string{"a": "b"})
- msg := fmt.Sprintf("%v", err)
- if msg != "prefix u not defined." {
- t.Fatalf("expected 'prefix u not defined' but got: %s", msg)
- }
+ assertErr(t, err)
}
-func TestNamespace(t *testing.T) {
+func TestNamespacePrefixQuery(t *testing.T) {
+ /*
+
+
row1-val1 | -row1-val2 | -row1-val3 | -
row3-val1 | -row3-val3 | -
- Hello,This is an example for gxpath. -
- - + +This is the first paragraph.
+ + */ + lines := 0 doc := createNode("", RootNode) + lines++ xhtml := doc.createChildNode("html", ElementNode) + xhtml.lines = lines xhtml.addAttribute("lang", "en") - - // The HTML head section. + lines++ + // head container head := xhtml.createChildNode("head", ElementNode) - n := head.createChildNode("title", ElementNode) - n = n.createChildNode("Hello", TextNode) - n = head.createChildNode("meta", ElementNode) - n.addAttribute("name", "language") - n.addAttribute("content", "en") - n = head.createChildNode("meta", ElementNode) - n.addAttribute("name", "description") - n.addAttribute("content", "some description") - // The HTML body section. + head.lines = lines + lines++ + title := head.createChildNode("title", ElementNode) + title.lines = lines + title.createChildNode("My page", TextNode) + lines++ + meta := head.createChildNode("meta", ElementNode) + meta.lines = lines + meta.addAttribute("name", "language") + meta.addAttribute("content", "en") + // skip the head + lines++ + lines++ body := xhtml.createChildNode("body", ElementNode) - n = body.createChildNode("h1", ElementNode) - n = n.createChildNode("\nThis is a H1\n", TextNode) - ul := body.createChildNode("ul", ElementNode) - n = ul.createChildNode("li", ElementNode) - n = n.createChildNode("a", ElementNode) - n.addAttribute("id", "1") - n.addAttribute("href", "/") - n = n.createChildNode("Home", TextNode) - n = ul.createChildNode("li", ElementNode) - n = n.createChildNode("a", ElementNode) - n.addAttribute("id", "2") - n.addAttribute("href", "/about") - n = n.createChildNode("about", TextNode) - n = ul.createChildNode("li", ElementNode) - n = n.createChildNode("a", ElementNode) - n.addAttribute("id", "3") - n.addAttribute("href", "/account") - n = n.createChildNode("login", TextNode) - n = ul.createChildNode("li", ElementNode) - - n = body.createChildNode("p", ElementNode) - n = n.createChildNode("Hello,This is an example for gxpath.", TextNode) - - n = body.createChildNode("footer", ElementNode) - n = n.createChildNode("footer script", TextNode) + body.lines = lines + lines++ + h2 := body.createChildNode("h2", ElementNode) + h2.lines = lines + h2.createChildNode("Welcome to my page", TextNode) + lines++ + links := []struct { + text string + href string + }{ + {text: "Home", href: "/"}, + {text: "About", href: "/About"}, + {text: "Login", href: "/account"}, + } - return xhtml + ul := body.createChildNode("ul", ElementNode) + ul.lines = lines + lines++ + for i := 0; i < len(links); i++ { + link := links[i] + li := ul.createChildNode("li", ElementNode) + li.lines = lines + lines++ + a := li.createChildNode("a", ElementNode) + a.lines = lines + a.addAttribute("href", link.href) + a.createChildNode(link.text, TextNode) + lines++ + // skip the