Blob Blame History Raw
# -*- coding: utf-8 -*-

"""
Test cases related to XPath evaluation and the XPath class
"""

import unittest, sys, os.path

this_dir = os.path.dirname(__file__)
if this_dir not in sys.path:
    sys.path.insert(0, this_dir) # needed for Py3

from common_imports import etree, HelperTestCase, _bytes, BytesIO
from common_imports import doctest, make_doctest

class ETreeXPathTestCase(HelperTestCase):
    """XPath tests etree"""

    def test_xpath_boolean(self):
        tree = self.parse('<a><b></b><b></b></a>')
        self.assertTrue(tree.xpath('boolean(/a/b)'))
        self.assertTrue(not tree.xpath('boolean(/a/c)'))

    def test_xpath_number(self):
        tree = self.parse('<a>1</a>')
        self.assertEqual(1.,
                          tree.xpath('number(/a)'))
        tree = self.parse('<a>A</a>')
        actual = str(tree.xpath('number(/a)'))
        expected = ['nan', '1.#qnan', 'nanq']
        if not actual.lower() in expected:
            self.fail('Expected a NAN value, got %s' % actual)
        
    def test_xpath_string(self):
        tree = self.parse('<a>Foo</a>')
        self.assertEqual('Foo',
                          tree.xpath('string(/a/text())'))

    def test_xpath_document_root(self):
        tree = self.parse('<a><b/></a>')
        self.assertEqual([],
                          tree.xpath('/'))

    def test_xpath_namespace(self):
        tree = self.parse('<a xmlns="test" xmlns:p="myURI"/>')
        self.assertTrue((None, "test") in tree.xpath('namespace::*'))
        self.assertTrue(('p', 'myURI') in tree.xpath('namespace::*'))

    def test_xpath_namespace_empty(self):
        tree = self.parse('<a/>')
        self.assertEqual([('xml', 'http://www.w3.org/XML/1998/namespace')],
                          tree.xpath('namespace::*'))

    def test_xpath_list_elements(self):
        tree = self.parse('<a><b>Foo</b><b>Bar</b></a>')
        root = tree.getroot()
        self.assertEqual([root[0], root[1]],
                          tree.xpath('/a/b'))

    def test_xpath_list_nothing(self):
        tree = self.parse('<a><b/></a>')
        self.assertEqual([],
                          tree.xpath('/a/c'))
        # this seems to pass a different code path, also should return nothing
        self.assertEqual([],
                          tree.xpath('/a/c/text()'))
    
    def test_xpath_list_text(self):
        tree = self.parse('<a><b>Foo</b><b>Bar</b></a>')
        root = tree.getroot()
        self.assertEqual(['Foo', 'Bar'],
                          tree.xpath('/a/b/text()'))

    def test_xpath_list_text_parent(self):
        tree = self.parse('<a><b>FooBar</b><b>BarFoo</b></a>')
        root = tree.getroot()
        self.assertEqual(['FooBar', 'BarFoo'],
                          tree.xpath('/a/b/text()'))
        self.assertEqual([root[0], root[1]],
                          [r.getparent() for r in tree.xpath('/a/b/text()')])

    def test_xpath_list_text_parent_no_smart_strings(self):
        tree = self.parse('<a><b>FooBar</b><b>BarFoo</b></a>')
        root = tree.getroot()
        self.assertEqual(['FooBar', 'BarFoo'],
                          tree.xpath('/a/b/text()', smart_strings=True))
        self.assertEqual([root[0], root[1]],
                          [r.getparent() for r in
                           tree.xpath('/a/b/text()', smart_strings=True)])
        self.assertEqual([None, None],
                          [r.attrname for r in
                           tree.xpath('/a/b/text()', smart_strings=True)])

        self.assertEqual(['FooBar', 'BarFoo'],
                          tree.xpath('/a/b/text()', smart_strings=False))
        self.assertEqual([False, False],
                          [hasattr(r, 'getparent') for r in
                           tree.xpath('/a/b/text()', smart_strings=False)])
        self.assertEqual([None, None],
                          [r.attrname for r in
                           tree.xpath('/a/b/text()', smart_strings=True)])

    def test_xpath_list_unicode_text_parent(self):
        xml = _bytes('<a><b>FooBar\\u0680\\u3120</b><b>BarFoo\\u0680\\u3120</b></a>').decode("unicode_escape")
        tree = self.parse(xml.encode('utf-8'))
        root = tree.getroot()
        self.assertEqual([_bytes('FooBar\\u0680\\u3120').decode("unicode_escape"),
                           _bytes('BarFoo\\u0680\\u3120').decode("unicode_escape")],
                          tree.xpath('/a/b/text()'))
        self.assertEqual([root[0], root[1]],
                          [r.getparent() for r in tree.xpath('/a/b/text()')])

    def test_xpath_list_attribute(self):
        tree = self.parse('<a b="B" c="C"/>')
        self.assertEqual(['B'],
                          tree.xpath('/a/@b'))

    def test_xpath_list_attribute_parent(self):
        tree = self.parse('<a b="BaSdFgHjKl" c="CqWeRtZuI"/>')
        results = tree.xpath('/a/@c')
        self.assertEqual(1, len(results))
        self.assertEqual('CqWeRtZuI', results[0])
        self.assertEqual(tree.getroot().tag, results[0].getparent().tag)

    def test_xpath_list_attribute_parent_no_smart_strings(self):
        tree = self.parse('<a b="BaSdFgHjKl" c="CqWeRtZuI"/>')

        results = tree.xpath('/a/@c', smart_strings=True)
        self.assertEqual(1, len(results))
        self.assertEqual('CqWeRtZuI', results[0])
        self.assertEqual('c', results[0].attrname)
        self.assertEqual(tree.getroot().tag, results[0].getparent().tag)

        results = tree.xpath('/a/@c', smart_strings=False)
        self.assertEqual(1, len(results))
        self.assertEqual('CqWeRtZuI', results[0])
        self.assertEqual(False, hasattr(results[0], 'getparent'))
        self.assertEqual(False, hasattr(results[0], 'attrname'))

    def test_xpath_text_from_other_document(self):
        xml_data = '''
        <table>
                <item xml:id="k1"><value>v1</value></item>
                <item xml:id="k2"><value>v2</value></item>
        </table>
        '''

        def lookup(dummy, id):
            return etree.XML(xml_data).xpath('id(%r)' % id)
        functions = {(None, 'lookup') : lookup}

        root = etree.XML('<dummy/>')
        values = root.xpath("lookup('k1')/value/text()",
                           extensions=functions)
        self.assertEqual(['v1'], values)
        self.assertEqual('value', values[0].getparent().tag)

    def test_xpath_list_comment(self):
        tree = self.parse('<a><!-- Foo --></a>')
        self.assertEqual(['<!-- Foo -->'],
                          list(map(repr, tree.xpath('/a/node()'))))

    def test_rel_xpath_boolean(self):
        root = etree.XML('<a><b><c/></b></a>')
        el = root[0]
        self.assertTrue(el.xpath('boolean(c)'))
        self.assertTrue(not el.xpath('boolean(d)'))

    def test_rel_xpath_list_elements(self):
        tree = self.parse('<a><c><b>Foo</b><b>Bar</b></c><c><b>Hey</b></c></a>')
        root = tree.getroot()
        c = root[0]
        self.assertEqual([c[0], c[1]],
                          c.xpath('b'))
        self.assertEqual([c[0], c[1], root[1][0]],
                          c.xpath('//b'))

    def test_xpath_ns(self):
        tree = self.parse('<a xmlns="uri:a"><b></b></a>')
        root = tree.getroot()
        self.assertEqual(
            [root[0]],
            tree.xpath('//foo:b', namespaces={'foo': 'uri:a'}))
        self.assertEqual(
            [],
            tree.xpath('//foo:b', namespaces={'foo': 'uri:c'}))
        self.assertEqual(
            [root[0]],
            root.xpath('//baz:b', namespaces={'baz': 'uri:a'}))

    def test_xpath_ns_none(self):
        tree = self.parse('<a xmlns="uri:a"><b></b></a>')
        root = tree.getroot()
        self.assertRaises(
            TypeError,
            root.xpath, '//b', namespaces={None: 'uri:a'})

    def test_xpath_ns_empty(self):
        tree = self.parse('<a xmlns="uri:a"><b></b></a>')
        root = tree.getroot()
        self.assertRaises(
            TypeError,
            root.xpath, '//b', namespaces={'': 'uri:a'})

    def test_xpath_error(self):
        tree = self.parse('<a/>')
        self.assertRaises(etree.XPathEvalError, tree.xpath, '\\fad')

    def test_xpath_class_error(self):
        self.assertRaises(SyntaxError, etree.XPath, '\\fad')
        self.assertRaises(etree.XPathSyntaxError, etree.XPath, '\\fad')

    def test_xpath_prefix_error(self):
        tree = self.parse('<a/>')
        self.assertRaises(etree.XPathEvalError, tree.xpath, '/fa:d')

    def test_xpath_class_prefix_error(self):
        tree = self.parse('<a/>')
        xpath = etree.XPath("/fa:d")
        self.assertRaises(etree.XPathEvalError, xpath, tree)

    def test_elementtree_getpath(self):
        a  = etree.Element("a")
        b  = etree.SubElement(a, "b")
        c  = etree.SubElement(a, "c")
        d1 = etree.SubElement(c, "d")
        d2 = etree.SubElement(c, "d")

        tree = etree.ElementTree(a)
        self.assertEqual('/a/c/d',
                         tree.getpath(d2)[:6])
        self.assertEqual([d2],
                         tree.xpath(tree.getpath(d2)))

    def test_elementtree_getpath_partial(self):
        a  = etree.Element("a")
        b  = etree.SubElement(a, "b")
        c  = etree.SubElement(a, "c")
        d1 = etree.SubElement(c, "d")
        d2 = etree.SubElement(c, "d")

        tree = etree.ElementTree(c)
        self.assertEqual('/c/d',
                         tree.getpath(d2)[:4])
        self.assertEqual([d2],
                         tree.xpath(tree.getpath(d2)))

    def test_xpath_evaluator(self):
        tree = self.parse('<a><b><c></c></b></a>')
        e = etree.XPathEvaluator(tree)
        root = tree.getroot()
        self.assertEqual(
            [root],
            e('//a'))

    def test_xpath_evaluator_tree(self):
        tree = self.parse('<a><b><c></c></b></a>')
        child_tree = etree.ElementTree(tree.getroot()[0])
        e = etree.XPathEvaluator(child_tree)
        self.assertEqual(
            [],
            e('a'))
        root = child_tree.getroot()
        self.assertEqual(
            [root[0]],
            e('c'))

    def test_xpath_evaluator_tree_absolute(self):
        tree = self.parse('<a><b><c></c></b></a>')
        child_tree = etree.ElementTree(tree.getroot()[0])
        e = etree.XPathEvaluator(child_tree)
        self.assertEqual(
            [],
            e('/a'))
        root = child_tree.getroot()
        self.assertEqual(
            [root],
            e('/b'))
        self.assertEqual(
            [],
            e('/c'))

    def test_xpath_evaluator_element(self):
        tree = self.parse('<a><b><c></c></b></a>')
        root = tree.getroot()
        e = etree.XPathEvaluator(root[0])
        self.assertEqual(
            [root[0][0]],
            e('c'))
        
    def test_xpath_extensions(self):
        def foo(evaluator, a):
            return 'hello %s' % a
        extension = {(None, 'foo'): foo}
        tree = self.parse('<a><b></b></a>')
        e = etree.XPathEvaluator(tree, extensions=[extension])
        self.assertEqual(
            "hello you", e("foo('you')"))

    def test_xpath_extensions_wrong_args(self):
        def foo(evaluator, a, b):
            return "hello %s and %s" % (a, b)
        extension = {(None, 'foo'): foo}
        tree = self.parse('<a><b></b></a>')
        e = etree.XPathEvaluator(tree, extensions=[extension])
        self.assertRaises(TypeError, e, "foo('you')")

    def test_xpath_extensions_error(self):
        def foo(evaluator, a):
            return 1/0
        extension = {(None, 'foo'): foo}
        tree = self.parse('<a/>')
        e = etree.XPathEvaluator(tree, extensions=[extension])
        self.assertRaises(ZeroDivisionError, e, "foo('test')")

    def test_xpath_extensions_nodes(self):
        def f(evaluator, arg):
            r = etree.Element('results')
            b = etree.SubElement(r, 'result')
            b.text = 'Hoi'
            b = etree.SubElement(r, 'result')
            b.text = 'Dag'
            return r

        x = self.parse('<a/>')
        e = etree.XPathEvaluator(x, extensions=[{(None, 'foo'): f}])
        r = e("foo('World')/result")
        self.assertEqual(2, len(r))
        self.assertEqual('Hoi', r[0].text)
        self.assertEqual('Dag', r[1].text)

    def test_xpath_extensions_nodes_append(self):
        def f(evaluator, nodes):
            r = etree.SubElement(nodes[0], 'results')
            b = etree.SubElement(r, 'result')
            b.text = 'Hoi'
            b = etree.SubElement(r, 'result')
            b.text = 'Dag'
            return r

        x = self.parse('<a/>')
        e = etree.XPathEvaluator(x, extensions=[{(None, 'foo'): f}])
        r = e("foo(/*)/result")
        self.assertEqual(2, len(r))
        self.assertEqual('Hoi', r[0].text)
        self.assertEqual('Dag', r[1].text)

    def test_xpath_extensions_nodes_append2(self):
        def f(evaluator, nodes):
            r = etree.Element('results')
            b = etree.SubElement(r, 'result')
            b.text = 'Hoi'
            b = etree.SubElement(r, 'result')
            b.text = 'Dag'
            r.append(nodes[0])
            return r

        x = self.parse('<result>Honk</result>')
        e = etree.XPathEvaluator(x, extensions=[{(None, 'foo'): f}])
        r = e("foo(/*)/result")
        self.assertEqual(3, len(r))
        self.assertEqual('Hoi',  r[0].text)
        self.assertEqual('Dag',  r[1].text)
        self.assertEqual('Honk', r[2].text)

    def test_xpath_context_node(self):
        tree = self.parse('<root><a/><b><c/></b></root>')

        check_call = []
        def check_context(ctxt, nodes):
            self.assertEqual(len(nodes), 1)
            check_call.append(nodes[0].tag)
            self.assertEqual(ctxt.context_node, nodes[0])
            return True

        find = etree.XPath("//*[p:foo(.)]",
                           namespaces={'p' : 'ns'},
                           extensions=[{('ns', 'foo') : check_context}])
        find(tree)

        check_call.sort()
        self.assertEqual(check_call, ["a", "b", "c", "root"])

    def test_xpath_eval_context_propagation(self):
        tree = self.parse('<root><a/><b><c/></b></root>')

        check_call = {}
        def check_context(ctxt, nodes):
            self.assertEqual(len(nodes), 1)
            tag = nodes[0].tag
            # empty during the "b" call, a "b" during the "c" call
            check_call[tag] = ctxt.eval_context.get("b")
            ctxt.eval_context[tag] = tag
            return True

        find = etree.XPath("//b[p:foo(.)]/c[p:foo(.)]",
                           namespaces={'p' : 'ns'},
                           extensions=[{('ns', 'foo') : check_context}])
        result = find(tree)

        self.assertEqual(result, [tree.getroot()[1][0]])
        self.assertEqual(check_call, {'b':None, 'c':'b'})

    def test_xpath_eval_context_clear(self):
        tree = self.parse('<root><a/><b><c/></b></root>')

        check_call = {}
        def check_context(ctxt):
            check_call["done"] = True
            # context must be empty for each new evaluation
            self.assertEqual(len(ctxt.eval_context), 0)
            ctxt.eval_context["test"] = True
            return True

        find = etree.XPath("//b[p:foo()]",
                           namespaces={'p' : 'ns'},
                           extensions=[{('ns', 'foo') : check_context}])
        result = find(tree)

        self.assertEqual(result, [tree.getroot()[1]])
        self.assertEqual(check_call["done"], True)

        check_call.clear()
        find = etree.XPath("//b[p:foo()]",
                           namespaces={'p' : 'ns'},
                           extensions=[{('ns', 'foo') : check_context}])
        result = find(tree)

        self.assertEqual(result, [tree.getroot()[1]])
        self.assertEqual(check_call["done"], True)

    def test_xpath_variables(self):
        x = self.parse('<a attr="true"/>')
        e = etree.XPathEvaluator(x)

        expr = "/a[@attr=$aval]"
        r = e(expr, aval=1)
        self.assertEqual(0, len(r))

        r = e(expr, aval="true")
        self.assertEqual(1, len(r))
        self.assertEqual("true", r[0].get('attr'))

        r = e(expr, aval=True)
        self.assertEqual(1, len(r))
        self.assertEqual("true", r[0].get('attr'))

    def test_xpath_variables_nodeset(self):
        x = self.parse('<a attr="true"/>')
        e = etree.XPathEvaluator(x)

        element = etree.Element("test-el")
        etree.SubElement(element, "test-sub")
        expr = "$value"
        r = e(expr, value=element)
        self.assertEqual(1, len(r))
        self.assertEqual(element.tag, r[0].tag)
        self.assertEqual(element[0].tag, r[0][0].tag)

    def test_xpath_extensions_mix(self):
        x = self.parse('<a attr="true"><test/></a>')

        class LocalException(Exception):
            pass

        def foo(evaluator, a, varval):
            etree.Element("DUMMY")
            if varval == 0:
                raise LocalException
            elif varval == 1:
                return ()
            elif varval == 2:
                return None
            elif varval == 3:
                return a[0][0]
            a = a[0]
            if a.get("attr") == str(varval):
                return a
            else:
                return etree.Element("NODE")

        extension = {(None, 'foo'): foo}
        e = etree.XPathEvaluator(x, extensions=[extension])
        del x

        self.assertRaises(LocalException, e, "foo(., 0)")
        self.assertRaises(LocalException, e, "foo(., $value)", value=0)

        r = e("foo(., $value)", value=1)
        self.assertEqual(len(r), 0)

        r = e("foo(.,  1)")
        self.assertEqual(len(r), 0)

        r = e("foo(., $value)", value=2)
        self.assertEqual(len(r), 0)

        r = e("foo(., $value)", value=3)
        self.assertEqual(len(r), 1)
        self.assertEqual(r[0].tag, "test")

        r = e("foo(., $value)", value="false")
        self.assertEqual(len(r), 1)
        self.assertEqual(r[0].tag, "NODE")

        r = e("foo(., 'false')")
        self.assertEqual(len(r), 1)
        self.assertEqual(r[0].tag, "NODE")

        r = e("foo(., 'true')")
        self.assertEqual(len(r), 1)
        self.assertEqual(r[0].tag, "a")
        self.assertEqual(r[0][0].tag, "test")

        r = e("foo(., $value)", value="true")
        self.assertEqual(len(r), 1)
        self.assertEqual(r[0].tag, "a")

        self.assertRaises(LocalException, e, "foo(., 0)")
        self.assertRaises(LocalException, e, "foo(., $value)", value=0)


