Edgewall Software

source: trunk/genshi/tests/path.py

Last change on this file was 1269, checked in by hodgestar, 10 years ago

Return correct value and properly namespaced attribute name when matching namespaced attributes with XPath expressions (fixes #572; thanks to Olemis Lang <olemis+trac@…> for bug report and suggestion for fix).

  • Property svn:eol-style set to native
File size: 26.9 KB
Line 
1# -*- coding: utf-8 -*-
2#
3# Copyright (C) 2006 Edgewall Software
4# All rights reserved.
5#
6# This software is licensed as described in the file COPYING, which
7# you should have received as part of this distribution. The terms
8# are also available at http://genshi.edgewall.org/wiki/License.
9#
10# This software consists of voluntary contributions made by many
11# individuals. For the exact contribution history, see the revision
12# history and logs, available at http://genshi.edgewall.org/log/.
13
14import doctest
15import unittest
16
17from genshi.core import Attrs, QName
18from genshi.input import XML
19from genshi.path import Path, PathParser, PathSyntaxError, GenericStrategy, \
20                        SingleStepStrategy, SimplePathStrategy
21
22
23class FakePath(Path):
24    def __init__(self, strategy):
25        self.strategy = strategy
26    def test(self, ignore_context = False):
27        return self.strategy.test(ignore_context)
28
29
30class PathTestCase(unittest.TestCase):
31
32    strategies = [GenericStrategy, SingleStepStrategy, SimplePathStrategy]
33
34    def test_error_no_absolute_path(self):
35        self.assertRaises(PathSyntaxError, Path, '/root')
36
37    def test_error_unsupported_axis(self):
38        self.assertRaises(PathSyntaxError, Path, '..')
39        self.assertRaises(PathSyntaxError, Path, 'parent::ma')
40
41    def test_1step(self):
42        xml = XML('<root><elem/></root>')
43        self._test_eval(
44            path = 'elem',
45            equiv = '<Path "child::elem">',
46            input = xml,
47            output = '<elem/>'
48        )
49        self._test_eval(
50            path = 'elem',
51            equiv = '<Path "child::elem">',
52            input = xml,
53            output = '<elem/>'
54        )
55        self._test_eval(
56            path = 'child::elem',
57            equiv = '<Path "child::elem">',
58            input = xml,
59            output = '<elem/>'
60        )
61        self._test_eval(
62            path = '//elem',
63            equiv = '<Path "descendant-or-self::elem">',
64            input = xml,
65            output = '<elem/>'
66        )
67        self._test_eval(
68            path = 'descendant::elem',
69            equiv = '<Path "descendant::elem">',
70            input = xml,
71            output = '<elem/>'
72        )
73
74    def test_1step_self(self):
75        xml = XML('<root><elem/></root>')
76        self._test_eval(
77            path = '.',
78            equiv = '<Path "self::node()">',
79            input = xml,
80            output = '<root><elem/></root>'
81        )
82        self._test_eval(
83            path = 'self::node()',
84            equiv = '<Path "self::node()">',
85            input = xml,
86            output = '<root><elem/></root>'
87        )
88
89    def test_1step_wildcard(self):
90        xml = XML('<root><elem/></root>')
91        self._test_eval(
92            path = '*',
93            equiv = '<Path "child::*">',
94            input = xml,
95            output = '<elem/>'
96        )
97        self._test_eval(
98            path = 'child::*',
99            equiv = '<Path "child::*">',
100            input = xml,
101            output = '<elem/>'
102        )
103        self._test_eval(
104            path = 'child::node()',
105            equiv = '<Path "child::node()">',
106            input = xml,
107            output = '<elem/>'
108        )
109        self._test_eval(
110            path = '//*',
111            equiv = '<Path "descendant-or-self::*">',
112            input = xml,
113            output = '<root><elem/></root>'
114        )
115
116    def test_1step_attribute(self):
117        self._test_eval(
118            path = '@foo',
119            equiv = '<Path "attribute::foo">',
120            input = XML('<root/>'),
121            output = ''
122        )
123        xml = XML('<root foo="bar"/>')
124        self._test_eval(
125            path = '@foo',
126            equiv = '<Path "attribute::foo">',
127            input = xml,
128            output = 'bar'
129        )
130        self._test_eval(
131            path = './@foo',
132            equiv = '<Path "self::node()/attribute::foo">',
133            input = xml,
134            output = 'bar'
135        )
136
137    def test_1step_text(self):
138        xml = XML('<root>Hey</root>')
139        self._test_eval(
140            path = 'text()',
141            equiv = '<Path "child::text()">',
142            input = xml,
143            output = 'Hey'
144        )
145        self._test_eval(
146            path = './text()',
147            equiv = '<Path "self::node()/child::text()">',
148            input = xml,
149            output = 'Hey'
150        )
151        self._test_eval(
152            path = '//text()',
153            equiv = '<Path "descendant-or-self::text()">',
154            input = xml,
155            output = 'Hey'
156        )
157        self._test_eval(
158            path = './/text()',
159            equiv = '<Path "self::node()/descendant-or-self::node()/child::text()">',
160            input = xml,
161            output = 'Hey'
162        )
163
164    def test_2step(self):
165        xml = XML('<root><foo/><bar/></root>')
166        self._test_eval('*', input=xml, output='<foo/><bar/>')
167        self._test_eval('bar', input=xml, output='<bar/>')
168        self._test_eval('baz', input=xml, output='')
169
170    def test_2step_attribute(self):
171        xml = XML('<elem class="x"><span id="joe">Hey Joe</span></elem>')
172        self._test_eval('@*', input=xml, output='x')
173        self._test_eval('./@*', input=xml, output='x')
174        self._test_eval('.//@*', input=xml, output='xjoe')
175        self._test_eval('*/@*', input=xml, output='joe')
176
177        xml = XML('<elem><foo id="1"/><foo id="2"/></elem>')
178        self._test_eval('@*', input=xml, output='')
179        self._test_eval('foo/@*', input=xml, output='12')
180
181    def test_2step_complex(self):
182        xml = XML('<root><foo><bar/></foo></root>')
183        self._test_eval(
184            path = 'foo/bar',
185            equiv = '<Path "child::foo/child::bar">',
186            input = xml,
187            output = '<bar/>'
188        )
189        self._test_eval(
190            path = './bar',
191            equiv = '<Path "self::node()/child::bar">',
192            input = xml,
193            output = ''
194        )
195        self._test_eval(
196            path = 'foo/*',
197            equiv = '<Path "child::foo/child::*">',
198            input = xml,
199            output = '<bar/>'
200        )
201        xml = XML('<root><foo><bar id="1"/></foo><bar id="2"/></root>')
202        self._test_eval(
203            path = './bar',
204            equiv = '<Path "self::node()/child::bar">',
205            input = xml,
206            output = '<bar id="2"/>'
207        )
208        xml = XML('''<table>
209            <tr><td>1</td><td>One</td></tr>
210            <tr><td>2</td><td>Two</td></tr>
211        </table>''')
212        self._test_eval(
213            path = 'tr/td[1]',
214            input = xml,
215            output = '<td>1</td><td>2</td>'
216        )
217        xml = XML('''<ul>
218            <li>item1
219                <ul><li>subitem11</li></ul>
220            </li>
221            <li>item2
222                <ul><li>subitem21</li></ul>
223            </li>
224        </ul>''')
225        self._test_eval(
226            path = 'li[2]/ul',
227            input = xml,
228            output = '<ul><li>subitem21</li></ul>'
229        )
230
231    def test_2step_text(self):
232        xml = XML('<root><item>Foo</item></root>')
233        self._test_eval(
234            path = 'item/text()',
235            equiv = '<Path "child::item/child::text()">',
236            input = xml,
237            output = 'Foo'
238        )
239        self._test_eval(
240            path = '*/text()',
241            equiv = '<Path "child::*/child::text()">',
242            input = xml,
243            output = 'Foo'
244        )
245        self._test_eval(
246            path = '//text()',
247            equiv = '<Path "descendant-or-self::text()">',
248            input = xml,
249            output = 'Foo'
250        )
251        self._test_eval(
252            path = './text()',
253            equiv = '<Path "self::node()/child::text()">',
254            input = xml,
255            output = ''
256        )
257        xml = XML('<root><item>Foo</item><item>Bar</item></root>')
258        self._test_eval(
259            path = 'item/text()',
260            equiv = '<Path "child::item/child::text()">',
261            input = xml,
262            output = 'FooBar'
263        )
264        xml = XML('<root><item><name>Foo</name><sub><name>Bar</name></sub></item></root>') 
265        self._test_eval(
266            path = 'item/name/text()',
267            equiv = '<Path "child::item/child::name/child::text()">',
268            input = xml,
269            output = 'Foo'
270        )
271
272    def test_3step(self):
273        xml = XML('<root><foo><bar/></foo></root>')
274        self._test_eval(
275            path = 'foo/*',
276            equiv = '<Path "child::foo/child::*">',
277            input = xml,
278            output = '<bar/>'
279        )
280
281    def test_3step_complex(self):
282        self._test_eval(
283            path = '*/bar',
284            equiv = '<Path "child::*/child::bar">',
285            input = XML('<root><foo><bar/></foo></root>'),
286            output = '<bar/>'
287        )
288        self._test_eval(
289            path = '//bar',
290            equiv = '<Path "descendant-or-self::bar">',
291            input = XML('<root><foo><bar id="1"/></foo><bar id="2"/></root>'),
292            output = '<bar id="1"/><bar id="2"/>'
293        )
294
295    def test_3step_complex_text(self):
296        xml = XML('<root><item><bar>Some text </bar><baz><bar>in here.</bar></baz></item></root>')
297        self._test_eval(
298            path = 'item/bar/text()',
299            equiv = '<Path "child::item/child::bar/child::text()">',
300            input = xml,
301            output = 'Some text '
302        )
303        self._test_eval(
304            path = 'item//bar/text()',
305            equiv = '<Path "child::item/descendant-or-self::node()/child::bar/child::text()">',
306            input = xml,
307            output = 'Some text in here.'
308        )
309
310    def test_node_type_comment(self):
311        xml = XML('<root><!-- commented --></root>')
312        self._test_eval(
313            path = 'comment()',
314            equiv = '<Path "child::comment()">',
315            input = xml,
316            output = '<!-- commented -->'
317        )
318
319    def test_node_type_text(self):
320        xml = XML('<root>Some text <br/>in here.</root>')
321        self._test_eval(
322            path = 'text()',
323            equiv = '<Path "child::text()">',
324            input = xml,
325            output = 'Some text in here.'
326        )
327
328    def test_node_type_node(self):
329        xml = XML('<root>Some text <br/>in here.</root>')
330        self._test_eval(
331            path = 'node()',
332            equiv = '<Path "child::node()">',
333            input = xml,
334            output = 'Some text <br/>in here.'
335        )
336
337    def test_node_type_processing_instruction(self):
338        xml = XML('<?python x = 2 * 3 ?><root><?php echo("x") ?></root>')
339        self._test_eval(
340            path = '//processing-instruction()',
341            equiv = '<Path "descendant-or-self::processing-instruction()">',
342            input = xml,
343            output = '<?python x = 2 * 3 ?><?php echo("x") ?>'
344        )
345        self._test_eval(
346            path = 'processing-instruction()',
347            equiv = '<Path "child::processing-instruction()">',
348            input = xml,
349            output = '<?php echo("x") ?>'
350        )
351        self._test_eval(
352            path = 'processing-instruction("php")',
353            equiv = '<Path "child::processing-instruction(\"php\")">',
354            input = xml,
355            output = '<?php echo("x") ?>'
356        )
357
358    def test_simple_union(self):
359        xml = XML("""<body>1<br />2<br />3<br /></body>""")
360        self._test_eval(
361            path = '*|text()',
362            equiv = '<Path "child::*|child::text()">',
363            input = xml,
364            output = '1<br/>2<br/>3<br/>'
365        )
366
367    def test_predicate_name(self):
368        xml = XML('<root><foo/><bar/></root>')
369        self._test_eval('*[name()="foo"]', input=xml, output='<foo/>')
370
371    def test_predicate_localname(self):
372        xml = XML('<root><foo xmlns="NS"/><bar/></root>')
373        self._test_eval('*[local-name()="foo"]', input=xml,
374                              output='<foo xmlns="NS"/>')
375
376    def test_predicate_namespace(self):
377        xml = XML('<root><foo xmlns="NS"/><bar/></root>')
378        self._test_eval('*[namespace-uri()="NS"]', input=xml,
379                                output='<foo xmlns="NS"/>')
380
381    def test_predicate_not_name(self):
382        xml = XML('<root><foo/><bar/></root>')
383        self._test_eval('*[not(name()="foo")]', input=xml,
384                              output='<bar/>')
385
386    def test_predicate_attr(self):
387        xml = XML('<root><item/><item important="very"/></root>')
388        self._test_eval('item[@important]', input=xml,
389                              output='<item important="very"/>')
390        self._test_eval('item[@important="very"]', input=xml,
391                              output='<item important="very"/>')
392
393    def test_predicate_attr_equality(self):
394        xml = XML('<root><item/><item important="notso"/></root>')
395        self._test_eval('item[@important="very"]', input=xml, output='')
396        self._test_eval('item[@important!="very"]', input=xml,
397                              output='<item/><item important="notso"/>')
398
399    def test_predicate_attr_greater_than(self):
400        xml = XML('<root><item priority="3"/></root>')
401        self._test_eval('item[@priority>3]', input=xml, output='')
402        self._test_eval('item[@priority>2]', input=xml,
403                              output='<item priority="3"/>')
404
405    def test_predicate_attr_less_than(self):
406        xml = XML('<root><item priority="3"/></root>')
407        self._test_eval('item[@priority<3]', input=xml, output='')
408        self._test_eval('item[@priority<4]', input=xml,
409                              output='<item priority="3"/>')
410
411    def test_predicate_attr_and(self):
412        xml = XML('<root><item/><item important="very"/></root>')
413        self._test_eval('item[@important and @important="very"]',
414                                input=xml, output='<item important="very"/>')
415        self._test_eval('item[@important and @important="notso"]',
416                                input=xml, output='')
417
418    def test_predicate_attr_or(self):
419        xml = XML('<root><item/><item important="very"/></root>')
420        self._test_eval('item[@urgent or @important]', input=xml,
421                              output='<item important="very"/>')
422        self._test_eval('item[@urgent or @notso]', input=xml, output='')
423
424    def test_predicate_boolean_function(self):
425        xml = XML('<root><foo>bar</foo></root>')
426        self._test_eval('*[boolean("")]', input=xml, output='')
427        self._test_eval('*[boolean("yo")]', input=xml,
428                              output='<foo>bar</foo>')
429        self._test_eval('*[boolean(0)]', input=xml, output='')
430        self._test_eval('*[boolean(42)]', input=xml,
431                              output='<foo>bar</foo>')
432        self._test_eval('*[boolean(false())]', input=xml, output='')
433        self._test_eval('*[boolean(true())]', input=xml,
434                              output='<foo>bar</foo>')
435
436    def test_predicate_ceil_function(self):
437        xml = XML('<root><foo>bar</foo></root>')
438        self._test_eval('*[ceiling("4.5")=5]', input=xml,
439                              output='<foo>bar</foo>')
440
441    def test_predicate_concat_function(self):
442        xml = XML('<root><foo>bar</foo></root>')
443        self._test_eval('*[name()=concat("f", "oo")]', input=xml,
444                              output='<foo>bar</foo>')
445
446    def test_predicate_contains_function(self):
447        xml = XML('<root><foo>bar</foo></root>')
448        self._test_eval('*[contains(name(), "oo")]', input=xml,
449                              output='<foo>bar</foo>')
450
451    def test_predicate_matches_function(self):
452        xml = XML('<root><foo>bar</foo><bar>foo</bar></root>')
453        self._test_eval('*[matches(name(), "foo|bar")]', input=xml,
454                              output='<foo>bar</foo><bar>foo</bar>')
455
456    def test_predicate_false_function(self):
457        xml = XML('<root><foo>bar</foo></root>')
458        self._test_eval('*[false()]', input=xml, output='')
459
460    def test_predicate_floor_function(self):
461        xml = XML('<root><foo>bar</foo></root>')
462        self._test_eval('*[floor("4.5")=4]', input=xml,
463                              output='<foo>bar</foo>')
464
465    def test_predicate_normalize_space_function(self):
466        xml = XML('<root><foo>bar</foo></root>')
467        self._test_eval('*[normalize-space(" foo   bar  ")="foo bar"]',
468                                input=xml, output='<foo>bar</foo>')
469
470    def test_predicate_number_function(self):
471        xml = XML('<root><foo>bar</foo></root>')
472        self._test_eval('*[number("3.0")=3]', input=xml,
473                              output='<foo>bar</foo>')
474        self._test_eval('*[number("3.0")=3.0]', input=xml,
475                              output='<foo>bar</foo>')
476        self._test_eval('*[number("0.1")=.1]', input=xml,
477                              output='<foo>bar</foo>')
478
479    def test_predicate_round_function(self):
480        xml = XML('<root><foo>bar</foo></root>')
481        self._test_eval('*[round("4.4")=4]', input=xml,
482                              output='<foo>bar</foo>')
483        self._test_eval('*[round("4.6")=5]', input=xml,
484                              output='<foo>bar</foo>')
485
486    def test_predicate_starts_with_function(self):
487        xml = XML('<root><foo>bar</foo></root>')
488        self._test_eval('*[starts-with(name(), "f")]', input=xml,
489                              output='<foo>bar</foo>')
490        self._test_eval('*[starts-with(name(), "b")]', input=xml,
491                              output='')
492
493    def test_predicate_string_length_function(self):
494        xml = XML('<root><foo>bar</foo></root>')
495        self._test_eval('*[string-length(name())=3]', input=xml,
496                              output='<foo>bar</foo>')
497
498    def test_predicate_substring_function(self):
499        xml = XML('<root><foo>bar</foo></root>')
500        self._test_eval('*[substring(name(), 1)="oo"]', input=xml,
501                              output='<foo>bar</foo>')
502        self._test_eval('*[substring(name(), 1, 1)="o"]', input=xml,
503                              output='<foo>bar</foo>')
504
505    def test_predicate_substring_after_function(self):
506        xml = XML('<root><foo>bar</foo></root>')
507        self._test_eval('*[substring-after(name(), "f")="oo"]', input=xml,
508                                output='<foo>bar</foo>')
509
510    def test_predicate_substring_before_function(self):
511        xml = XML('<root><foo>bar</foo></root>')
512        self._test_eval('*[substring-before(name(), "oo")="f"]',
513                                input=xml, output='<foo>bar</foo>')
514
515    def test_predicate_translate_function(self):
516        xml = XML('<root><foo>bar</foo></root>')
517        self._test_eval('*[translate(name(), "fo", "ba")="baa"]',
518                                input=xml, output='<foo>bar</foo>')
519
520    def test_predicate_true_function(self):
521        xml = XML('<root><foo>bar</foo></root>')
522        self._test_eval('*[true()]', input=xml, output='<foo>bar</foo>')
523
524    def test_predicate_variable(self):
525        xml = XML('<root><foo>bar</foo></root>')
526        self._test_eval(
527            path = '*[name()=$bar]',
528            input = xml,
529            output = '<foo>bar</foo>',
530            variables = {'bar': 'foo'}
531        )
532
533    def test_predicate_position(self):
534        xml = XML('<root><foo id="a1"/><foo id="a2"/><foo id="a3"/></root>')
535        self._test_eval('*[2]', input=xml, output='<foo id="a2"/>')
536
537    def test_predicate_attr_and_position(self):
538        xml = XML('<root><foo/><foo id="a1"/><foo id="a2"/></root>')
539        self._test_eval('*[@id][2]', input=xml, output='<foo id="a2"/>')
540
541    def test_predicate_position_and_attr(self):
542        xml = XML('<root><foo/><foo id="a1"/><foo id="a2"/></root>')
543        self._test_eval('*[1][@id]', input=xml, output='')
544        self._test_eval('*[2][@id]', input=xml, output='<foo id="a1"/>')
545
546    def test_predicate_advanced_position(self):
547        xml = XML('<root><a><b><c><d><e/></d></c></b></a></root>')
548        self._test_eval(   'descendant-or-self::*/'
549                                'descendant-or-self::*/'
550                                'descendant-or-self::*[2]/'
551                                'self::*/descendant::*[3]', input=xml,
552                                output='<d><e/></d>')
553
554    def test_predicate_child_position(self):
555        xml = XML('\
556<root><a><b>1</b><b>2</b><b>3</b></a><a><b>4</b><b>5</b></a></root>')
557        self._test_eval('//a/b[2]', input=xml, output='<b>2</b><b>5</b>')
558        self._test_eval('//a/b[3]', input=xml, output='<b>3</b>')
559
560    def test_name_with_namespace(self):
561        xml = XML('<root xmlns:f="FOO"><f:foo>bar</f:foo></root>')
562        self._test_eval(
563            path = 'f:foo',
564            equiv = '<Path "child::f:foo">',
565            input = xml,
566            output = '<foo xmlns="FOO">bar</foo>',
567            namespaces = {'f': 'FOO'}
568        )
569
570    def test_wildcard_with_namespace(self):
571        xml = XML('<root xmlns:f="FOO"><f:foo>bar</f:foo></root>')
572        self._test_eval(
573            path = 'f:*',
574            equiv = '<Path "child::f:*">',
575            input = xml,
576            output = '<foo xmlns="FOO">bar</foo>',
577            namespaces = {'f': 'FOO'}
578        )
579
580    def test_predicate_termination(self):
581        """
582        Verify that a patch matching the self axis with a predicate doesn't
583        cause an infinite loop. See <http://genshi.edgewall.org/ticket/82>.
584        """
585        xml = XML('<ul flag="1"><li>a</li><li>b</li></ul>')
586        self._test_eval('.[@flag="1"]/*', input=xml,
587                              output='<li>a</li><li>b</li>')
588
589        xml = XML('<ul flag="1"><li>a</li><li>b</li></ul>')
590        self._test_eval('.[@flag="0"]/*', input=xml, output='')
591
592    def test_attrname_with_namespace(self):
593        xml = XML('<root xmlns:f="FOO"><foo f:bar="baz"/></root>')
594        self._test_eval('foo[@f:bar]', input=xml,
595                              output='<foo xmlns:ns1="FOO" ns1:bar="baz"/>',
596                              namespaces={'f': 'FOO'})
597
598    def test_attrwildcard_with_namespace(self):
599        xml = XML('<root xmlns:f="FOO"><foo f:bar="baz"/></root>')
600        self._test_eval('foo[@f:*]', input=xml,
601                              output='<foo xmlns:ns1="FOO" ns1:bar="baz"/>',
602                              namespaces={'f': 'FOO'})
603
604    def test_self_and_descendant(self):
605        xml = XML('<root><foo/></root>')
606        self._test_eval('self::root', input=xml, output='<root><foo/></root>')
607        self._test_eval('self::foo', input=xml, output='')
608        self._test_eval('descendant::root', input=xml, output='')
609        self._test_eval('descendant::foo', input=xml, output='<foo/>')
610        self._test_eval('descendant-or-self::root', input=xml, 
611                                output='<root><foo/></root>')
612        self._test_eval('descendant-or-self::foo', input=xml, output='<foo/>')
613
614    def test_long_simple_paths(self):
615        xml = XML('<root><a><b><a><d><a><b><a><b><a><b><a><c>!'
616                    '</c></a></b></a></b></a></b></a></d></a></b></a></root>')
617        self._test_eval('//a/b/a/b/a/c', input=xml, output='<c>!</c>')
618        self._test_eval('//a/b/a/c', input=xml, output='<c>!</c>')
619        self._test_eval('//a/c', input=xml, output='<c>!</c>')
620        self._test_eval('//c', input=xml, output='<c>!</c>')
621        # Please note that a//b is NOT the same as a/descendant::b
622        # it is a/descendant-or-self::node()/b, which SimplePathStrategy
623        # does NOT support
624        self._test_eval('a/b/descendant::a/c', input=xml, output='<c>!</c>')
625        self._test_eval('a/b/descendant::a/d/descendant::a/c',
626                              input=xml, output='<c>!</c>')
627        self._test_eval('a/b/descendant::a/d/a/c', input=xml, output='')
628        self._test_eval('//d/descendant::b/descendant::b/descendant::b'
629                              '/descendant::c', input=xml, output='<c>!</c>')
630        self._test_eval('//d/descendant::b/descendant::b/descendant::b'
631                              '/descendant::b/descendant::c', input=xml,
632                              output='')
633
634    def test_attr_selection(self):
635        xml = XML('<root><foo bar="abc"></foo></root>')
636        path = Path('foo/@bar')
637        result = path.select(xml)
638        self.assertEqual(list(result), [
639            Attrs([(QName('bar'), u'abc')])
640        ])
641
642    def test_attr_selection_with_namespace(self):
643        xml = XML(
644            '<root xmlns:ns1="http://example.com">'
645            '<foo ns1:bar="abc"></foo>'
646            '</root>')
647        path = Path('foo/@ns1:bar')
648        result = path.select(xml, namespaces={'ns1': 'http://example.com'})
649        self.assertEqual(list(result), [
650            Attrs([(QName('http://example.com}bar'), u'abc')])
651        ])
652
653    def _test_support(self, strategy_class, text):
654        path = PathParser(text, None, -1).parse()[0]
655        return strategy_class.supports(path)
656
657    def test_simple_strategy_support(self):
658        self.assert_(self._test_support(SimplePathStrategy, 'a/b'))
659        self.assert_(self._test_support(SimplePathStrategy, 'self::a/b'))
660        self.assert_(self._test_support(SimplePathStrategy, 'descendant::a/b'))
661        self.assert_(self._test_support(SimplePathStrategy,
662                         'descendant-or-self::a/b'))
663        self.assert_(self._test_support(SimplePathStrategy, '//a/b'))
664        self.assert_(self._test_support(SimplePathStrategy, 'a/@b'))
665        self.assert_(self._test_support(SimplePathStrategy, 'a/text()'))
666
667        # a//b is a/descendant-or-self::node()/b
668        self.assert_(not self._test_support(SimplePathStrategy, 'a//b'))
669        self.assert_(not self._test_support(SimplePathStrategy, 'node()/@a'))
670        self.assert_(not self._test_support(SimplePathStrategy, '@a'))
671        self.assert_(not self._test_support(SimplePathStrategy, 'foo:bar'))
672        self.assert_(not self._test_support(SimplePathStrategy, 'a/@foo:bar'))
673
674    def _test_strategies(self, input, path, output,
675                         namespaces=None, variables=None):
676        for strategy in self.strategies:
677            if not strategy.supports(path):
678                continue
679            s = strategy(path)
680            rendered = FakePath(s).select(input, namespaces=namespaces,
681                                          variables=variables) \
682                                  .render(encoding=None)
683            msg = 'Bad render using %s strategy' % str(strategy)
684            msg += '\nExpected:\t%r' % output
685            msg += '\nRendered:\t%r' % rendered
686            self.assertEqual(output, rendered, msg)
687
688    def _test_eval(self, path, equiv=None, input=None, output='',
689                         namespaces=None, variables=None):
690        path = Path(path)
691        if equiv is not None:
692            self.assertEqual(equiv, repr(path))
693
694        if input is None:
695            return
696
697        rendered = path.select(input, namespaces=namespaces,
698                               variables=variables).render(encoding=None)
699        msg = 'Bad output using whole path'
700        msg += '\nExpected:\t%r' % output
701        msg += '\nRendered:\t%r' % rendered
702        self.assertEqual(output, rendered, msg)
703
704        if len(path.paths) == 1:
705            self._test_strategies(input, path.paths[0], output,
706                                  namespaces=namespaces, variables=variables)
707
708
709def suite():
710    suite = unittest.TestSuite()
711    suite.addTest(doctest.DocTestSuite(Path.__module__))
712    suite.addTest(unittest.makeSuite(PathTestCase, 'test'))
713    return suite
714
715
716if __name__ == '__main__':
717    unittest.main(defaultTest='suite')
Note: See TracBrowser for help on using the repository browser.