1
2
3 """
4 Test cases related to XPath evaluation and the XPath class
5 """
6
7 import unittest
8 from StringIO import StringIO
9
10 from common_imports import etree, HelperTestCase, doctest
11
13 """XPath tests etree"""
14
16 tree = self.parse('<a><b></b><b></b></a>')
17 self.assert_(tree.xpath('boolean(/a/b)'))
18 self.assert_(not tree.xpath('boolean(/a/c)'))
19
21 tree = self.parse('<a>1</a>')
22 self.assertEquals(1.,
23 tree.xpath('number(/a)'))
24 tree = self.parse('<a>A</a>')
25 actual = str(tree.xpath('number(/a)'))
26 expected = ['nan', '1.#qnan', 'nanq']
27 if not actual.lower() in expected:
28 self.fail('Expected a NAN value, got %s' % actual)
29
31 tree = self.parse('<a>Foo</a>')
32 self.assertEquals('Foo',
33 tree.xpath('string(/a/text())'))
34
36 tree = self.parse('<a><b/></a>')
37 self.assertEquals([],
38 tree.xpath('/'))
39
41 tree = self.parse('<a xmlns="test" xmlns:p="myURI"/>')
42 self.assert_((None, "test") in tree.xpath('namespace::*'))
43 self.assert_(('p', 'myURI') in tree.xpath('namespace::*'))
44
46 tree = self.parse('<a/>')
47 self.assertEquals([('xml', 'http://www.w3.org/XML/1998/namespace')],
48 tree.xpath('namespace::*'))
49
51 tree = self.parse('<a><b>Foo</b><b>Bar</b></a>')
52 root = tree.getroot()
53 self.assertEquals([root[0], root[1]],
54 tree.xpath('/a/b'))
55
57 tree = self.parse('<a><b/></a>')
58 self.assertEquals([],
59 tree.xpath('/a/c'))
60
61 self.assertEquals([],
62 tree.xpath('/a/c/text()'))
63
65 tree = self.parse('<a><b>Foo</b><b>Bar</b></a>')
66 root = tree.getroot()
67 self.assertEquals(['Foo', 'Bar'],
68 tree.xpath('/a/b/text()'))
69
71 tree = self.parse('<a><b>FooBar</b><b>BarFoo</b></a>')
72 root = tree.getroot()
73 self.assertEquals(['FooBar', 'BarFoo'],
74 tree.xpath('/a/b/text()'))
75 self.assertEquals([root[0], root[1]],
76 [r.getparent() for r in tree.xpath('/a/b/text()')])
77
79 xml = u'<a><b>FooBar\u0680\u3120</b><b>BarFoo\u0680\u3120</b></a>'
80 tree = self.parse(xml.encode('utf-8'))
81 root = tree.getroot()
82 self.assertEquals([u'FooBar\u0680\u3120', u'BarFoo\u0680\u3120'],
83 tree.xpath('/a/b/text()'))
84 self.assertEquals([root[0], root[1]],
85 [r.getparent() for r in tree.xpath('/a/b/text()')])
86
88 tree = self.parse('<a b="B" c="C"/>')
89 self.assertEquals(['B'],
90 tree.xpath('/a/@b'))
91
93 tree = self.parse('<a b="BaSdFgHjKl" c="CqWeRtZuI"/>')
94 results = tree.xpath('/a/@c')
95 self.assertEquals(1, len(results))
96 self.assertEquals('CqWeRtZuI', results[0])
97 self.assertEquals(tree.getroot().tag, results[0].getparent().tag)
98
103
105 root = etree.XML('<a><b><c/></b></a>')
106 el = root[0]
107 self.assert_(el.xpath('boolean(c)'))
108 self.assert_(not el.xpath('boolean(d)'))
109
111 tree = self.parse('<a><c><b>Foo</b><b>Bar</b></c><c><b>Hey</b></c></a>')
112 root = tree.getroot()
113 c = root[0]
114 self.assertEquals([c[0], c[1]],
115 c.xpath('b'))
116 self.assertEquals([c[0], c[1], root[1][0]],
117 c.xpath('//b'))
118
120 tree = self.parse('<a xmlns="uri:a"><b></b></a>')
121 root = tree.getroot()
122 self.assertEquals(
123 [root[0]],
124 tree.xpath('//foo:b', namespaces={'foo': 'uri:a'}))
125 self.assertEquals(
126 [],
127 tree.xpath('//foo:b', namespaces={'foo': 'uri:c'}))
128 self.assertEquals(
129 [root[0]],
130 root.xpath('//baz:b', namespaces={'baz': 'uri:a'}))
131
133 tree = self.parse('<a xmlns="uri:a"><b></b></a>')
134 root = tree.getroot()
135 self.assertRaises(
136 TypeError,
137 root.xpath, '//b', namespaces={None: 'uri:a'})
138
140 tree = self.parse('<a xmlns="uri:a"><b></b></a>')
141 root = tree.getroot()
142 self.assertRaises(
143 TypeError,
144 root.xpath, '//b', namespaces={'': 'uri:a'})
145
149
153
157
162
175
188
190 tree = self.parse('<a><b><c></c></b></a>')
191 e = etree.XPathEvaluator(tree)
192 root = tree.getroot()
193 self.assertEquals(
194 [root],
195 e.evaluate('//a'))
196
198 tree = self.parse('<a><b><c></c></b></a>')
199 child_tree = etree.ElementTree(tree.getroot()[0])
200 e = etree.XPathEvaluator(child_tree)
201 self.assertEquals(
202 [],
203 e.evaluate('a'))
204 root = child_tree.getroot()
205 self.assertEquals(
206 [root[0]],
207 e.evaluate('c'))
208
210 tree = self.parse('<a><b><c></c></b></a>')
211 child_tree = etree.ElementTree(tree.getroot()[0])
212 e = etree.XPathEvaluator(child_tree)
213 self.assertEquals(
214 [],
215 e.evaluate('/a'))
216 root = child_tree.getroot()
217 self.assertEquals(
218 [root],
219 e.evaluate('/b'))
220 self.assertEquals(
221 [],
222 e.evaluate('/c'))
223
231
233 def foo(evaluator, a):
234 return 'hello %s' % a
235 extension = {(None, 'foo'): foo}
236 tree = self.parse('<a><b></b></a>')
237 e = etree.XPathEvaluator(tree, extensions=[extension])
238 self.assertEquals(
239 "hello you", e.evaluate("foo('you')"))
240
242 def foo(evaluator, a, b):
243 return "hello %s and %s" % (a, b)
244 extension = {(None, 'foo'): foo}
245 tree = self.parse('<a><b></b></a>')
246 e = etree.XPathEvaluator(tree, extensions=[extension])
247 self.assertRaises(TypeError, e.evaluate, "foo('you')")
248
250 def foo(evaluator, a):
251 return 1/0
252 extension = {(None, 'foo'): foo}
253 tree = self.parse('<a/>')
254 e = etree.XPathEvaluator(tree, extensions=[extension])
255 self.assertRaises(ZeroDivisionError, e.evaluate, "foo('test')")
256
265
266 x = self.parse('<a/>')
267 e = etree.XPathEvaluator(x, extensions=[{(None, 'foo'): f}])
268 r = e.evaluate("foo('World')/result")
269 self.assertEquals(2, len(r))
270 self.assertEquals('Hoi', r[0].text)
271 self.assertEquals('Dag', r[1].text)
272
281
282 x = self.parse('<a/>')
283 e = etree.XPathEvaluator(x, extensions=[{(None, 'foo'): f}])
284 r = e.evaluate("foo(/*)/result")
285 self.assertEquals(2, len(r))
286 self.assertEquals('Hoi', r[0].text)
287 self.assertEquals('Dag', r[1].text)
288
298
299 x = self.parse('<result>Honk</result>')
300 e = etree.XPathEvaluator(x, extensions=[{(None, 'foo'): f}])
301 r = e.evaluate("foo(/*)/result")
302 self.assertEquals(3, len(r))
303 self.assertEquals('Hoi', r[0].text)
304 self.assertEquals('Dag', r[1].text)
305 self.assertEquals('Honk', r[2].text)
306
308 tree = self.parse('<root><a/><b><c/></b></root>')
309
310 check_call = []
311 def check_context(ctxt, nodes):
312 self.assertEquals(len(nodes), 1)
313 check_call.append(nodes[0].tag)
314 self.assertEquals(ctxt.context_node, nodes[0])
315 return True
316
317 find = etree.XPath("//*[p:foo(.)]",
318 namespaces={'p' : 'ns'},
319 extensions=[{('ns', 'foo') : check_context}])
320 find(tree)
321
322 check_call.sort()
323 self.assertEquals(check_call, ["a", "b", "c", "root"])
324
326 tree = self.parse('<root><a/><b><c/></b></root>')
327
328 check_call = {}
329 def check_context(ctxt, nodes):
330 self.assertEquals(len(nodes), 1)
331 tag = nodes[0].tag
332
333 check_call[tag] = ctxt.eval_context.get("b")
334 ctxt.eval_context[tag] = tag
335 return True
336
337 find = etree.XPath("//b[p:foo(.)]/c[p:foo(.)]",
338 namespaces={'p' : 'ns'},
339 extensions=[{('ns', 'foo') : check_context}])
340 result = find(tree)
341
342 self.assertEquals(result, [tree.getroot()[1][0]])
343 self.assertEquals(check_call, {'b':None, 'c':'b'})
344
346 tree = self.parse('<root><a/><b><c/></b></root>')
347
348 check_call = {}
349 def check_context(ctxt):
350 check_call["done"] = True
351
352 self.assertEquals(len(ctxt.eval_context), 0)
353 ctxt.eval_context["test"] = True
354 return True
355
356 find = etree.XPath("//b[p:foo()]",
357 namespaces={'p' : 'ns'},
358 extensions=[{('ns', 'foo') : check_context}])
359 result = find(tree)
360
361 self.assertEquals(result, [tree.getroot()[1]])
362 self.assertEquals(check_call["done"], True)
363
364 check_call.clear()
365 find = etree.XPath("//b[p:foo()]",
366 namespaces={'p' : 'ns'},
367 extensions=[{('ns', 'foo') : check_context}])
368 result = find(tree)
369
370 self.assertEquals(result, [tree.getroot()[1]])
371 self.assertEquals(check_call["done"], True)
372
374 x = self.parse('<a attr="true"/>')
375 e = etree.XPathEvaluator(x)
376
377 expr = "/a[@attr=$aval]"
378 r = e.evaluate(expr, aval=1)
379 self.assertEquals(0, len(r))
380
381 r = e.evaluate(expr, aval="true")
382 self.assertEquals(1, len(r))
383 self.assertEquals("true", r[0].get('attr'))
384
385 r = e.evaluate(expr, aval=True)
386 self.assertEquals(1, len(r))
387 self.assertEquals("true", r[0].get('attr'))
388
390 x = self.parse('<a attr="true"/>')
391 e = etree.XPathEvaluator(x)
392
393 element = etree.Element("test-el")
394 etree.SubElement(element, "test-sub")
395 expr = "$value"
396 r = e.evaluate(expr, value=element)
397 self.assertEquals(1, len(r))
398 self.assertEquals(element.tag, r[0].tag)
399 self.assertEquals(element[0].tag, r[0][0].tag)
400
402 x = self.parse('<a attr="true"><test/></a>')
403
404 class LocalException(Exception):
405 pass
406
407 def foo(evaluator, a, varval):
408 etree.Element("DUMMY")
409 if varval == 0:
410 raise LocalException
411 elif varval == 1:
412 return ()
413 elif varval == 2:
414 return None
415 elif varval == 3:
416 return a[0][0]
417 a = a[0]
418 if a.get("attr") == str(varval):
419 return a
420 else:
421 return etree.Element("NODE")
422
423 extension = {(None, 'foo'): foo}
424 e = etree.XPathEvaluator(x, extensions=[extension])
425 del x
426
427 self.assertRaises(LocalException, e.evaluate, "foo(., 0)")
428 self.assertRaises(LocalException, e.evaluate, "foo(., $value)", value=0)
429
430 r = e.evaluate("foo(., $value)", value=1)
431 self.assertEqual(len(r), 0)
432
433 r = e.evaluate("foo(., 1)")
434 self.assertEqual(len(r), 0)
435
436 r = e.evaluate("foo(., $value)", value=2)
437 self.assertEqual(len(r), 0)
438
439 r = e.evaluate("foo(., $value)", value=3)
440 self.assertEqual(len(r), 1)
441 self.assertEqual(r[0].tag, "test")
442
443 r = e.evaluate("foo(., $value)", value="false")
444 self.assertEqual(len(r), 1)
445 self.assertEqual(r[0].tag, "NODE")
446
447 r = e.evaluate("foo(., 'false')")
448 self.assertEqual(len(r), 1)
449 self.assertEqual(r[0].tag, "NODE")
450
451 r = e.evaluate("foo(., 'true')")
452 self.assertEqual(len(r), 1)
453 self.assertEqual(r[0].tag, "a")
454 self.assertEqual(r[0][0].tag, "test")
455
456 r = e.evaluate("foo(., $value)", value="true")
457 self.assertEqual(len(r), 1)
458 self.assertEqual(r[0].tag, "a")
459
460 self.assertRaises(LocalException, e.evaluate, "foo(., 0)")
461 self.assertRaises(LocalException, e.evaluate, "foo(., $value)", value=0)
462
463
465 "Tests for the XPath class"
467 x = self.parse('<a attr="true"/>')
468
469 expr = etree.XPath("/a[@attr != 'true']")
470 r = expr.evaluate(x)
471 self.assertEquals(0, len(r))
472
473 expr = etree.XPath("/a[@attr = 'true']")
474 r = expr.evaluate(x)
475 self.assertEquals(1, len(r))
476
477 expr = etree.XPath( expr.path )
478 r = expr.evaluate(x)
479 self.assertEquals(1, len(r))
480
482 x = self.parse('<a><b/><c/></a>')
483 root = x.getroot()
484
485 expr = etree.XPath("./b")
486 r = expr.evaluate(root)
487 self.assertEquals(1, len(r))
488 self.assertEquals('b', r[0].tag)
489
490 expr = etree.XPath("./*")
491 r = expr.evaluate(root)
492 self.assertEquals(2, len(r))
493
495 x = self.parse('<a attr="true"/>')
496
497 expr = etree.XPath("/a[@attr=$aval]")
498 r = expr.evaluate(x, aval=False)
499 self.assertEquals(0, len(r))
500
501 r = expr.evaluate(x, aval=True)
502 self.assertEquals(1, len(r))
503
505 self.assertRaises(SyntaxError, etree.XPath, '\\fad')
506
509
511 "Tests for the ETXPath class"
513 x = self.parse('<a><b xmlns="nsa"/><b xmlns="nsb"/></a>')
514
515 expr = etree.ETXPath("/a/{nsa}b")
516 r = expr.evaluate(x)
517 self.assertEquals(1, len(r))
518 self.assertEquals('{nsa}b', r[0].tag)
519
520 expr = etree.ETXPath("/a/{nsb}b")
521 r = expr.evaluate(x)
522 self.assertEquals(1, len(r))
523 self.assertEquals('{nsb}b', r[0].tag)
524
526 x = self.parse(u'<a><b xmlns="nsa\uf8d2"/><b xmlns="nsb\uf8d1"/></a>')
527
528 expr = etree.ETXPath(u"/a/{nsa\uf8d2}b")
529 r = expr.evaluate(x)
530 self.assertEquals(1, len(r))
531 self.assertEquals(u'{nsa\uf8d2}b', r[0].tag)
532
533 expr = etree.ETXPath(u"/a/{nsb\uf8d1}b")
534 r = expr.evaluate(x)
535 self.assertEquals(1, len(r))
536 self.assertEquals(u'{nsb\uf8d1}b', r[0].tag)
537
538 SAMPLE_XML = etree.parse(StringIO("""
539 <body>
540 <tag>text</tag>
541 <section>
542 <tag>subtext</tag>
543 </section>
544 <tag />
545 <tag />
546 </body>
547 """))
548
551
554
557
560
563
566
568 return ", ".join(map(str, (s, f, b, map(tag, st))))
569
571 st1.extend(st2)
572 return st1
573
576
579
580 uri = "http://www.example.com/"
581
582 extension = {(None, 'stringTest'): stringTest,
583 (None, 'floatTest'): floatTest,
584 (None, 'booleanTest'): booleanTest,
585 (None, 'setTest'): setTest,
586 (None, 'setTest2'): setTest2,
587 (None, 'argsTest1'): argsTest1,
588 (None, 'argsTest2'): argsTest2,
589 (None, 'resultTypesTest'): resultTypesTest,
590 (None, 'resultTypesTest2'): resultTypesTest2,}
591
593 """
594 Test xpath extension functions.
595
596 >>> root = SAMPLE_XML
597 >>> e = etree.XPathEvaluator(root, extensions=[extension])
598 >>> e.evaluate("stringTest('you')")
599 'Hello you'
600 >>> e.evaluate(u"stringTest('\xe9lan')")
601 u'Hello \\xe9lan'
602 >>> e.evaluate("stringTest('you','there')")
603 Traceback (most recent call last):
604 ...
605 TypeError: stringTest() takes exactly 2 arguments (3 given)
606 >>> e.evaluate("floatTest(2)")
607 6.0
608 >>> e.evaluate("booleanTest(true())")
609 False
610 >>> map(tag, e.evaluate("setTest(/body/tag)"))
611 ['tag']
612 >>> map(tag, e.evaluate("setTest2(/body/*)"))
613 ['tag', 'section']
614 >>> e.evaluate("argsTest1('a',1.5,true(),/body/tag)")
615 "a, 1.5, True, ['tag', 'tag', 'tag']"
616 >>> map(tag, e.evaluate("argsTest2(/body/tag, /body/section)"))
617 ['tag', 'section', 'tag', 'tag']
618 >>> e.evaluate("resultTypesTest()")
619 Traceback (most recent call last):
620 ...
621 XPathResultError: This is not a node: 'x'
622 >>> try:
623 ... e.evaluate("resultTypesTest2()")
624 ... except etree.XPathResultError:
625 ... print "Got error"
626 Got error
627 """
628
630 suite = unittest.TestSuite()
631 suite.addTests([unittest.makeSuite(ETreeXPathTestCase)])
632 suite.addTests([unittest.makeSuite(ETreeXPathClassTestCase)])
633 suite.addTests([unittest.makeSuite(ETreeETXPathClassTestCase)])
634 suite.addTests([doctest.DocTestSuite()])
635 suite.addTests(
636 [doctest.DocFileSuite('../../../doc/xpathxslt.txt')])
637 return suite
638
639 if __name__ == '__main__':
640 print 'to test use test.py %s' % __file__
641