class ETreeXPathClassTestCase(HelperTestCase):
    "Tests for the XPath class"
    def test_xpath_compile_doc(self):
        x = self.parse('<a attr="true"/>')

        expr = etree.XPath("/a[@attr != 'true']")
        r = expr(x)
        self.assertEqual(0, len(r))

        expr = etree.XPath("/a[@attr = 'true']")
        r = expr(x)
        self.assertEqual(1, len(r))

        expr = etree.XPath( expr.path )
        r = expr(x)
        self.assertEqual(1, len(r))

    def test_xpath_compile_element(self):
        x = self.parse('<a><b/><c/></a>')
        root = x.getroot()

        expr = etree.XPath("./b")
        r = expr(root)
        self.assertEqual(1, len(r))
        self.assertEqual('b', r[0].tag)

        expr = etree.XPath("./*")
        r = expr(root)
        self.assertEqual(2, len(r))

    def test_xpath_compile_vars(self):
        x = self.parse('<a attr="true"/>')

        expr = etree.XPath("/a[@attr=$aval]")
        r = expr(x, aval=False)
        self.assertEqual(0, len(r))

        r = expr(x, aval=True)
        self.assertEqual(1, len(r))

    def test_xpath_compile_error(self):
        self.assertRaises(SyntaxError, etree.XPath, '\\fad')

    def test_xpath_elementtree_error(self):
        self.assertRaises(ValueError, etree.XPath('*'), etree.ElementTree())


class ETreeXPathExsltTestCase(HelperTestCase):
    "Tests for the EXSLT support in XPath (requires libxslt 1.1.25+)"

    NSMAP = dict(
        date = "http://exslt.org/dates-and-times",
        math = "http://exslt.org/math",
        set  = "http://exslt.org/sets",
        str  = "http://exslt.org/strings",
        )

    def test_xpath_exslt_functions_date(self):
        tree = self.parse('<a><b>2009-11-12</b><b>2008-12-11</b></a>')

        match_dates = tree.xpath('//b[date:year(string()) = 2009]',
                                 namespaces=self.NSMAP)
        self.assertTrue(match_dates, str(match_dates))
        self.assertEqual(len(match_dates), 1, str(match_dates))
        self.assertEqual(match_dates[0].text, '2009-11-12')

    def test_xpath_exslt_functions_strings(self):
        tree = self.parse('<a><b>2009-11-12</b><b>2008-12-11</b></a>')

        aligned_date = tree.xpath(
            'str:align(string(//b[1]), "%s", "center")' % ('-'*20),
            namespaces=self.NSMAP)
        self.assertTrue(aligned_date, str(aligned_date))
        self.assertEqual(aligned_date, '-----2009-11-12-----')


class ETreeETXPathClassTestCase(HelperTestCase):
    "Tests for the ETXPath class"
    def test_xpath_compile_ns(self):
        x = self.parse('<a><b xmlns="nsa"/><b xmlns="nsb"/></a>')

        expr = etree.ETXPath("/a/{nsa}b")
        r = expr(x)
        self.assertEqual(1, len(r))
        self.assertEqual('{nsa}b', r[0].tag)

        expr = etree.ETXPath("/a/{nsb}b")
        r = expr(x)
        self.assertEqual(1, len(r))
        self.assertEqual('{nsb}b', r[0].tag)

    # disabled this test as non-ASCII characters in namespace URIs are
    # not acceptable
    def _test_xpath_compile_unicode(self):
        x = self.parse(_bytes('<a><b xmlns="http://nsa/\\uf8d2"/><b xmlns="http://nsb/\\uf8d1"/></a>'
                              ).decode("unicode_escape"))

        expr = etree.ETXPath(_bytes("/a/{http://nsa/\\uf8d2}b").decode("unicode_escape"))
        r = expr(x)
        self.assertEqual(1, len(r))
        self.assertEqual(_bytes('{http://nsa/\\uf8d2}b').decode("unicode_escape"), r[0].tag)

        expr = etree.ETXPath(_bytes("/a/{http://nsb/\\uf8d1}b").decode("unicode_escape"))
        r = expr(x)
        self.assertEqual(1, len(r))
        self.assertEqual(_bytes('{http://nsb/\\uf8d1}b').decode("unicode_escape"), r[0].tag)

SAMPLE_XML = etree.parse(BytesIO("""
<body>
  <tag>text</tag>
  <section>
    <tag>subtext</tag>
  </section>
  <tag />
  <tag />
</body>
"""))

def tag(elem):
    return elem.tag

def tag_or_value(elem):
    return getattr(elem, 'tag', elem)

def stringTest(ctxt, s1):
    return "Hello "+s1

def stringListTest(ctxt, s1):
    return ["Hello "] + list(s1) +  ["!"]
    
def floatTest(ctxt, f1):
    return f1+4

def booleanTest(ctxt, b1):
    return not b1
    
def setTest(ctxt, st1):
    return st1[0]
    
def setTest2(ctxt, st1):
    return st1[0:2]

def argsTest1(ctxt, s, f, b, st):
    return ", ".join(map(str, (s, f, b, list(map(tag, st)))))

def argsTest2(ctxt, st1, st2):
    st1.extend(st2)
    return st1

def resultTypesTest(ctxt):
    return [None,None]

def resultTypesTest2(ctxt):
    return resultTypesTest
    
uri = "http://www.example.com/"

extension = {(None, 'stringTest'): stringTest,
             (None, 'stringListTest'): stringListTest,
             (None, 'floatTest'): floatTest,
             (None, 'booleanTest'): booleanTest,
             (None, 'setTest'): setTest,
             (None, 'setTest2'): setTest2,
             (None, 'argsTest1'): argsTest1,
             (None, 'argsTest2'): argsTest2,
             (None, 'resultTypesTest'): resultTypesTest,
             (None, 'resultTypesTest2'): resultTypesTest2,}

def xpath():
    """
    Test xpath extension functions.
    
    >>> root = SAMPLE_XML
    >>> e = etree.XPathEvaluator(root, extensions=[extension])
    >>> e("stringTest('you')")
    'Hello you'
    >>> e(_bytes("stringTest('\\\\xe9lan')").decode("unicode_escape"))
    u'Hello \\xe9lan'
    >>> e("stringTest('you','there')")   #doctest: +ELLIPSIS
    Traceback (most recent call last):
    ...
    TypeError: stringTest() takes... 2 ...arguments ...
    >>> e("floatTest(2)")
    6.0
    >>> e("booleanTest(true())")
    False
    >>> list(map(tag, e("setTest(/body/tag)")))
    ['tag']
    >>> list(map(tag, e("setTest2(/body/*)")))
    ['tag', 'section']
    >>> list(map(tag_or_value, e("stringListTest(/body/tag)")))
    ['Hello ', 'tag', 'tag', 'tag', '!']
    >>> e("argsTest1('a',1.5,true(),/body/tag)")
    "a, 1.5, True, ['tag', 'tag', 'tag']"
    >>> list(map(tag, e("argsTest2(/body/tag, /body/section)")))
    ['tag', 'section', 'tag', 'tag']
    >>> e("resultTypesTest()")
    Traceback (most recent call last):
    ...
    XPathResultError: This is not a supported node-set result: None
    >>> try:
    ...     e("resultTypesTest2()")
    ... except etree.XPathResultError:
    ...     print("Got error")
    Got error
    """

if sys.version_info[0] >= 3:
    xpath.__doc__ = xpath.__doc__.replace(" u'", " '")
    xpath.__doc__ = xpath.__doc__.replace(" XPathResultError",
                                          " lxml.etree.XPathResultError")
    xpath.__doc__ = xpath.__doc__.replace(" exactly 2 arguments",
                                          " exactly 2 positional arguments")

def test_suite():
    suite = unittest.TestSuite()
    suite.addTests([unittest.makeSuite(ETreeXPathTestCase)])
    suite.addTests([unittest.makeSuite(ETreeXPathClassTestCase)])
    if etree.LIBXSLT_COMPILED_VERSION >= (1,1,25):
        suite.addTests([unittest.makeSuite(ETreeXPathExsltTestCase)])
    suite.addTests([unittest.makeSuite(ETreeETXPathClassTestCase)])
    suite.addTests([doctest.DocTestSuite()])
    suite.addTests(
        [make_doctest('../../../doc/xpathxslt.txt')])
    return suite

if __name__ == '__main__':
    print('to test use test.py %s' % __file__)